# 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_lib.db import model_base from oslo_config import cfg from oslo_log import helpers as log from oslo_log import log as logging from oslo_utils import excutils import sqlalchemy as sa from gbpservice.network.neutronv2 import local_api from gbpservice.neutron.extensions import driver_proxy_group as pg_ext from gbpservice.neutron.extensions import group_policy as gbp_ext from gbpservice.neutron.services.grouppolicy import ( group_policy_driver_api as api) from gbpservice.neutron.services.grouppolicy.common import exceptions as exc LOG = logging.getLogger(__name__) GBP_CONF = cfg.CONF.group_policy_group opts = [ cfg.StrOpt('default_l3_policy_name', default='default', help=_("Name of each tenant's default L3 policy.")), cfg.IntOpt('default_ip_version', default=4, help=_("IP version (4, 6 or 46) for implicitly created default " "L3 policies.")), cfg.StrOpt('default_ip_pool', default=GBP_CONF.default_ip_pool, help=_("IP pool for implicitly created default L3 policies, " "from which subnets are allocated for policy target " "groups.")), cfg.IntOpt('default_subnet_prefix_length', default=24, help=_("Subnet prefix length for implicitly created default L3 " "polices, controlling size of subnets allocated for " "policy target groups.")), cfg.StrOpt('default_external_segment_name', default='default', help=_("Name of default External Segment. This will be used " "whenever a new EP/L3P is created without a referenced " "External Segment. Set to None if a completely " "explicit workflow is preferred.")), ] cfg.CONF.register_opts(opts, "group_policy_implicit_policy") class OwnedL2Policy(model_base.BASEV2): """An L2 Policy owned by the mapping driver.""" __tablename__ = 'gpm_owned_l2_policies' l2_policy_id = sa.Column(sa.String(36), sa.ForeignKey('gp_l2_policies.id', ondelete='CASCADE'), nullable=False, primary_key=True) class OwnedL3Policy(model_base.BASEV2): """An L3 Policy owned by the mapping driver.""" __tablename__ = 'gpm_owned_l3_policies' l3_policy_id = sa.Column(sa.String(36), sa.ForeignKey('gp_l3_policies.id', ondelete='CASCADE'), nullable=False, primary_key=True) class ImplicitPolicyBase(api.PolicyDriver, local_api.LocalAPI): @log.log_method_call def initialize(self): gpip = cfg.CONF.group_policy_implicit_policy gpconf = cfg.CONF.group_policy gpproxy = cfg.CONF.group_policy_proxy_group self._proxy_group_enabled = (pg_ext.PROXY_GROUP in gpconf.extension_drivers) self._default_l3p_name = gpip.default_l3_policy_name self._default_ip_version = gpip.default_ip_version self._default_ip_pool = gpip.default_ip_pool self._default_subnet_prefix_length = gpip.default_subnet_prefix_length self._default_proxy_ip_pool = gpproxy.default_proxy_ip_pool self._default_proxy_subnet_prefix_length = ( gpproxy.default_proxy_subnet_prefix_length) self._default_es_name = gpip.default_external_segment_name def _create_implicit_l3_policy(self, context): tenant_id = context.current['tenant_id'] filter = {'tenant_id': [tenant_id], 'name': [self._default_l3p_name]} l3ps = self._get_l3_policies(context._plugin_context, filter) l3p = l3ps and l3ps[0] if not l3p: attrs = {'tenant_id': tenant_id, 'name': self._default_l3p_name, 'description': _("Implicitly created L3 policy"), 'ip_version': self._default_ip_version, 'ip_pool': self._default_ip_pool, 'shared': context.current.get('shared', False), 'subnet_prefix_length': self._default_subnet_prefix_length} if self._proxy_group_enabled: attrs['proxy_ip_pool'] = ( self._default_proxy_ip_pool) attrs['proxy_subnet_prefix_length'] = ( self._default_proxy_subnet_prefix_length) try: l3p = self._create_l3_policy(context._plugin_context, attrs) self._mark_l3_policy_owned(context._plugin_context.session, l3p['id']) except exc.DefaultL3PolicyAlreadyExists: with excutils.save_and_reraise_exception( reraise=False) as ctxt: LOG.debug("Possible concurrent creation of default L3 " "policy for tenant %s", tenant_id) l3ps = self._get_l3_policies(context._plugin_context, filter) l3p = l3ps and l3ps[0] if not l3p: LOG.warning( "Caught DefaultL3PolicyAlreadyExists, " "but default L3 policy not concurrently " "created for tenant %s", tenant_id) ctxt.reraise = True except exc.OverlappingIPPoolsInSameTenantNotAllowed: with excutils.save_and_reraise_exception(): LOG.info("Caught " "OverlappingIPPoolsinSameTenantNotAllowed " "during creation of default L3 policy for " "tenant %s", tenant_id) context.current['l3_policy_id'] = l3p['id'] def _use_implicit_l3_policy(self, context): self._create_implicit_l3_policy(context) context.set_l3_policy_id(context.current['l3_policy_id']) def _create_implicit_l2_policy(self, context): attrs = {'tenant_id': context.current['tenant_id'], 'name': context.current['name'], 'description': _("Implicitly created L2 policy"), 'l3_policy_id': None, 'shared': context.current.get('shared', False), 'network_id': None} if context.current.get('proxied_group_id'): # The L3P has to be the same as the proxied group group = context._plugin.get_policy_target_group( context._plugin_context, context.current['proxied_group_id']) l2p = context._plugin.get_l2_policy( context._plugin_context, group['l2_policy_id']) attrs['l3_policy_id'] = l2p['l3_policy_id'] l2p = self._create_l2_policy(context._plugin_context, attrs) context.current['l2_policy_id'] = l2p['id'] self._mark_l2_policy_owned(context._plugin_context.session, l2p['id']) def _use_implicit_l2_policy(self, context): self._create_implicit_l2_policy(context) context.set_l2_policy_id(context.current['l2_policy_id']) def _mark_l2_policy_owned(self, session, l2p_id): with session.begin(subtransactions=True): owned = OwnedL2Policy(l2_policy_id=l2p_id) session.add(owned) def _l2_policy_is_owned(self, session, l2p_id): with session.begin(subtransactions=True): return (session.query(OwnedL2Policy). filter_by(l2_policy_id=l2p_id). first() is not None) def _mark_l3_policy_owned(self, session, l3p_id): with session.begin(subtransactions=True): owned = OwnedL3Policy(l3_policy_id=l3p_id) session.add(owned) def _l3_policy_is_owned(self, session, l3p_id): with session.begin(subtransactions=True): return (session.query(OwnedL3Policy). filter_by(l3_policy_id=l3p_id). first() is not None) def _cleanup_l3_policy(self, context, l3p_id): if self._l3_policy_is_owned(context._plugin_context.session, l3p_id): # REVISIT(rkukura): Add check_unused parameter to # local_api._delete_l3_policy()? context._plugin.delete_l3_policy(context._plugin_context, l3p_id, check_unused=True) def _cleanup_l2_policy(self, context, l2p_id): if self._l2_policy_is_owned(context._plugin_context.session, l2p_id): try: self._delete_l2_policy(context._plugin_context, l2p_id) except gbp_ext.L2PolicyInUse: LOG.info( "Cannot delete implicit L2 Policy %s because it's " "in use.", l2p_id) def _validate_default_external_segment(self, context): # REVISIT(ivar): find a better way to retrieve the default ES if self._default_es_name == context.current['name']: filters = {'name': [self._default_es_name]} ess = context._plugin.get_external_segments( context._plugin_context, filters) if [x for x in ess if x['id'] != context.current['id']]: raise exc.DefaultExternalSegmentAlreadyExists( es_name=self._default_es_name) def _use_implicit_external_segment(self, context): if not self._default_es_name: return filter = {'name': [self._default_es_name]} ess = self._get_external_segments(context._plugin_context, filter) # Multiple default ES may exist, this can happen when a per-tenant # default ES gets his shared attribute flipped. Always prefer the # specific tenant's ES if any. for es in ess: if es['tenant_id'] == context.current['tenant_id']: default = es break else: default = ess and ess[0] if default: # Set default ES context.set_external_segment(default['id']) class ImplicitPolicyDriver(ImplicitPolicyBase): """Implicit Policy driver for Group Policy plugin. This driver ensures that the l2_policy_id attribute of PolicyTargetGroup references an L2Policy instance and that the l3_policy_id attribute of L2Policy references an L3Policy instance when the default value of None is specified. """ @log.log_method_call def initialize(self): super(ImplicitPolicyDriver, self).initialize() @log.log_method_call def create_policy_target_group_postcommit(self, context): if not context.current['l2_policy_id']: self._use_implicit_l2_policy(context) @log.log_method_call def update_policy_target_group_postcommit(self, context): old_l2p_id = context.original['l2_policy_id'] new_l2p_id = context.current['l2_policy_id'] if old_l2p_id != new_l2p_id: self._cleanup_l2_policy(context, old_l2p_id) if not new_l2p_id: self._use_implicit_l2_policy(context) @log.log_method_call def delete_policy_target_group_postcommit(self, context): l2p_id = context.current['l2_policy_id'] self._cleanup_l2_policy(context, l2p_id) @log.log_method_call def create_l2_policy_postcommit(self, context): if not context.current['l3_policy_id']: self._use_implicit_l3_policy(context) @log.log_method_call def update_l2_policy_postcommit(self, context): old_l3p_id = context.original['l3_policy_id'] new_l3p_id = context.current['l3_policy_id'] if old_l3p_id != new_l3p_id: self._cleanup_l3_policy(context, old_l3p_id) if not new_l3p_id: self._use_implicit_l3_policy(context) @log.log_method_call def delete_l2_policy_postcommit(self, context): l3p_id = context.current['l3_policy_id'] self._cleanup_l3_policy(context, l3p_id) @log.log_method_call def create_external_segment_precommit(self, context): self._validate_default_external_segment(context) @log.log_method_call def create_external_policy_postcommit(self, context): if not context.current['external_segments']: self._use_implicit_external_segment(context) @log.log_method_call def update_external_policy_postcommit(self, context): pass @log.log_method_call def create_l3_policy_precommit(self, context): if self._default_l3p_name == context.current['name']: LOG.debug("Creating default L3 policy: %s", context.current) tenant_id = context.current['tenant_id'] filter = {'tenant_id': [tenant_id], 'name': [self._default_l3p_name]} l3ps = context._plugin.get_l3_policies(context._plugin_context, filter) if [x for x in l3ps if x['id'] != context.current['id']]: LOG.debug("Rejecting default L3 policy: %s", context.current) raise exc.DefaultL3PolicyAlreadyExists( l3p_name=self._default_l3p_name) @log.log_method_call def create_l3_policy_postcommit(self, context): if not context.current['external_segments']: self._use_implicit_external_segment(context) @log.log_method_call def update_l3_policy_postcommit(self, context): pass