Files
group-based-policy/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/apic_mapper.py
Sumit Naiksatam 94653826b6 [aim-mapping] Auto PTG per L2P
This change automatically creates a PTG per L2P. This PTG is created as a
reverse map of the "default" EPG that is being created per L2P per Neutron
network. We will henceforth refer to this PTG as "auto" PTG.

The ID of the auto PTG is derived from the ID of the L2P as a MD5 hash
calculation (for uniqueness) and persisted in the format:
"auto<hash_of_l2p_id>". It is thus always possible to determine the ID of the
auto PTG from the ID of the L2P and no additional state needs to be maintained.

The initial name of the auto PTG is derived from the ID of the L2P to ease
debugging and troubleshooting, and takes the form: "auto-ptg-<l2p_id>". This
name is mutable (just like any other PTG). The aim_mapping driver does not
have any specical meaning for this name, and does not care about after it
implicitly sets it at the time of the auto PTG creation. Note that changing
the name of the PTG will not result in a change in the display_name of the
default EPG that the auto PTG maps to (since this property is owned by the
apic_aim mechanism driver).

Any Contracts configured directly on the default EPG will be preserved
regardless of any changes to the auto PTG.

The auto PTG cannot be deleted by the end user and doing so will result in
an error.

The shared status of the auto PTG is made consistent with the shared status
of the L2P (once set, it cannot be changed).

The auto PTG is deleted when the corresponding L2P is deleted.

This patch also gets rid of the apic name_mapper's policy_target_group()
function which mapped a policy_target_group's identity to an APIC EPG name.
This is replaced by the function apic_epg_name_for_policy_target_group()
which is now part of the aim_mapping policy driver.

Change-Id: I76977449419fed7984efb534ab88612c88cd3d54
(cherry picked from commit f424a47183)
2016-12-01 19:59:57 +00:00

199 lines
7.6 KiB
Python

