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
This commit is contained in:
Divya ChanneGowda 2014-07-15 14:33:30 -07:00
parent 50126be5ba
commit b24aba2059
3 changed files with 211 additions and 14 deletions

View File

@ -32,11 +32,14 @@ from neutron.db import external_net_db
from neutron.db import extraroute_db from neutron.db import extraroute_db
from neutron.db import l3_db from neutron.db import l3_db
from neutron.db import quota_db # noqa 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 external_net
from neutron.extensions import l3 from neutron.extensions import l3
from neutron.extensions import portbindings from neutron.extensions import portbindings
from neutron.extensions import securitygroup as ext_sg
from neutron.openstack.common import excutils from neutron.openstack.common import excutils
from neutron.openstack.common import importutils 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 config
from neutron.plugins.nuage.common import constants from neutron.plugins.nuage.common import constants
from neutron.plugins.nuage.common import exceptions as nuage_exc 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, external_net_db.External_net_db_mixin,
extraroute_db.ExtraRoute_db_mixin, extraroute_db.ExtraRoute_db_mixin,
l3_db.L3_NAT_db_mixin, l3_db.L3_NAT_db_mixin,
netpartition.NetPartitionPluginBase): netpartition.NetPartitionPluginBase,
sg_db.SecurityGroupDbMixin):
"""Class that implements Nuage Networks' plugin functionality.""" """Class that implements Nuage Networks' plugin functionality."""
supported_extension_aliases = ["router", "binding", "external-net", supported_extension_aliases = ["router", "binding", "external-net",
"net-partition", "nuage-router", "net-partition", "nuage-router",
"nuage-subnet", "quotas", "extraroute"] "nuage-subnet", "quotas",
"extraroute", "security-group"]
binding_view = "extension:port_binding:view" binding_view = "extension:port_binding:view"
@ -122,18 +127,93 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2,
'net_partition': net_partition, 'net_partition': net_partition,
'ip': port['fixed_ips'][0]['ip_address'], 'ip': port['fixed_ips'][0]['ip_address'],
'no_of_ports': len(ports), '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): def create_port(self, context, port):
session = context.session session = context.session
with session.begin(subtransactions=True): 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) port = super(NuagePlugin, self).create_port(context, port)
device_owner = port.get('device_owner', None) device_owner = port.get('device_owner', None)
if (device_owner and if device_owner not in constants.AUTO_CREATE_PORT_OWNERS:
device_owner not in constants.AUTO_CREATE_PORT_OWNERS):
if 'fixed_ips' not in port or len(port['fixed_ips']) == 0: if 'fixed_ips' not in port or len(port['fixed_ips']) == 0:
return self._extend_port_dict_binding(context, port) return self._extend_port_dict_binding(context, port)
subnet_id = port['fixed_ips'][0]['subnet_id'] subnet_id = port['fixed_ips'][0]['subnet_id']
@ -154,17 +234,23 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2,
super(NuagePlugin, self).delete_port( super(NuagePlugin, self).delete_port(
context, context,
port['id']) 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) return self._extend_port_dict_binding(context, port)
def update_port(self, context, id, port): def update_port(self, context, id, port):
p = port['port'] p = port['port']
sg_groups = None
if p.get('device_owner', '').startswith( if p.get('device_owner', '').startswith(
constants.NOVA_PORT_OWNER_PREF): constants.NOVA_PORT_OWNER_PREF):
session = context.session session = context.session
with session.begin(subtransactions=True): with session.begin(subtransactions=True):
port = self._get_port(context, id) port = self._get_port(context, id)
port.update(p) 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) return self._make_port_dict(port)
subnet_id = port['fixed_ips'][0]['subnet_id'] subnet_id = port['fixed_ips'][0]['subnet_id']
@ -178,24 +264,48 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2,
'neutron_port_id': id, 'neutron_port_id': id,
} }
nuage_port = self.nuageclient.get_nuage_port_by_id(params) nuage_port = self.nuageclient.get_nuage_port_by_id(params)
if not nuage_port: if not nuage_port or not nuage_port.get('nuage_vport_id'):
msg = (_("Port %s not found on VSD") % id)
raise n_exc.BadRequest(resource='port', msg=msg)
if not nuage_port['nuage_vport_id']:
self._create_update_port(context, port, self._create_update_port(context, port,
subnet_mapping[ subnet_mapping[
'net_partition_id'], 'net_partition_id'],
subnet_mapping['nuage_subnet_id']) subnet_mapping['nuage_subnet_id'])
updated_port = self._make_port_dict(port) 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: else:
updated_port = super(NuagePlugin, self).update_port(context, id, updated_port = super(NuagePlugin, self).update_port(context, id,
port) 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 return updated_port
@lockutils.synchronized('delete-port', 'nuage-del', external=True)
def delete_port(self, context, id, l3_port_check=True): def delete_port(self, context, id, l3_port_check=True):
if l3_port_check: if l3_port_check:
self.prevent_l3_port_deletion(context, id) self.prevent_l3_port_deletion(context, id)
port = self._get_port(context, id) port = self._get_port(context, id)
nuage_vif_id = None
params = { params = {
'neutron_port_id': id, 'neutron_port_id': id,
} }
@ -212,12 +322,19 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2,
sub_id) sub_id)
if not subnet_mapping: if not subnet_mapping:
return super(NuagePlugin, self).delete_port(context, id) 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'] netpart_id = subnet_mapping['net_partition_id']
net_partition = nuagedb.get_net_partition_by_id(context.session, net_partition = nuagedb.get_net_partition_by_id(context.session,
netpart_id) 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 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 # This was a VM Port
filters = {'device_id': [port['device_id']]} filters = {'device_id': [port['device_id']]}
ports = self.get_ports(context, filters) ports = self.get_ports(context, filters)
@ -226,7 +343,7 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2,
'net_partition': net_partition, 'net_partition': net_partition,
'tenant': port['tenant_id'], 'tenant': port['tenant_id'],
'mac': port['mac_address'], 'mac': port['mac_address'],
'nuage_vif_id': nuage_port['nuage_vif_id'], 'nuage_vif_id': nuage_vif_id,
'id': port['device_id'] 'id': port['device_id']
} }
self.nuageclient.delete_vms(params) self.nuageclient.delete_vms(params)
@ -264,6 +381,10 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2,
def create_network(self, context, network): def create_network(self, context, network):
net = network['network'] net = network['network']
with context.session.begin(subtransactions=True): with context.session.begin(subtransactions=True):
self._ensure_default_security_group(
context,
network['network']['tenant_id']
)
net = super(NuagePlugin, self).create_network(context, net = super(NuagePlugin, self).create_network(context,
network) network)
self._process_l3_create(context, net, network['network']) self._process_l3_create(context, net, network['network'])
@ -1010,4 +1131,50 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2,
if fip: if fip:
self.nuageclient.delete_nuage_floatingip( self.nuageclient.delete_nuage_floatingip(
fip['nuage_fip_id']) fip['nuage_fip_id'])
super(NuagePlugin, self).delete_floatingip(context, id) 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])

View File

@ -161,3 +161,27 @@ class FakeNuageClient(object):
def get_usergroup(self, tenant, net_partition_id): def get_usergroup(self, tenant, net_partition_id):
return uuidutils.generate_uuid(), uuidutils.generate_uuid() 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

View File

@ -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.nuage import fake_nuageclient
from neutron.tests.unit import test_db_plugin 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_extraroute as extraroute_test
from neutron.tests.unit import test_extension_security_group as test_sg
from neutron.tests.unit import test_l3_plugin from neutron.tests.unit import test_l3_plugin
API_EXT_PATH = os.path.dirname(extensions.__file__) API_EXT_PATH = os.path.dirname(extensions.__file__)
@ -287,3 +288,8 @@ class TestNuageExtrarouteTestCase(NuagePluginV2TestCase,
def test_network_update_external_failure(self): def test_network_update_external_failure(self):
self._test_network_update_external_failure() self._test_network_update_external_failure()
class TestNuageSecurityGroupTestCase(NuagePluginV2TestCase,
test_sg.TestSecurityGroups):
pass