port extra attribute extension for gbp mapping

Just a few lines of code to propose a way for internal users to
specify extra port attributes when creating a PT.
There's no persistence or API changes involved, and the workflow
will look like the "implicit port" workflow. The advantage of
having this is that some tweaks can be made to the Neutron port
by still having GBP as the port owner.

Closes-Bug: 1527360
Change-Id: Iebcda544d0b5aee3e02e4828228d662997008251
(cherry picked from commit 0d5cedc413)
This commit is contained in:
Ivar Lazzaro
2015-12-11 15:05:16 -08:00
committed by Sumit Naiksatam
parent b1d3ff08c4
commit b81798bd30
7 changed files with 173 additions and 6 deletions

View File

@@ -20,6 +20,7 @@ from sqlalchemy import orm
from gbpservice.neutron.db import gbp_quota_db as gquota
from gbpservice.neutron.db.grouppolicy import group_policy_db as gpdb
from gbpservice.neutron.extensions import group_policy as gpolicy
from gbpservice.neutron.services.grouppolicy.common import exceptions
LOG = logging.getLogger(__name__)
@@ -110,10 +111,11 @@ class GroupPolicyMappingDbPlugin(gpdb.GroupPolicyDbPlugin):
"""Group Policy Mapping interface implementation using SQLAlchemy models.
"""
def _make_policy_target_dict(self, pt, fields=None):
def _make_policy_target_dict(self, pt, fields=None, **kwargs):
res = super(GroupPolicyMappingDbPlugin,
self)._make_policy_target_dict(pt)
res['port_id'] = pt.port_id
res.update(kwargs)
return self._fields(res, fields)
def _make_policy_target_group_dict(self, ptg, fields=None):
@@ -226,11 +228,35 @@ class GroupPolicyMappingDbPlugin(gpdb.GroupPolicyDbPlugin):
context.session.query(PTGToSubnetAssociation).filter_by(
subnet_id=subnet_id)]
def _validate_pt_port_exta_attributes(self, context, pt):
attributes = pt.get('port_attributes')
if attributes:
# Check network ID not overridden
if 'network_id' in attributes:
raise exceptions.InvalidPortExtraAttributes(
attribute='network_id', reason='read only attribute')
if 'fixed_ips' in attributes:
ptg = self.get_policy_target_group(
context, pt['policy_target_group_id'])
subnets = ptg['subnets']
for fixed_ip in attributes.get('fixed_ips'):
if fixed_ip['subnet_id'] not in subnets:
raise exceptions.InvalidPortExtraAttributes(
attribute='fixed_ips:subnet_id',
reason='subnet not in PTG')
if 'allowed_address_pairs' in attributes:
# REVISIT(ivar); Could be allowed with certain restrictions,
# but we don't have a use case for it right now
raise exceptions.InvalidPortExtraAttributes(
attribute='allowed_address_pairs',
reason='read only attribute')
@log.log
def create_policy_target(self, context, policy_target):
pt = policy_target['policy_target']
tenant_id = self._get_tenant_id_for_create(context, pt)
with context.session.begin(subtransactions=True):
self._validate_pt_port_exta_attributes(context, pt)
pt_db = PolicyTargetMapping(id=uuidutils.generate_uuid(),
tenant_id=tenant_id,
name=pt['name'],
@@ -240,7 +266,8 @@ class GroupPolicyMappingDbPlugin(gpdb.GroupPolicyDbPlugin):
port_id=pt['port_id'],
cluster_id=pt['cluster_id'])
context.session.add(pt_db)
return self._make_policy_target_dict(pt_db)
return self._make_policy_target_dict(
pt_db, port_attributes=pt.get('port_attributes', {}))
@log.log
def get_policy_targets_count(self, context, filters=None):

View File

