NSX|V3: mac learning support

The platform now provides MAC learning support. This patch creates
a mac learning profile and adds that to the port, if enabled.

The code is backwards compatiable. That is, if the V3 version
does not support the MAC learning then the platform will throw
and exception and the profile will not be created.

Change-Id: I62765f6c7bdd2802d3eec60e8163869886a403b0
This commit is contained in:
Gary Kotton 2016-05-15 02:06:18 -07:00
parent e76d7bf9c8
commit b8928a64fb
3 changed files with 106 additions and 1 deletions

View File

@ -71,6 +71,7 @@ class AbstractRESTResource(object):
class SwitchingProfileTypes(object):
IP_DISCOVERY = 'IpDiscoverySwitchingProfile'
MAC_LEARNING = 'MacManagementSwitchingProfile'
PORT_MIRRORING = 'PortMirroringSwitchingProfile'
QOS = 'QosSwitchingProfile'
SPOOF_GUARD = 'SpoofGuardSwitchingProfile'
@ -153,6 +154,18 @@ class SwitchingProfile(AbstractRESTResource):
bpdu_filter=bpdu_filter,
block_non_ip_traffic=True)
def create_mac_learning_profile(self, display_name,
description, tags=None):
mac_learning = {
'enabled': True,
}
return self.create(SwitchingProfileTypes.MAC_LEARNING,
display_name=display_name,
description=description,
tags=tags or [],
mac_learning=mac_learning,
source_mac_change_allowed=True)
@classmethod
def build_switch_profile_ids(cls, client, *profiles):
ids = []

View File

