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 45750c603a)
This commit is contained in:
Harald Jensås 2021-02-16 17:25:17 +01:00
parent bd38beda66
commit 0937375643
2 changed files with 131 additions and 72 deletions

View File

@ -12,6 +12,7 @@
# under the License.
import copy
import ipaddress
from oslo_config import cfg
from oslo_log import log as logging
@ -948,8 +949,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:
@ -1117,7 +1118,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.
@ -1126,31 +1127,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:
@ -1194,7 +1212,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:

View File

@ -447,42 +447,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',
@ -499,9 +505,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])
@ -522,21 +528,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',
@ -643,10 +634,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')
@ -2041,20 +2052,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',
@ -2677,6 +2710,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')