add service_management attribute to PTG

Partially implements blueprint node-centric-chain-plugin

Change-Id: I5cf7d95648800dd1f872e77fe7f346e5dce1a49f
This commit is contained in:
Ivar Lazzaro 2015-06-07 20:48:50 -07:00
parent 4f591851a0
commit b9a168af76
16 changed files with 198 additions and 34 deletions

View File

@ -22,6 +22,7 @@
"create_policy_target_group": "",
"create_policy_target_group:shared": "rule:admin_only",
"create_policy_target_group:service_management": "rule:admin_only",
"get_policy_target_group": "rule:admin_or_owner or rule:shared_ptg",
"update_policy_target_group:shared": "rule:admin_only",

View File

@ -114,6 +114,7 @@ class PolicyTargetGroup(gquota.GBPQuotaBase, model_base.BASEV2,
PTGToPRSConsumingAssociation,
backref='consuming_policy_target_group', cascade='all, delete-orphan')
shared = sa.Column(sa.Boolean)
service_management = sa.Column(sa.Boolean)
class L2Policy(gquota.GBPQuotaBase, model_base.BASEV2, models_v2.HasId,
@ -826,7 +827,8 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase,
'description': ptg['description'],
'l2_policy_id': ptg['l2_policy_id'],
'network_service_policy_id': ptg['network_service_policy_id'],
'shared': ptg.get('shared', False), }
'shared': ptg.get('shared', False),
'service_management': ptg.get('service_management', False)}
res['policy_targets'] = [
pt['id'] for pt in ptg['policy_targets']]
res['provided_policy_rule_sets'] = (
@ -1065,6 +1067,13 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase,
value = None
return value
def _validate_service_management_ptg(self, context, tenant_id):
# Verify whether a Management PTG already exists for this tenant
if self.get_policy_target_groups(
context, {'tenant_id': [tenant_id],
'service_management': [True]}):
raise gpolicy.ManagementPolicyTargetGroupExists()
@staticmethod
def validate_ip_pool(ip_pool, ip_version):
attr._validate_subnet(ip_pool)
@ -1153,12 +1162,15 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase,
ptg = policy_target_group['policy_target_group']
tenant_id = self._get_tenant_id_for_create(context, ptg)
with context.session.begin(subtransactions=True):
if ptg['service_management']:
self._validate_service_management_ptg(context, tenant_id)
ptg_db = PolicyTargetGroup(
id=uuidutils.generate_uuid(), tenant_id=tenant_id,
name=ptg['name'], description=ptg['description'],
l2_policy_id=ptg['l2_policy_id'],
network_service_policy_id=ptg['network_service_policy_id'],
shared=ptg.get('shared', False))
shared=ptg.get('shared', False),
service_management=ptg.get('service_management', False))
context.session.add(ptg_db)
self._process_policy_rule_sets_for_ptg(context, ptg_db, ptg)
return self._make_policy_target_group_dict(ptg_db)

View File

@ -204,14 +204,15 @@ class GroupPolicyMappingDbPlugin(gpdb.GroupPolicyDbPlugin):
ptg = policy_target_group['policy_target_group']
tenant_id = self._get_tenant_id_for_create(context, ptg)
with context.session.begin(subtransactions=True):
ptg_db = PolicyTargetGroupMapping(id=uuidutils.generate_uuid(),
tenant_id=tenant_id,
name=ptg['name'],
description=ptg['description'],
l2_policy_id=ptg['l2_policy_id'],
network_service_policy_id=
ptg['network_service_policy_id'],
shared=ptg.get('shared', False))
if ptg['service_management']:
self._validate_service_management_ptg(context, tenant_id)
ptg_db = PolicyTargetGroupMapping(
id=uuidutils.generate_uuid(), tenant_id=tenant_id,
name=ptg['name'], description=ptg['description'],
l2_policy_id=ptg['l2_policy_id'],
network_service_policy_id=ptg['network_service_policy_id'],
shared=ptg.get('shared', False),
service_management=ptg.get('service_management', False))
context.session.add(ptg_db)
if 'subnets' in ptg:
for subnet in ptg['subnets']:

View File

@ -1 +1 @@
5358a28fb97d
dea911257ac6

View File

@ -0,0 +1,40 @@
# 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.
#
"""service_management_ptg
"""
# revision identifiers, used by Alembic.
revision = 'dea911257ac6'
down_revision = '5358a28fb97d'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.add_column(
'gp_policy_target_groups',
sa.Column('service_management', sa.Boolean)
)
op.add_column(
'sc_instances',
sa.Column('management_ptg_id', sa.String(length=36), nullable=True)
)
def downgrade():
pass