@@ -94,6 +94,11 @@ class InvalidPortForPTG(GroupPolicyBadRequest):
"%(policy_target_group_id)s.")
class InvalidPortExtraAttributes(GroupPolicyBadRequest):
message = _("Port extra attribute %(attribute)s is invalid for the "
"following reason: %(reason)s")
class InvalidSubnetForPTG(GroupPolicyBadRequest):
message = _("Subnet %(subnet_id)s does not belong to network "
"%(network_id)s associated with L2P %(l2p_id)s for PTG "

View File

@@ -1326,6 +1326,7 @@ class ResourceMappingDriver(api.PolicyDriver, local_api.LocalAPI,
'admin_state_up': True}
if context.current.get('group_default_gateway'):
attrs['fixed_ips'][0]['ip_address'] = subnet['gateway_ip']
attrs.update(context.current.get('port_attributes', {}))
port = self._create_port(context._plugin_context, attrs)
port_id = port['id']
self._mark_port_owned(context._plugin_context.session, port_id)
@@ -2449,10 +2450,10 @@ class ResourceMappingDriver(api.PolicyDriver, local_api.LocalAPI,
if subnet == port_subnet_id:
break
else:
raise exc.InvalidPortForPTG(port_id=port_id,
ptg_subnet_id=",".join(ptg.get('subnets')),
port_subnet_id=port_subnet_id,
policy_target_group_id=ptg_id)
raise exc.InvalidPortForPTG(
port_id=port_id, ptg_subnet_id=",".join(ptg.get('subnets')),
port_subnet_id=port_subnet_id,
policy_target_group_id=ptg_id)
def _get_ptg_l3p(self, context, ptg):
l3p_id = context._plugin.get_l2_policy(

View File

@@ -352,6 +352,8 @@ class GroupPolicyPlugin(group_policy_mapping_db.GroupPolicyMappingDbPlugin):
result['id'])
self.delete_policy_target(context, result['id'])
# Strip the extra port attributes
result.pop('port_attributes', None)
return result
@log.log

View File

@@ -13,12 +13,14 @@
import webob.exc
from neutron import context as nctx
from neutron.db import api as db_api
from neutron.db import model_base
from neutron.tests.unit.extensions import test_l3
from neutron.tests.unit import testlib_api
from gbpservice.neutron.db.grouppolicy import group_policy_mapping_db as gpmdb
from gbpservice.neutron.services.grouppolicy.common import exceptions as gpexc
from gbpservice.neutron.tests.unit.db.grouppolicy import (
test_group_policy_db as tgpdb)
@@ -244,3 +246,25 @@ class TestMappedGroupResourceAttrs(GroupPolicyMappingDbTestCase):
self._test_list_resources(
'external_segment', [external_segments[0]],
query_params='subnet_id=' + subnets[0])
def test_pt_port_extra_attributes_fail(self):
ptg = self.create_policy_target_group()['policy_target_group']
ctx = nctx.get_admin_context()
self.assertRaises(gpexc.InvalidPortExtraAttributes,
self._gbp_plugin.create_policy_target,
ctx, {'policy_target': {
'description': '', 'name': '',
'port_id': None, 'cluster_id': '',
'policy_target_group_id': ptg['id'],
'port_attributes': {'network_id': ''},
'tenant_id': self._tenant_id}})
self.assertRaises(gpexc.InvalidPortExtraAttributes,
self._gbp_plugin.create_policy_target,
ctx, {'policy_target': {
'description': '', 'name': '',
'port_id': None, 'cluster_id': '',
'policy_target_group_id': ptg['id'],
'port_attributes': {
'allowed_address_pairs': ''},
'tenant_id': self._tenant_id}})

View File

@@ -37,6 +37,7 @@ from gbpservice.common import utils
from gbpservice.neutron.db.grouppolicy import group_policy_db as gpdb
from gbpservice.neutron.db import servicechain_db
from gbpservice.neutron.services.grouppolicy.common import constants as gconst
from gbpservice.neutron.services.grouppolicy.common import exceptions as gpexc
from gbpservice.neutron.services.grouppolicy import config
from gbpservice.neutron.services.grouppolicy.drivers import chain_mapping
from gbpservice.neutron.services.grouppolicy.drivers import nsp_manager
@@ -779,6 +780,97 @@ class TestPolicyTarget(ResourceMappingTestCase, TestClusterIdMixin):
self.create_policy_target(
policy_target_group_id=ptg['id'], expected_res_status=500)
def test_port_extra_attributes(self, extra=None):
extra = extra or {}
ptg = self.create_policy_target_group()['policy_target_group']
# Issue internal call
ctx = nctx.get_admin_context()
data = {'description': '', 'name': '',
'port_id': None, 'cluster_id': '',
'policy_target_group_id': ptg['id'],
'port_attributes': {'mac_address':
'aa:bb:cc:dd:ee:ff'},
'tenant_id': self._tenant_id}
data.update(extra)
pt = self._gbp_plugin.create_policy_target(
ctx, {'policy_target': data})
port = self._get_object('ports', pt['port_id'], self.api)['port']
self.assertEqual('aa:bb:cc:dd:ee:ff', port['mac_address'])
def test_weird_port_extra_attributes_ignored(self, extra=None):
extra = extra or {}
ptg = self.create_policy_target_group()['policy_target_group']
# Calling an internal API, all additional weird attributes are ignored.
# This will change once we go to a separated server
ctx = nctx.get_admin_context()
data = {'description': '', 'name': '',
'port_id': None, 'cluster_id': '',
'policy_target_group_id': ptg['id'],
'port_attributes': {'non_port_attribute': ''},
'tenant_id': self._tenant_id}
data.update(extra)
pt = self._gbp_plugin.create_policy_target(
ctx, {'policy_target': data})
self.assertIsNotNone(pt['id'])
def test_port_extra_attributes_fixed_ips(self, extra=None):
extra = extra or {}
l2p = self.create_l2_policy()['l2_policy']
network = self._get_object('networks', l2p['network_id'], self.api)
with self.subnet(network=network, cidr='10.10.1.0/24') as subnet:
subnet = subnet['subnet']
ptg = self.create_policy_target_group(
subnets=[subnet['id']],
l2_policy_id=l2p['id'])['policy_target_group']
fixed_ips = [{'subnet_id': subnet['id'],
'ip_address': '10.10.1.10'}]
ctx = nctx.get_admin_context()
data = {'description': '', 'name': '',
'port_id': None, 'cluster_id': '',
'policy_target_group_id': ptg['id'],
'port_attributes': {'fixed_ips': fixed_ips},
'tenant_id': self._tenant_id}
data.update(extra)
pt = self._gbp_plugin.create_policy_target(
ctx, {'policy_target': data})
port = self._get_object('ports', pt['port_id'], self.api)['port']
self.assertEqual(fixed_ips, port['fixed_ips'])
# Now use a different subnet
ctx = nctx.get_admin_context()
data = {'description': '', 'name': '', 'port_id': None,
'cluster_id': '', 'policy_target_group_id': ptg['id'],
'port_attributes': {'fixed_ips': [{
'subnet_id': 'not_in_ptg',
'ip_address': '10.10.1.10'}]},
'tenant_id': self._tenant_id}
data.update(extra)
self.assertRaises(gpexc.InvalidPortExtraAttributes,
self._gbp_plugin.create_policy_target,
ctx, {'policy_target': data})
def test_port_extra_attributes_implicit(self, extra=None):
extra = extra or {}
ptg = self.create_policy_target_group()['policy_target_group']
ctx = nctx.get_admin_context()
data = {'description': '', 'name': '',
'port_id': None, 'cluster_id': '',
'policy_target_group_id': ptg['id'],
'port_attributes': {'mac_address':
'aa:bb:cc:dd:ee:ff'},
'tenant_id': self._tenant_id}
data.update(extra)
pt = self._gbp_plugin.create_policy_target(
ctx, {'policy_target': data})
# Port exists
self._get_object('ports', pt['port_id'], self.api,
expected_res_status=200)
self.delete_policy_target(pt['id'], expected_res_status=204)
# Port is gone since owned by GBP
self._get_object('ports', pt['port_id'], self.api,
expected_res_status=404)
class TestPolicyTargetGroupWithDNSConfiguration(ResourceMappingTestCase):

View File

@@ -314,6 +314,22 @@ class TestPolicyTarget(ResourceMappingProxyGroupGBPTestCase,
super(TestPolicyTarget, self).test_implicit_port_lifecycle(
proxy_ip_pool='182.169.0.0/16')
def test_weird_port_extra_attributes_ignored(self):
super(TestPolicyTarget, self).test_weird_port_extra_attributes_ignored(
extra={'proxy_gateway': False, 'group_default_gateway': False})
def test_port_extra_attributes(self):
super(TestPolicyTarget, self).test_port_extra_attributes(
extra={'proxy_gateway': False, 'group_default_gateway': False})
def test_port_extra_attributes_fixed_ips(self):
super(TestPolicyTarget, self).test_port_extra_attributes_fixed_ips(
extra={'proxy_gateway': False, 'group_default_gateway': False})
def test_port_extra_attributes_implicit(self):
super(TestPolicyTarget, self).test_port_extra_attributes_implicit(
extra={'proxy_gateway': False, 'group_default_gateway': False})
class TestPolicyTargetGroup(ResourceMappingProxyGroupGBPTestCase,
test_resource_mapping.TestPolicyTargetGroup):