From 46ac3932f98f3239c69b786bcbf494071f0f5bd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Jens=C3=A5s?= Date: Wed, 16 May 2018 13:24:40 +0200 Subject: [PATCH] OS::Nova::Server: Extend addresses attr to include subnets This change adds 'subnets' to the addresses attribute of the OS::Nova::Server resource. This enables resolving the subnet properties for the server resource which brings parity with OS::Neutron::Port's 'subnets' attribute. Story: 1766946 Task: 19689 Change-Id: I6927f1e142e83df7258e259bffc86819e4cbe881 --- .../engine/resources/openstack/nova/server.py | 59 +++++++++++++++---- heat/tests/openstack/nova/test_server.py | 27 +++++++++ ...-attr-include-subnet-62d74e161e1db1af.yaml | 8 +++ 3 files changed, 82 insertions(+), 12 deletions(-) create mode 100644 releasenotes/notes/server-addresses-attr-include-subnet-62d74e161e1db1af.yaml diff --git a/heat/engine/resources/openstack/nova/server.py b/heat/engine/resources/openstack/nova/server.py index be6469945d..de5f487709 100644 --- a/heat/engine/resources/openstack/nova/server.py +++ b/heat/engine/resources/openstack/nova/server.py @@ -629,13 +629,23 @@ class Server(server_base.BaseServer, sh.SchedulerHintsMixin, type=attributes.Schema.STRING ), ADDRESSES: attributes.Schema( - _('A dict of all network addresses with corresponding port_id. ' - 'Each network will have two keys in dict, they are network ' - 'name and network id. ' - 'The port ID may be obtained through the following expression: ' - '"{get_attr: [, addresses, , 0, ' - 'port]}".'), - type=attributes.Schema.MAP + _('A dict of all network addresses with corresponding port_id and ' + 'subnets. Each network will have two keys in dict, they are ' + 'network name and network id. The port ID may be obtained ' + 'through the following expression: ``{get_attr: [, ' + 'addresses, , 0, port]}``. The subnets may ' + 'be obtained trough the following expression: ``{get_attr: ' + '[, addresses, , 0, subnets]}``.'), + 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.'), + previous_status=support.SupportStatus( + status=support.SUPPORTED + ) + ) ), NETWORKS_ATTR: attributes.Schema( _('A dict of assigned network addresses of the form: ' @@ -929,8 +939,8 @@ class Server(server_base.BaseServer, sh.SchedulerHintsMixin, return result def _get_live_networks(self, server, props): - reality_nets = self._add_port_for_address(server, - extend_networks=False) + reality_nets = self._add_attrs_for_address(server, + extend_networks=False) reality_net_ids = {} client_plugin = self.client_plugin('neutron') for net_key in reality_nets: @@ -1078,10 +1088,28 @@ class Server(server_base.BaseServer, sh.SchedulerHintsMixin, return bdm_v2_list - def _add_port_for_address(self, server, extend_networks=True): - """Method adds port id to list of addresses. + def _get_port_subnets_attr(self, port_id): + 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( + fixed_ip['subnet_id'])['subnet']) + except Exception as ex: + LOG.warning("Failed to fetch resource attributes: %s", ex) + return + return subnets + + def _add_attrs_for_address(self, server, extend_networks=True): + """Method adds port id and subnets attributes to list of addresses. This method is used only for resolving attributes. + :param server: The server resource + :param extend_networks: When False the network is not extended, i.e + the net is returned without replacing name on + id. """ nets = copy.deepcopy(server.addresses) or {} ifaces = server.interface_list() @@ -1092,6 +1120,13 @@ class Server(server_base.BaseServer, sh.SchedulerHintsMixin, for addr in nets[net_name]: addr['port'] = ip_mac_mapping_on_port_id.get( (addr['addr'], addr['OS-EXT-IPS-MAC:mac_addr'])) + # _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 extend_networks: + addr['subnets'] = self._get_port_subnets_attr(addr['port']) + if extend_networks: return self._extend_networks(nets) else: @@ -1134,7 +1169,7 @@ class Server(server_base.BaseServer, sh.SchedulerHintsMixin, self.client_plugin().ignore_not_found(e) return '' if name == self.ADDRESSES: - return self._add_port_for_address(server) + return self._add_attrs_for_address(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 02a9e033a1..224379dde5 100644 --- a/heat/tests/openstack/nova/test_server.py +++ b/heat/tests/openstack/nova/test_server.py @@ -250,6 +250,11 @@ class ServersTest(common.HeatTestCase): self.patchobject(glance.GlanceClientPlugin, 'find_image_by_name_or_id', side_effect=image_side_effect) + self.port_show = self.patchobject(neutronclient.Client, + 'show_port') + self.subnet_show = self.patchobject(neutronclient.Client, + 'show_subnet') + def _limits_absolute(self): max_personality = mock.Mock() max_personality.name = 'maxPersonality' @@ -431,6 +436,26 @@ class ServersTest(common.HeatTestCase): self.patchobject(return_server, 'interface_list', return_value=interfaces) self.patchobject(self.fc.servers, 'tag_list', return_value=['test']) + + self.port_show.return_value = { + 'port': {'id': '1234', + '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' + } + } + self.subnet_show.return_value = subnet_dict + public_ip = return_server.networks['public'][0] self.assertEqual('1234', server.FnGetAtt('addresses')['public'][0]['port']) @@ -446,6 +471,8 @@ 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']], + server.FnGetAtt('addresses')['private'][0]['subnets']) self.assertEqual(private_ip, server.FnGetAtt('networks')['private'][0]) diff --git a/releasenotes/notes/server-addresses-attr-include-subnet-62d74e161e1db1af.yaml b/releasenotes/notes/server-addresses-attr-include-subnet-62d74e161e1db1af.yaml new file mode 100644 index 0000000000..8503d3893b --- /dev/null +++ b/releasenotes/notes/server-addresses-attr-include-subnet-62d74e161e1db1af.yaml @@ -0,0 +1,8 @@ +--- +features: + - | + Adds ``subnets`` to the ``addresses`` attribute of + ``OS::Nova::Server`` resource. This enables resolving the subnet + properties for the server resource which brings parity with + ``OS::Neutron::Port``'s ``subnets`` attribute. +