From 2aa3643e6ed918117fbd11bc05dd497be301a8ca Mon Sep 17 00:00:00 2001 From: Sumit Naiksatam Date: Tue, 7 Feb 2017 23:40:42 -0800 Subject: [PATCH] Application Policy Groups Change-Id: I7dcab3b9920ac9bd298115c6744735ba59bff419 Implements: blueprint application-group --- .../neutron/db/grouppolicy/group_policy_db.py | 94 +++++++++++ .../db/grouppolicy/group_policy_mapping_db.py | 2 + .../alembic_migrations/versions/HEAD | 2 +- .../b6e301d5757f_application_policy_group.py | 55 +++++++ gbpservice/neutron/extensions/group_policy.py | 68 ++++++++ .../grouppolicy/drivers/dummy_driver.py | 24 +++ .../services/grouppolicy/extension_manager.py | 15 ++ .../grouppolicy/group_policy_context.py | 30 ++++ .../grouppolicy/group_policy_driver_api.py | 147 ++++++++++++++++++ .../neutron/services/grouppolicy/plugin.py | 105 ++++++++++++- .../grouppolicy/policy_driver_manager.py | 27 ++++ gbpservice/neutron/tests/unit/common.py | 19 +++ .../db/grouppolicy/test_group_policy_db.py | 117 ++++++++++++++ .../tests/unit/test_extension_group_policy.py | 106 +++++++++++++ 14 files changed, 809 insertions(+), 2 deletions(-) create mode 100644 gbpservice/neutron/db/migration/alembic_migrations/versions/b6e301d5757f_application_policy_group.py diff --git a/gbpservice/neutron/db/grouppolicy/group_policy_db.py b/gbpservice/neutron/db/grouppolicy/group_policy_db.py index 1623cedae..9ddc41358 100644 --- a/gbpservice/neutron/db/grouppolicy/group_policy_db.py +++ b/gbpservice/neutron/db/grouppolicy/group_policy_db.py @@ -92,6 +92,9 @@ class PolicyTargetGroup(model_base.BASEV2, BaseSharedGbpResource): } policy_targets = orm.relationship(PolicyTarget, backref='policy_target_group') + application_policy_group_id = sa.Column( + sa.String(36), sa.ForeignKey('gp_application_policy_groups.id'), + nullable=True) l2_policy_id = sa.Column(sa.String(36), sa.ForeignKey('gp_l2_policies.id'), nullable=True) @@ -107,6 +110,13 @@ class PolicyTargetGroup(model_base.BASEV2, BaseSharedGbpResource): service_management = sa.Column(sa.Boolean) +class ApplicationPolicyGroup(model_base.BASEV2, BaseSharedGbpResource): + """It is a collection of policy_targets.""" + __tablename__ = 'gp_application_policy_groups' + policy_target_groups = orm.relationship( + PolicyTargetGroup, backref='application_policy_group') + + class L2Policy(model_base.BASEV2, BaseSharedGbpResource): """Represents a L2 Policy for a collection of policy_target_groups.""" __tablename__ = 'gp_l2_policies' @@ -400,6 +410,15 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase, raise gpolicy.PolicyTargetGroupNotFound( policy_target_group_id=policy_target_group_id) + def _get_application_policy_group(self, context, + application_policy_group_id): + try: + return self._get_by_id( + context, ApplicationPolicyGroup, application_policy_group_id) + except exc.NoResultFound: + raise gpolicy.ApplicationPolicyGroupNotFound( + application_policy_group_id=application_policy_group_id) + def _get_l2_policy(self, context, l2_policy_id): try: return self._get_by_id(context, L2Policy, l2_policy_id) @@ -674,6 +693,12 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase, ptg_db = self._get_policy_target_group(context, ptg_id) ptg_db.l2_policy_id = l2p_id + def _set_application_policy_group_for_policy_target_group( + self, context, ptg_id, apg_id): + with context.session.begin(subtransactions=True): + ptg_db = self._get_policy_target_group(context, ptg_id) + ptg_db.application_policy_group_id = apg_id + def _set_network_service_policy_for_policy_target_group( self, context, ptg_id, nsp_id): with context.session.begin(subtransactions=True): @@ -792,6 +817,8 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase, def _make_policy_target_group_dict(self, ptg, fields=None): res = self._populate_common_fields_in_dict(ptg) res['l2_policy_id'] = ptg['l2_policy_id'] + res['application_policy_group_id'] = ptg.get( + 'application_policy_group_id', None) res['network_service_policy_id'] = ptg['network_service_policy_id'] res['service_management'] = ptg.get('service_management', False) res['policy_targets'] = [ @@ -804,6 +831,12 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase, 'consumed_policy_rule_sets']]) return self._fields(res, fields) + def _make_application_policy_group_dict(self, apg, fields=None): + res = self._populate_common_fields_in_dict(apg) + res['policy_target_groups'] = [ + ptg['id'] for ptg in apg['policy_target_groups']] + return self._fields(res, fields) + def _make_l2_policy_dict(self, l2p, fields=None): res = self._populate_common_fields_in_dict(l2p) res['l3_policy_id'] = l2p['l3_policy_id'] @@ -1111,6 +1144,8 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase, id=uuidutils.generate_uuid(), tenant_id=tenant_id, name=ptg['name'], description=ptg['description'], l2_policy_id=ptg['l2_policy_id'], + application_policy_group_id=ptg.get( + 'application_policy_group_id', None), network_service_policy_id=ptg['network_service_policy_id'], shared=ptg.get('shared', False), service_management=ptg.get('service_management', False), @@ -1173,6 +1208,65 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase, return self._get_collection_count(context, PolicyTargetGroup, filters=filters) + @log.log_method_call + def create_application_policy_group(self, context, + application_policy_group): + apg = application_policy_group['application_policy_group'] + tenant_id = self._get_tenant_id_for_create(context, apg) + with context.session.begin(subtransactions=True): + apg_db = ApplicationPolicyGroup( + id=uuidutils.generate_uuid(), tenant_id=tenant_id, + name=apg['name'], description=apg['description'], + shared=apg.get('shared', False), + status=apg.get('status'), + status_details=apg.get('status_details')) + context.session.add(apg_db) + return self._make_application_policy_group_dict(apg_db) + + @log.log_method_call + def update_application_policy_group(self, context, + application_policy_group_id, + application_policy_group): + apg = application_policy_group['application_policy_group'] + with context.session.begin(subtransactions=True): + apg_db = self._get_application_policy_group( + context, application_policy_group_id) + apg_db.update(apg) + return self._make_application_policy_group_dict(apg_db) + + @log.log_method_call + def delete_application_policy_group(self, context, + application_policy_group_id): + with context.session.begin(subtransactions=True): + apg_db = self._get_application_policy_group( + context, application_policy_group_id) + context.session.delete(apg_db) + + @log.log_method_call + def get_application_policy_group(self, context, + application_policy_group_id, fields=None): + apg = self._get_application_policy_group(context, + application_policy_group_id) + return self._make_application_policy_group_dict(apg, fields) + + @log.log_method_call + def get_application_policy_groups(self, context, filters=None, + fields=None, sorts=None, limit=None, + marker=None, page_reverse=False): + marker_obj = self._get_marker_obj( + context, 'application_policy_group', limit, marker) + return self._get_collection(context, ApplicationPolicyGroup, + self._make_application_policy_group_dict, + filters=filters, fields=fields, + sorts=sorts, limit=limit, + marker_obj=marker_obj, + page_reverse=page_reverse) + + @log.log_method_call + def get_application_policy_groups_count(self, context, filters=None): + return self._get_collection_count(context, ApplicationPolicyGroup, + filters=filters) + @log.log_method_call def create_l2_policy(self, context, l2_policy): l2p = l2_policy['l2_policy'] diff --git a/gbpservice/neutron/db/grouppolicy/group_policy_mapping_db.py b/gbpservice/neutron/db/grouppolicy/group_policy_mapping_db.py index 1033b4a8f..032c40044 100644 --- a/gbpservice/neutron/db/grouppolicy/group_policy_mapping_db.py +++ b/gbpservice/neutron/db/grouppolicy/group_policy_mapping_db.py @@ -450,6 +450,8 @@ class GroupPolicyMappingDbPlugin(gpdb.GroupPolicyDbPlugin): id=uuid, tenant_id=tenant_id, name=ptg['name'], description=ptg['description'], l2_policy_id=ptg['l2_policy_id'], + application_policy_group_id=ptg.get( + 'application_policy_group_id', None), network_service_policy_id=ptg['network_service_policy_id'], shared=ptg.get('shared', False), service_management=ptg.get('service_management', False)) diff --git a/gbpservice/neutron/db/migration/alembic_migrations/versions/HEAD b/gbpservice/neutron/db/migration/alembic_migrations/versions/HEAD index d0b0033fb..f160b03ee 100644 --- a/gbpservice/neutron/db/migration/alembic_migrations/versions/HEAD +++ b/gbpservice/neutron/db/migration/alembic_migrations/versions/HEAD @@ -1 +1 @@ -daaa11a358a2 \ No newline at end of file +b6e301d5757f diff --git a/gbpservice/neutron/db/migration/alembic_migrations/versions/b6e301d5757f_application_policy_group.py b/gbpservice/neutron/db/migration/alembic_migrations/versions/b6e301d5757f_application_policy_group.py new file mode 100644 index 000000000..972e4d556 --- /dev/null +++ b/gbpservice/neutron/db/migration/alembic_migrations/versions/b6e301d5757f_application_policy_group.py @@ -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. +# + +"""application_policy_group + +Revision ID: b6e301d5757f +Revises: daaa11a358a2 +Create Date: 2017-02-10 01:15:32.361753 + +""" + +# revision identifiers, used by Alembic. +revision = 'b6e301d5757f' +down_revision = 'daaa11a358a2' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + + op.create_table( + 'gp_application_policy_groups', + sa.Column('id', sa.String(36), nullable=False), + sa.Column('tenant_id', sa.String(length=255), nullable=True), + sa.Column('name', sa.String(length=50), nullable=True), + sa.Column('description', sa.String(length=255), nullable=True), + sa.Column('status', sa.String(length=16), nullable=True), + sa.Column('status_details', sa.String(length=4096), nullable=True), + sa.PrimaryKeyConstraint('id')) + + op.add_column( + 'gp_policy_target_groups', + sa.Column('application_policy_group_id', sa.String(length=36), + nullable=True)) + + op.create_foreign_key('gp_application_policy_group_ibfk_1', + source='gp_policy_target_groups', + referent='gp_application_policy_groups', + local_cols=['application_policy_group_id'], + remote_cols=['id']) + + +def downgrade(): + pass diff --git a/gbpservice/neutron/extensions/group_policy.py b/gbpservice/neutron/extensions/group_policy.py index 53321e87a..52eeea201 100644 --- a/gbpservice/neutron/extensions/group_policy.py +++ b/gbpservice/neutron/extensions/group_policy.py @@ -55,6 +55,11 @@ class PolicyTargetGroupNotFound(nexc.NotFound): "be found") +class ApplicationPolicyGroupNotFound(nexc.NotFound): + message = _("Application Policy Group %(application_policy_group_id)s " + "could not be found") + + class ManagementPolicyTargetGroupExists(nexc.BadRequest): message = _("Service Management Policy Target Group already exists for " "this tenant.") @@ -404,6 +409,8 @@ NETWORK_SERVICE_POLICIES = 'network_service_policies' EXTERNAL_POLICIES = 'external_policies' EXTERNAL_SEGMENTS = 'external_segments' NAT_POOLS = 'nat_pools' +APPLICATION_POLICY_GROUPS = 'application_policy_groups' + RESOURCE_ATTRIBUTE_MAP = { POLICY_TARGETS: { @@ -454,6 +461,10 @@ RESOURCE_ATTRIBUTE_MAP = { 'l2_policy_id': {'allow_post': True, 'allow_put': True, 'validate': {'type:uuid_or_none': None}, 'default': None, 'is_visible': True}, + 'application_policy_group_id': {'allow_post': True, 'allow_put': True, + 'validate': + {'type:uuid_or_none': None}, + 'default': None, 'is_visible': True}, 'provided_policy_rule_sets': {'allow_post': True, 'allow_put': True, 'validate': {'type:dict_or_none': None}, 'convert_to': @@ -477,6 +488,32 @@ RESOURCE_ATTRIBUTE_MAP = { 'is_visible': True, 'required_by_policy': True, 'enforce_policy': True}, }, + APPLICATION_POLICY_GROUPS: { + 'id': {'allow_post': False, 'allow_put': False, + 'validate': {'type:uuid': None}, 'is_visible': True, + 'primary_key': True}, + 'name': {'allow_post': True, 'allow_put': True, + 'validate': {'type:gbp_resource_name': None}, + 'default': '', 'is_visible': True}, + 'description': {'allow_post': True, 'allow_put': True, + 'validate': {'type:string': None}, + 'is_visible': True, 'default': ''}, + 'tenant_id': {'allow_post': True, 'allow_put': False, + 'validate': {'type:string': None}, + 'required_by_policy': True, 'is_visible': True}, + 'status': {'allow_post': False, 'allow_put': False, + 'is_visible': True}, + 'status_details': {'allow_post': False, 'allow_put': False, + 'is_visible': True}, + 'policy_target_groups': {'allow_post': False, 'allow_put': False, + 'validate': {'type:uuid_list': None}, + 'convert_to': attr.convert_none_to_empty_list, + 'default': None, 'is_visible': True}, + attr.SHARED: {'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, 'validate': {'type:uuid': None}, 'is_visible': True, @@ -860,6 +897,10 @@ group_based_policy_quota_opts = [ default=-1, help=_('Number of L2 Policies allowed per tenant. ' 'A negative value means unlimited.')), + cfg.IntOpt('quota_application_policy_group', + default=-1, + help=_('Number of Application Policy Groups allowed per tenant.' + ' A negative value means unlimited.')), cfg.IntOpt('quota_policy_target_group', default=-1, help=_('Number of Policy Target Groups allowed per tenant. ' @@ -1009,6 +1050,33 @@ class GroupPolicyPluginBase(service_base.ServicePluginBase): def delete_policy_target_group(self, context, policy_target_group_id): pass + @abc.abstractmethod + def get_application_policy_groups(self, context, filters=None, + fields=None): + pass + + @abc.abstractmethod + def get_application_policy_group(self, context, + application_policy_group_id, + fields=None): + pass + + @abc.abstractmethod + def create_application_policy_group(self, context, + application_policy_group): + pass + + @abc.abstractmethod + def update_application_policy_group(self, context, + application_policy_group_id, + application_policy_group): + pass + + @abc.abstractmethod + def delete_application_policy_group(self, context, + application_policy_group_id): + pass + @abc.abstractmethod def get_l2_policies(self, context, filters=None, fields=None): pass diff --git a/gbpservice/neutron/services/grouppolicy/drivers/dummy_driver.py b/gbpservice/neutron/services/grouppolicy/drivers/dummy_driver.py index 6409494e9..71afe99f9 100644 --- a/gbpservice/neutron/services/grouppolicy/drivers/dummy_driver.py +++ b/gbpservice/neutron/services/grouppolicy/drivers/dummy_driver.py @@ -70,6 +70,30 @@ class NoopDriver(api.PolicyDriver): def delete_policy_target_group_postcommit(self, context): pass + @log.log_method_call + def create_application_policy_group_precommit(self, context): + pass + + @log.log_method_call + def create_application_policy_group_postcommit(self, context): + pass + + @log.log_method_call + def update_application_policy_group_precommit(self, context): + pass + + @log.log_method_call + def update_application_policy_group_postcommit(self, context): + pass + + @log.log_method_call + def delete_application_policy_group_precommit(self, context): + pass + + @log.log_method_call + def delete_application_policy_group_postcommit(self, context): + pass + @log.log_method_call def create_l2_policy_precommit(self, context): pass diff --git a/gbpservice/neutron/services/grouppolicy/extension_manager.py b/gbpservice/neutron/services/grouppolicy/extension_manager.py index 870319e34..07a6950a9 100644 --- a/gbpservice/neutron/services/grouppolicy/extension_manager.py +++ b/gbpservice/neutron/services/grouppolicy/extension_manager.py @@ -118,6 +118,21 @@ class ExtensionManager(stevedore.named.NamedExtensionManager): for driver in self.ordered_ext_drivers: driver.obj.extend_policy_target_group_dict(session, result) + def process_create_application_policy_group(self, session, data, result): + """Call all extension drivers during PTG creation.""" + self._call_on_ext_drivers("process_create_application_policy_group", + session, data, result) + + def process_update_application_policy_group(self, session, data, result): + """Call all extension drivers during PTG update.""" + self._call_on_ext_drivers("process_update_application_policy_group", + session, data, result) + + def extend_application_policy_group_dict(self, session, result): + """Call all extension drivers to extend PTG dictionary.""" + for driver in self.ordered_ext_drivers: + driver.obj.extend_application_policy_group_dict(session, result) + def process_create_l2_policy(self, session, data, result): """Call all extension drivers during L2P creation.""" self._call_on_ext_drivers("process_create_l2_policy", diff --git a/gbpservice/neutron/services/grouppolicy/group_policy_context.py b/gbpservice/neutron/services/grouppolicy/group_policy_context.py index 68b574236..85e6548b8 100644 --- a/gbpservice/neutron/services/grouppolicy/group_policy_context.py +++ b/gbpservice/neutron/services/grouppolicy/group_policy_context.py @@ -69,6 +69,16 @@ class PolicyTargetGroupContext(GroupPolicyContext, l2_policy_id) self._policy_target_group['l2_policy_id'] = l2_policy_id + def set_application_policy_group_id(self, application_policy_group_id): + self._plugin._validate_shared_create( + self._plugin, self._plugin_context, self._policy_target_group, + 'policy_target_group') + self._plugin._set_application_policy_group_for_policy_target_group( + self._plugin_context, self._policy_target_group['id'], + application_policy_group_id) + self._policy_target_group['application_policy_group_id'] = ( + application_policy_group_id) + def set_network_service_policy_id(self, network_service_policy_id): nsp_id = network_service_policy_id self._plugin._set_network_service_policy_for_policy_target_group( @@ -85,6 +95,26 @@ class PolicyTargetGroupContext(GroupPolicyContext, self.add_subnet(subnet_id) +class ApplicationPolicyGroupContext(GroupPolicyContext, + api.ApplicationPolicyGroupContext): + + def __init__(self, plugin, plugin_context, application_policy_group, + original_application_policy_group=None): + super(ApplicationPolicyGroupContext, self).__init__( + plugin, plugin_context) + self._application_policy_group = application_policy_group + self._original_application_policy_group = ( + original_application_policy_group) + + @property + def current(self): + return self._application_policy_group + + @property + def original(self): + return self._original_application_policy_group + + class L2PolicyContext(GroupPolicyContext, api.L2PolicyContext): def __init__(self, plugin, plugin_context, l2_policy, diff --git a/gbpservice/neutron/services/grouppolicy/group_policy_driver_api.py b/gbpservice/neutron/services/grouppolicy/group_policy_driver_api.py index 061f0ff46..663759803 100644 --- a/gbpservice/neutron/services/grouppolicy/group_policy_driver_api.py +++ b/gbpservice/neutron/services/grouppolicy/group_policy_driver_api.py @@ -97,6 +97,17 @@ class PolicyTargetGroupContext(object): """ pass + @abc.abstractmethod + def set_application_policy_group_id(self, application_policy_group_id): + """Set the application_policy_group for the policy_target_group. + + :param application_policy_group_id: application_policy_group for + the policy_target_group. + + Set the application_policy_group for the policy_target_group. + """ + pass + @abc.abstractmethod def set_network_service_policy_id(self, network_service_policy_id): """Set the network_service_policy for the policy_target_group. @@ -119,6 +130,37 @@ class PolicyTargetGroupContext(object): pass +@six.add_metaclass(abc.ABCMeta) +class ApplicationPolicyGroupContext(object): + """Context passed to policy engine for APG resource changes. + + ApplicationPolicyGroupContext instance wraps a application_policy_group + resource. It provides helper methods for accessing other relevant + information. Results from expensive operations are cached for convenient + access. + """ + + @abc.abstractproperty + def current(self): + """Return the current state of the application_policy_group. + + Return the current state of the application_policy_group, as defined by + GroupPolicyPlugin.create_application_policy_group. + """ + pass + + @abc.abstractproperty + def original(self): + """Return the original state of the application_policy_group. + + Return the original state of the application_policy_group, prior to a + call to update_application_policy_group. Method is only valid within + calls to update_application_policy_group_precommit and + update_application_policy_group_postcommit. + """ + pass + + @six.add_metaclass(abc.ABCMeta) class L2PolicyContext(object): """Context passed to policy engine for l2_policy resource changes. @@ -648,6 +690,69 @@ class PolicyDriver(object): """ pass + def create_application_policy_group_precommit(self, context): + """Allocate resources for a new application_policy_group. + + :param context: ApplicationPolicyGroupContext instance describing the + new application_policy_group. + """ + pass + + def create_application_policy_group_postcommit(self, context): + """Create a application_policy_group. + + :param context: ApplicationPolicyGroupContext instance describing the + new application_policy_group. + """ + pass + + def update_application_policy_group_precommit(self, context): + """Update resources of a application_policy_group. + + :param context: ApplicationPolicyGroupContext instance describing the + new state of the application_policy_group, as well as the original + state prior to the update_application_policy_group call. + """ + pass + + def update_application_policy_group_postcommit(self, context): + """Update a application_policy_group. + + :param context: ApplicationPolicyGroupContext instance describing the + new state of the application_policy_group, as well as the original + state prior to the update_application_policy_group call. + """ + pass + + def delete_application_policy_group_precommit(self, context): + """Delete resources for a application_policy_group. + + :param context: ApplicationPolicyGroupContext instance describing the + current state of the application_policy_group, prior to the call to + delete it. + """ + pass + + def delete_application_policy_group_postcommit(self, context): + """Delete a application_policy_group. + + :param context: ApplicationPolicyGroupContext instance describing the + current state of the application_policy_group, prior to the call to + delete it. + """ + pass + + def get_application_policy_group_status(self, context): + """Get most recent status of a application_policy_group. + + :param context: ApplicationPolicyGroupContext instance describing the + current state of the application_policy_group, prior to the call to + this get. + Driver can update the status and status_details. This status change + will be reflected as the new status and status_details of the resource. + """ + pass + def create_l2_policy_precommit(self, context): """Allocate resources for a new l2_policy. @@ -1390,6 +1495,48 @@ class ExtensionDriver(object): """ pass + def process_create_application_policy_group(self, session, data, result): + """Process extended attributes for application_policy_group creation. + + :param session: database session + :param data: dictionary of incoming application_policy_group data + :param result: application_policy_group dictionary to extend + + Called inside transaction context on session to validate and + persist any extended application_policy_group attributes defined by + this driver. Extended attribute values must also be added to + result. + """ + pass + + def process_update_application_policy_group(self, session, data, result): + """Process extended attributes for application_policy_group update. + + :param session: database session + :param data: dictionary of incoming application_policy_group data + :param result: application_policy_group dictionary to extend + + Called inside transaction context on session to validate and + update any extended application_policy_group attributes defined by + this driver. Extended attribute values, whether updated or + not, must also be added to result. + """ + pass + + def extend_application_policy_group_dict(self, session, result): + """Add extended attributes to application_policy_group dictionary. + + :param session: database session + :param result: application_policy_group dictionary to extend + + Called inside transaction context on session to add any + extended attributes defined by this driver to a + application_policy_group dictionary to be used for mechanism driver + calls and/or returned as the result of a application_policy_group + operation. + """ + pass + def process_create_l2_policy(self, session, data, result): """Process extended attributes for l2_policy creation. diff --git a/gbpservice/neutron/services/grouppolicy/plugin.py b/gbpservice/neutron/services/grouppolicy/plugin.py index f512a8bef..bbc3bac85 100644 --- a/gbpservice/neutron/services/grouppolicy/plugin.py +++ b/gbpservice/neutron/services/grouppolicy/plugin.py @@ -112,7 +112,8 @@ class GroupPolicyPlugin(group_policy_mapping_db.GroupPolicyMappingDbPlugin): 'nat_pool': {'external_segment_id': 'external_segment'}, 'policy_target': {'policy_target_group_id': - 'policy_target_group'} + 'policy_target_group'}, + 'application_policy_group': {} } _plurals = None @@ -421,6 +422,7 @@ class GroupPolicyPlugin(group_policy_mapping_db.GroupPolicyMappingDbPlugin): l2_policy=group_policy_mapping_db.L2PolicyMapping, policy_target=group_policy_mapping_db.PolicyTargetMapping, policy_target_group=group_policy_mapping_db.PolicyTargetGroupMapping, + application_policy_group=gpdb.ApplicationPolicyGroup, policy_classifier=gpdb.PolicyClassifier, policy_action=gpdb.PolicyAction, policy_rule=gpdb.PolicyRule, @@ -683,6 +685,107 @@ class GroupPolicyPlugin(group_policy_mapping_db.GroupPolicyMappingDbPlugin): filters=filters, fields=fields, sorts=sorts, limit=limit, marker=marker, page_reverse=page_reverse) + @log.log_method_call + def create_application_policy_group(self, context, + application_policy_group): + self._ensure_tenant( + context, application_policy_group['application_policy_group']) + session = context.session + pdm = self.policy_driver_manager + with session.begin(subtransactions=True): + result = super(GroupPolicyPlugin, + self).create_application_policy_group( + context, application_policy_group) + self.extension_manager.process_create_application_policy_group( + session, application_policy_group, result) + self._validate_shared_create(self, context, result, + 'application_policy_group') + policy_context = p_context.ApplicationPolicyGroupContext( + self, context, result) + pdm.create_application_policy_group_precommit(policy_context) + + try: + pdm.create_application_policy_group_postcommit(policy_context) + except Exception: + with excutils.save_and_reraise_exception(): + LOG.exception(_LE("create_application_policy_group_postcommit " + "failed, deleting APG %s"), + result['id']) + self.delete_application_policy_group(context, result['id']) + + return self.get_application_policy_group(context, result['id']) + + @log.log_method_call + def update_application_policy_group(self, context, + application_policy_group_id, + application_policy_group): + session = context.session + pdm = self.policy_driver_manager + with session.begin(subtransactions=True): + original_application_policy_group = ( + self.get_application_policy_group( + context, application_policy_group_id)) + updated_application_policy_group = super( + GroupPolicyPlugin, self).update_application_policy_group( + context, application_policy_group_id, + application_policy_group) + + self.extension_manager.process_update_application_policy_group( + session, application_policy_group, + updated_application_policy_group) + self._validate_shared_update( + self, context, original_application_policy_group, + updated_application_policy_group, 'application_policy_group') + policy_context = p_context.ApplicationPolicyGroupContext( + self, context, updated_application_policy_group, + original_application_policy_group= + original_application_policy_group) + pdm.update_application_policy_group_precommit(policy_context) + + pdm.update_application_policy_group_postcommit(policy_context) + + return self.get_application_policy_group(context, + application_policy_group_id) + + @log.log_method_call + def delete_application_policy_group(self, context, + application_policy_group_id): + session = context.session + pdm = self.policy_driver_manager + with session.begin(subtransactions=True): + application_policy_group = self.get_application_policy_group( + context, application_policy_group_id) + policy_context = p_context.ApplicationPolicyGroupContext( + self, context, application_policy_group) + pdm.delete_application_policy_group_precommit(policy_context) + + super(GroupPolicyPlugin, self).delete_application_policy_group( + context, application_policy_group_id) + + try: + pdm.delete_application_policy_group_postcommit(policy_context) + except Exception: + LOG.exception(_LE("delete_application_policy_group_postcommit " + "failed for application_policy_group %s"), + application_policy_group_id) + + @log.log_method_call + def get_application_policy_group(self, context, + application_policy_group_id, fields=None): + return self._get_resource(context, 'application_policy_group', + application_policy_group_id, + 'ApplicationPolicyGroupContext', + fields=fields) + + @log.log_method_call + def get_application_policy_groups(self, context, filters=None, fields=None, + sorts=None, limit=None, marker=None, + page_reverse=False): + return self._get_resources( + context, 'application_policy_group', + 'ApplicationPolicyGroupContext', filters=filters, fields=fields, + sorts=sorts, limit=limit, marker=marker, page_reverse=page_reverse) + @log.log_method_call def create_l2_policy(self, context, l2_policy): self._ensure_tenant(context, l2_policy['l2_policy']) diff --git a/gbpservice/neutron/services/grouppolicy/policy_driver_manager.py b/gbpservice/neutron/services/grouppolicy/policy_driver_manager.py index a0da397da..b33394ad5 100644 --- a/gbpservice/neutron/services/grouppolicy/policy_driver_manager.py +++ b/gbpservice/neutron/services/grouppolicy/policy_driver_manager.py @@ -194,6 +194,33 @@ class PolicyDriverManager(stevedore.named.NamedExtensionManager): def get_policy_target_group_status(self, context): self._call_on_drivers("get_policy_target_group_status", context) + def create_application_policy_group_precommit(self, context): + self._call_on_drivers("create_application_policy_group_precommit", + context) + + def create_application_policy_group_postcommit(self, context): + self._call_on_drivers("create_application_policy_group_postcommit", + context) + + def update_application_policy_group_precommit(self, context): + self._call_on_drivers("update_application_policy_group_precommit", + context) + + def update_application_policy_group_postcommit(self, context): + self._call_on_drivers("update_application_policy_group_postcommit", + context) + + def delete_application_policy_group_precommit(self, context): + self._call_on_drivers("delete_application_policy_group_precommit", + context) + + def delete_application_policy_group_postcommit(self, context): + self._call_on_drivers("delete_application_policy_group_postcommit", + context, continue_on_failure=True) + + def get_application_policy_group_status(self, context): + self._call_on_drivers("get_application_policy_group_status", context) + def create_l2_policy_precommit(self, context): self._call_on_drivers("create_l2_policy_precommit", context) diff --git a/gbpservice/neutron/tests/unit/common.py b/gbpservice/neutron/tests/unit/common.py index 1aacd4697..30380d856 100644 --- a/gbpservice/neutron/tests/unit/common.py +++ b/gbpservice/neutron/tests/unit/common.py @@ -41,9 +41,27 @@ def get_update_policy_target_attrs(): return {'name': 'new_name'} +@gbp_attributes +def get_create_application_policy_group_default_attrs(): + return {'name': '', 'description': '', 'shared': False} + + +@gbp_attributes +def get_create_application_policy_group_attrs(): + return {'name': 'apg1', 'tenant_id': _uuid(), + 'description': 'test application_policy_group', + 'shared': False} + + +@gbp_attributes +def get_update_application_policy_group_attrs(): + return {'name': 'new_name'} + + @gbp_attributes def get_create_policy_target_group_default_attrs(): return {'name': '', 'description': '', 'l2_policy_id': None, + 'application_policy_group_id': None, 'provided_policy_rule_sets': {}, 'consumed_policy_rule_sets': {}, 'network_service_policy_id': None, 'shared': False, @@ -55,6 +73,7 @@ def get_create_policy_target_group_attrs(): return {'name': 'ptg1', 'tenant_id': _uuid(), 'description': 'test policy_target group', 'l2_policy_id': _uuid(), + 'application_policy_group_id': _uuid(), 'provided_policy_rule_sets': {_uuid(): None}, 'consumed_policy_rule_sets': {_uuid(): None}, 'network_service_policy_id': _uuid(), diff --git a/gbpservice/neutron/tests/unit/db/grouppolicy/test_group_policy_db.py b/gbpservice/neutron/tests/unit/db/grouppolicy/test_group_policy_db.py index d34eecf19..9461c3dcc 100644 --- a/gbpservice/neutron/tests/unit/db/grouppolicy/test_group_policy_db.py +++ b/gbpservice/neutron/tests/unit/db/grouppolicy/test_group_policy_db.py @@ -506,6 +506,78 @@ class TestGroupResources(GroupPolicyDbTestCase): self.assertEqual( res['policy_target_group']['network_service_policy_id'], None) + def _test_create_and_show_application_policy_group(self): + name = "apg1" + attrs = cm.get_create_application_policy_group_default_attrs( + name=name) + + apg = self.create_application_policy_group( + name=name) + + for k, v in attrs.iteritems(): + self.assertEqual(apg['application_policy_group'][k], v) + + self._test_show_resource('application_policy_group', + apg['application_policy_group']['id'], attrs) + return apg + + def test_create_associate_apg_with_ptgs(self): + apg = self._test_create_and_show_application_policy_group() + apg_id = apg['application_policy_group']['id'] + + # Create two PTGs that use this NSP + name = "ptg1" + attrs = cm.get_create_policy_target_group_default_attrs( + name=name, application_policy_group_id=apg_id) + attrs['provided_policy_rule_sets'] = [] + attrs['consumed_policy_rule_sets'] = [] + + ptg1 = self.create_policy_target_group( + name=name, + application_policy_group_id=apg_id)['policy_target_group'] + for k, v in attrs.iteritems(): + self.assertEqual(v, ptg1[k]) + self._test_show_resource( + 'policy_target_group', ptg1['id'], attrs) + + apg = self._show_resource(apg_id, 'application_policy_groups')[ + 'application_policy_group'] + self.assertEqual([ptg1['id']], apg['policy_target_groups']) + + ptg2 = self.create_policy_target_group(name=name)[ + 'policy_target_group'] + del attrs['application_policy_group_id'] + for k, v in attrs.iteritems(): + self.assertEqual(v, ptg2[k]) + self._test_show_resource('policy_target_group', ptg2['id'], attrs) + + apg = self._show_resource(apg_id, 'application_policy_groups')[ + 'application_policy_group'] + self.assertEqual([ptg1['id']], apg['policy_target_groups']) + + # Update the PTG to set the APG + data = {'policy_target_group': {'application_policy_group_id': apg_id}} + req = self.new_update_request('policy_target_groups', data, ptg2['id']) + res = self.deserialize(self.fmt, req.get_response(self.ext_api)) + self.assertEqual( + apg_id, res['policy_target_group']['application_policy_group_id']) + + apg = self._show_resource(apg_id, 'application_policy_groups')[ + 'application_policy_group'] + self.assertItemsEqual([ptg1['id'], ptg2['id']], + apg['policy_target_groups']) + + # Update the PTG to unset the APG + data = {'policy_target_group': {'application_policy_group_id': None}} + req = self.new_update_request('policy_target_groups', data, ptg2['id']) + res = self.deserialize(self.fmt, req.get_response(self.ext_api)) + self.assertEqual( + None, res['policy_target_group']['application_policy_group_id']) + + apg = self._show_resource(apg_id, 'application_policy_groups')[ + 'application_policy_group'] + self.assertEqual([ptg1['id']], apg['policy_target_groups']) + def test_list_policy_target_groups(self): ptgs = ( [self.create_policy_target_group(name='ptg1', description='ptg'), @@ -561,6 +633,51 @@ class TestGroupResources(GroupPolicyDbTestCase): self.assertRaises(gpolicy.PolicyTargetGroupNotFound, self.plugin.get_policy_target_group, ctx, ptg_id) + def test_create_and_show_application_policy_group(self): + self._test_create_and_show_application_policy_group() + + def test_list_application_policy_groups(self): + apgs = ( + [self.create_application_policy_group(name='apg1', + description='apg'), + self.create_application_policy_group(name='apg2', + description='apg'), + self.create_application_policy_group(name='apg3', + description='apg')]) + self._test_list_resources('application_policy_group', apgs, + query_params='description=apg') + + def test_update_application_policy_group(self): + name = "new_application_policy_group1" + description = 'new desc' + attrs = cm.get_create_application_policy_group_default_attrs( + name=name, description=description) + apg = self.create_application_policy_group() + data = {'application_policy_group': + {'name': name, 'description': description}} + req = self.new_update_request('application_policy_groups', data, + apg['application_policy_group']['id']) + res = self.deserialize(self.fmt, req.get_response(self.ext_api)) + + for k, v in attrs.iteritems(): + self.assertEqual(res['application_policy_group'][k], v) + + self._test_show_resource('application_policy_group', + apg['application_policy_group']['id'], attrs) + + def test_delete_application_policy_group(self): + ctx = context.get_admin_context() + + apg = self.create_application_policy_group() + apg_id = apg['application_policy_group']['id'] + + req = self.new_delete_request('application_policy_groups', apg_id) + res = req.get_response(self.ext_api) + self.assertEqual(webob.exc.HTTPNoContent.code, res.status_int) + self.assertRaises(gpolicy.ApplicationPolicyGroupNotFound, + self.plugin.get_application_policy_group, ctx, + apg_id) + def test_create_and_show_l2_policy(self): l3p_id = self.create_l3_policy()['l3_policy']['id'] attrs = cm.get_create_l2_policy_default_attrs(l3_policy_id=l3p_id) diff --git a/gbpservice/neutron/tests/unit/test_extension_group_policy.py b/gbpservice/neutron/tests/unit/test_extension_group_policy.py index 689327e80..23872ca44 100644 --- a/gbpservice/neutron/tests/unit/test_extension_group_policy.py +++ b/gbpservice/neutron/tests/unit/test_extension_group_policy.py @@ -34,6 +34,8 @@ GP_PLUGIN_BASE_NAME = ( GROUPPOLICY_URI = 'grouppolicy' POLICY_TARGETS_URI = GROUPPOLICY_URI + '/' + 'policy_targets' POLICY_TARGET_GROUPS_URI = GROUPPOLICY_URI + '/' + 'policy_target_groups' +APPLICATION_POLICY_GROUPS_URI = ( + GROUPPOLICY_URI + '/' + 'application_policy_groups') L2_POLICIES_URI = GROUPPOLICY_URI + '/' + 'l2_policies' L3_POLICIES_URI = GROUPPOLICY_URI + '/' + 'l3_policies' POLICY_RULES_URI = GROUPPOLICY_URI + '/' + 'policy_rules' @@ -262,6 +264,110 @@ class GroupPolicyExtensionTestCase(test_extensions_base.ExtensionTestCase): def test_delete_policy_target_group(self): self._test_entity_delete('policy_target_group') + def _test_create_application_policy_group(self, data, expected_value, + default_data=None): + if not default_data: + default_data = data + + self.instance.create_application_policy_group.return_value = ( + expected_value) + res = self.api.post(_get_path(APPLICATION_POLICY_GROUPS_URI, + fmt=self.fmt), + self.serialize(data), + content_type='application/%s' % self.fmt) + self.instance.create_application_policy_group.assert_called_once_with( + mock.ANY, application_policy_group=default_data) + self.assertEqual(exc.HTTPCreated.code, res.status_int) + res = self.deserialize(res) + self.assertIn('application_policy_group', res) + self.assertEqual(expected_value, res['application_policy_group']) + + def test_create_application_policy_group_with_defaults(self): + application_policy_group_id = _uuid() + data = {'application_policy_group': {'tenant_id': _uuid()}} + default_attrs = ( + self.get_create_application_policy_group_default_attrs()) + default_data = copy.copy(data) + default_data['application_policy_group'].update(default_attrs) + expected_value = copy.deepcopy( + default_data['application_policy_group']) + expected_value['id'] = application_policy_group_id + + self._test_create_application_policy_group(data, expected_value, + default_data) + + def test_create_application_policy_group(self): + application_policy_group_id = _uuid() + data = {'application_policy_group': + self.get_create_application_policy_group_attrs()} + expected_value = copy.deepcopy(data['application_policy_group']) + expected_value['id'] = application_policy_group_id + + self._test_create_application_policy_group(data, expected_value) + + def test_list_application_policy_groups(self): + application_policy_group_id = _uuid() + expected_value = [{'tenant_id': _uuid(), + 'id': application_policy_group_id}] + + self.instance.get_application_policy_groups.return_value = ( + expected_value) + + res = self.api.get(_get_path(APPLICATION_POLICY_GROUPS_URI, + fmt=self.fmt)) + + self.instance.get_application_policy_groups.assert_called_once_with( + mock.ANY, fields=mock.ANY, filters=mock.ANY) + self.assertEqual(exc.HTTPOk.code, res.status_int) + res = self.deserialize(res) + self.assertIn('application_policy_groups', res) + self.assertEqual(expected_value, res['application_policy_groups']) + + def test_get_application_policy_group(self): + application_policy_group_id = _uuid() + expected_value = {'tenant_id': _uuid(), + 'id': application_policy_group_id} + + self.instance.get_application_policy_group.return_value = ( + expected_value) + + res = self.api.get(_get_path(APPLICATION_POLICY_GROUPS_URI, + id=application_policy_group_id, + fmt=self.fmt)) + + self.instance.get_application_policy_group.assert_called_once_with( + mock.ANY, application_policy_group_id, fields=mock.ANY) + self.assertEqual(exc.HTTPOk.code, res.status_int) + res = self.deserialize(res) + self.assertIn('application_policy_group', res) + self.assertEqual(expected_value, res['application_policy_group']) + + def test_update_application_policy_group(self): + application_policy_group_id = _uuid() + update_data = {'application_policy_group': + self.get_update_application_policy_group_attrs()} + expected_value = {'tenant_id': _uuid(), + 'id': application_policy_group_id} + + self.instance.update_application_policy_group.return_value = ( + expected_value) + + res = self.api.put(_get_path(APPLICATION_POLICY_GROUPS_URI, + id=application_policy_group_id, + fmt=self.fmt), + self.serialize(update_data)) + + self.instance.update_application_policy_group.assert_called_once_with( + mock.ANY, application_policy_group_id, + application_policy_group=update_data) + self.assertEqual(exc.HTTPOk.code, res.status_int) + res = self.deserialize(res) + self.assertIn('application_policy_group', res) + self.assertEqual(expected_value, res['application_policy_group']) + + def test_delete_application_policy_group(self): + self._test_entity_delete('application_policy_group') + def _test_create_l2_policy(self, data, expected_value, default_data=None): if not default_data: default_data = data