[apic-mapping] Automatic PTG per L2P
This change automatically creates a PTG per L2P. This PTG is created as a reverse map of the "shadow" EPG that was already being created per L2P by the apic_mapping policy driver.. 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. In order to maintain the reverse-mapping integrity between the shadow EPG and the auto PTG, an entry is created in the apic name-mapping DB that maps the ID of the auto PTG to the "apic-name" of the "shadow" EPG. 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 apic_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. The auto PTG cannot be deleted by the end user and doing so will result in an error. The user can update the name, description, provided and consumed PRS for the auto PTG, but cannot update any other attributes 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 (attempted in the pre-commit phase). To prevent forward mapping of the auto PTG to a new EPG, all above operations are invoked on the GBP DB mixin (parent of the GBP plugin). This ensures that the apic_mapping policy driver is not invoked for the create and delete auto PTG operations during L2P creation and deletion. The creation of the auto PTG is controlled by a configuration and is disabled by default thus allowing this new feature to be turned ON only where needed. All existing deployments should not see any change in behavior as long as they choose not to turn ON this feature. This configuration is as follows: [apic_mapping] create_auto_ptg=<True or False> As the commit title suggests, this is currently only a apic_mapping driver specific feature. It may evolve to a GBP feature with a well defined auto PTG attribute definition for the L2P (and/or accessor APIs). The convention used for the Auto PTG name and the ID format could change as a part of this evolution. Change-Id: Ie132ace0fc9f78baa0034a6f30f2ee758bb271c0
This commit is contained in:
parent
adc1a2fe9f
commit
6d56931196
|
@ -427,8 +427,11 @@ class GroupPolicyMappingDbPlugin(gpdb.GroupPolicyDbPlugin):
|
|||
with context.session.begin(subtransactions=True):
|
||||
if ptg['service_management']:
|
||||
self._validate_service_management_ptg(context, tenant_id)
|
||||
uuid = ptg.get('id')
|
||||
if not uuid:
|
||||
uuid = uuidutils.generate_uuid()
|
||||
ptg_db = PolicyTargetGroupMapping(
|
||||
id=uuidutils.generate_uuid(), tenant_id=tenant_id,
|
||||
id=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'],
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
# under the License.
|
||||
|
||||
import copy
|
||||
import hashlib
|
||||
import netaddr
|
||||
import re
|
||||
|
||||
|
@ -63,6 +64,7 @@ from gbpservice.neutron.services.grouppolicy.drivers.cisco.apic import (
|
|||
from gbpservice.neutron.services.grouppolicy.drivers.cisco.apic import (
|
||||
nova_client as nclient)
|
||||
from gbpservice.neutron.services.grouppolicy import group_policy_context
|
||||
from gbpservice.neutron.services.grouppolicy import plugin as gbp_plugin
|
||||
|
||||
|
||||
HOST_SNAT_POOL = 'host-snat-pool-for-internal-use'
|
||||
|
@ -74,6 +76,24 @@ LOG = logging.getLogger(__name__)
|
|||
UNMANAGED_SEGMENT = _("External Segment %s is not managed by APIC mapping "
|
||||
"driver.")
|
||||
PRE_EXISTING_SEGMENT = _("Pre-existing external segment %s not found.")
|
||||
AUTO_PTG_NAME_PREFIX = 'auto-ptg-%s'
|
||||
# Note that this prefix should not exceede 4 characters
|
||||
AUTO_PTG_ID_PREFIX = 'auto%s'
|
||||
AUTO_PTG_MUTABLE_KEYS = ['name', 'description', 'consumed_policy_rule_sets',
|
||||
'provided_policy_rule_sets']
|
||||
|
||||
opts = [
|
||||
cfg.BoolOpt('create_auto_ptg',
|
||||
default=False,
|
||||
help=_("Automatically create a PTG when a L2 Policy "
|
||||
"gets created. This is currently an apic_mapping "
|
||||
"policy driver specific feature and may evolve "
|
||||
"to be a broader GBP feature. As a part of the "
|
||||
"evolution, new APIs may be added, the Auto PTG "
|
||||
"naming, and ID format convention may change.")),
|
||||
]
|
||||
|
||||
cfg.CONF.register_opts(opts, "apic_mapping")
|
||||
|
||||
|
||||
class PolicyRuleUpdateNotSupportedOnApicDriver(gpexc.GroupPolicyBadRequest):
|
||||
|
@ -196,6 +216,15 @@ class AdminOnlyOperation(gpexc.GroupPolicyBadRequest):
|
|||
message = _("This operation is reserved to admins")
|
||||
|
||||
|
||||
class AutoPTGAttrsUpdateNotSupported(gpexc.GroupPolicyBadRequest):
|
||||
message = _("Update of attributes: %(attrs)s for auto PTG "
|
||||
"%(id)s not supported.")
|
||||
|
||||
|
||||
class AutoPTGDeleteNotSupported(gpexc.GroupPolicyBadRequest):
|
||||
message = _("Auto PTG %(id)s cannot be deleted.")
|
||||
|
||||
|
||||
class TenantSpecificNatEpg(model_base.BASEV2):
|
||||
"""Tenants that use a specific NAT EPG for an external segment."""
|
||||
__tablename__ = 'gp_apic_tenant_specific_nat_epg'
|
||||
|
@ -277,6 +306,11 @@ class ApicMappingDriver(api.ResourceMappingDriver,
|
|||
self.l3out_vlan_alloc = l3out_vlan_alloc.L3outVlanAlloc()
|
||||
self.l3out_vlan_alloc.sync_vlan_allocations(
|
||||
self.apic_manager.ext_net_dict)
|
||||
self.create_auto_ptg = cfg.CONF.apic_mapping.create_auto_ptg
|
||||
if self.create_auto_ptg:
|
||||
LOG.info(_LI('Auto PTG creation configuration set, '
|
||||
'this will result in automatic creation of a PTG '
|
||||
'per L2 Policy'))
|
||||
|
||||
def _setup_rpc_listeners(self):
|
||||
self.endpoints = [rpc.GBPServerRpcCallback(self, self.notifier)]
|
||||
|
@ -1028,7 +1062,11 @@ class ApicMappingDriver(api.ResourceMappingDriver,
|
|||
return
|
||||
|
||||
def delete_policy_target_group_precommit(self, context):
|
||||
pass
|
||||
if self.create_auto_ptg:
|
||||
auto_ptg_id = self._get_auto_ptg_id(
|
||||
context.current['l2_policy_id'])
|
||||
if context.current['id'] == auto_ptg_id:
|
||||
raise AutoPTGDeleteNotSupported(id=auto_ptg_id)
|
||||
|
||||
def delete_policy_target_group_postcommit(self, context):
|
||||
if not self.name_mapper._is_apic_reference(context.current):
|
||||
|
@ -1070,6 +1108,14 @@ class ApicMappingDriver(api.ResourceMappingDriver,
|
|||
self._unset_any_contract(context.current)
|
||||
|
||||
def delete_l2_policy_precommit(self, context):
|
||||
if self.create_auto_ptg:
|
||||
auto_ptg_id = self._get_auto_ptg_id(context.current['id'])
|
||||
auto_ptg = context._plugin.get_policy_target_group(
|
||||
context._plugin_context, auto_ptg_id)
|
||||
if auto_ptg:
|
||||
self._db_plugin(context._plugin).delete_policy_target_group(
|
||||
context._plugin_context, auto_ptg_id)
|
||||
self.apic_manager.db.delete_apic_name(auto_ptg_id)
|
||||
if not self.name_mapper._is_apic_reference(context.current):
|
||||
super(ApicMappingDriver, self).delete_l2_policy_precommit(context)
|
||||
|
||||
|
@ -1167,6 +1213,15 @@ class ApicMappingDriver(api.ResourceMappingDriver,
|
|||
self.create_policy_rule_postcommit(context, transaction=None)
|
||||
|
||||
def update_policy_target_group_precommit(self, context):
|
||||
if self.create_auto_ptg:
|
||||
auto_ptg_id = self._get_auto_ptg_id(
|
||||
context.original['l2_policy_id'])
|
||||
if context.current['id'] == auto_ptg_id:
|
||||
updated_attrs = [key for key in context.current if
|
||||
context.original[key] != context.current[key]]
|
||||
if list(set(updated_attrs) - set(AUTO_PTG_MUTABLE_KEYS)):
|
||||
raise AutoPTGAttrsUpdateNotSupported(
|
||||
id=auto_ptg_id, attrs=updated_attrs)
|
||||
if not self.name_mapper._is_apic_reference(context.current):
|
||||
if set(context.original['subnets']) != set(
|
||||
context.current['subnets']):
|
||||
|
@ -2658,6 +2713,27 @@ class ApicMappingDriver(api.ResourceMappingDriver,
|
|||
self.apic_manager.set_contract_for_epg(
|
||||
tenant, shadow_epg, contract, provider=True,
|
||||
contract_owner=tenant, transaction=trs)
|
||||
if self.create_auto_ptg:
|
||||
data = {
|
||||
"id": self._get_auto_ptg_id(l2p['id']),
|
||||
"name": self._get_auto_ptg_name(l2p),
|
||||
"description": "System created auto PTG per L2P",
|
||||
"l2_policy_id": l2p['id'],
|
||||
"proxied_group_id": None,
|
||||
"proxy_type": None,
|
||||
"proxy_group_id": attributes.ATTR_NOT_SPECIFIED,
|
||||
"network_service_policy_id": None,
|
||||
"service_management": False,
|
||||
"shared": l2p['shared'],
|
||||
}
|
||||
auto_ptg = self._db_plugin(
|
||||
context._plugin).create_policy_target_group(
|
||||
context._plugin_context,
|
||||
{'policy_target_group': data})
|
||||
# Since we are reverse mapping the APIC EPG to the default PTG
|
||||
# we will map the id of the PTG to the APIC EPG name
|
||||
self.apic_manager.db.update_apic_name(
|
||||
auto_ptg['id'], 'policy_target_group', shadow_epg)
|
||||
|
||||
def _associate_service_filter(self, tenant, contract, filter_name,
|
||||
entry_name, transaction=None, **attrs):
|
||||
|
@ -3814,3 +3890,12 @@ class ApicMappingDriver(api.ResourceMappingDriver,
|
|||
"""
|
||||
if EOC_PREFIX in ptg.get('description', ''):
|
||||
return ptg['description'][len(EOC_PREFIX):]
|
||||
|
||||
def _db_plugin(self, plugin_obj):
|
||||
return super(gbp_plugin.GroupPolicyPlugin, plugin_obj)
|
||||
|
||||
def _get_auto_ptg_name(self, l2p):
|
||||
return AUTO_PTG_NAME_PREFIX % l2p['id']
|
||||
|
||||
def _get_auto_ptg_id(self, l2p_id):
|
||||
return AUTO_PTG_ID_PREFIX % hashlib.md5(l2p_id).hexdigest()
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
# limitations under the License.
|
||||
|
||||
import copy
|
||||
import hashlib
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
@ -2042,7 +2043,7 @@ class TestPolicyTargetGroupVlanNetwork(ApicMappingVlanTestCase,
|
|||
self.assertFalse(port['port']['admin_state_up'])
|
||||
|
||||
|
||||
class TestL2Policy(ApicMappingTestCase):
|
||||
class TestL2PolicyBase(ApicMappingTestCase):
|
||||
|
||||
def _test_l2_policy_created_on_apic(self, shared=False):
|
||||
l2p = self.create_l2_policy(name="l2p", shared=shared)['l2_policy']
|
||||
|
@ -2056,12 +2057,6 @@ class TestL2Policy(ApicMappingTestCase):
|
|||
tenant, amap.SHADOW_PREFIX + l2p['id'], bd_owner=tenant,
|
||||
bd_name=l2p['id'], transaction=mock.ANY)
|
||||
|
||||
def test_l2_policy_created_on_apic(self):
|
||||
self._test_l2_policy_created_on_apic()
|
||||
|
||||
def test_l2_policy_created_on_apic_shared(self):
|
||||
self._test_l2_policy_created_on_apic(shared=True)
|
||||
|
||||
def _test_l2_policy_deleted_on_apic(self, shared=False):
|
||||
l2p = self.create_l2_policy(name="l2p", shared=shared)['l2_policy']
|
||||
req = self.new_delete_request('l2_policies', l2p['id'], self.fmt)
|
||||
|
@ -2081,6 +2076,15 @@ class TestL2Policy(ApicMappingTestCase):
|
|||
self._check_call_list(expected_calls,
|
||||
mgr.delete_contract.call_args_list)
|
||||
|
||||
|
||||
class TestL2Policy(TestL2PolicyBase):
|
||||
|
||||
def test_l2_policy_created_on_apic(self):
|
||||
self._test_l2_policy_created_on_apic()
|
||||
|
||||
def test_l2_policy_created_on_apic_shared(self):
|
||||
self._test_l2_policy_created_on_apic(shared=True)
|
||||
|
||||
def test_l2_policy_deleted_on_apic(self):
|
||||
self._test_l2_policy_deleted_on_apic()
|
||||
|
||||
|
@ -2127,6 +2131,67 @@ class TestL2Policy(ApicMappingTestCase):
|
|||
self.assertFalse(subnet & subnet2)
|
||||
|
||||
|
||||
class TestL2PolicyWithAutoPTG(TestL2PolicyBase):
|
||||
|
||||
def setUp(self, **kwargs):
|
||||
config.cfg.CONF.set_override(
|
||||
'create_auto_ptg', True, group='apic_mapping')
|
||||
super(TestL2PolicyWithAutoPTG, self).setUp(**kwargs)
|
||||
self._neutron_context = context.Context(
|
||||
'', kwargs.get('tenant_id', self._tenant_id),
|
||||
is_admin_context=False)
|
||||
|
||||
def _test_auto_ptg_created(self, shared=False):
|
||||
ptg = self._gbp_plugin.get_policy_target_groups(
|
||||
self._neutron_context)[0]
|
||||
l2p_id = ptg['l2_policy_id']
|
||||
self.assertEqual(amap.AUTO_PTG_NAME_PREFIX % l2p_id, str(ptg['name']))
|
||||
auto_ptg_id = amap.AUTO_PTG_ID_PREFIX % hashlib.md5(l2p_id).hexdigest()
|
||||
self.assertEqual(auto_ptg_id, ptg['id'])
|
||||
self.assertEqual(shared, ptg['shared'])
|
||||
# Only certain attributes can be updated for auto PTG
|
||||
prs1 = self.create_policy_rule_set(
|
||||
name='p1', shared=shared)['policy_rule_set']
|
||||
prs2 = self.create_policy_rule_set(
|
||||
name='c1', shared=shared)['policy_rule_set']
|
||||
self.update_policy_target_group(
|
||||
ptg['id'], name='something-else', description='something-else',
|
||||
provided_policy_rule_sets={prs1['id']: 'scope'},
|
||||
consumed_policy_rule_sets={prs2['id']: 'scope'},
|
||||
expected_res_status=webob.exc.HTTPOk.code)
|
||||
# Update for other attributes fails
|
||||
res = self.update_policy_target_group(
|
||||
ptg['id'], service_management=True,
|
||||
expected_res_status=webob.exc.HTTPBadRequest.code)
|
||||
self.assertEqual('AutoPTGAttrsUpdateNotSupported',
|
||||
res['NeutronError']['type'])
|
||||
# Auto PTG cannot be deleted by user
|
||||
res = self.delete_policy_target_group(
|
||||
ptg['id'], expected_res_status=webob.exc.HTTPBadRequest.code)
|
||||
self.assertEqual('AutoPTGDeleteNotSupported',
|
||||
res['NeutronError']['type'])
|
||||
|
||||
def _test_auto_ptg_deleted(self, shared=False):
|
||||
ptgs = self._gbp_plugin.get_policy_target_groups(self._neutron_context)
|
||||
self.assertEqual(0, len(ptgs))
|
||||
|
||||
def test_l2_policy_created_on_apic(self):
|
||||
self._test_l2_policy_created_on_apic()
|
||||
self._test_auto_ptg_created()
|
||||
|
||||
def test_l2_policy_created_on_apic_shared(self):
|
||||
self._test_l2_policy_created_on_apic(shared=True)
|
||||
self._test_auto_ptg_created(shared=True)
|
||||
|
||||
def test_l2_policy_deleted_on_apic(self):
|
||||
self._test_l2_policy_deleted_on_apic()
|
||||
self._test_auto_ptg_deleted()
|
||||
|
||||
def test_l2_policy_deleted_on_apic_shared(self):
|
||||
self._test_l2_policy_deleted_on_apic(shared=True)
|
||||
self._test_auto_ptg_deleted()
|
||||
|
||||
|
||||
class TestL3Policy(ApicMappingTestCase):
|
||||
|
||||
def _test_l3_policy_created_on_apic(self, shared=False):
|
||||
|
|
Loading…
Reference in New Issue