[apic-mapping] NameAlias support for tenant

1. will set the nameAlias field when the 1st PTG is being
created under the tenant. There will be a delay when user creates
L3P, L2P or PRS first but we are ok with that after discussing
with Mandeep.
2. once the tenant is already in apic, we will update the nameAlias
field whenever the corresponding project name has been changed on
OS side. We do this through keystone notifications.

Partial-Bug: 1642783
Change-Id: I797c1d8ba762734f191b07103a2723d2644c78ca
This commit is contained in:
Kent Wu
2016-12-12 16:04:45 -08:00
parent be792291c1
commit 8f3481cc2e
2 changed files with 95 additions and 0 deletions

View File

@@ -48,6 +48,7 @@ from opflexagent import rpc
from oslo_concurrency import lockutils
from oslo_config import cfg
from oslo_log import log as logging
import oslo_messaging
from oslo_serialization import jsonutils
import sqlalchemy as sa
@@ -238,6 +239,42 @@ PROXY_PORT_PREFIX = "opflex_proxy:"
EOC_PREFIX = "opflex_eoc:"
class KeystoneNotificationEndpoint(object):
filter_rule = oslo_messaging.NotificationFilter(
event_type='identity.project.update')
def __init__(self, mechanism_driver):
self._driver = mechanism_driver
def info(self, ctxt, publisher_id, event_type, payload, metadata):
LOG.debug("Keystone notification getting called!")
tenant_id = payload.get('resource_info')
# malformed notification?
if not tenant_id:
return None
if self._driver.single_tenant_mode:
return None
# we only update tenants which have been created in APIC. For other
# cases, their nameAlias will be set when the first PTG is being
# created under that tenant
if not (self._driver.apic_manager.apic_mapper.
is_tenant_in_apic(tenant_id)):
return None
new_tenant_name = (self._driver.apic_manager.apic_mapper.
update_tenant_name(tenant_id))
if new_tenant_name:
obj = {}
obj['tenant_id'] = tenant_id
obj['name'] = new_tenant_name
apic_tenant_name = self._driver._tenant_by_sharing_policy(obj)
self._driver.apic_manager.update_name_alias(
self._driver.apic_manager.apic.fvTenant, apic_tenant_name,
nameAlias=new_tenant_name)
return oslo_messaging.NotificationResult.HANDLED
class ApicMappingDriver(api.ResourceMappingDriver,
ha_ip_db.HAIPOwnerDbMixin):
"""Apic Mapping driver for Group Policy plugin.
@@ -292,6 +329,11 @@ class ApicMappingDriver(api.ResourceMappingDriver,
self._setup_rpc_listeners()
self.apic_manager = ApicMappingDriver.get_apic_manager()
self.name_mapper = name_manager.ApicNameManager(self.apic_manager)
self.keystone_notification_exchange = (self.apic_manager.
keystone_notification_exchange)
self.keystone_notification_topic = (self.apic_manager.
keystone_notification_topic)
self._setup_keystone_notification_listeners()
self.enable_dhcp_opt = self.apic_manager.enable_optimized_dhcp
self.enable_metadata_opt = self.apic_manager.enable_optimized_metadata
self.nat_enabled = self.apic_manager.use_vmm
@@ -308,6 +350,7 @@ class ApicMappingDriver(api.ResourceMappingDriver,
LOG.info(_LI('Auto PTG creation configuration set, '
'this will result in automatic creation of a PTG '
'per L2 Policy'))
self.tenants_with_name_alias_set = set()
def _setup_rpc_listeners(self):
self.endpoints = [rpc.GBPServerRpcCallback(self, self.notifier)]
@@ -317,6 +360,17 @@ class ApicMappingDriver(api.ResourceMappingDriver,
fanout=False)
return self.conn.consume_in_threads()
def _setup_keystone_notification_listeners(self):
transport = oslo_messaging.get_transport(cfg.CONF)
targets = [oslo_messaging.Target(
exchange=self.keystone_notification_exchange,
topic=self.keystone_notification_topic, fanout=True)]
endpoints = [KeystoneNotificationEndpoint(self)]
pool = "listener-workers"
server = oslo_messaging.get_notification_listener(
transport, targets, endpoints, executor='eventlet', pool=pool)
server.start()
def _setup_rpc(self):
self.notifier = rpc.AgentNotifierApi(topics.AGENT)
@@ -889,6 +943,22 @@ class ApicMappingDriver(api.ResourceMappingDriver,
else:
self.name_mapper.has_valid_name(context.current)
def _update_tenant_name_alias(self, tenant_id, aci_tenant):
if self.single_tenant_mode or aci_tenant == apic_manager.TENANT_COMMON:
return
# set the tenant nameAlias if this is the
# first PTG being created under this tenant
if (tenant_id in self.tenants_with_name_alias_set):
return
tenant_name_alias = self.apic_manager.apic_mapper.get_tenant_name(
tenant_id, require_keystone_session=True)
if not tenant_name_alias:
return
self.apic_manager.update_name_alias(
self.apic_manager.apic.fvTenant, aci_tenant,
nameAlias=tenant_name_alias)
self.tenants_with_name_alias_set.add(tenant_id)
def create_policy_target_group_postcommit(self, context):
if not context.current['subnets']:
self._use_implicit_subnet(context)
@@ -916,6 +986,8 @@ class ApicMappingDriver(api.ResourceMappingDriver,
self.apic_manager.apic.fvAEPg, tenant,
self.apic_manager.app_profile_name,
epg, nameAlias=context.current['name'])
self._update_tenant_name_alias(context.current['tenant_id'],
tenant)
l3p = context._plugin.get_l3_policy(
context._plugin_context, l2_policy_object['l3_policy_id'])

View File

@@ -131,6 +131,9 @@ class ApicMappingTestCase(
mock.patch('gbpservice.neutron.services.grouppolicy.drivers.cisco.'
'apic.apic_mapping.ApicMappingDriver.'
'_setup_rpc_listeners').start()
mock.patch('gbpservice.neutron.services.grouppolicy.drivers.cisco.'
'apic.apic_mapping.ApicMappingDriver.'
'_setup_keystone_notification_listeners').start()
nova_client = mock.patch(
'gbpservice.neutron.services.grouppolicy.drivers.cisco.'
'apic.nova_client.NovaClient.get_server').start()
@@ -261,6 +264,9 @@ class ApicMappingTestCase(
if single_tenant_mode:
self.driver.single_tenant_mode = True
self.driver.single_tenant_name = APIC_SINGLE_TENANT_NAME
aci_mapper = self.driver.apic_manager.apic_mapper
aci_mapper.get_tenant_name.return_value = ('old_name')
aci_mapper.update_tenant_name.return_value = ('new_name')
def tearDown(self):
sys.modules["apicapi"] = self.saved_apicapi
@@ -1852,6 +1858,9 @@ class TestPolicyTargetGroup(ApicMappingTestCase):
nameAlias=amap.SHADOW_PREFIX + 'ptg1'),
mock.call(mgr.apic.fvAEPg, tenant, mgr.app_profile_name,
ptg['id'], nameAlias='ptg1')]
if not shared and not self.single_tenant_mode:
expected_calls.append(
mock.call(mgr.apic.fvTenant, tenant, nameAlias='old_name'))
self._check_call_list(expected_calls,
mgr.update_name_alias.call_args_list)
@@ -2139,6 +2148,20 @@ class TestPolicyTargetGroup(ApicMappingTestCase):
name="ptg1", l2_policy_id=l2p_id,
shared=shared)['policy_target_group']
def test_keystone_notification_endpoint(self):
self.driver.apic_manager.apic_mapper.is_tenant_in_apic = mock.Mock(
return_value=True)
payload = {}
payload['resource_info'] = 'some_id'
keystone_ep = amap.KeystoneNotificationEndpoint(self.driver)
keystone_ep.info(None, None, None, payload, None)
mgr = self.driver.apic_manager
if not self.driver.single_tenant_mode:
mgr.update_name_alias.assert_called_once_with(
mgr.apic.fvTenant, 'some_id', nameAlias='new_name')
else:
mgr.update_name_alias.assert_not_called()
class TestPolicyTargetGroupSingleTenant(TestPolicyTargetGroup):
def setUp(self):