group-based-policy/gbpservice/neutron/services/grouppolicy/drivers/implicit_policy.py

330 lines
14 KiB
Python

# 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._i18n import _LI
from gbpservice._i18n import _LW
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__)
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 or 6) for implicitly created default L3 "
"policies.")),
cfg.StrOpt('default_ip_pool',
default='10.0.0.0/8',
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, clean_session=True):
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,
clean_session)
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,
clean_session)
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, clean_session)
l3p = l3ps and l3ps[0]
if not l3p:
LOG.warning(_LW(
"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(_LI("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, clean_session=True):
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,
clean_session)
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, clean_session=True):
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, clean_session=True):
if self._l2_policy_is_owned(context._plugin_context.session, l2p_id):
try:
self._delete_l2_policy(context._plugin_context, l2p_id,
clean_session)
except gbp_ext.L2PolicyInUse:
LOG.info(_LI(
"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