[apic-mapping] Segmentation Label extension for PT

This patch defines a new extension: cisco_apic_gbp_segmentation_label,
for the apic policy drivers. An extension attribute:
segmentation_labels, that extends the Policy Target definition, is
being introduced in this extension.

A corresponding extension driver: apic_segmentation_label, that processes
this extension, is also being added. This extension driver should be
configured for this extension to be available. The driver name should be
added to the existing list of extension drivers under:
[group_policy]
extension_drivers=<existing_ext_drivers>,apic_segmentation_label

The segementation_labels attribute is a list of strings. Each string can
be upto 255 characters long. These labels are not interpreted by GBP
but are instead passed downstream by the apic policy driver. It is
assumed that these are defined outside of OpenStack and the backend
system can appropriately interpret them.

The get_gbp_details() RPC call implemented by the apic policy driver
will return the segmentation_labels in its body if the
'segmentation_labels' attribute is populated for the policy_target.

A CLI option: --segmentation-labels will be provided for the
policy_target create and update operations. This CLI option will accept
a comma separated string as the option value.

Change-Id: I360bf9f7f1d4bdca76d4f16b7535a6416f430830
This commit is contained in:
Sumit Naiksatam 2016-09-17 09:53:05 -07:00
parent 8784548bce
commit 2cb02e2ea1
10 changed files with 343 additions and 3 deletions

View File

@ -0,0 +1,49 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from neutron.db import model_base
import sqlalchemy as sa
class ApicSegmentationLabelDB(model_base.BASEV2):
__tablename__ = 'gp_apic_mapping_segmentation_lablels'
policy_target_id = sa.Column(
sa.String(36), sa.ForeignKey('gp_policy_targets.id',
ondelete="CASCADE"), primary_key=True)
segmentation_label = sa.Column(sa.String(255), primary_key=True)
class ApicSegmentationLabelDBMixin(object):
def get_policy_target_segmentation_labels(self, session, policy_target_id):
rows = (session.query(ApicSegmentationLabelDB).filter_by(
policy_target_id=policy_target_id).all())
return rows
def get_policy_target_segmentation_label(self, session, policy_target_id,
segmentation_label):
row = (session.query(ApicSegmentationLabelDB).filter_by(
policy_target_id=policy_target_id,
segmentation_label=segmentation_label).one())
return row
def add_policy_target_segmentation_label(self, session, policy_target_id,
segmentation_label):
row = ApicSegmentationLabelDB(policy_target_id=policy_target_id,
segmentation_label=segmentation_label)
session.add(row)
def delete_policy_target_segmentation_label(
self, session, policy_target_id, segmentation_label):
row = self.get_policy_target_segmentation_label(
session, policy_target_id, segmentation_label)
session.delete(row)

View File

