From b24aba20596670ceae118054ca46c568c9a91fac Mon Sep 17 00:00:00 2001 From: Divya ChanneGowda Date: Tue, 15 Jul 2014 14:33:30 -0700 Subject: [PATCH] Implements securitygroup extension for nuage plugin In Nuage VSP, the scope of vport-tag(Neutron securitygroup equivalent) is either per router or per subnet whereas securitygroup scope in Neutron is per tenant. Because of this, the mapping between neutron and VSP resource always happens at the port create or update time, such that port's router/subnet is known and thus sg attachment point in VSP is known. On port-update --security-group sg1, if this is the first port getting attached to that security-group, corresponding vport-tag(for sg) and rules( for sg rules) are created on VSP. Subsequent port-update for the same sg1 will just update the port to vport-tag binding. When security-group and security-group-rules are deleted on neutron, corresponding vport-tag and rules are deleted on the VSP. Implements: blueprint securitygroup-ext-for-nuage-plugin Change-Id: If6f7f16ad727ae9394008fb81b56c785c2404c8f --- neutron/plugins/nuage/plugin.py | 195 ++++++++++++++++-- neutron/tests/unit/nuage/fake_nuageclient.py | 24 +++ neutron/tests/unit/nuage/test_nuage_plugin.py | 6 + 3 files changed, 211 insertions(+), 14 deletions(-) diff --git a/neutron/plugins/nuage/plugin.py b/neutron/plugins/nuage/plugin.py index 164154cfd..f64e587f9 100644 --- a/neutron/plugins/nuage/plugin.py +++ b/neutron/plugins/nuage/plugin.py @@ -32,11 +32,14 @@ from neutron.db import external_net_db from neutron.db import extraroute_db from neutron.db import l3_db from neutron.db import quota_db # noqa +from neutron.db import securitygroups_db as sg_db from neutron.extensions import external_net from neutron.extensions import l3 from neutron.extensions import portbindings +from neutron.extensions import securitygroup as ext_sg from neutron.openstack.common import excutils from neutron.openstack.common import importutils +from neutron.openstack.common import lockutils from neutron.plugins.nuage.common import config from neutron.plugins.nuage.common import constants from neutron.plugins.nuage.common import exceptions as nuage_exc @@ -50,11 +53,13 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2, external_net_db.External_net_db_mixin, extraroute_db.ExtraRoute_db_mixin, l3_db.L3_NAT_db_mixin, - netpartition.NetPartitionPluginBase): + netpartition.NetPartitionPluginBase, + sg_db.SecurityGroupDbMixin): """Class that implements Nuage Networks' plugin functionality.""" supported_extension_aliases = ["router", "binding", "external-net", "net-partition", "nuage-router", - "nuage-subnet", "quotas", "extraroute"] + "nuage-subnet", "quotas", + "extraroute", "security-group"] binding_view = "extension:port_binding:view" @@ -122,18 +127,93 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2, 'net_partition': net_partition, 'ip': port['fixed_ips'][0]['ip_address'], 'no_of_ports': len(ports), - 'tenant': port['tenant_id'] + 'tenant': port['tenant_id'], } - self.nuageclient.create_vms(params) + nuage_vm = self.nuageclient.create_vms(params) + if nuage_vm: + if port['fixed_ips'][0]['ip_address'] != str(nuage_vm['ip']): + self._update_port_ip(context, port, nuage_vm['ip']) + def _get_router_by_subnet(self, context, subnet_id): + filters = { + 'fixed_ips': {'subnet_id': [subnet_id]}, + 'device_owner': [os_constants.DEVICE_OWNER_ROUTER_INTF] + } + router_port = self.get_ports(context, filters=filters) + if not router_port: + msg = (_("Router for subnet %s not found ") % subnet_id) + raise n_exc.BadRequest(resource='port', msg=msg) + return router_port[0]['device_id'] + + def _process_port_create_security_group(self, context, port, + sec_group): + if not attributes.is_attr_set(sec_group): + port[ext_sg.SECURITYGROUPS] = [] + return + port_id = port['id'] + with context.session.begin(subtransactions=True): + for sg_id in sec_group: + super(NuagePlugin, + self)._create_port_security_group_binding(context, + port_id, + sg_id) + try: + vptag_vport_list = [] + for sg_id in sec_group: + params = { + 'neutron_port_id': port_id + } + nuage_port = self.nuageclient.get_nuage_port_by_id(params) + if nuage_port and nuage_port.get('nuage_vport_id'): + nuage_vport_id = nuage_port['nuage_vport_id'] + sg = self._get_security_group(context, sg_id) + sg_rules = self.get_security_group_rules( + context, + {'security_group_id': [sg_id]}) + sg_params = { + 'nuage_port': nuage_port, + 'sg': sg, + 'sg_rules': sg_rules + } + nuage_vptag_id = ( + self.nuageclient.process_port_create_security_group( + sg_params)) + vptag_vport = { + 'nuage_vporttag_id': nuage_vptag_id + } + vptag_vport_list.append(vptag_vport) + + if vptag_vport_list: + params = { + 'vptag_vport_list': vptag_vport_list, + 'nuage_vport_id': nuage_vport_id + } + self.nuageclient.update_nuage_vport(params) + except Exception: + with excutils.save_and_reraise_exception(): + for sg_id in sec_group: + super(NuagePlugin, + self)._delete_port_security_group_bindings(context, + port_id) + # Convert to list as a set might be passed here and + # this has to be serialized + port[ext_sg.SECURITYGROUPS] = (list(sec_group) if sec_group else []) + + def _delete_port_security_group_bindings(self, context, port_id): + super(NuagePlugin, + self)._delete_port_security_group_bindings(context, port_id) + self.nuageclient.delete_port_security_group_bindings(port_id) + + @lockutils.synchronized('create_port', 'nuage-port', external=True) def create_port(self, context, port): session = context.session with session.begin(subtransactions=True): + p = port['port'] + self._ensure_default_security_group_on_port(context, port) port = super(NuagePlugin, self).create_port(context, port) device_owner = port.get('device_owner', None) - if (device_owner and - device_owner not in constants.AUTO_CREATE_PORT_OWNERS): + if device_owner not in constants.AUTO_CREATE_PORT_OWNERS: if 'fixed_ips' not in port or len(port['fixed_ips']) == 0: return self._extend_port_dict_binding(context, port) subnet_id = port['fixed_ips'][0]['subnet_id'] @@ -154,17 +234,23 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2, super(NuagePlugin, self).delete_port( context, port['id']) + if ext_sg.SECURITYGROUPS in p: + self._process_port_create_security_group( + context, + port, + p[ext_sg.SECURITYGROUPS]) return self._extend_port_dict_binding(context, port) def update_port(self, context, id, port): p = port['port'] + sg_groups = None if p.get('device_owner', '').startswith( constants.NOVA_PORT_OWNER_PREF): session = context.session with session.begin(subtransactions=True): port = self._get_port(context, id) port.update(p) - if 'fixed_ips' not in port or len(port['fixed_ips']) == 0: + if not port.get('fixed_ips'): return self._make_port_dict(port) subnet_id = port['fixed_ips'][0]['subnet_id'] @@ -178,24 +264,48 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2, 'neutron_port_id': id, } nuage_port = self.nuageclient.get_nuage_port_by_id(params) - if not nuage_port: - msg = (_("Port %s not found on VSD") % id) - raise n_exc.BadRequest(resource='port', msg=msg) - if not nuage_port['nuage_vport_id']: + if not nuage_port or not nuage_port.get('nuage_vport_id'): self._create_update_port(context, port, subnet_mapping[ 'net_partition_id'], subnet_mapping['nuage_subnet_id']) updated_port = self._make_port_dict(port) + sg_port = self._extend_port_dict_security_group( + updated_port, + port + ) + sg_groups = sg_port[ext_sg.SECURITYGROUPS] else: updated_port = super(NuagePlugin, self).update_port(context, id, port) + if not updated_port.get('fixed_ips'): + return updated_port + subnet_id = updated_port['fixed_ips'][0]['subnet_id'] + subnet_mapping = nuagedb.get_subnet_l2dom_by_id(context.session, + subnet_id) + if subnet_mapping: + if sg_groups: + self._delete_port_security_group_bindings(context, + updated_port['id']) + self._process_port_create_security_group(context, + updated_port, + sg_groups) + elif ext_sg.SECURITYGROUPS in p: + self._delete_port_security_group_bindings(context, + updated_port['id']) + self._process_port_create_security_group( + context, + updated_port, + p[ext_sg.SECURITYGROUPS] + ) return updated_port + @lockutils.synchronized('delete-port', 'nuage-del', external=True) def delete_port(self, context, id, l3_port_check=True): if l3_port_check: self.prevent_l3_port_deletion(context, id) port = self._get_port(context, id) + nuage_vif_id = None params = { 'neutron_port_id': id, } @@ -212,12 +322,19 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2, sub_id) if not subnet_mapping: return super(NuagePlugin, self).delete_port(context, id) + + # Need to call this explicitly to delete vport to vporttag binding + if ext_sg.SECURITYGROUPS in port: + self._delete_port_security_group_bindings(context, id) + netpart_id = subnet_mapping['net_partition_id'] net_partition = nuagedb.get_net_partition_by_id(context.session, netpart_id) - # Need to call this explicitly to delete vport_vporttag_mapping + # Need to call this explicitly to delete vport if constants.NOVA_PORT_OWNER_PREF in port['device_owner']: + if nuage_port: + nuage_vif_id = nuage_port['nuage_vif_id'] # This was a VM Port filters = {'device_id': [port['device_id']]} ports = self.get_ports(context, filters) @@ -226,7 +343,7 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2, 'net_partition': net_partition, 'tenant': port['tenant_id'], 'mac': port['mac_address'], - 'nuage_vif_id': nuage_port['nuage_vif_id'], + 'nuage_vif_id': nuage_vif_id, 'id': port['device_id'] } self.nuageclient.delete_vms(params) @@ -264,6 +381,10 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2, def create_network(self, context, network): net = network['network'] with context.session.begin(subtransactions=True): + self._ensure_default_security_group( + context, + network['network']['tenant_id'] + ) net = super(NuagePlugin, self).create_network(context, network) self._process_l3_create(context, net, network['network']) @@ -1010,4 +1131,50 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2, if fip: self.nuageclient.delete_nuage_floatingip( fip['nuage_fip_id']) - super(NuagePlugin, self).delete_floatingip(context, id) \ No newline at end of file + super(NuagePlugin, self).delete_floatingip(context, id) + + def delete_security_group(self, context, id): + filters = {'security_group_id': [id]} + ports = self._get_port_security_group_bindings(context, + filters) + if ports: + raise ext_sg.SecurityGroupInUse(id=id) + sg_rules = self.get_security_group_rules(context, + {'security_group_id': [id]}) + + if sg_rules: + self.nuageclient.delete_nuage_sgrule(sg_rules) + self.nuageclient.delete_nuage_secgroup(id) + + super(NuagePlugin, self).delete_security_group(context, id) + + def create_security_group_rule(self, context, security_group_rule): + sg_rule = security_group_rule['security_group_rule'] + self.nuageclient.validate_nuage_sg_rule_definition(sg_rule) + sg_id = sg_rule['security_group_id'] + + local_sg_rule = super(NuagePlugin, + self).create_security_group_rule( + context, security_group_rule) + + try: + nuage_vptag = self.nuageclient.get_sg_vptag_mapping(sg_id) + if nuage_vptag: + sg_params = { + 'sg_id': sg_id, + 'neutron_sg_rule': local_sg_rule, + 'vptag': nuage_vptag + } + self.nuageclient.create_nuage_sgrule(sg_params) + except Exception: + with excutils.save_and_reraise_exception(): + super(NuagePlugin, + self).delete_security_group_rule(context, + local_sg_rule['id']) + + return local_sg_rule + + def delete_security_group_rule(self, context, id): + local_sg_rule = self.get_security_group_rule(context, id) + super(NuagePlugin, self).delete_security_group_rule(context, id) + self.nuageclient.delete_nuage_sgrule([local_sg_rule]) diff --git a/neutron/tests/unit/nuage/fake_nuageclient.py b/neutron/tests/unit/nuage/fake_nuageclient.py index dfad78608..bbfecd487 100644 --- a/neutron/tests/unit/nuage/fake_nuageclient.py +++ b/neutron/tests/unit/nuage/fake_nuageclient.py @@ -161,3 +161,27 @@ class FakeNuageClient(object): def get_usergroup(self, tenant, net_partition_id): return uuidutils.generate_uuid(), uuidutils.generate_uuid() + + def get_sg_vptag_mapping(self, id): + pass + + def validate_nuage_sg_rule_definition(self, params): + pass + + def create_nuage_sgrule(self, params): + pass + + def update_nuage_vport(self, params): + pass + + def delete_nuage_sgrule(self, params): + pass + + def delete_nuage_secgroup(self, params): + pass + + def process_port_create_security_group(self, params): + pass + + def delete_port_security_group_bindings(self, params): + pass diff --git a/neutron/tests/unit/nuage/test_nuage_plugin.py b/neutron/tests/unit/nuage/test_nuage_plugin.py index 244ebbde2..332494c48 100644 --- a/neutron/tests/unit/nuage/test_nuage_plugin.py +++ b/neutron/tests/unit/nuage/test_nuage_plugin.py @@ -29,6 +29,7 @@ from neutron.tests.unit import _test_extension_portbindings as test_bindings from neutron.tests.unit.nuage import fake_nuageclient from neutron.tests.unit import test_db_plugin from neutron.tests.unit import test_extension_extraroute as extraroute_test +from neutron.tests.unit import test_extension_security_group as test_sg from neutron.tests.unit import test_l3_plugin API_EXT_PATH = os.path.dirname(extensions.__file__) @@ -287,3 +288,8 @@ class TestNuageExtrarouteTestCase(NuagePluginV2TestCase, def test_network_update_external_failure(self): self._test_network_update_external_failure() + + +class TestNuageSecurityGroupTestCase(NuagePluginV2TestCase, + test_sg.TestSecurityGroups): + pass -- 2.45.2