From c775afcd3d23d9ebe1172246f71a577a83aed387 Mon Sep 17 00:00:00 2001 From: huangtianhua Date: Mon, 9 May 2016 19:01:23 +0800 Subject: [PATCH] Set security_groups when create internal ports for nova server Make sure nova server be created in correct security groups if user specified subnet and security_groups when create/update server. Closes-Bug: #1571975 (cherry picked from commit 66b6490705affa7e11b4fce43f8f1face9e8767b) Conflicts: heat/engine/resources/openstack/nova/server_network_mixin.py heat/tests/nova/test_server.py heat_integrationtests/functional/test_nova_server_networks.py Change-Id: Ic93cad4def90f3da25390d871d6a8c14ffe1c5ae --- .../engine/resources/openstack/nova/server.py | 6 +- .../openstack/nova/server_network_mixin.py | 21 ++- heat/tests/nova/test_server.py | 126 +++++++++++++++++- .../functional/test_nova_server_networks.py | 20 ++- 4 files changed, 162 insertions(+), 11 deletions(-) diff --git a/heat/engine/resources/openstack/nova/server.py b/heat/engine/resources/openstack/nova/server.py index cf63d2dce7..9cfd5af279 100644 --- a/heat/engine/resources/openstack/nova/server.py +++ b/heat/engine/resources/openstack/nova/server.py @@ -750,7 +750,8 @@ class Server(stack_user.StackUser, sh.SchedulerHintsMixin, scheduler_hints = self._scheduler_hints( self.properties[self.SCHEDULER_HINTS]) - nics = self._build_nics(self.properties[self.NETWORKS]) + nics = self._build_nics(self.properties[self.NETWORKS], + security_groups=security_groups) block_device_mapping = self._build_block_device_mapping( self.properties[self.BLOCK_DEVICE_MAPPING]) block_device_mapping_v2 = self._build_block_device_mapping_v2( @@ -1011,12 +1012,13 @@ class Server(stack_user.StackUser, sh.SchedulerHintsMixin, updaters = [] new_networks = prop_diff.get(self.NETWORKS) old_networks = self.properties[self.NETWORKS] + security_groups = self.properties[self.SECURITY_GROUPS] if not server: server = self.client().servers.get(self.resource_id) interfaces = server.interface_list() remove_ports, add_nets = self.calculate_networks( - old_networks, new_networks, interfaces) + old_networks, new_networks, interfaces, security_groups) for port in remove_ports: updaters.append( diff --git a/heat/engine/resources/openstack/nova/server_network_mixin.py b/heat/engine/resources/openstack/nova/server_network_mixin.py index eac23d8ef6..49af5abc26 100644 --- a/heat/engine/resources/openstack/nova/server_network_mixin.py +++ b/heat/engine/resources/openstack/nova/server_network_mixin.py @@ -88,7 +88,8 @@ class ServerNetworkMixin(object): 'network': net} raise exception.StackValidationFailed(message=msg) - def _create_internal_port(self, net_data, net_number): + def _create_internal_port(self, net_data, net_number, + security_groups=None): name = _('%(server)s-port-%(number)s') % {'server': self.name, 'number': net_number} @@ -105,6 +106,11 @@ class ServerNetworkMixin(object): if body: kwargs.update({'fixed_ips': [body]}) + if security_groups: + sec_uuids = self.client_plugin( + 'neutron').get_secgroup_uuids(security_groups) + kwargs['security_groups'] = sec_uuids + port = self.client('neutron').create_port({'port': kwargs})['port'] # Store ids (used for floating_ip association, updating, etc.) @@ -181,7 +187,7 @@ class ServerNetworkMixin(object): for port_id in new_ports: self._data_update_ports(port_id, 'add', port_type='external_ports') - def _build_nics(self, networks): + def _build_nics(self, networks, security_groups=None): if not networks: return None @@ -193,7 +199,9 @@ class ServerNetworkMixin(object): if net.get(self.NETWORK_PORT): nic_info['port-id'] = net[self.NETWORK_PORT] elif self.is_using_neutron() and net.get(self.NETWORK_SUBNET): - nic_info['port-id'] = self._create_internal_port(net, idx) + nic_info['port-id'] = self._create_internal_port( + net, idx, security_groups) + # if nic_info including 'port-id', do not set ip for nic if not nic_info.get('port-id'): if net.get(self.NETWORK_FIXED_IP): @@ -273,7 +281,8 @@ class ServerNetworkMixin(object): if net is not None: net['port'] = props['port'] - def calculate_networks(self, old_nets, new_nets, ifaces): + def calculate_networks(self, old_nets, new_nets, ifaces, + security_groups=None): remove_ports = [] add_nets = [] attach_first_free_port = False @@ -328,8 +337,8 @@ class ServerNetworkMixin(object): if net.get(self.NETWORK_PORT): handler_kwargs['port_id'] = net.get(self.NETWORK_PORT) elif self.is_using_neutron() and net.get(self.NETWORK_SUBNET): - handler_kwargs['port_id'] = self._create_internal_port(net, - idx) + handler_kwargs['port_id'] = self._create_internal_port( + net, idx, security_groups) add_nets.append(handler_kwargs) diff --git a/heat/tests/nova/test_server.py b/heat/tests/nova/test_server.py index 54b98e667b..74b20a521c 100644 --- a/heat/tests/nova/test_server.py +++ b/heat/tests/nova/test_server.py @@ -35,6 +35,7 @@ from heat.engine.clients.os import zaqar from heat.engine import environment from heat.engine import resource from heat.engine.resources.openstack.nova import server as servers +from heat.engine.resources.openstack.nova import server_network_mixin as snm from heat.engine.resources import scheduler_hints as sh from heat.engine import scheduler from heat.engine import stack as parser @@ -112,6 +113,20 @@ resources: network: 12345 ''' +tmpl_server_with_sub_secu_group = """ +heat_template_version: 2015-10-15 +resources: + server: + type: OS::Nova::Server + properties: + flavor: m1.small + image: F17-x86_64-gold + networks: + - subnet: 2a60cbaa-3d33-4af6-a9ce-83594ac546fc + security_groups: + - my_seg +""" + server_with_sw_config_personality = """ heat_template_version: 2014-10-16 resources: @@ -394,6 +409,53 @@ class ServersTest(common.HeatTestCase): scheduler.TaskRunner(server.create)() self.m.VerifyAll() + def test_server_create_with_subnet_security_group(self): + stack_name = 'server_with_subnet_security_group' + self.patchobject(nova.NovaClientPlugin, '_create', + return_value=self.fc) + return_server = self.fc.servers.list()[1] + (tmpl, stack) = self._setup_test_stack( + stack_name, test_templ=tmpl_server_with_sub_secu_group) + + resource_defns = tmpl.resource_definitions(stack) + server = servers.Server('server_with_sub_secu', + resource_defns['server'], stack) + self._mock_get_image_id_success('F17-x86_64-gold', mox.IgnoreArg()) + mock_find_net = self.patchobject( + snm.ServerNetworkMixin, + '_get_network_id', + return_value='05d8e681-4b37-4570-bc8d-810089f706b2') + mock_find_sub = self.patchobject( + snm.ServerNetworkMixin, + '_get_subnet_id', + return_value='2a60cbaa-3d33-4af6-a9ce-83594ac546fc') + + sec_uuids = ['86c0f8ae-23a8-464f-8603-c54113ef5467'] + self.patchobject(neutron.NeutronClientPlugin, + 'get_secgroup_uuids', return_value=sec_uuids) + self.patchobject(server, 'store_external_ports') + self.patchobject(neutron.NeutronClientPlugin, + 'network_id_from_subnet_id', + return_value='05d8e681-4b37-4570-bc8d-810089f706b2') + mock_create_port = self.patchobject( + neutronclient.Client, 'create_port') + + self.patchobject( + self.fc.servers, 'create', return_value=return_server) + + scheduler.TaskRunner(server.create)() + self.assertEqual((server.CREATE, server.COMPLETE), server.state) + + kwargs = {'network_id': '05d8e681-4b37-4570-bc8d-810089f706b2', + 'fixed_ips': [ + {'subnet_id': '2a60cbaa-3d33-4af6-a9ce-83594ac546fc'}], + 'security_groups': sec_uuids, + 'name': 'server_with_sub_secu-port-0', + } + mock_create_port.assert_called_with({'port': kwargs}) + self.assertEqual(3, mock_find_net.call_count) + self.assertEqual(2, mock_find_sub.call_count) + def test_server_create_with_image_id(self): return_server = self.fc.servers.list()[1] return_server.id = '5678' @@ -3401,6 +3463,59 @@ class ServersTest(common.HeatTestCase): self.assertEqual((server.UPDATE, server.COMPLETE), server.state) self.m.VerifyAll() + def test_server_update_subnet_with_security_group(self): + return_server = self.fc.servers.list()[3] + return_server.id = '9102' + + server = self._create_test_server(return_server, 'update_subnet') + # set old properties for 'networks' and 'security_groups' + server.t['Properties']['networks'] = [ + {'subnet': 'aaa09d50-8c23-4498-a542-aa0deb24f73e'}] + server.t['Properties']['security_groups'] = ['the_sg'] + # set new property 'networks' + new_networks = [{'subnet': '2a60cbaa-3d33-4af6-a9ce-83594ac546fc'}] + update_template = copy.deepcopy(server.t) + update_template['Properties']['networks'] = new_networks + + sec_uuids = ['86c0f8ae-23a8-464f-8603-c54113ef5467'] + + self.patchobject(self.fc.servers, 'get', return_value=return_server) + self.patchobject(neutron.NeutronClientPlugin, + 'get_secgroup_uuids', return_value=sec_uuids) + self.stub_ImageConstraint_validate() + mock_find_net = self.patchobject( + snm.ServerNetworkMixin, + '_get_network_id', + return_value='05d8e681-4b37-4570-bc8d-810089f706b2') + mock_find_sub = self.patchobject( + snm.ServerNetworkMixin, + '_get_subnet_id', + return_value='2a60cbaa-3d33-4af6-a9ce-83594ac546fc') + + mock_create_port = self.patchobject( + neutronclient.Client, 'create_port') + + iface = self.create_fake_iface('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', + '05d8e681-4b37-4570-bc8d-810089f706b2', + '1.2.3.4') + self.patchobject(return_server, 'interface_list', return_value=[iface]) + mock_detach = self.patchobject(return_server, 'interface_detach') + mock_attach = self.patchobject(return_server, 'interface_attach') + + scheduler.TaskRunner(server.update, update_template, before=server.t)() + self.assertEqual((server.UPDATE, server.COMPLETE), server.state) + self.assertEqual(1, mock_detach.call_count) + self.assertEqual(1, mock_attach.call_count) + self.assertEqual(3, mock_find_net.call_count) + self.assertEqual(1, mock_find_sub.call_count) + kwargs = {'network_id': '05d8e681-4b37-4570-bc8d-810089f706b2', + 'fixed_ips': [ + {'subnet_id': '2a60cbaa-3d33-4af6-a9ce-83594ac546fc'}], + 'security_groups': sec_uuids, + 'name': 'update_subnet-port-0', + } + mock_create_port.assert_called_with({'port': kwargs}) + def test_server_update_empty_networks_with_complex_parameters(self): return_server = self.fc.servers.list()[3] return_server.id = '9102' @@ -4051,6 +4166,8 @@ class ServerInternalPortTest(common.HeatTestCase): properties: flavor: m1.small image: F17-x86_64-gold + security_groups: + - test_sec networks: - network: 4321 subnet: 1234 @@ -4062,12 +4179,16 @@ class ServerInternalPortTest(common.HeatTestCase): self.resolve.side_effect = ['4321', '1234'] self.patchobject(server, '_validate_belonging_subnet_to_net') + self.patchobject(neutron.NeutronClientPlugin, + 'get_secgroup_uuids', return_value=['5566']) + self.port_create.return_value = {'port': {'id': '111222'}} data_set = self.patchobject(resource.Resource, 'data_set') network = [{'network': '4321', 'subnet': '1234', 'fixed_ip': '127.0.0.1'}] - server._build_nics(network) + security_groups = ['test_sec'] + server._build_nics(network, security_groups) self.port_create.assert_called_once_with( {'port': {'name': 'server-port-0', @@ -4075,7 +4196,8 @@ class ServerInternalPortTest(common.HeatTestCase): 'fixed_ips': [{ 'ip_address': '127.0.0.1', 'subnet_id': '1234' - }]}}) + }], + 'security_groups': ['5566']}}) data_set.assert_called_once_with('internal_ports', '[{"id": "111222"}]') diff --git a/heat_integrationtests/functional/test_nova_server_networks.py b/heat_integrationtests/functional/test_nova_server_networks.py index f71bb20d98..f14b95841c 100644 --- a/heat_integrationtests/functional/test_nova_server_networks.py +++ b/heat_integrationtests/functional/test_nova_server_networks.py @@ -31,6 +31,16 @@ resources: properties: network: {get_resource: net} cidr: 11.11.11.0/24 + security_group: + type: OS::Neutron::SecurityGroup + properties: + name: the_sg + description: Ping and SSH + rules: + - protocol: icmp + - protocol: tcp + port_range_min: 22 + port_range_max: 22 server: type: OS::Nova::Server properties: @@ -39,6 +49,8 @@ resources: networks: - subnet: {get_resource: subnet} fixed_ip: 11.11.11.11 + security_groups: + - {get_resource: security_group} outputs: networks: value: {get_attr: [server, networks]} @@ -55,7 +67,7 @@ class CreateServerTest(functional_base.FunctionalTestsBase): output = self._stack_output(stack, output_key) return output - def test_create_server_with_subnet_fixed_ip(self): + def test_create_server_with_subnet_fixed_ip_sec_group(self): parms = {'flavor': self.conf.minimal_instance_type, 'image': self.conf.minimal_image_ref} stack_identifier = self.stack_create( @@ -64,3 +76,9 @@ class CreateServerTest(functional_base.FunctionalTestsBase): parameters=parms) networks = self.get_outputs(stack_identifier, 'networks') self.assertEqual(['11.11.11.11'], networks['my_net']) + + server_resource = self.client.resources.get( + stack_identifier, 'server') + server_id = server_resource.physical_resource_id + server = self.compute_client.servers.get(server_id) + self.assertEqual([{"name": "the_sg"}], server.security_groups)