@ -0,0 +1,45 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""segmentation labels for PT (apic)
Revision ID: 092e4b1aeb0a
Revises: d4bb487a81b8
Create Date: 2016-09-18 18:58:26.810742
"""
# revision identifiers, used by Alembic.
revision = '092e4b1aeb0a'
down_revision = 'd4bb487a81b8'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.create_table(
'gp_apic_mapping_segmentation_labels',
sa.Column('policy_target_id', sa.String(length=36), nullable=False),
sa.Column('segmentation_label', sa.String(length=255),
nullable=False),
sa.ForeignKeyConstraint(
['policy_target_id'], ['gp_policy_targets.id'],
name='gp_apic_mapping_segmentation_lablel_fk_ptid',
ondelete='CASCADE'),
sa.PrimaryKeyConstraint('policy_target_id', 'segmentation_label')
)
def downgrade():
pass

View File

@ -1 +1 @@
d4bb487a81b8
092e4b1aeb0a

View File

@ -0,0 +1,55 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from neutron.api import extensions
from neutron.api.v2 import attributes as attr
from gbpservice.neutron.extensions import group_policy as gp
CISCO_APIC_GBP_SEGMENTATION_LABEL_EXT = 'cisco_apic_gbp_label_segmentation'
EXTENDED_ATTRIBUTES_2_0 = {
gp.POLICY_TARGETS: {
'segmentation_labels': {
'allow_post': True, 'allow_put': True, 'default': None,
'validate': {'type:list_of_unique_strings': None},
'convert_to': attr.convert_none_to_empty_list,
'is_visible': True},
},
}
class Apic_segmentation_label(extensions.ExtensionDescriptor):
@classmethod
def get_name(cls):
return "APIC GBP Segmentation Extension"
@classmethod
def get_alias(cls):
return CISCO_APIC_GBP_SEGMENTATION_LABEL_EXT
@classmethod
def get_description(cls):
return _("This extension supports a list of (micro)segmentation "
"labels that can be applied to the Policy Target resource.")
@classmethod
def get_updated(cls):
return "2016-08-03T10:00:00-00:00"
def get_extended_resources(self, version):
if version == "2.0":
return EXTENDED_ATTRIBUTES_2_0
else:
return {}

View File

@ -303,6 +303,7 @@ class ApicMappingDriver(api.ResourceMappingDriver,
self.nat_enabled = self.apic_manager.use_vmm
self.per_tenant_nat_epg = self.apic_manager.per_tenant_nat_epg
self._gbp_plugin = None
self._apic_segmentation_label_driver = None
self.l3out_vlan_alloc = l3out_vlan_alloc.L3outVlanAlloc()
self.l3out_vlan_alloc.sync_vlan_allocations(
self.apic_manager.ext_net_dict)
@ -330,6 +331,17 @@ class ApicMappingDriver(api.ResourceMappingDriver,
.get("GROUP_POLICY"))
return self._gbp_plugin
@property
def apic_segmentation_label_driver(self):
if not self._apic_segmentation_label_driver:
ext_drivers = self.gbp_plugin.extension_manager.ordered_ext_drivers
for driver in ext_drivers:
if 'apic_segmentation_label' == driver.name:
self._apic_segmentation_label_driver = (
driver.obj)
break
return self._apic_segmentation_label_driver
# HA RPC call
def update_ip_owner(self, ip_owner_info):
# Needs to handle proxy ports
@ -506,6 +518,9 @@ class ApicMappingDriver(api.ResourceMappingDriver,
# Active chain head must have changed in a concurrent
# operation, get out of here
pass
if self.apic_segmentation_label_driver and pt and (
'segmentation_labels' in pt):
details['segmentation_labels'] = pt['segmentation_labels']
return details
def get_snat_ip_for_vrf(self, context, vrf_id, network, es_name=None):

View File

@ -0,0 +1,70 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_log import log as logging
from gbpservice.neutron.db.grouppolicy.extensions import (
apic_segmentation_label_db as db)
from gbpservice.neutron.extensions import apic_segmentation_label as aslext
from gbpservice.neutron.services.grouppolicy import (
group_policy_driver_api as api)
LOG = logging.getLogger(__name__)
class ApicSegmentationLabelExtensionDriver(api.ExtensionDriver,
db.ApicSegmentationLabelDBMixin):
_supported_extension_alias = aslext.CISCO_APIC_GBP_SEGMENTATION_LABEL_EXT
_extension_dict = aslext.EXTENDED_ATTRIBUTES_2_0
def __init__(self):
LOG.debug("APIC Segmentation Label Extension Driver __init__")
self._policy_driver = None
def initialize(self):
pass
@property
def extension_alias(self):
return self._supported_extension_alias
def process_create_policy_target(self, session, data, result):
pt = data['policy_target']
if 'segmentation_labels' in pt:
for label in pt['segmentation_labels']:
self.add_policy_target_segmentation_label(
session, policy_target_id=result['id'],
segmentation_label=label)
def process_update_policy_target(self, session, data, result):
pt = data['policy_target']
if not 'segmentation_labels' in pt:
return
rows = self.get_policy_target_segmentation_labels(
session, policy_target_id=result['id'])
old_labels = [r.segmentation_label for r in rows]
add_labels = list(set(pt['segmentation_labels']) - set(old_labels))
for label in add_labels:
self.add_policy_target_segmentation_label(
session, policy_target_id=result['id'],
segmentation_label=label)
delete_labels = list(set(old_labels) - set(pt['segmentation_labels']))
for label in delete_labels:
self.delete_policy_target_segmentation_label(
session, policy_target_id=result['id'],
segmentation_label=label)
def extend_policy_target_dict(self, session, result):
rows = self.get_policy_target_segmentation_labels(
session, policy_target_id=result['id'])
labels = [r.segmentation_label for r in rows]
result['segmentation_labels'] = labels

View File

@ -94,6 +94,8 @@ class MockCallRecorder(mock.Mock):
class ApicMappingTestCase(
test_rmd.ResourceMappingTestCase,
mocked.ControllerMixin, mocked.ConfigMixin):
_extension_drivers = ['apic_segmentation_label']
_extension_path = None
def setUp(self, sc_plugin=None, nat_enabled=True,
pre_existing_l3out=False, default_agent_conf=True,
@ -105,6 +107,13 @@ class ApicMappingTestCase(
cfg.CONF.register_opts(sg_cfg.security_group_opts, 'SECURITYGROUP')
config.cfg.CONF.set_override('enable_security_group', False,
group='SECURITYGROUP')
if not cfg.CONF.group_policy.extension_drivers:
config.cfg.CONF.set_override('extension_drivers',
self._extension_drivers,
group='group_policy')
if self._extension_path:
config.cfg.CONF.set_override(
'api_extensions_path', self._extension_path)
n_rpc.create_connection = mock.Mock()
amap.ApicMappingDriver.get_apic_manager = mock.Mock(
return_value=mock.MagicMock(
@ -425,12 +434,17 @@ class TestPolicyTarget(ApicMappingTestCase):
ptg = self.create_policy_target_group(
name="ptg1", l2_policy_id=l2p['id'],
network_service_policy_id=nsp['id'])['policy_target_group']
segmentation_labels = ['label1', 'label2']
pt1 = self.create_policy_target(
policy_target_group_id=ptg['id'])['policy_target']
policy_target_group_id=ptg['id'],
segmentation_labels=segmentation_labels)['policy_target']
self._bind_port_to_host(pt1['port_id'], 'h1')
mapping = self.driver.get_gbp_details(context.get_admin_context(),
device='tap%s' % pt1['port_id'], host='h1')
if 'apic_segmentation_label' in self._extension_drivers:
self.assertItemsEqual(segmentation_labels,
mapping['segmentation_labels'])
req_mapping = self.driver.request_endpoint_details(
context.get_admin_context(),
request={'device': 'tap%s' % pt1['port_id'], 'host': 'h1',

View File

@ -0,0 +1,90 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from neutron.db import api as db_api
from gbpservice.neutron.db.grouppolicy.extensions import (
apic_segmentation_label_db as db)
from gbpservice.neutron.tests.unit.services.grouppolicy import (
test_extension_driver_api as test_ext_base)
class ExtensionDriverTestCaseMixin(object):
def test_pt_lifecycle(self):
ptg = self.create_policy_target_group()['policy_target_group']
pt = self.create_policy_target(
policy_target_group_id=ptg['id'])['policy_target']
self.assertEqual([], pt['segmentation_labels'])
pt = self.show_policy_target(
pt['id'], expected_res_status=200)['policy_target']
self.assertEqual([], pt['segmentation_labels'])
self.delete_policy_target(pt['id'], expected_res_status=204)
labels = []
pt = self.create_policy_target(
policy_target_group_id=ptg['id'],
segmentation_labels=labels)['policy_target']
self.assertItemsEqual(labels, pt['segmentation_labels'])
pt = self.show_policy_target(
pt['id'], expected_res_status=200)['policy_target']
self.assertItemsEqual([], pt['segmentation_labels'])
self.delete_policy_target(pt['id'], expected_res_status=204)
labels = ['red', 'blue']
pt = self.create_policy_target(
policy_target_group_id=ptg['id'],
segmentation_labels=labels)['policy_target']
self.assertItemsEqual(labels, pt['segmentation_labels'])
pt = self.show_policy_target(
pt['id'], expected_res_status=200)['policy_target']
self.assertItemsEqual(labels, pt['segmentation_labels'])
labels = ['green', 'black', 'red']
pt = self.update_policy_target(
pt['id'], segmentation_labels=labels,
expected_res_status=200)['policy_target']
self.assertItemsEqual(labels, pt['segmentation_labels'])
pt = self.show_policy_target(
pt['id'], expected_res_status=200)['policy_target']
self.assertItemsEqual(labels, pt['segmentation_labels'])
labels = []
pt = self.update_policy_target(
pt['id'], segmentation_labels=labels,
expected_res_status=200)['policy_target']
self.assertItemsEqual(labels, pt['segmentation_labels'])
pt = self.show_policy_target(
pt['id'], expected_res_status=200)['policy_target']
self.assertItemsEqual(labels, pt['segmentation_labels'])
labels = ['black']
pt = self.update_policy_target(
pt['id'], segmentation_labels=labels,
expected_res_status=200)['policy_target']
self.assertItemsEqual(labels, pt['segmentation_labels'])
pt = self.show_policy_target(
pt['id'], expected_res_status=200)['policy_target']
self.assertItemsEqual(labels, pt['segmentation_labels'])
self.delete_policy_target(pt['id'], expected_res_status=204)
session = db_api.get_session()
rows = (session.query(db.ApicSegmentationLabelDB).filter_by(
policy_target_id=pt['id']).all())
self.assertEqual([], rows)
class ExtensionDriverTestCase(test_ext_base.ExtensionDriverTestBase,
ExtensionDriverTestCaseMixin):
_extension_drivers = ['apic_segmentation_label']
_extension_path = None

View File

@ -36,7 +36,8 @@ class ApicMappingStitchingPlumberGBPTestCase(
def setUp(self, plumber='stitching_plumber'):
cfg.CONF.set_override(
'extension_drivers', ['proxy_group'], group='group_policy')
'extension_drivers', ['apic_segmentation_label',
'proxy_group'], group='group_policy')
cfg.CONF.set_override('node_plumber', plumber,
group='node_composition_plugin')
super(ApicMappingStitchingPlumberGBPTestCase, self).setUp(

View File

@ -59,6 +59,7 @@ gbpservice.neutron.group_policy.extension_drivers =
test = gbpservice.neutron.tests.unit.services.grouppolicy.test_extension_driver_api:TestExtensionDriver
proxy_group = gbpservice.neutron.services.grouppolicy.drivers.extensions.proxy_group_driver:ProxyGroupDriver
aim_extension = gbpservice.neutron.services.grouppolicy.drivers.extensions.aim_mapping_extension_driver:AIMExtensionDriver
apic_segmentation_label = gbpservice.neutron.services.grouppolicy.drivers.extensions.apic_segmentation_label_driver:ApicSegmentationLabelExtensionDriver
gbpservice.neutron.group_policy.policy_drivers =
dummy = gbpservice.neutron.services.grouppolicy.drivers.dummy_driver:NoopDriver
implicit_policy = gbpservice.neutron.services.grouppolicy.drivers.implicit_policy:ImplicitPolicyDriver