From 54c26453a0a8e8cb574858c7e1d362d0abea3822 Mon Sep 17 00:00:00 2001 From: Sergey Kraynev Date: Wed, 16 Sep 2015 09:59:52 -0400 Subject: [PATCH] Store external ports in Server data We need to store info about all ports of server, so add storing external ports in data. It will be useful for rollback and replace. Change-Id: I931315995fae05ded7b2593c69aca7d51ce490c4 implements bp rich-network-prop --- .../tests/test_rackspace_cloud_server.py | 3 + .../engine/resources/openstack/nova/server.py | 15 +++-- .../openstack/nova/server_network_mixin.py | 38 ++++++++++-- heat/tests/nova/test_server.py | 62 +++++++++++++++++++ 4 files changed, 109 insertions(+), 9 deletions(-) diff --git a/contrib/rackspace/rackspace/tests/test_rackspace_cloud_server.py b/contrib/rackspace/rackspace/tests/test_rackspace_cloud_server.py index b9c150302..2395c7c73 100644 --- a/contrib/rackspace/rackspace/tests/test_rackspace_cloud_server.py +++ b/contrib/rackspace/rackspace/tests/test_rackspace_cloud_server.py @@ -115,6 +115,7 @@ class CloudServersTest(common.HeatTestCase): server = cloud_server.CloudServer(server_name, resource_defns['WebServer'], stack) + self.patchobject(server, 'store_external_ports') self._stub_server_validate(server, image_id or 'CentOS 5.2', 1) if stub_create: @@ -322,6 +323,7 @@ class CloudServersTest(common.HeatTestCase): resource_defns = tmpl.resource_definitions(stack) server = cloud_server.CloudServer('WebServer', resource_defns['WebServer'], stack) + self.patchobject(server, 'store_external_ports') class Interface(object): def __init__(self, id, addresses): @@ -453,6 +455,7 @@ class CloudServersTest(common.HeatTestCase): resource_defns = tmpl.resource_definitions(stack) server = cloud_server.CloudServer('WebServer', resource_defns['WebServer'], stack) + self.patchobject(server, 'store_external_ports') mock_servers_create = mock.Mock(return_value=return_server) self.fc.servers.create = mock_servers_create image_id = mock.ANY diff --git a/heat/engine/resources/openstack/nova/server.py b/heat/engine/resources/openstack/nova/server.py index 8ad66ca6f..83567e915 100644 --- a/heat/engine/resources/openstack/nova/server.py +++ b/heat/engine/resources/openstack/nova/server.py @@ -755,7 +755,10 @@ class Server(stack_user.StackUser, sh.SchedulerHintsMixin, return server.id def check_create_complete(self, server_id): - return self.client_plugin()._check_active(server_id) + check = self.client_plugin()._check_active(server_id) + if check: + self.store_external_ports() + return check def handle_check(self): server = self.client().servers.get(self.resource_id) @@ -1084,7 +1087,10 @@ class Server(stack_user.StackUser, sh.SchedulerHintsMixin, prg.complete = check_complete(*prg.checker_args, **prg.checker_kwargs) break - return all(prg.complete for prg in updaters) + status = all(prg.complete for prg in updaters) + if status: + self.store_external_ports() + return status def metadata_update(self, new_metadata=None): ''' @@ -1276,8 +1282,9 @@ class Server(stack_user.StackUser, sh.SchedulerHintsMixin, self._delete_temp_url() self._delete_queue() - if self.data().get('internal_ports'): - self._delete_internal_ports() + # remove internal and external ports + self._delete_internal_ports() + self.data_delete('external_ports') try: self.client().servers.delete(self.resource_id) diff --git a/heat/engine/resources/openstack/nova/server_network_mixin.py b/heat/engine/resources/openstack/nova/server_network_mixin.py index 7e78e9602..8d6046d72 100644 --- a/heat/engine/resources/openstack/nova/server_network_mixin.py +++ b/heat/engine/resources/openstack/nova/server_network_mixin.py @@ -120,6 +120,7 @@ class ServerNetworkMixin(object): return port['id'] def _delete_internal_port(self, port_id): + """Delete physical port by id.""" try: self.client('neutron').delete_port(port_id) except Exception as ex: @@ -133,8 +134,8 @@ class ServerNetworkMixin(object): self.data_delete('internal_ports') - def _data_update_ports(self, port_id, action): - data = self._data_get_ports() + def _data_update_ports(self, port_id, action, port_type='internal_ports'): + data = self._data_get_ports(port_type) if action == 'add': data.append({'id': port_id}) @@ -144,12 +145,39 @@ class ServerNetworkMixin(object): data.remove(port) break - self.data_set('internal_ports', jsonutils.dumps(data)) + self.data_set(port_type, jsonutils.dumps(data)) - def _data_get_ports(self): - data = self.data().get('internal_ports') + def _data_get_ports(self, port_type='internal_ports'): + data = self.data().get(port_type) return jsonutils.loads(data) if data else [] + def store_external_ports(self): + """Store in resource's data IDs of ports created by nova for server. + + If no port property is specified and no internal port has been created, + nova client takes no port-id and calls port creating into server + creating. We need to store information about that ports, so store + their IDs to data with key `external_ports`. + """ + server = self.client().servers.get(self.resource_id) + ifaces = server.interface_list() + external_port_ids = set(iface.port_id for iface in ifaces) + # need to make sure external_ports data doesn't store ids of non-exist + # ports. Delete such port_id if it's needed. + data_external_port_ids = set( + port['id'] for port in self._data_get_ports('external_ports')) + for port_id in data_external_port_ids - external_port_ids: + self._data_update_ports(port_id, 'delete', + port_type='external_ports') + + internal_port_ids = set(port['id'] for port in self._data_get_ports()) + # add ids of new external ports which not contains in external_ports + # data yet. Also, exclude ids of internal ports. + new_ports = ((external_port_ids - internal_port_ids) - + data_external_port_ids) + for port_id in new_ports: + self._data_update_ports(port_id, 'add', port_type='external_ports') + def _build_nics(self, networks): if not networks: return None diff --git a/heat/tests/nova/test_server.py b/heat/tests/nova/test_server.py index ee2765399..11ae6b1cc 100644 --- a/heat/tests/nova/test_server.py +++ b/heat/tests/nova/test_server.py @@ -150,6 +150,7 @@ class ServersTest(common.HeatTestCase): res = self.stack['WebServer'] res.client = mock.Mock() res.client().servers.get.return_value = server + self.patchobject(res, 'store_external_ports') return res def test_check(self): @@ -197,6 +198,8 @@ class ServersTest(common.HeatTestCase): server = servers.Server(str(name), resource_defns['WebServer'], self.stack) + self.patchobject(server, 'store_external_ports') + self._mock_get_image_id_success(image_id or 'CentOS 5.2', 1) self.m.StubOutWithMock(nova.NovaClientPlugin, '_create') @@ -351,6 +354,7 @@ class ServersTest(common.HeatTestCase): resource_defns = tmpl.resource_definitions(stack) server = servers.Server('create_metadata_test_server', resource_defns['WebServer'], stack) + self.patchobject(server, 'store_external_ports') instance_meta = {'a': "1"} image_id = mox.IgnoreArg() @@ -558,6 +562,7 @@ class ServersTest(common.HeatTestCase): resource_defns = tmpl.resource_definitions(stack) server = servers.Server('WebServer', resource_defns['WebServer'], stack) + self.patchobject(server, 'store_external_ports') self.m.StubOutWithMock(nova.NovaClientPlugin, '_create') nova.NovaClientPlugin._create().AndReturn(self.fc) @@ -592,6 +597,7 @@ class ServersTest(common.HeatTestCase): resource_defns = tmpl.resource_definitions(stack) server = servers.Server('WebServer', resource_defns['WebServer'], stack) + self.patchobject(server, 'store_external_ports') self.rpc_client = mock.MagicMock() server._rpc_client = self.rpc_client @@ -632,6 +638,7 @@ class ServersTest(common.HeatTestCase): resource_defns = tmpl.resource_definitions(stack) server = servers.Server('WebServer', resource_defns['WebServer'], stack) + self.patchobject(server, 'store_external_ports') self.rpc_client = mock.MagicMock() server._rpc_client = self.rpc_client @@ -674,6 +681,7 @@ class ServersTest(common.HeatTestCase): resource_defns = tmpl.resource_definitions(stack) server = servers.Server('WebServer', resource_defns['WebServer'], stack) + self.patchobject(server, 'store_external_ports') self.m.StubOutWithMock(nova.NovaClientPlugin, '_create') self.m.StubOutWithMock(server, 'heat') @@ -758,6 +766,7 @@ class ServersTest(common.HeatTestCase): resource_defns = tmpl.resource_definitions(stack) server = servers.Server('WebServer', resource_defns['WebServer'], stack) + self.patchobject(server, 'store_external_ports') self.m.StubOutWithMock(nova.NovaClientPlugin, '_create') @@ -837,6 +846,7 @@ class ServersTest(common.HeatTestCase): resource_defns = tmpl.resource_definitions(stack) server = servers.Server('WebServer', resource_defns['WebServer'], stack) + self.patchobject(server, 'store_external_ports') self.m.StubOutWithMock(nova.NovaClientPlugin, '_create') self.m.StubOutWithMock(swift.SwiftClientPlugin, '_create') @@ -930,6 +940,7 @@ class ServersTest(common.HeatTestCase): resource_defns = tmpl.resource_definitions(stack) server = servers.Server('WebServer', resource_defns['WebServer'], stack) + self.patchobject(server, 'store_external_ports') ncp = self.patchobject(nova.NovaClientPlugin, '_create') zcc = self.patchobject(zaqar.ZaqarClientPlugin, 'create_for_tenant') @@ -1017,6 +1028,7 @@ class ServersTest(common.HeatTestCase): resource_defns = tmpl.resource_definitions(stack) server = servers.Server('WebServer', resource_defns['WebServer'], stack) + self.patchobject(server, 'store_external_ports') mock_client.return_value = self.fc self.fc.servers.create = mock.Mock(return_value=return_server) @@ -1044,6 +1056,7 @@ class ServersTest(common.HeatTestCase): resource_defns = tmpl.resource_definitions(stack) server = servers.Server('WebServer', resource_defns['WebServer'], stack) + self.patchobject(server, 'store_external_ports') mock_client.return_value = self.fc self.fc.servers.create = mock.Mock(return_value=return_server) @@ -1073,6 +1086,7 @@ class ServersTest(common.HeatTestCase): resource_defns = t.resource_definitions(stack) server = servers.Server(server_name, resource_defns['WebServer'], stack) + self.patchobject(server, 'store_external_ports') # server.uuid is only available once the resource has been added. stack.add_resource(server) @@ -3642,6 +3656,8 @@ class ServersTest(common.HeatTestCase): self.m.StubOutWithMock(nova.NovaClientPlugin, '_create') nova.NovaClientPlugin._create().MultipleTimes().AndReturn(self.fc) + self.patchobject(stack['server'], 'store_external_ports') + return_server = self.fc.servers.list()[1] return_server.id = '1234' @@ -3702,6 +3718,8 @@ class ServersTest(common.HeatTestCase): utils.dummy_context(), 'snapshot_policy', tmpl) stack.store() + self.patchobject(stack['WebServer'], 'store_external_ports') + mock_plugin = self.patchobject(nova.NovaClientPlugin, '_create') mock_plugin.return_value = self.fc @@ -3745,6 +3763,8 @@ class ServersTest(common.HeatTestCase): utils.dummy_context(), 'snapshot_policy', tmpl) stack.store() + self.patchobject(stack['WebServer'], 'store_external_ports') + mock_plugin = self.patchobject(nova.NovaClientPlugin, '_create') mock_plugin.return_value = self.fc @@ -4101,3 +4121,45 @@ class ServerInternalPortTest(common.HeatTestCase): server._data = {"internal_ports": ''} data = server._data_get_ports() self.assertEqual([], data) + + def test_store_external_ports(self): + tmpl = """ + heat_template_version: 2015-10-15 + resources: + server: + type: OS::Nova::Server + properties: + flavor: m1.small + image: F17-x86_64-gold + networks: + - network: 4321 + """ + t, stack, server = self._return_template_stack_and_rsrc_defn('test', + tmpl) + + class Fake(object): + def interface_list(self): + return [iface('1122'), + iface('1122'), + iface('2233'), + iface('3344')] + + server.client = mock.Mock() + server.client().servers.get.return_value = Fake() + + server._data = {"internal_ports": '[{"id": "1122"}]', + "external_ports": '[{"id": "3344"},{"id": "5566"}]'} + + iface = collections.namedtuple('iface', ['port_id']) + update_data = self.patchobject(server, '_data_update_ports') + + server.store_external_ports() + self.assertEqual(2, update_data.call_count) + self.assertEqual(('5566', 'delete',), + update_data.call_args_list[0][0]) + self.assertEqual({'port_type': 'external_ports'}, + update_data.call_args_list[0][1]) + self.assertEqual(('2233', 'add',), + update_data.call_args_list[1][0]) + self.assertEqual({'port_type': 'external_ports'}, + update_data.call_args_list[1][1])