@ -78,7 +78,9 @@ from vmware_nsx.common import utils
from vmware_nsx.db import db as nsx_db
from vmware_nsx.db import extended_security_group
from vmware_nsx.db import extended_security_group_rule as extend_sg_rule
from vmware_nsx.db import maclearning as mac_db
from vmware_nsx.dhcp_meta import rpc as nsx_rpc
from vmware_nsx.extensions import maclearning as mac_ext
from vmware_nsx.extensions import securitygrouplogging as sg_logging
from vmware_nsx.nsxlib import v3 as nsxlib
from vmware_nsx.nsxlib.v3 import client as nsx_client
@ -96,6 +98,7 @@ LOG = log.getLogger(__name__)
NSX_V3_PSEC_PROFILE_NAME = 'neutron_port_spoof_guard_profile'
NSX_V3_NO_PSEC_PROFILE_NAME = 'nsx-default-spoof-guard-vif-profile'
NSX_V3_DHCP_PROFILE_NAME = 'neutron_port_dhcp_profile'
NSX_V3_MAC_LEARNING_PROFILE_NAME = 'neutron_port_mac_learning_profile'
# NOTE(asarfaty): the order of inheritance here is important. in order for the
@ -112,7 +115,8 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
portbindings_db.PortBindingMixin,
portsecurity_db.PortSecurityDbMixin,
extradhcpopt_db.ExtraDhcpOptMixin,
dns_db.DNSDbMixin):
dns_db.DNSDbMixin,
mac_db.MacLearningDbMixin):
__native_bulk_support = True
__native_pagination_support = True
@ -199,6 +203,18 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
msg = _("Unable to initialize NSX v3 DHCP "
"switching profile: %s") % NSX_V3_DHCP_PROFILE_NAME
raise nsx_exc.NsxPluginException(msg)
LOG.debug("Initializing NSX v3 Mac Learning switching profile")
self._mac_learning_profile = None
try:
self._mac_learning_profile = self._init_mac_learning_profile()
# Only expose the extension if it is supported
self.supported_extension_aliases.append('mac-learning')
except Exception as e:
LOG.warning(_LW("Unable to initialize NSX v3 MAC Learning "
"profile: %(name)s. Reason: %s(reason)"),
{'name': NSX_V3_MAC_LEARNING_PROFILE_NAME,
'reason': e})
self._unsubscribe_callback_events()
if cfg.CONF.api_replay_mode:
self.supported_extension_aliases.append('api-replay')
@ -283,6 +299,26 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
SWITCH_SECURITY),
profile_id=profile[0]['id']) if profile else None
def _init_mac_learning_profile(self):
with locking.LockManager.get_lock('nsxv3_mac_learning_profile_init'):
profile = self._get_mac_learning_profile()
if not profile:
self._switching_profiles.create_mac_learning_profile(
NSX_V3_MAC_LEARNING_PROFILE_NAME,
'Neutron MAC Learning Profile',
tags=utils.build_v3_api_version_tag())
return self._get_mac_learning_profile()
def _get_mac_learning_profile(self):
if self._mac_learning_profile:
return self._mac_learning_profile
profile = self._switching_profiles.find_by_display_name(
NSX_V3_MAC_LEARNING_PROFILE_NAME)
return nsx_resources.SwitchingProfileTypeId(
profile_type=(nsx_resources.SwitchingProfileTypes.
MAC_LEARNING),
profile_id=profile[0]['id']) if profile else None
def _get_port_security_profile_id(self):
return nsx_resources.SwitchingProfile.build_switch_profile_ids(
self._switching_profiles, self._get_port_security_profile())[0]
@ -1137,6 +1173,12 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
qos_profile_id = self._get_qos_profile_id(context, qos_policy_id)
profiles.append(qos_profile_id)
# Add mac_learning profile if it exists and is configured
if (self._mac_learning_profile and
validators.is_attr_set(port_data.get(mac_ext.MAC_LEARNING)) and
port_data.get(mac_ext.MAC_LEARNING) is True):
profiles.append(self._mac_learning_profile)
name = self._get_port_name(context, port_data)
nsx_net_id = port_data[pbin.VIF_DETAILS]['nsx-logical-switch-id']
@ -1451,6 +1493,12 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
self._process_port_create_security_group(
context, port_data, sgids)
self._extend_port_dict_binding(context, port_data)
if validators.is_attr_set(port_data.get(mac_ext.MAC_LEARNING)):
self._create_mac_learning_state(context, port_data)
elif mac_ext.MAC_LEARNING in port_data:
# This is due to the fact that the default is
# ATTR_NOT_SPECIFIED
port_data.pop(mac_ext.MAC_LEARNING)
# Operations to backend should be done outside of DB transaction.
# NOTE(arosen): ports on external networks are nat rules and do
@ -1669,6 +1717,11 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
if qos_profile_id is not None:
switch_profile_ids.append(qos_profile_id)
# Add mac_learning profile if it exists and is configured
if (self._mac_learning_profile and
updated_port.get(mac_ext.MAC_LEARNING) is True):
switch_profile_ids.append(self._mac_learning_profile)
try:
self._port_client.update(
lport_id, vif_uuid, name=name,
@ -1728,6 +1781,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
self._assert_on_external_net_with_compute(port['port'])
self._assert_on_external_net_port_with_qos(port['port'])
old_mac_learning_state = original_port.get(mac_ext.MAC_LEARNING)
updated_port = super(NsxV3Plugin, self).update_port(context,
id, port)
@ -1750,6 +1804,11 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
self._process_portbindings_create_and_update(
context, port['port'], updated_port)
self._extend_port_dict_binding(context, updated_port)
new_mac_learning_state = updated_port.get(mac_ext.MAC_LEARNING)
if (new_mac_learning_state is not None and
old_mac_learning_state != new_mac_learning_state):
self._update_mac_learning_state(context, id,
new_mac_learning_state)
address_bindings = self._build_address_bindings(updated_port)
if port_security and address_bindings:

View File

@ -152,6 +152,39 @@ class TestSwitchingProfileTestCase(nsxlib_testcase.NsxClientTestCase):
'block_non_ip_traffic': True
}, sort_keys=True))
def test_create_mac_learning_profile(self):
tags = [
{
'scope': 'os-project-id',
'tag': 'tenant-1'
},
{
'scope': 'os-api-version',
'tag': '2.1.1.0'
}
]
mocked_resource = self._mocked_switching_profile()
mocked_resource.create_mac_learning_profile(
'neutron-mac-learning', 'mac-learning-for-neutron',
tags=tags)
test_client.assert_json_call(
'post', mocked_resource,
'https://1.2.3.4/api/v1/switching-profiles',
data=jsonutils.dumps({
'mac_learning': {
'enabled': True,
},
'resource_type': profile_types.MAC_LEARNING,
'display_name': 'neutron-mac-learning',
'description': 'mac-learning-for-neutron',
'tags': tags,
'source_mac_change_allowed': True,
}, sort_keys=True))
def test_find_by_display_name(self):
resp_resources = {
'results': [