diff --git a/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/mechanism_driver.py b/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/mechanism_driver.py index f2c14f93e..f6c11f436 100644 --- a/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/mechanism_driver.py +++ b/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/mechanism_driver.py @@ -28,6 +28,7 @@ from aim import utils as aim_utils from neutron.agent import securitygroups_rpc from neutron.callbacks import events from neutron.callbacks import registry +from neutron.callbacks import resources from neutron.common import rpc as n_rpc from neutron.common import topics as n_topics from neutron.db import api as db_api @@ -36,6 +37,7 @@ from neutron.db.models import allowed_address_pair as n_addr_pair_db from neutron.db.models import l3 as l3_db from neutron.db.models import securitygroup as sg_models from neutron.db import models_v2 +from neutron.db import provisioning_blocks from neutron.db import rbac_db_models from neutron.db import segments_db from neutron.extensions import external_net @@ -90,6 +92,9 @@ L3OUT_NODE_PROFILE_NAME = 'NodeProfile' L3OUT_IF_PROFILE_NAME = 'IfProfile' L3OUT_EXT_EPG = 'ExtEpg' +SUPPORTED_VNIC_TYPES = [portbindings.VNIC_NORMAL, + portbindings.VNIC_DIRECT] + AGENT_TYPE_DVS = 'DVS agent' VIF_TYPE_DVS = 'dvs' PROMISCUOUS_TYPES = [n_constants.DEVICE_OWNER_DHCP, @@ -1686,8 +1691,7 @@ class ApicMechanismDriver(api_plus.MechanismDriver, # Check the VNIC type. vnic_type = port.get(portbindings.VNIC_TYPE, portbindings.VNIC_NORMAL) - if vnic_type not in [portbindings.VNIC_NORMAL, - portbindings.VNIC_DIRECT]: + if vnic_type not in SUPPORTED_VNIC_TYPES: LOG.debug("Refusing to bind due to unsupported vnic_type: %s", vnic_type) return @@ -1779,6 +1783,30 @@ class ApicMechanismDriver(api_plus.MechanismDriver, port = context.current self._really_update_sg_rule_with_remote_group_set( context, port, port['security_groups'], is_delete=False) + self._insert_provisioning_block(context) + + def _insert_provisioning_block(self, context): + # we insert a status barrier to prevent the port from transitioning + # to active until the agent reports back that the wiring is done + port = context.current + if (not context.host or + port['status'] == n_constants.PORT_STATUS_ACTIVE): + # no point in putting in a block if the status is already ACTIVE + return + + # Check the VNIC type. + vnic_type = port.get(portbindings.VNIC_TYPE, + portbindings.VNIC_NORMAL) + if vnic_type not in SUPPORTED_VNIC_TYPES: + LOG.debug("No provisioning_block due to unsupported vnic_type: %s", + vnic_type) + return + + if (context.host_agents(ofcst.AGENT_TYPE_OPFLEX_OVS) or + context.host_agents(AGENT_TYPE_DVS)): + provisioning_blocks.add_provisioning_component( + context._plugin_context, port['id'], resources.PORT, + provisioning_blocks.L2_AGENT_ENTITY) def update_port_precommit(self, context): port = context.current @@ -1800,6 +1828,7 @@ class ApicMechanismDriver(api_plus.MechanismDriver, self._update_sg_rule_with_remote_group_set(context, port) registry.notify(sfc_cts.GBP_PORT, events.PRECOMMIT_UPDATE, self, driver_context=context) + self._insert_provisioning_block(context) def update_port_postcommit(self, context): port = context.current diff --git a/gbpservice/neutron/tests/unit/plugins/ml2plus/test_apic_aim.py b/gbpservice/neutron/tests/unit/plugins/ml2plus/test_apic_aim.py index 1b42ef6a2..5094a0c61 100644 --- a/gbpservice/neutron/tests/unit/plugins/ml2plus/test_apic_aim.py +++ b/gbpservice/neutron/tests/unit/plugins/ml2plus/test_apic_aim.py @@ -35,8 +35,10 @@ from keystoneclient.v3 import client as ksc_client from neutron.api import extensions from neutron.callbacks import registry from neutron.db import api as db_api +from neutron.db import provisioning_blocks from neutron.db import segments_db from neutron.plugins.ml2 import config +from neutron.plugins.ml2 import driver_context from neutron.tests.unit.api import test_extensions from neutron.tests.unit.db import test_db_base_plugin_v2 as test_plugin from neutron.tests.unit.extensions import test_address_scope @@ -2928,6 +2930,38 @@ class TestAimMapping(ApicAimTestCase): is_implicit=True)['subnetpool'] self.assertTrue(sp3['is_implicit']) + def test_dhcp_provisioning_blocks_inserted_on_update(self): + ctx = n_context.get_admin_context() + plugin = directory.get_plugin() + + def _fake_dhcp_agent(): + agent = mock.Mock() + plugin = directory.get_plugin() + return mock.patch.object( + plugin, 'get_dhcp_agents_hosting_networks', + return_value=[agent]).start() + + dhcp_agt_mock = _fake_dhcp_agent() + update_dict = {'binding:host_id': 'newhost'} + + def _host_agents(self, agent_type): + if agent_type == ofcst.AGENT_TYPE_OPFLEX_OVS: + fake_agent = {"alive": False} + return [fake_agent] + + orig_host_agents = getattr(driver_context.PortContext, "host_agents") + setattr(driver_context.PortContext, "host_agents", _host_agents) + + with self.port() as port: + with mock.patch.object(provisioning_blocks, + 'add_provisioning_component') as ap: + port['port'].update(update_dict) + plugin.update_port(ctx, port['port']['id'], port) + ap.assert_called() + + setattr(driver_context.PortContext, "host_agents", orig_host_agents) + dhcp_agt_mock.stop() + class TestSyncState(ApicAimTestCase): @staticmethod