View File

@ -95,6 +95,9 @@ class ServiceChainInstance(gquota.GBPQuotaBase, model_base.BASEV2,
consumer_ptg_id = sa.Column(sa.String(36),
# sa.ForeignKey('gp_policy_target_groups.id'),
nullable=True)
management_ptg_id = sa.Column(sa.String(36),
# sa.ForeignKey('gp_policy_target_groups.id'),
nullable=True)
classifier_id = sa.Column(sa.String(36),
# sa.ForeignKey('gp_policy_classifiers.id'),
nullable=True)
@ -228,6 +231,7 @@ class ServiceChainDbPlugin(schain.ServiceChainPluginBase,
'config_param_values': instance['config_param_values'],
'provider_ptg_id': instance['provider_ptg_id'],
'consumer_ptg_id': instance['consumer_ptg_id'],
'management_ptg_id': instance['management_ptg_id'],
'classifier_id': instance['classifier_id']}
res['servicechain_specs'] = [sc_spec['servicechain_spec_id']
for sc_spec in instance['specs']]
@ -485,6 +489,18 @@ class ServiceChainDbPlugin(schain.ServiceChainPluginBase,
instance = servicechain_instance['servicechain_instance']
tenant_id = self._get_tenant_id_for_create(context, instance)
with context.session.begin(subtransactions=True):
if not instance['management_ptg_id']:
management_groups = (
self._grouppolicy_plugin.get_policy_target_groups(
context, {'service_management': [True],
'tenant_id': [instance['tenant_id']]}))
if not management_groups:
# Fall back on shared service management
management_groups = (
self._grouppolicy_plugin.get_policy_target_groups(
context, {'service_management': [True]}))
if management_groups:
instance['management_ptg_id'] = management_groups[0]['id']
instance_db = ServiceChainInstance(
id=uuidutils.generate_uuid(),
tenant_id=tenant_id, name=instance['name'],
@ -492,6 +508,7 @@ class ServiceChainDbPlugin(schain.ServiceChainPluginBase,
config_param_values=instance['config_param_values'],
provider_ptg_id=instance['provider_ptg_id'],
consumer_ptg_id=instance['consumer_ptg_id'],
management_ptg_id=instance['management_ptg_id'],
classifier_id=instance['classifier_id'])
self._process_specs_for_instance(context, instance_db, instance)
context.session.add(instance_db)

View File

@ -58,6 +58,11 @@ class PolicyTargetGroupNotFound(nexc.NotFound):
"be found")
class ManagementPolicyTargetGroupExists(nexc.BadRequest):
message = _("Service Management Policy Target Group already exists for "
"this tenant.")
class L2PolicyNotFound(nexc.NotFound):
message = _("L2Policy %(l2_policy_id)s could not be found")
@ -432,6 +437,11 @@ RESOURCE_ATTRIBUTE_MAP = {
'default': False, 'convert_to': attr.convert_to_boolean,
'is_visible': True, 'required_by_policy': True,
'enforce_policy': True},
'service_management': {'allow_post': True, 'allow_put': True,
'default': False,
'convert_to': attr.convert_to_boolean,
'is_visible': True, 'required_by_policy': True,
'enforce_policy': True},
},
L2_POLICIES: {
'id': {'allow_post': False, 'allow_put': False,

View File

@ -188,6 +188,10 @@ RESOURCE_ATTRIBUTE_MAP = {
'validate': {'type:uuid_or_none': None},
'is_visible': True, 'default': None,
'required': True},
'management_ptg_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:uuid_or_none': None},
'is_visible': True, 'default': None,
'required': True},
'classifier_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:uuid_or_none': None},
'is_visible': True, 'default': None,

View File

