From 99b6187f3ac2c66df8118f48b0f2f5a6d99b03a5 Mon Sep 17 00:00:00 2001 From: huangtianhua Date: Mon, 10 Jul 2017 14:39:04 +0800 Subject: [PATCH] Update security groups for server interfaces Update the security groups for server interfaces if only 'network' is specified when update networks of nova server, to make sure the security groups info correctly. Change-Id: I0635d50b52c430885cb3d7733fb565a5c113fad2 Closes-Bug: #1703279 --- heat/engine/clients/os/nova.py | 9 ++- .../openstack/nova/server_network_mixin.py | 22 ++++-- heat/tests/openstack/nova/test_server.py | 67 +++++++++++++++++++ 3 files changed, 92 insertions(+), 6 deletions(-) diff --git a/heat/engine/clients/os/nova.py b/heat/engine/clients/os/nova.py index 4bfa94836d..eb9c030b1b 100644 --- a/heat/engine/clients/os/nova.py +++ b/heat/engine/clients/os/nova.py @@ -686,10 +686,15 @@ echo -e '%s\tALL=(ALL)\tNOPASSWD: ALL' >> /etc/sudoers server.interface_detach(port_id) return True - def interface_attach(self, server_id, port_id=None, net_id=None, fip=None): + def interface_attach(self, server_id, port_id=None, net_id=None, fip=None, + security_groups=None): server = self.fetch_server(server_id) if server: - server.interface_attach(port_id, net_id, fip) + attachment = server.interface_attach(port_id, net_id, fip) + if not port_id and security_groups: + props = {'security_groups': security_groups} + self.clients.client('neutron').update_port( + attachment.port_id, {'port': props}) return True else: return False diff --git a/heat/engine/resources/openstack/nova/server_network_mixin.py b/heat/engine/resources/openstack/nova/server_network_mixin.py index fe45a9e6c9..4e72ed7198 100644 --- a/heat/engine/resources/openstack/nova/server_network_mixin.py +++ b/heat/engine/resources/openstack/nova/server_network_mixin.py @@ -349,7 +349,8 @@ class ServerNetworkMixin(object): return topology['id'] - def _calculate_using_str_network(self, ifaces, str_net): + def _calculate_using_str_network(self, ifaces, str_net, + security_groups=None): add_nets = [] remove_ports = [iface.port_id for iface in ifaces or []] if str_net == self.NETWORK_AUTO: @@ -359,8 +360,12 @@ class ServerNetworkMixin(object): if len(nets) > 1: msg = 'Multiple possible networks found.' raise exception.UnableToAutoAllocateNetwork(message=msg) - - add_nets.append({'port_id': None, 'net_id': nets[0], 'fip': None}) + handle_args = {'port_id': None, 'net_id': nets[0], 'fip': None} + if security_groups: + sg_ids = self.client_plugin( + 'neutron').get_secgroup_uuids(security_groups) + handle_args['security_groups'] = sg_ids + add_nets.append(handle_args) return remove_ports, add_nets def _calculate_using_list_networks(self, old_nets, new_nets, ifaces, @@ -435,6 +440,10 @@ class ServerNetworkMixin(object): # according to similar behavior during instance creation if not new_nets: handler_kwargs = {'port_id': None, 'net_id': None, 'fip': None} + if security_groups: + sec_uuids = self.client_plugin( + 'neutron').get_secgroup_uuids(security_groups) + handler_kwargs['security_groups'] = sec_uuids add_nets.append(handler_kwargs) else: # attach section similar for both variants that @@ -452,6 +461,10 @@ class ServerNetworkMixin(object): if not handler_kwargs['port_id']: handler_kwargs['net_id'] = self._get_network_id(net) + if security_groups: + sec_uuids = self.client_plugin( + 'neutron').get_secgroup_uuids(security_groups) + handler_kwargs['security_groups'] = sec_uuids if handler_kwargs['net_id']: handler_kwargs['fip'] = net.get('fixed_ip') @@ -490,7 +503,8 @@ class ServerNetworkMixin(object): security_groups=None): new_str_net = self._str_network(new_nets) if new_str_net: - return self._calculate_using_str_network(ifaces, new_str_net) + return self._calculate_using_str_network(ifaces, new_str_net, + security_groups) else: return self._calculate_using_list_networks( old_nets, new_nets, ifaces, security_groups) diff --git a/heat/tests/openstack/nova/test_server.py b/heat/tests/openstack/nova/test_server.py index 6af71e3c83..4be2952caa 100644 --- a/heat/tests/openstack/nova/test_server.py +++ b/heat/tests/openstack/nova/test_server.py @@ -3582,6 +3582,73 @@ class ServersTest(common.HeatTestCase): } mock_create_port.assert_called_with({'port': kwargs}) + def test_server_update_subnet_to_network_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' + before_props = self.server_props.copy() + before_props['networks'] = [ + {'subnet': 'aaa09d50-8c23-4498-a542-aa0deb24f73e'} + ] + before_props['security_groups'] = ['the_sg'] + # set new property 'networks' + new_networks = [{'network': '2a60cbaa-3d33-4af6-a9ce-83594ac546fc'}] + update_props = self.server_props.copy() + update_props['networks'] = new_networks + update_props['security_groups'] = ['the_sg'] + update_template = server.t.freeze(properties=update_props) + server.t = server.t.freeze(properties=before_props) + + 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.patchobject(neutron.NeutronClientPlugin, + 'network_id_from_subnet_id', + return_value='05d8e681-4b37-4570-bc8d-810089f706b2') + + 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') + + def interface_attach_mock(port, net): + class attachment(object): + def __init__(self, port_id, net_id): + self.port_id = port_id + self.net_id = net_id + + return attachment(port, net) + + mock_attach.return_value = interface_attach_mock( + 'ad4a231b-67f7-45fe-aee9-461176b48203', + '2a60cbaa-3d33-4af6-a9ce-83594ac546fc') + mock_detach_check = self.patchobject(nova.NovaClientPlugin, + 'check_interface_detach', + return_value=True) + mock_attach_check = self.patchobject(nova.NovaClientPlugin, + 'check_interface_attach', + return_value=True) + mock_update_port = self.patchobject( + neutronclient.Client, 'update_port') + + 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(1, mock_detach_check.call_count) + self.assertEqual(1, mock_attach_check.call_count) + kwargs = {'security_groups': sec_uuids} + mock_update_port.assert_called_with( + 'ad4a231b-67f7-45fe-aee9-461176b48203', + {'port': kwargs}) + def test_server_update_empty_networks_with_complex_parameters(self): return_server = self.fc.servers.list()[3] return_server.id = '9102'