# Copyright (c) 2016 Cisco Systems Inc.
# All Rights Reserved.
#
# 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.
import re
from neutron._i18n import _LI
LOG = None
NAME_TYPE_TENANT = 'tenant'
NAME_TYPE_NETWORK = 'network'
NAME_TYPE_ADDRESS_SCOPE = 'address_scope'
NAME_TYPE_ROUTER = 'router'
NAME_TYPE_POLICY_TARGET_GROUP = 'policy_target_group'
NAME_TYPE_L3_POLICY = 'l3_policy'
NAME_TYPE_L2_POLICY = 'l2_policy'
NAME_TYPE_POLICY_RULE_SET = 'policy_rule_set'
NAME_TYPE_POLICY_RULE = 'policy_rule'
NAME_TYPE_EXTERNAL_SEGMENT = 'external_segment'
NAME_TYPE_EXTERNAL_POLICY = 'external_policy'
NAME_TYPE_NAT_POOL = 'nat_pool'
MAX_APIC_NAME_LENGTH = 46
# TODO(rkukura): This is name mapper is copied from the apicapi repo,
# and modified to pass in resource names rather than calling the core
# plugin to get them, and to use the existing DB session. We need
# decide whether to make these changes in apicapi (maybe on a branch),
# move this some other repo, or keep it here. The changes are not
# backwards compatible. The implementation should also be cleaned up
# and simplified. For example, sessions should be passed in place of
# contexts, and the core plugin calls eliminated.
def truncate(string, max_length):
if max_length < 0:
return ''
return string[:max_length] if len(string) > max_length else string
class APICNameMapper(object):
def __init__(self, db, log):
self.db = db
self.min_suffix = 5
global LOG
LOG = log.getLogger(__name__)
def mapper(name_type):
"""Wrapper to land all the common operations between mappers."""
def wrap(func):
def inner(inst, session, resource_id, resource_name=None,
prefix=None):
# REVISIT(Bob): Optional argument for reserving characters in
# the prefix?
saved_name = inst.db.get_apic_name(session,
resource_id,
name_type)
if saved_name:
result = saved_name[0]
# REVISIT(Sumit): Should this name mapper be aware of
# this prefixing logic, or should we instead prepend
# the prefix at the point from where this is being
# invoked. The latter approach has the disadvantage
# of having to replicate the logic in many places.
if prefix:
result = prefix + result
result = truncate(result, MAX_APIC_NAME_LENGTH)
return result
name = ''
try:
name = func(inst, session, resource_id, resource_name)
except Exception as e:
LOG.warn(("Exception in looking up name %s"), name_type)
LOG.error(e.message)
purged_id = re.sub(r"-+", "-", resource_id)
result = purged_id[:inst.min_suffix]
if name:
name = re.sub(r"-+", "-", name)
# Keep as many uuid chars as possible
id_suffix = "_" + result
max_name_length = MAX_APIC_NAME_LENGTH - len(id_suffix)
result = truncate(name, max_name_length) + id_suffix
result = truncate(result, MAX_APIC_NAME_LENGTH)
# Remove forbidden whitespaces
result = result.replace(' ', '')
result = inst._grow_id_if_needed(
session, purged_id, name_type, result,
start=inst.min_suffix)
else:
result = purged_id
inst.db.add_apic_name(session, resource_id,
name_type, result)
if prefix:
result = prefix + result
result = truncate(result, MAX_APIC_NAME_LENGTH)
return result
return inner
return wrap
def _grow_id_if_needed(self, session, resource_id, name_type,
current_result, start=0):
result = current_result
if result.endswith('_'):
result = result[:-1]
try:
x = 0
while True:
if self.db.get_filtered_apic_names(session,
neutron_type=name_type,
apic_name=result):
if x == 0 and start == 0:
result += '_'
# This name overlaps, add more ID characters
result += resource_id[start + x]
x += 1
else:
break
except AttributeError:
LOG.info(_LI("Current DB API doesn't support "
"get_filtered_apic_names."))
except IndexError:
LOG.debug("Ran out of ID characters.")
return result
@mapper(NAME_TYPE_TENANT)
def tenant(self, session, tenant_id, tenant_name=None):
return tenant_name
@mapper(NAME_TYPE_NETWORK)
def network(self, session, network_id, network_name=None):
return network_name
@mapper(NAME_TYPE_ADDRESS_SCOPE)
def address_scope(self, session, address_scope_id,
address_scope_name=None):
return address_scope_name
@mapper(NAME_TYPE_ROUTER)
def router(self, session, router_id, router_name=None):
return router_name
@mapper(NAME_TYPE_L3_POLICY)
def l3_policy(self, context, l3_policy_id):
l3_policy = context._plugin.get_l3_policy(context._plugin_context,
l3_policy_id)
return l3_policy['name']
@mapper(NAME_TYPE_L2_POLICY)
def l2_policy(self, context, l2_policy_id, l2_policy_name=None):
return l2_policy_name
@mapper(NAME_TYPE_POLICY_RULE_SET)
def policy_rule_set(self, context, policy_rule_set_id,
policy_rule_set_name=None):
return policy_rule_set_name
@mapper(NAME_TYPE_POLICY_RULE)
def policy_rule(self, context, policy_rule_id,
policy_rule_name=None):
return policy_rule_name
@mapper(NAME_TYPE_EXTERNAL_SEGMENT)
def external_segment(self, context, external_segment_id):
external_segment = context._plugin.get_external_segment(
context._plugin_context, external_segment_id)
return external_segment['name']
@mapper(NAME_TYPE_EXTERNAL_POLICY)
def external_policy(self, context, external_policy_id):
external_policy = context._plugin.get_external_policy(
context._plugin_context, external_policy_id)
return external_policy['name']
@mapper(NAME_TYPE_NAT_POOL)
def nat_pool(self, context, nat_pool_id):
nat_pool = context._plugin.get_nat_pool(context._plugin_context,
nat_pool_id)
return nat_pool['name']
def delete_apic_name(self, session, object_id):
self.db.delete_apic_name(session, object_id)