[AIM] Implement the allowed_vm_names feature
basically port the following patch over from apic_mapping driver:
https://review.openstack.org/#/c/385218/
Change-Id: I79ebd4f283dba573090331926e268310f4cd4bce
(cherry picked from commit 852d379b23)
This commit is contained in:
@@ -144,6 +144,8 @@ class ApicMechanismDriver(api_plus.MechanismDriver):
|
||||
self.aim = aim_manager.AimManager()
|
||||
self._core_plugin = None
|
||||
self._l3_plugin = None
|
||||
self._gbp_plugin = None
|
||||
self._aim_mapping = None
|
||||
# Get APIC configuration and subscribe for changes
|
||||
self.enable_metadata_opt = (
|
||||
cfg.CONF.ml2_apic_aim.enable_optimized_metadata)
|
||||
@@ -1110,21 +1112,25 @@ class ApicMechanismDriver(api_plus.MechanismDriver):
|
||||
self._notify_port_update_bulk(context, ports_to_notify)
|
||||
|
||||
def bind_port(self, context):
|
||||
current = context.current
|
||||
port = context.current
|
||||
LOG.debug("Attempting to bind port %(port)s on network %(net)s",
|
||||
{'port': current['id'],
|
||||
{'port': port['id'],
|
||||
'net': context.network.current['id']})
|
||||
|
||||
# Check the VNIC type.
|
||||
vnic_type = current.get(portbindings.VNIC_TYPE,
|
||||
portbindings.VNIC_NORMAL)
|
||||
vnic_type = port.get(portbindings.VNIC_TYPE,
|
||||
portbindings.VNIC_NORMAL)
|
||||
if vnic_type not in [portbindings.VNIC_NORMAL]:
|
||||
LOG.debug("Refusing to bind due to unsupported vnic_type: %s",
|
||||
vnic_type)
|
||||
return
|
||||
|
||||
# For compute ports, try to bind DVS agent first.
|
||||
if current['device_owner'].startswith('compute:'):
|
||||
if port['device_owner'].startswith('compute:'):
|
||||
if (self.aim_mapping and not
|
||||
self.aim_mapping.check_allow_vm_names(context, port)):
|
||||
return
|
||||
|
||||
# For compute ports, try to bind DVS agent first.
|
||||
if self._agent_bind_port(context, AGENT_TYPE_DVS,
|
||||
self._dvs_bind_port):
|
||||
return
|
||||
@@ -1320,6 +1326,20 @@ class ApicMechanismDriver(api_plus.MechanismDriver):
|
||||
self._l3_plugin = plugins[pconst.L3_ROUTER_NAT]
|
||||
return self._l3_plugin
|
||||
|
||||
@property
|
||||
def gbp_plugin(self):
|
||||
if not self._gbp_plugin:
|
||||
self._gbp_plugin = (manager.NeutronManager.get_service_plugins()
|
||||
.get("GROUP_POLICY"))
|
||||
return self._gbp_plugin
|
||||
|
||||
@property
|
||||
def aim_mapping(self):
|
||||
if not self._aim_mapping and self.gbp_plugin:
|
||||
self._aim_mapping = (self.gbp_plugin.policy_driver_manager.
|
||||
policy_drivers['aim_mapping'].obj)
|
||||
return self._aim_mapping
|
||||
|
||||
def _merge_status(self, aim_ctx, sync_state, resource):
|
||||
status = self.aim.get_status(aim_ctx, resource)
|
||||
if not status:
|
||||
|
||||
@@ -11,12 +11,14 @@
|
||||
# under the License.
|
||||
|
||||
import hashlib
|
||||
import re
|
||||
|
||||
from aim.api import resource as aim_resource
|
||||
from aim import context as aim_context
|
||||
from aim import utils as aim_utils
|
||||
from neutron._i18n import _LE
|
||||
from neutron._i18n import _LI
|
||||
from neutron._i18n import _LW
|
||||
from neutron.agent.linux import dhcp
|
||||
from neutron.api.v2 import attributes
|
||||
from neutron.common import constants as n_constants
|
||||
@@ -49,9 +51,10 @@ from gbpservice.neutron.services.grouppolicy.drivers.cisco.apic import (
|
||||
aim_mapping_rpc as aim_rpc)
|
||||
from gbpservice.neutron.services.grouppolicy.drivers.cisco.apic import (
|
||||
apic_mapping_lib as alib)
|
||||
from gbpservice.neutron.services.grouppolicy.drivers.cisco.apic import (
|
||||
nova_client as nclient)
|
||||
from gbpservice.neutron.services.grouppolicy import plugin as gbp_plugin
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
FORWARD = 'Forward'
|
||||
REVERSE = 'Reverse'
|
||||
@@ -145,6 +148,7 @@ class AIMMappingDriver(nrd.CommonNeutronBase, aim_rpc.AIMMappingRPCMixin):
|
||||
super(AIMMappingDriver, self).initialize()
|
||||
self._apic_aim_mech_driver = None
|
||||
self._apic_segmentation_label_driver = None
|
||||
self._apic_allowed_vm_name_driver = None
|
||||
self.create_auto_ptg = cfg.CONF.aim_mapping.create_auto_ptg
|
||||
if self.create_auto_ptg:
|
||||
LOG.info(_LI('Auto PTG creation configuration set, '
|
||||
@@ -180,6 +184,21 @@ class AIMMappingDriver(nrd.CommonNeutronBase, aim_rpc.AIMMappingRPCMixin):
|
||||
break
|
||||
return self._apic_segmentation_label_driver
|
||||
|
||||
@property
|
||||
def apic_allowed_vm_name_driver(self):
|
||||
if self._apic_allowed_vm_name_driver is False:
|
||||
return False
|
||||
if not self._apic_allowed_vm_name_driver:
|
||||
ext_drivers = (self.gbp_plugin.extension_manager.
|
||||
ordered_ext_drivers)
|
||||
for driver in ext_drivers:
|
||||
if 'apic_allowed_vm_name' == driver.name:
|
||||
self._apic_allowed_vm_name_driver = driver.obj
|
||||
break
|
||||
if not self._apic_allowed_vm_name_driver:
|
||||
self._apic_allowed_vm_name_driver = False
|
||||
return self._apic_allowed_vm_name_driver
|
||||
|
||||
@log.log_method_call
|
||||
def ensure_tenant(self, plugin_context, tenant_id):
|
||||
self.aim_mech_driver.ensure_tenant(plugin_context, tenant_id)
|
||||
@@ -1030,6 +1049,32 @@ class AIMMappingDriver(nrd.CommonNeutronBase, aim_rpc.AIMMappingRPCMixin):
|
||||
np_db.update({'subnet_id': None})
|
||||
self._delete_subnet_on_nat_pool_delete(context)
|
||||
|
||||
def check_allow_vm_names(self, context, port):
|
||||
ok_to_bind = True
|
||||
ptg, pt = self._port_id_to_ptg(context._plugin_context, port['id'])
|
||||
# enforce the allowed_vm_names rules if possible
|
||||
if (ptg and port['device_id'] and
|
||||
self.apic_allowed_vm_name_driver):
|
||||
l2p = self._get_l2_policy(context._plugin_context,
|
||||
ptg['l2_policy_id'])
|
||||
l3p = self.gbp_plugin.get_l3_policy(
|
||||
context._plugin_context, l2p['l3_policy_id'])
|
||||
if l3p.get('allowed_vm_names'):
|
||||
ok_to_bind = False
|
||||
vm = nclient.NovaClient().get_server(port['device_id'])
|
||||
for allowed_vm_name in l3p['allowed_vm_names']:
|
||||
match = re.search(allowed_vm_name, vm.name)
|
||||
if match:
|
||||
ok_to_bind = True
|
||||
break
|
||||
if not ok_to_bind:
|
||||
LOG.warning(_LW("Failed to bind the port due to "
|
||||
"allowed_vm_names rules %(rules)s "
|
||||
"for VM: %(vm)s"),
|
||||
{'rules': l3p['allowed_vm_names'],
|
||||
'vm': vm.name})
|
||||
return ok_to_bind
|
||||
|
||||
def _reject_shared_update(self, context, type):
|
||||
if context.original.get('shared') != context.current.get('shared'):
|
||||
raise SharedAttributeUpdateNotSupported(type=type)
|
||||
|
||||
@@ -85,7 +85,8 @@ class AIMBaseTestCase(test_nr_base.CommonNeutronBaseTestCase,
|
||||
test_ext_base.ExtensionDriverTestBase,
|
||||
test_aim_md.ApicAimTestMixin,
|
||||
test_address_scope.AddressScopeTestCase):
|
||||
_extension_drivers = ['aim_extension', 'apic_segmentation_label']
|
||||
_extension_drivers = ['aim_extension', 'apic_segmentation_label',
|
||||
'apic_allowed_vm_name']
|
||||
_extension_path = None
|
||||
|
||||
def setUp(self, policy_drivers=None, core_plugin=None, ml2_options=None,
|
||||
@@ -2655,6 +2656,38 @@ class TestPolicyTarget(AIMBaseTestCase):
|
||||
self._check_call_list(exp_calls,
|
||||
self.driver.aim_mech_driver._notify_port_update.call_args_list)
|
||||
|
||||
def test_bind_port_with_allowed_vm_names(self):
|
||||
allowed_vm_names = ['safe_vm*', '^secure_vm*']
|
||||
l3p = self.create_l3_policy(name='myl3',
|
||||
allowed_vm_names=allowed_vm_names)['l3_policy']
|
||||
l2p = self.create_l2_policy(
|
||||
name='myl2', l3_policy_id=l3p['id'])['l2_policy']
|
||||
ptg = self.create_policy_target_group(
|
||||
name="ptg1", l2_policy_id=l2p['id'])['policy_target_group']
|
||||
pt = self.create_policy_target(
|
||||
policy_target_group_id=ptg['id'])['policy_target']
|
||||
|
||||
nova_client = mock.patch(
|
||||
'gbpservice.neutron.services.grouppolicy.drivers.cisco.'
|
||||
'apic.nova_client.NovaClient.get_server').start()
|
||||
vm = mock.Mock()
|
||||
vm.name = 'secure_vm1'
|
||||
nova_client.return_value = vm
|
||||
newp1 = self._bind_port_to_host(pt['port_id'], 'h1')
|
||||
self.assertEqual(newp1['port']['binding:vif_type'], 'ovs')
|
||||
|
||||
# bind again
|
||||
vm.name = 'bad_vm1'
|
||||
newp1 = self._bind_port_to_host(pt['port_id'], 'h2')
|
||||
self.assertEqual(newp1['port']['binding:vif_type'], 'binding_failed')
|
||||
|
||||
# update l3p with empty allowed_vm_names
|
||||
l3p = self.update_l3_policy(l3p['id'], tenant_id=l3p['tenant_id'],
|
||||
allowed_vm_names=[],
|
||||
expected_res_status=200)['l3_policy']
|
||||
newp1 = self._bind_port_to_host(pt['port_id'], 'h3')
|
||||
self.assertEqual(newp1['port']['binding:vif_type'], 'ovs')
|
||||
|
||||
|
||||
class TestPolicyTargetRollback(AIMBaseTestCase):
|
||||
|
||||
|
||||
Reference in New Issue
Block a user