diff --git a/heat/engine/resources/openstack/nova/server.py b/heat/engine/resources/openstack/nova/server.py index 4e3f73241d..c5cd227eba 100644 --- a/heat/engine/resources/openstack/nova/server.py +++ b/heat/engine/resources/openstack/nova/server.py @@ -638,13 +638,16 @@ class Server(server_base.BaseServer, sh.SchedulerHintsMixin, 'through the following expression: ``{get_attr: [, ' 'addresses, , 0, port]}``. The subnets may ' 'be obtained trough the following expression: ``{get_attr: ' - '[, addresses, , 0, subnets]}``.'), + '[, addresses, , 0, subnets]}``. ' + 'The network may be obtained through the following expression: ' + '``{get_attr: [, addresses, , 0, ' + 'network]}``.'), type=attributes.Schema.MAP, support_status=support.SupportStatus( version='11.0.0', status=support.SUPPORTED, - message=_('The attribute was extended to include subnets with ' - 'version 11.0.0.'), + message=_('The attribute was extended to include subnets and ' + 'network with version 11.0.0.'), previous_status=support.SupportStatus( status=support.SUPPORTED ) @@ -1072,11 +1075,9 @@ class Server(server_base.BaseServer, sh.SchedulerHintsMixin, return bdm_v2_list - def _get_port_subnets_attr(self, port_id): + def _get_subnets_attr(self, fixed_ips): subnets = [] try: - fixed_ips = self.client('neutron').show_port( - port_id)['port']['fixed_ips'] for fixed_ip in fixed_ips: if fixed_ip.get('subnet_id'): subnets.append(self.client('neutron').show_subnet( @@ -1086,8 +1087,15 @@ class Server(server_base.BaseServer, sh.SchedulerHintsMixin, return return subnets + def _get_network_attr(self, network_id): + try: + return self.client('neutron').show_network(network_id)['network'] + except Exception as ex: + LOG.warning("Failed to fetch resource attributes: %s", ex) + return + def _add_attrs_for_address(self, server, extend_networks=True): - """Method adds port id and subnets attributes to list of addresses. + """Adds port id, subnets and network attributes to addresses list. This method is used only for resolving attributes. :param server: The server resource @@ -1108,8 +1116,17 @@ class Server(server_base.BaseServer, sh.SchedulerHintsMixin, # 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 extend_networks: - addr['subnets'] = self._get_port_subnets_attr(addr['port']) + if not extend_networks: + 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']) if extend_networks: return self._extend_networks(nets) diff --git a/heat/tests/openstack/nova/test_server.py b/heat/tests/openstack/nova/test_server.py index ce4de32353..2731d301cd 100644 --- a/heat/tests/openstack/nova/test_server.py +++ b/heat/tests/openstack/nova/test_server.py @@ -253,6 +253,8 @@ class ServersTest(common.HeatTestCase): 'show_port') self.subnet_show = self.patchobject(neutronclient.Client, 'show_subnet') + self.network_show = self.patchobject(neutronclient.Client, + 'show_network') def _limits_absolute(self): max_personality = mock.Mock() @@ -441,6 +443,7 @@ class ServersTest(common.HeatTestCase): self.port_show.return_value = { 'port': {'id': '1234', + 'network_id': 'the_network', 'fixed_ips': [{ 'ip_address': '4.5.6.7', 'subnet_id': 'the_subnet'}] @@ -453,10 +456,20 @@ class ServersTest(common.HeatTestCase): 'allocation_pools': [{'start': '10.0.0.2', 'end': u'10.0.0.254'}], 'gateway_ip': '10.0.0.1', - 'id': 'the_subnet' + '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 public_ip = return_server.networks['public'][0] self.assertEqual('1234', @@ -475,6 +488,8 @@ class ServersTest(common.HeatTestCase): server.FnGetAtt('addresses')['private'][0]['addr']) self.assertEqual([subnet_dict['subnet']], server.FnGetAtt('addresses')['private'][0]['subnets']) + self.assertEqual(network_dict['network'], + server.FnGetAtt('addresses')['private'][0]['network']) self.assertEqual(private_ip, server.FnGetAtt('networks')['private'][0]) @@ -494,6 +509,21 @@ 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', diff --git a/releasenotes/notes/server-addresses-attr-include-network-0f5359ecc33b4718.yaml b/releasenotes/notes/server-addresses-attr-include-network-0f5359ecc33b4718.yaml new file mode 100644 index 0000000000..5b3f12539e --- /dev/null +++ b/releasenotes/notes/server-addresses-attr-include-network-0f5359ecc33b4718.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Adds ``network`` to the ``addresses`` attribute of + ``OS::Nova::Server`` resource. This enables resolving the network + properties for the server resource. +