diff --git a/neutron/plugins/nuage/plugin.py b/neutron/plugins/nuage/plugin.py index 164154cfd13..f64e587f951 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 dfad786084d..bbfecd487bd 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 244ebbde223..332494c4898 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