@ -1978,6 +1978,7 @@ class ResourceMappingDriver(api.PolicyDriver):
'servicechain_specs': sc_spec,
'provider_ptg_id': provider_ptg_id,
'consumer_ptg_id': consumer_ptg_id,
'management_ptg_id': None,
'classifier_id': classifier_id,
'config_param_values': jsonutils.dumps(config_param_values)}
return self._create_resource(self._servicechain_plugin,

View File

@ -23,16 +23,18 @@ def get_gbp_plugin():
def get_node_driver_context(sc_plugin, context, sc_instance,
current_node, original_node=None,
management_group=None, service_targets=None):
service_targets=None):
specs = sc_plugin.get_servicechain_specs(
context, filters={'id': sc_instance['servicechain_specs']})
position = _calculate_node_position(specs, current_node['id'])
provider, _ = _get_ptg_or_ep(
context, sc_instance['provider_ptg_id'])
context, sc_instance['provider_ptg_id'])
consumer, is_consumer_external = _get_ptg_or_ep(
context, sc_instance['consumer_ptg_id'])
context, sc_instance['consumer_ptg_id'])
management, _ = _get_ptg_or_ep(context, sc_instance['management_ptg_id'])
classifier = get_gbp_plugin().get_policy_classifier(
context, sc_instance['classifier_id'])
context, sc_instance['classifier_id'])
current_profile = sc_plugin.get_service_profile(
context, current_node['service_profile_id'])
original_profile = sc_plugin.get_service_profile(
@ -51,7 +53,7 @@ def get_node_driver_context(sc_plugin, context, sc_instance,
current_service_profile=current_profile,
provider_group=provider,
consumer_group=consumer,
management_group=management_group,
management_group=management,
original_service_chain_node=original_node,
original_service_profile=original_profile,
service_targets=service_targets,

View File

@ -23,6 +23,7 @@
"create_policy_target_group": "",
"get_policy_target_group": "rule:admin_or_owner or rule:shared_ptg",
"create_policy_target_group:service_management": "rule:admin_only",
"create_l2_policy": "",
"get_l2_policy": "rule:admin_or_owner or rule:shared_l2p",

View File

@ -44,7 +44,8 @@ def get_create_policy_target_group_default_attrs():
return {'name': '', 'description': '', 'l2_policy_id': None,
'provided_policy_rule_sets': {},
'consumed_policy_rule_sets': {},
'network_service_policy_id': None, 'shared': False}
'network_service_policy_id': None, 'shared': False,
'service_management': False}
@gbp_attributes
@ -55,7 +56,7 @@ def get_create_policy_target_group_attrs():
'provided_policy_rule_sets': {_uuid(): None},
'consumed_policy_rule_sets': {_uuid(): None},
'network_service_policy_id': _uuid(),
'shared': False}
'shared': False, 'service_management': False}
@gbp_attributes
@ -389,6 +390,7 @@ def get_create_servicechain_instance_attrs():
'tenant_id': _uuid(),
'provider_ptg_id': _uuid(),
'consumer_ptg_id': _uuid(),
'management_ptg_id': _uuid(),
'classifier_id': _uuid(),
'config_param_values': "{}",
'description': 'test servicechain instance'

View File

