diff options
author | Harald Jensås <hjensas@redhat.com> | 2021-02-16 17:25:17 +0100 |
---|---|---|
committer | rabi <ramishra@redhat.com> | 2022-02-24 20:24:19 +0530 |
commit | 576e1d6f6247f633606b0f67fa0bb72274e1194a (patch) | |
tree | 5bba4e8e433718d96acb57d665824fe3fe35c7d4 | |
parent | cbe9be5e7625d374fc6cfbb63b7e3ef5335bf330 (diff) | |
download | heat-576e1d6f6247f633606b0f67fa0bb72274e1194a.tar.gz |
Use neutron client for server.addresses
The server.addresses (/servers/{server_id}/ips)
endpoint can contain stale data causing attribute
lookups to fail.
This change replaces the use of server.addresses
and instead uses the neutron client to list ports
with 'device_id' matching the server id.
Story: 2008632
Task: 41843
Related: RHBZ#1902230
Change-Id: I1b9293041f2ad92eac0e9bc9646e7b2d7c6f7fd0
(cherry picked from commit 45750c603ac9965afd5b4e53ecff65977b887b95)
-rw-r--r-- | heat/engine/resources/openstack/nova/server.py | 58 | ||||
-rw-r--r-- | heat/tests/openstack/nova/test_server.py | 145 |
2 files changed, 131 insertions, 72 deletions
diff --git a/heat/engine/resources/openstack/nova/server.py b/heat/engine/resources/openstack/nova/server.py index 0302514d9..7081c78c9 100644 --- a/heat/engine/resources/openstack/nova/server.py +++ b/heat/engine/resources/openstack/nova/server.py @@ -12,6 +12,7 @@ # under the License. import copy +import ipaddress from oslo_config import cfg from oslo_log import log as logging @@ -955,8 +956,8 @@ class Server(server_base.BaseServer, sh.SchedulerHintsMixin, return result def _get_live_networks(self, server, props): - reality_nets = self._add_attrs_for_address(server, - extend_networks=False) + reality_nets = self._get_server_addresses(server, + extend_networks=False) reality_net_ids = {} client_plugin = self.client_plugin('neutron') for net_key in reality_nets: @@ -1124,7 +1125,7 @@ class Server(server_base.BaseServer, sh.SchedulerHintsMixin, LOG.warning("Failed to fetch resource attributes: %s", ex) return - def _add_attrs_for_address(self, server, extend_networks=True): + def _get_server_addresses(self, server, extend_networks=True): """Adds port id, subnets and network attributes to addresses list. This method is used only for resolving attributes. @@ -1133,31 +1134,48 @@ class Server(server_base.BaseServer, sh.SchedulerHintsMixin, the net is returned without replacing name on id. """ - nets = copy.deepcopy(server.addresses) or {} - ifaces = server.interface_list() - ip_mac_mapping_on_port_id = dict(((iface.fixed_ips[0]['ip_address'], - iface.mac_addr), iface.port_id) - for iface in ifaces) - for net_name in nets: - for addr in nets[net_name]: - addr['port'] = ip_mac_mapping_on_port_id.get( - (addr['addr'], addr['OS-EXT-IPS-MAC:mac_addr'])) + nets = {} + ifaces = self.client('neutron').list_ports(device_id=server.id) + for port in ifaces['ports']: + net_label = self.client('neutron').list_networks( + id=port['network_id'])['networks'][0]['name'] + net = nets.setdefault(net_label, []) + for fixed_ip in port['fixed_ips']: + addr = {'addr': fixed_ip.get('ip_address'), + 'OS-EXT-IPS-MAC:mac_addr': port['mac_address'], + 'OS-EXT-IPS:type': 'fixed', + 'port': port['id']} + + try: + addr['version'] = ipaddress.ip_address( + addr['addr']).version, + except ValueError: + addr['version'] = None + + if addr['addr']: + fips = self.client('neutron').list_floatingips( + fixed_ip_address=addr['addr']) + for fip in fips['floatingips']: + net.append({ + 'addr': fip['floating_ip_address'], + 'version': addr['version'], + 'OS-EXT-IPS-MAC:mac_addr': port['mac_address'], + 'OS-EXT-IPS:type': 'floating', + 'port': None}) + # _get_live_networks() uses this method to get reality_nets. # We don't need to get subnets and network in that case. Only # do the external calls if extend_networks is true, i.e called # from _resolve_attribute() if not extend_networks: + net.append(addr) continue - try: - port = self.client('neutron').show_port( - addr['port'])['port'] - except Exception as ex: - addr['subnets'], addr['network'] = None, None - LOG.warning("Failed to fetch resource attributes: %s", ex) - continue + addr['subnets'] = self._get_subnets_attr(port['fixed_ips']) addr['network'] = self._get_network_attr(port['network_id']) + net.append(addr) + if extend_networks: return self._extend_networks(nets) else: @@ -1201,7 +1219,7 @@ class Server(server_base.BaseServer, sh.SchedulerHintsMixin, self.client_plugin().ignore_not_found(e) return '' if name == self.ADDRESSES: - return self._add_attrs_for_address(server) + return self._get_server_addresses(server) if name == self.NETWORKS_ATTR: return self._extend_networks(server.networks) if name == self.INSTANCE_NAME: diff --git a/heat/tests/openstack/nova/test_server.py b/heat/tests/openstack/nova/test_server.py index 47ab98963..42c40b57b 100644 --- a/heat/tests/openstack/nova/test_server.py +++ b/heat/tests/openstack/nova/test_server.py @@ -446,42 +446,48 @@ class ServersTest(common.HeatTestCase): ip='5.6.9.8'), create_fake_iface(port='1013', mac='fa:16:3e:8c:44:cc', - ip='10.13.12.13')] + ip='10.13.12.13', + subnet='private_subnet_id')] + ports = [dict(id=interfaces[0].port_id, + mac_address=interfaces[0].mac_addr, + fixed_ips=interfaces[0].fixed_ips, + network_id='public_id'), + dict(id=interfaces[1].port_id, + mac_address=interfaces[1].mac_addr, + fixed_ips=interfaces[1].fixed_ips, + network_id='public_id'), + dict(id=interfaces[2].port_id, + mac_address=interfaces[2].mac_addr, + fixed_ips=interfaces[2].fixed_ips, + network_id='private_id')] + public_net = dict(id='public_id', + name='public', + mtu=1500, + subnets=['public_subnet_id']) + private_net = dict(id='private_id', + name='private', + mtu=1500, + subnets=['private_subnet_id']) + private_subnet = dict(id='private_subnet_id', + name='private_subnet', + cidr='private_cidr', + allocation_pools=[{'start': 'start_addr', + 'end': 'end_addr'}], + gateway_ip='private_gateway', + network_id='private_id') self.patchobject(self.fc.servers, 'get', return_value=return_server) - self.patchobject(return_server, 'interface_list', - return_value=interfaces) + self.patchobject(neutronclient.Client, 'list_ports', + return_value={'ports': ports}) + self.patchobject(neutronclient.Client, 'list_networks', + side_effect=[{'networks': [public_net]}, + {'networks': [public_net]}, + {'networks': [private_net]}]) + self.patchobject(neutronclient.Client, 'list_floatingips', + return_value={'floatingips': []}) self.patchobject(self.fc.servers, 'tag_list', return_value=['test']) - - self.port_show.return_value = { - 'port': {'id': '1234', - 'network_id': 'the_network', - 'fixed_ips': [{ - 'ip_address': '4.5.6.7', - 'subnet_id': 'the_subnet'}] - } - } - subnet_dict = { - 'subnet': { - 'name': 'subnet_name', - 'cidr': '10.0.0.0/24', - 'allocation_pools': [{'start': '10.0.0.2', - 'end': u'10.0.0.254'}], - 'gateway_ip': '10.0.0.1', - 'id': 'the_subnet', - 'network_id': 'the_network' - } - } - network_dict = { - 'network': { - 'name': 'network_name', - 'mtu': 1500, - 'subnets': [subnet_dict['subnet']['id']], - 'id': 'the_network' - } - } - self.subnet_show.return_value = subnet_dict - self.network_show.return_value = network_dict + self.subnet_show.return_value = {'subnet': private_subnet} + self.network_show.return_value = {'network': private_net} public_ip = return_server.networks['public'][0] self.assertEqual('1234', @@ -498,9 +504,9 @@ class ServersTest(common.HeatTestCase): server.FnGetAtt('addresses')['private'][0]['port']) self.assertEqual(private_ip, server.FnGetAtt('addresses')['private'][0]['addr']) - self.assertEqual([subnet_dict['subnet']], + self.assertEqual([private_subnet], server.FnGetAtt('addresses')['private'][0]['subnets']) - self.assertEqual(network_dict['network'], + self.assertEqual(private_net, server.FnGetAtt('addresses')['private'][0]['network']) self.assertEqual(private_ip, server.FnGetAtt('networks')['private'][0]) @@ -521,21 +527,6 @@ class ServersTest(common.HeatTestCase): self.assertIsNone(server.FnGetAtt('tags')) self.assertEqual({}, server.FnGetAtt('os_collect_config')) - def test_server_network_subnet_address_attr_port_not_found(self): - return_server = self.fc.servers.list()[1] - server_name = 'network-subnet-attr-server' - server = self._create_test_server(return_server, server_name) - interfaces = [create_fake_iface(port='1234', - mac='fa:16:3e:8c:22:aa', - ip='4.5.6.7')] - self.patchobject(return_server, 'interface_list', - return_value=interfaces) - self.port_show.side_effect = neutron.exceptions.NotFound() - self.assertEqual(None, - server.FnGetAtt('addresses')['private'][0]['subnets']) - self.assertEqual(None, - server.FnGetAtt('addresses')['private'][0]['network']) - def test_server_create_metadata(self): stack_name = 'create_metadata_test_stack' self.patchobject(nova.NovaClientPlugin, 'client', @@ -642,10 +633,30 @@ class ServersTest(common.HeatTestCase): create_fake_iface(port='1013', mac='fa:16:3e:8c:44:cc', ip='10.13.12.13')] + ports = [dict(id=interfaces[0].port_id, + mac_address=interfaces[0].mac_addr, + fixed_ips=interfaces[0].fixed_ips, + network_id='public_id'), + dict(id=interfaces[1].port_id, + mac_address=interfaces[1].mac_addr, + fixed_ips=interfaces[1].fixed_ips, + network_id='public_id'), + dict(id=interfaces[2].port_id, + mac_address=interfaces[2].mac_addr, + fixed_ips=interfaces[2].fixed_ips, + network_id='private_id')] + public_net = dict(id='public_id', name='public') + private_net = dict(id='private_id', name='private') self.patchobject(self.fc.servers, 'get', return_value=return_server) - self.patchobject(return_server, 'interface_list', - return_value=interfaces) + self.patchobject(neutronclient.Client, 'list_ports', + return_value={'ports': ports}) + self.patchobject(neutronclient.Client, 'list_networks', + side_effect=[{'networks': [public_net]}, + {'networks': [public_net]}, + {'networks': [private_net]}]) + self.patchobject(neutronclient.Client, 'list_floatingips', + return_value={'floatingips': []}) self.patchobject(return_server, 'interface_detach') self.patchobject(return_server, 'interface_attach') @@ -2038,20 +2049,42 @@ class ServersTest(common.HeatTestCase): server.properties.data['networks'] = [{'network': 'public_id', 'fixed_ip': '5.6.9.8'}] + public_net = dict(id='public_id', name='public') + private_net = dict(id='private_id', name='private') iface0 = create_fake_iface(port='aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', net='public', ip='5.6.9.8', mac='fa:16:3e:8c:33:aa') + port0 = dict(id=iface0.port_id, + network_id=iface0.net_id, + mac_address=iface0.mac_addr, + fixed_ips=iface0.fixed_ips) iface1 = create_fake_iface(port='bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', net='public', ip='4.5.6.7', mac='fa:16:3e:8c:22:aa') + port1 = dict(id=iface1.port_id, + network_id=iface1.net_id, + mac_address=iface1.mac_addr, + fixed_ips=iface1.fixed_ips) iface2 = create_fake_iface(port='cccccccc-cccc-cccc-cccc-cccccccccccc', net='private', ip='10.13.12.13', mac='fa:16:3e:8c:44:cc') + port2 = dict(id=iface2.port_id, + network_id=iface2.net_id, + mac_address=iface2.mac_addr, + fixed_ips=iface2.fixed_ips) self.patchobject(return_server, 'interface_list', return_value=[iface0, iface1, iface2]) + self.patchobject(neutronclient.Client, 'list_ports', + return_value={'ports': [port0, port1, port2]}) + self.patchobject(neutronclient.Client, 'list_networks', + side_effect=[{'networks': [public_net]}, + {'networks': [public_net]}, + {'networks': [private_net]}]) + self.patchobject(neutronclient.Client, 'list_floatingips', + return_value={'floatingips': []}) self.patchobject(neutron.NeutronClientPlugin, 'find_resourceid_by_name_or_id', @@ -2674,6 +2707,14 @@ class ServersTest(common.HeatTestCase): self.patchobject(neutron.NeutronClientPlugin, 'find_resourceid_by_name_or_id', return_value=None) + self.patchobject(neutronclient.Client, 'list_ports', + return_value={'ports': [{'id': 'p_id', + 'name': 'p_name', + 'fixed_ips': [], + 'network_id': 'n_id'}]}) + self.patchobject(neutronclient.Client, 'list_networks', + return_value={'networks': [{'id': 'n_id', + 'name': 'empty_net'}]}) self.patchobject(self.fc.servers, 'get', return_value=return_server) self.patchobject(return_server, 'interface_list', return_value=[]) mock_detach = self.patchobject(return_server, 'interface_detach') |