Просмотр исходного кода

Make OpenStack FIP detach + visibility robust

Two related bugs surfaced by test_instance_methods on the CI run for
PR #328:

* ``remove_floating_ip`` was clearing port_id via
  ``Connection.network.update_ip(fip, port_id=None)``. Some
  openstacksdk versions strip ``None`` kwargs from the PUT body, which
  meant the Neutron-side disassociation never happened and the FIP
  stayed bound to the instance's port. Switch to a direct neutronclient
  ``update_floatingip(id, {'floatingip': {'port_id': None}})`` call so
  the wire payload explicitly carries ``port_id: null``. Also make
  remove_floating_ip a no-op (instead of an AttributeError) when the
  FIP id has already been deleted, which happens when a test's nested
  cleanup_actions detach + delete in sequence.

* ``_all_addresses`` was unioning Nova's view of ``server.addresses``
  with a live Neutron query. Nova's info_cache lags ~60s and isn't
  re-queried on server-show, so a FIP detached on the Neutron side
  still appeared in ``public_ips`` / ``private_ips`` until Nova caught
  up. Read only **fixed** IPs from Nova (filtering by
  ``OS-EXT-IPS:type``) and trust Neutron exclusively for floating IPs.
Nuwan Goonasekera 1 день назад
Родитель
Сommit
4138c6d49c
1 измененных файлов с 22 добавлено и 15 удалено
  1. 22 15
      cloudbridge/providers/openstack/resources.py

+ 22 - 15
cloudbridge/providers/openstack/resources.py

@@ -328,20 +328,21 @@ class OpenStackInstance(BaseInstance):
     def _all_addresses(self):
         """All IP addresses associated with this instance.
 
-        Combines the addresses Nova reports (via server.addresses /
-        ``_os_instance.networks``, populated from Nova's info_cache) with
-        any floating IPs Neutron currently has bound to the instance's
-        ports. Nova's info_cache is refreshed by a periodic task on a
-        ~60s cadence and is not re-synced on a plain server-show, so a
-        FIP attached via the Neutron API (as add_floating_ip does)
-        otherwise wouldn't show up until the next sync.
+        Nova's info_cache (which backs ``server.addresses``) is refreshed
+        by a periodic task on a ~60s cadence and is not re-queried on a
+        plain server-show. That makes it lag both ways: a FIP just
+        attached via Neutron won't appear, and a FIP just detached via
+        Neutron will still appear. So we deliberately read only fixed
+        IPs from Nova and ask Neutron live for the current floating IPs.
         """
         addrs = set()
-        for _, network_addrs in self._os_instance.networks.items():
-            for address in network_addrs:
-                addrs.add(address)
-        # Query Neutron for any floating IPs bound to this instance's
-        # ports — these may not yet be reflected in Nova's cached view.
+        for _, addr_list in self._os_instance.addresses.items():
+            for entry in addr_list:
+                if entry.get('OS-EXT-IPS:type') == 'floating':
+                    continue
+                ip = entry.get('addr') or entry.get('OS-EXT-IPS-MAC:addr')
+                if ip:
+                    addrs.add(ip)
         try:
             for port in self._provider.os_conn.network.ports(
                     device_id=self.id):
@@ -519,13 +520,19 @@ class OpenStackInstance(BaseInstance):
         Remove a floating IP address from this instance.
 
         Same rationale as add_floating_ip; the Nova action endpoint is
-        gone, so detach by clearing port_id on the Neutron FIP.
+        gone, so detach by clearing port_id on the Neutron FIP. We go
+        through neutronclient directly rather than openstacksdk
+        Connection.network.update_ip(...) because some openstacksdk
+        versions drop ``None`` kwargs from the PUT body, which leaves
+        port_id unchanged on the server side.
         """
         log.debug("Removing floating IP adress: %s", floating_ip)
         fip = (floating_ip if isinstance(floating_ip, OpenStackFloatingIP)
                else self._get_fip(floating_ip))
-        # pylint:disable=protected-access
-        self._provider.os_conn.network.update_ip(fip._ip, port_id=None)
+        if fip is None:
+            return
+        self._provider.neutron.update_floatingip(
+            fip.id, {'floatingip': {'port_id': None}})
 
     def add_vm_firewall(self, firewall):
         """