@ -432,6 +432,7 @@ class TestServiceChainResources(ServiceChainDbTestCase):
servicechain_specs=[scs_id],
provider_ptg_id=policy_target_group_id,
consumer_ptg_id=policy_target_group_id,
management_ptg_id=policy_target_group_id,
classifier_id=classifier_id,
config_param_values=config_param_values)
@ -439,6 +440,7 @@ class TestServiceChainResources(ServiceChainDbTestCase):
servicechain_specs=[scs_id],
provider_ptg_id=policy_target_group_id,
consumer_ptg_id=policy_target_group_id,
management_ptg_id=policy_target_group_id,
classifier_id=classifier_id,
config_param_values=config_param_values)
for k, v in attrs.iteritems():
@ -498,16 +500,19 @@ class TestServiceChainResources(ServiceChainDbTestCase):
scs_id = self.create_servicechain_spec()['servicechain_spec']['id']
provider_ptg_id = uuidutils.generate_uuid()
consumer_ptg_id = uuidutils.generate_uuid()
management_ptg_id = uuidutils.generate_uuid()
classifier_id = uuidutils.generate_uuid()
attrs = cm.get_create_servicechain_instance_default_attrs(
name=name, description=description, servicechain_specs=[scs_id],
provider_ptg_id=provider_ptg_id, consumer_ptg_id=consumer_ptg_id,
management_ptg_id=management_ptg_id,
classifier_id=classifier_id,
config_param_values=config_param_values)
sci = self.create_servicechain_instance(
servicechain_specs=[scs_id], provider_ptg_id=provider_ptg_id,
consumer_ptg_id=consumer_ptg_id, classifier_id=classifier_id,
consumer_ptg_id=consumer_ptg_id,
management_ptg_id=management_ptg_id, classifier_id=classifier_id,
config_param_values=config_param_values)
data = {'servicechain_instance': {'name': name,
'description': description,

View File

@ -660,6 +660,16 @@ class TestPolicyTargetGroup(GroupPolicyPluginTestCase):
self._create_ptg_on_shared(tenant_id='other',
expected_res_status=201)
def test_multiple_service_ptg_fails(self):
self.create_policy_target_group(
service_management=True, is_admin_context=True,
expected_res_status=201)
res = self.create_policy_target_group(
service_management=True, is_admin_context=True,
expected_res_status=400)
self.assertEqual('ManagementPolicyTargetGroupExists',
res['NeutronError']['type'])
class TestExternalSegment(GroupPolicyPluginTestCase):

View File

@ -79,6 +79,10 @@ class NodeCompositionPluginTestCase(
servicechain_plugin = plugins.get(pconst.SERVICECHAIN)
return servicechain_plugin
def _create_service_profile(self, **kwargs):
"""Create service profile wrapper that can be used by drivers."""
return self.create_service_profile(**kwargs)
def _create_redirect_rule(self, spec_id):
action = self.create_policy_action(action_type='REDIRECT',
action_value=spec_id)
@ -95,7 +99,7 @@ class NodeCompositionPluginTestCase(
return prs
def _create_simple_service_chain(self, number_of_nodes=1):
prof = self.create_service_profile(
prof = self._create_service_profile(
service_type='LOADBALANCER',
vendor=self.SERVICE_PROFILE_VENDOR)['service_profile']
@ -140,7 +144,7 @@ class NodeCompositionPluginTestCase(
def test_context_attributes(self):
# Verify Context attributes for simple config
plugin_context = n_context.get_admin_context()
profile = self.create_service_profile(
profile = self._create_service_profile(
service_type="LOADBALANCER",
vendor=self.SERVICE_PROFILE_VENDOR)['service_profile']
node = self.create_servicechain_node(
@ -150,8 +154,10 @@ class NodeCompositionPluginTestCase(
nodes=[node['id']])['servicechain_spec']
provider = self.create_policy_target_group()['policy_target_group']
consumer = self.create_policy_target_group()['policy_target_group']
management = self.create_policy_target_group()['policy_target_group']
management = self.create_policy_target_group(
service_management=True)['policy_target_group']
classifier = self.create_policy_classifier()['policy_classifier']
instance = self.create_servicechain_instance(
provider_ptg_id=provider['id'], consumer_ptg_id=consumer['id'],
servicechain_specs=[spec['id']], classifier_id=classifier['id'])[
@ -159,8 +165,7 @@ class NodeCompositionPluginTestCase(
# Verify created without errors
ctx = ncp_context.get_node_driver_context(
self.plugin, plugin_context, instance, node,
management_group=management)
self.plugin, plugin_context, instance, node)
self.assertIsNotNone(ctx.gbp_plugin)
self.assertIsNotNone(ctx.sc_plugin)
@ -221,7 +226,7 @@ class NodeCompositionPluginTestCase(
return jsonutils.dumps({'Parameters':
dict((x, {}) for x in params)})
prof = self.create_service_profile(
prof = self._create_service_profile(
service_type='LOADBALANCER', shared=True,
vendor=self.SERVICE_PROFILE_VENDOR,
tenant_id='admin')['service_profile']
@ -319,7 +324,7 @@ class NodeCompositionPluginTestCase(
validate_update.side_effect = exc.NodeCompositionPluginBadRequest(
resource='node', msg='reason')
prof = self.create_service_profile(
prof = self._create_service_profile(
service_type='LOADBALANCER',
vendor=self.SERVICE_PROFILE_VENDOR)['service_profile']
@ -344,7 +349,7 @@ class NodeCompositionPluginTestCase(
res['NeutronError']['type'])
def test_update_instantiated_profile_fails(self):
prof = self.create_service_profile(
prof = self._create_service_profile(
service_type='LOADBALANCER',
vendor=self.SERVICE_PROFILE_VENDOR)['service_profile']
@ -375,7 +380,7 @@ class NodeCompositionPluginTestCase(
create_1.side_effect = n_exc.NeutronException()
# This happens without error
profile = self.create_service_profile(
profile = self._create_service_profile(
service_type="TYPE",
vendor=self.SERVICE_PROFILE_VENDOR)['service_profile']
node = self.create_servicechain_node(
@ -400,7 +405,7 @@ class NodeCompositionPluginTestCase(
create_2 = drivers[1].validate_create = mock.Mock()
create_2.side_effect = n_exc.NeutronException()
profile = self.create_service_profile(
profile = self._create_service_profile(
service_type="TYPE",
vendor=self.SERVICE_PROFILE_VENDOR)['service_profile']
node = self.create_servicechain_node(
@ -418,7 +423,7 @@ class NodeCompositionPluginTestCase(
def test_multiple_nodes_update(self):
update = self.driver.update = mock.Mock()
prof = self.create_service_profile(
prof = self._create_service_profile(
service_type='LOADBALANCER',
vendor=self.SERVICE_PROFILE_VENDOR)['service_profile']
node = self.create_servicechain_node(
@ -498,7 +503,7 @@ class NodeCompositionPluginTestCase(
add = self.driver.update_policy_target_added = mock.Mock()
rem = self.driver.update_policy_target_removed = mock.Mock()
prof = self.create_service_profile(
prof = self._create_service_profile(
service_type='LOADBALANCER',
vendor=self.SERVICE_PROFILE_VENDOR)['service_profile']
node = self.create_servicechain_node(
@ -541,7 +546,7 @@ class NodeCompositionPluginTestCase(
add = self.driver.update_policy_target_added = mock.Mock()
rem = self.driver.update_policy_target_removed = mock.Mock()
prof = self.create_service_profile(
prof = self._create_service_profile(
service_type='LOADBALANCER',
vendor=self.SERVICE_PROFILE_VENDOR)['service_profile']
node = self.create_servicechain_node(
@ -584,9 +589,9 @@ class NodeCompositionPluginTestCase(
expected_res_status=201)['servicechain_spec']
prs = self._create_redirect_prs(spec['id'])['policy_rule_set']
self.create_policy_target_group(
provided_policy_rule_sets={prs['id']: ''})['policy_target_group']
provided_policy_rule_sets={prs['id']: ''})
self.create_policy_target_group(
consumed_policy_rule_sets={prs['id']: ''})['policy_target_group']
consumed_policy_rule_sets={prs['id']: ''})
# Verify notification issued for update in the driver
# REVISIT(Magesh): When bug #1446587 is fixed, we should test by
@ -599,6 +604,58 @@ class NodeCompositionPluginTestCase(
plugin_context, instances[0]['id'])
update_hook.assert_called_with(mock.ANY)
def test_context_no_management(self):
# Verify Context attributes for simple config
plugin_context = n_context.get_admin_context()
plugin_context.is_admin = False
plugin_context.is_advsvc = False
plugin_context.tenant_id = 'test-tenant'
node = self._create_profiled_servicechain_node()['servicechain_node']
spec = self.create_servicechain_spec(
nodes=[node['id']])['servicechain_spec']
provider = self.create_policy_target_group()['policy_target_group']
consumer = self.create_policy_target_group()['policy_target_group']
# Verify admin created SM is None
management = self.create_policy_target_group(
service_management=True, tenant_id='admin',
is_admin_context=True)['policy_target_group']
pc = self.create_policy_classifier()['policy_classifier']
instance = self.create_servicechain_instance(
provider_ptg_id=provider['id'], consumer_ptg_id=consumer['id'],
servicechain_specs=[spec['id']],
classifier_id=pc['id'])['servicechain_instance']
ctx = ncp_context.get_node_driver_context(
self.plugin, plugin_context, instance, node)
self.assertIsNone(ctx.management)
self.delete_policy_target_group(management['id'],
is_admin_context=True)
shared_management = self.create_policy_target_group(
service_management=True, tenant_id='admin',
is_admin_context=True, shared=True)['policy_target_group']
instance = self.create_servicechain_instance(
provider_ptg_id=provider['id'], consumer_ptg_id=consumer['id'],
servicechain_specs=[spec['id']],
classifier_id=pc['id'])['servicechain_instance']
# Now admin Service Management PTG is visible
ctx = ncp_context.get_node_driver_context(
self.plugin, plugin_context, instance, node)
self.assertEqual(shared_management['id'], ctx.management['id'])
# Private management overrides shared one
private_management = self.create_policy_target_group(
service_management=True,
is_admin_context=True)['policy_target_group']
instance = self.create_servicechain_instance(
provider_ptg_id=provider['id'], consumer_ptg_id=consumer['id'],
servicechain_specs=[spec['id']],
classifier_id=pc['id'])['servicechain_instance']
ctx = ncp_context.get_node_driver_context(
self.plugin, plugin_context, instance, node)
self.assertEqual(private_management['id'], ctx.management['id'])
class AgnosticChainPlumberTestCase(NodeCompositionPluginTestCase):

View File

@ -267,6 +267,7 @@ class ServiceChainExtensionTestCase(test_extensions_base.ExtensionTestCase):
'tenant_id': _uuid(),
'provider_ptg_id': _uuid(),
'consumer_ptg_id': _uuid(),
'management_ptg_id': _uuid(),
'classifier_id': _uuid(),
}
}