Merge "[apic] on demand synchronization This patch changes the synchronizator behavior:"

This commit is contained in:
Jenkins 2016-02-01 20:27:36 +00:00 committed by Gerrit Code Review
commit ab1b96611d
4 changed files with 53 additions and 16 deletions

View File

@ -37,18 +37,14 @@ class SynchronizerBase(object):
def sync(self, f, *args, **kwargs):
"""Fire synchronization based on interval.
Interval can be 0 for 'sync once' >0 for 'sync periodically' and
<0 for 'no sync'
Interval can be >0 for 'sync periodically' and
<=0 for 'no sync'
"""
if self.interval:
if self.interval > 0:
loop_call = loopingcall.FixedIntervalLoopingCall(f, *args,
**kwargs)
loop_call.start(interval=self.interval)
return loop_call
else:
# Fire once
f(*args, **kwargs)
if self.interval and self.interval > 0:
loop_call = loopingcall.FixedIntervalLoopingCall(f, *args,
**kwargs)
loop_call.start(interval=self.interval)
return loop_call
class ApicBaseSynchronizer(SynchronizerBase):

View File

@ -17,8 +17,10 @@ from apicapi import apic_manager
from keystoneclient.v2_0 import client as keyclient
import netaddr
from neutron.common import constants as n_constants
from neutron.common import exceptions as n_exc
from neutron.plugins.common import constants
from neutron.plugins.ml2 import driver_api as api
from neutron.plugins.ml2 import driver_context
from neutron.plugins.ml2 import models
from oslo_concurrency import lockutils
from oslo_config import cfg
@ -30,6 +32,12 @@ from networking_cisco.plugins.ml2.drivers.cisco.apic import config
LOG = logging.getLogger(__name__)
APIC_SYNC_NETWORK = 'apic-sync-network'
class ReservedSynchronizationName(n_exc.BadRequest):
message = _("The name used for this network is reserved for on demand "
"synchronization.")
class APICMechanismDriver(api.MechanismDriver):
@ -64,6 +72,10 @@ class APICMechanismDriver(api.MechanismDriver):
return apic_sync.ApicRouterSynchronizer(inst,
apic_config.apic_sync_interval)
@staticmethod
def _is_network_context(ctx):
return isinstance(ctx, driver_context.NetworkContext)
def initialize(self):
# initialize apic
self.apic_manager = APICMechanismDriver.get_apic_manager()
@ -78,7 +90,10 @@ class APICMechanismDriver(api.MechanismDriver):
inst.synchronizer = (
APICMechanismDriver.get_base_synchronizer(inst))
inst.synchronizer.sync_base()
# pylint: disable=not-callable
if args and APICMechanismDriver._is_network_context(args[0]):
if (args[0]._plugin_context.is_admin and
args[0].current['name'] == APIC_SYNC_NETWORK):
inst.synchronizer._sync_base()
return f(inst, *args, **kwargs)
return inner
@ -211,6 +226,10 @@ class APICMechanismDriver(api.MechanismDriver):
@sync_init
def create_network_postcommit(self, context):
# The following validation is not happening in the precommit to avoid
# database lock timeout
if context.current['name'] == APIC_SYNC_NETWORK:
raise ReservedSynchronizationName()
if not context.current.get('router:external'):
tenant_id = context.current['tenant_id']
network_id = context.current['id']

View File

@ -68,6 +68,9 @@ class TestCiscoApicMechDriver(base.BaseTestCase,
name_mapper=mock.Mock(), ext_net_dict=self.external_network_dict)
self.driver.apic_manager.apic.transaction = self.fake_transaction
self.synchronizer = mock.Mock()
md.APICMechanismDriver.get_base_synchronizer = mock.Mock(
return_value=self.synchronizer)
def test_initialize(self):
self.driver.initialize()
@ -165,12 +168,30 @@ class TestCiscoApicMechDriver(base.BaseTestCase,
ctx = self._get_network_context(mocked.APIC_TENANT,
mocked.APIC_NETWORK,
TEST_SEGMENT1)
ctx._plugin_context.is_admin = True
md.APICMechanismDriver._is_network_context = mock.Mock(
return_value=True)
mgr = self.driver.apic_manager
self.driver.create_network_postcommit(ctx)
mgr.ensure_bd_created_on_apic.assert_called_once_with(
mocked.APIC_TENANT, mocked.APIC_NETWORK, transaction='transaction')
mgr.ensure_epg_created.assert_called_once_with(
mocked.APIC_TENANT, mocked.APIC_NETWORK, transaction='transaction')
# Sync not called
self.assertEqual(0, self.synchronizer._sync_base.call_count)
def test_create_sync_network(self):
ctx = self._get_network_context(mocked.APIC_TENANT,
mocked.APIC_NETWORK,
TEST_SEGMENT1,
name=md.APIC_SYNC_NETWORK)
ctx._plugin_context.is_admin = True
md.APICMechanismDriver._is_network_context = mock.Mock(
return_value=True)
self.assertRaises(
md.ReservedSynchronizationName,
self.driver.create_network_postcommit, ctx)
self.assertEqual(1, self.synchronizer._sync_base.call_count)
def test_create_external_network_postcommit(self):
ctx = self._get_network_context(mocked.APIC_TENANT,
@ -226,9 +247,9 @@ class TestCiscoApicMechDriver(base.BaseTestCase,
self.assertFalse(mgr.ensure_subnet_created_on_apic.called)
def _get_network_context(self, tenant_id, net_id, seg_id=None,
seg_type='vlan', external=False):
seg_type='vlan', external=False, name=None):
network = {'id': net_id,
'name': net_id + '-name',
'name': name or (net_id + '-name'),
'tenant_id': tenant_id,
'provider:segmentation_id': seg_id}
if external:
@ -271,6 +292,7 @@ class FakeNetworkContext(object):
def __init__(self, network, segments):
self._network = network
self._segments = segments
self._plugin_context = mock.Mock()
@property
def current(self):

View File

@ -63,7 +63,7 @@ class TestCiscoApicSync(base.BaseTestCase):
sync.core_plugin.get_subnets.return_value = [{'id': 'sub'}]
sync.core_plugin.get_ports.return_value = [{'id': 'port',
'network_id': 'net'}]
sync.sync_base()
sync._sync_base()
self.assertEqual(1, self.driver.create_network_postcommit.call_count)
self.assertEqual(1, self.driver.create_subnet_postcommit.call_count)
self.assertEqual(1, self.get_locked_port_and_binding.call_count)
@ -75,6 +75,6 @@ class TestCiscoApicSync(base.BaseTestCase):
sync.core_plugin.get_ports.return_value = [{'id': 'port',
'network_id': 'net',
'device_id': 'dev'}]
sync.sync_router()
sync._sync_router()
self.assertEqual(
1, self.driver.add_router_interface_postcommit.call_count)