From 0196f837529fd6510ba2746425ae29e844afef09 Mon Sep 17 00:00:00 2001 From: Vasyl Saienko Date: Fri, 9 Dec 2016 14:54:15 +0200 Subject: [PATCH 1/7] Add vlan aware VMs support With this patch ngs starts supporting attaching of trunk port to baremetal server. Only VLAN Neutron network is supported. There are two ways to configure trunk port: * C1: When segmentation details for trunk ports are inherited from Neutron network. VLAN translation support is not required. Added implementation for cisco, arista, OVS on Linux. * C2: When user set segmentation details for trunk port explicitly. Switch should support VLAN translation for this case. Implement only for OVS on Linux, experimental. NetmikoSwitch.plug_port_to_network() is deprecated. New bind_port() should be used instead. New switch config option: vlan_translation_supported was introduced. This option defines if switch support vlan translation which affect the way how trunk is configured. Change-Id: If084382f4c17438e5142f51f88d6e436f8b85082 (cherry picked from commit 1163a1f062dbf53d9eb5891effb8182fee6a0c21) (cherry picked from commit 7dfe3e2f16cc17f10f07ea389298aa95548a1127) (cherry picked from commit c54e9ba5d4a65a5b2b3ad8a7ae1a58e5ac017e1a) (cherry picked from commit 701e93f85487b372d6a4dd732a242ef4bb0a4d93) --- networking_generic_switch/devices/__init__.py | 2 +- .../devices/netmiko_devices/__init__.py | 41 +++++++++ .../devices/netmiko_devices/arista.py | 12 +++ .../devices/netmiko_devices/cisco.py | 12 +++ .../devices/netmiko_devices/dell.py | 11 +++ networking_generic_switch/exceptions.py | 5 ++ .../generic_switch_mech.py | 51 ++++++++++-- .../tests/unit/netmiko/test_arista_eos.py | 39 +++++++++ .../tests/unit/netmiko/test_cisco_ios.py | 39 +++++++++ .../tests/unit/netmiko/test_dell.py | 47 +++++++++++ .../tests/unit/netmiko/test_netmiko_base.py | 83 +++++++++++++++++++ .../tests/unit/test_devices.py | 8 ++ .../tests/unit/test_generic_switch_mech.py | 6 +- 13 files changed, 349 insertions(+), 7 deletions(-) diff --git a/networking_generic_switch/devices/__init__.py b/networking_generic_switch/devices/__init__.py index 4eda1edb..db2b4bf0 100644 --- a/networking_generic_switch/devices/__init__.py +++ b/networking_generic_switch/devices/__init__.py @@ -175,7 +175,7 @@ def del_network(self, segmentation_id, network_id): pass @abc.abstractmethod - def plug_port_to_network(self, port_id, segmentation_id): + def plug_port_to_network(self, port_id, segmentation_id, trunk_details=None, vtr=False): pass @abc.abstractmethod diff --git a/networking_generic_switch/devices/netmiko_devices/__init__.py b/networking_generic_switch/devices/netmiko_devices/__init__.py index 7f0e2c53..ca7d1c95 100644 --- a/networking_generic_switch/devices/netmiko_devices/__init__.py +++ b/networking_generic_switch/devices/netmiko_devices/__init__.py @@ -90,6 +90,10 @@ class NetmikoSwitch(devices.GenericSwitchDevice): SAVE_CONFIGURATION = None + SET_NATIVE_VLAN = None + + ALLOW_NETWORK_ON_TRUNK = None + ERROR_MSG_PATTERNS = () """Sequence of error message patterns. @@ -276,6 +280,27 @@ def del_network(self, segmentation_id, network_id): network_name=network_name) return self.send_commands_to_device(cmds) + @check_output('plug port trunk') + def plug_port_to_network_trunk(self, port, segmentation_id, trunk_details=None, vtr=False): + cmd_set = [] + vts = self.ngs_config.get('vlan_translation_supported', False) + # NOTE(vsaienko) Always use vlan translation if it is supported. + if vts: + cmd_set.extend(self.get_trunk_port_cmds_vlan_translation( + port, segmentation_id, trunk_details)) + else: + if vtr: + msg = _("Cannot bind_port VLAN aware port as switch %s " + "doesn't support VLAN translation. " + "But it is required.") % self.config['ip'] + raise exc.GenericSwitchNotSupported(error=msg) + else: + cmd_set.extend( + self.get_trunk_port_cmds_no_vlan_translation( + port, segmentation_id, trunk_details)) + + self.send_commands_to_device(cmd_set) + @check_output('plug port') def plug_port_to_network(self, port, segmentation_id): cmds = [] @@ -409,3 +434,19 @@ def check_output(self, output, operation): raise exc.GenericSwitchNetmikoConfigError( config=device_utils.sanitise_config(self.config), error=msg) + + def get_trunk_port_cmds_no_vlan_translation(self, port_id, segmentation_id, trunk_details): + cmd_set = [] + cmd_set.extend( + self._format_commands(self.SET_NATIVE_VLAN, + port=port_id, + segmentation_id=segmentation_id)) + for sub_port in trunk_details.get('sub_ports'): + cmd_set.extend( + self._format_commands( + self.ALLOW_NETWORK_ON_TRUNK, port=port_id, + segmentation_id=sub_port['segmentation_id'])) + return cmd_set + + def get_trunk_port_cmds_vlan_translation(self, port_id, segmentation_id, trunk_details): + pass diff --git a/networking_generic_switch/devices/netmiko_devices/arista.py b/networking_generic_switch/devices/netmiko_devices/arista.py index a9500ac9..c32e338b 100644 --- a/networking_generic_switch/devices/netmiko_devices/arista.py +++ b/networking_generic_switch/devices/netmiko_devices/arista.py @@ -37,3 +37,15 @@ class AristaEos(netmiko_devices.NetmikoSwitch): 'no switchport mode trunk', 'switchport trunk allowed vlan none' ) + + SET_NATIVE_VLAN = ( + 'interface {port}', + 'switchport mode trunk', + 'switchport trunk native vlan {segmentation_id}', + 'switchport trunk allowed vlan add {segmentation_id}' + ) + + ALLOW_NETWORK_ON_TRUNK = ( + 'interface {port}', + 'switchport trunk allowed vlan add {segmentation_id}' + ) diff --git a/networking_generic_switch/devices/netmiko_devices/cisco.py b/networking_generic_switch/devices/netmiko_devices/cisco.py index 4b919763..1587c6cd 100644 --- a/networking_generic_switch/devices/netmiko_devices/cisco.py +++ b/networking_generic_switch/devices/netmiko_devices/cisco.py @@ -39,6 +39,18 @@ class CiscoIos(netmiko_devices.NetmikoSwitch): 'switchport trunk allowed vlan none' ) + SET_NATIVE_VLAN = ( + 'interface {port}', + 'switchport mode trunk', + 'switchport trunk native vlan {segmentation_id}', + 'switchport trunk allowed vlan add {segmentation_id}' + ) + + ALLOW_NETWORK_ON_TRUNK = ( + 'interface {port}', + 'switchport trunk allowed vlan add {segmentation_id}' + ) + class CiscoNxOS(netmiko_devices.NetmikoSwitch): """Netmiko device driver for Cisco Nexus switches running NX-OS.""" diff --git a/networking_generic_switch/devices/netmiko_devices/dell.py b/networking_generic_switch/devices/netmiko_devices/dell.py index 6f197918..d8150cea 100644 --- a/networking_generic_switch/devices/netmiko_devices/dell.py +++ b/networking_generic_switch/devices/netmiko_devices/dell.py @@ -70,6 +70,17 @@ class DellOS10(netmiko_devices.NetmikoSwitch): "exit", ) + SET_NATIVE_VLAN = ( + 'interface {port}', + 'switchport mode trunk', + 'switchport access vlan {segmentation_id}', + ) + + ALLOW_NETWORK_ON_TRUNK = ( + 'interface {port}', + 'switchport trunk allowed vlan {segmentation_id}' + ) + ERROR_MSG_PATTERNS = () """Sequence of error message patterns. diff --git a/networking_generic_switch/exceptions.py b/networking_generic_switch/exceptions.py index 96b57e44..12d8c5f2 100644 --- a/networking_generic_switch/exceptions.py +++ b/networking_generic_switch/exceptions.py @@ -53,3 +53,8 @@ class GenericSwitchNetmikoConfigError(GenericSwitchException): class GenericSwitchBatchError(GenericSwitchException): message = _("Batching error: %(device)s, error: %(error)s") + + +class GenericSwitchNotSupported(GenericSwitchException): + message = _("Requested feature is not supported by " + "networking-generic-switch. %(error)s") diff --git a/networking_generic_switch/generic_switch_mech.py b/networking_generic_switch/generic_switch_mech.py index 237ac99c..0e223a20 100644 --- a/networking_generic_switch/generic_switch_mech.py +++ b/networking_generic_switch/generic_switch_mech.py @@ -18,11 +18,13 @@ from neutron_lib.api.definitions import portbindings from neutron_lib.callbacks import resources from neutron_lib.plugins.ml2 import api +from neutron_lib.plugins import directory from oslo_log import log as logging from networking_generic_switch import config as gsw_conf from networking_generic_switch import devices from networking_generic_switch.devices import utils as device_utils +from networking_generic_switch import exceptions as ngs_exc LOG = logging.getLogger(__name__) @@ -393,6 +395,23 @@ def delete_port_postcommit(self, context): if self._is_port_bound(port): self._unplug_port_from_network(port, context.network.current) + def _is_vlan_translation_required(self, trunk_details): + """Check if vlan translation is required to configure specific trunk. + + :returns: True if vlan translation is required, False otherwise. + """ + core_plugin = directory.get_plugin() + for sub_port in trunk_details.get('sub_ports', {}): + sp_id = sub_port['port_id'] + sp_sid = sub_port['segmentation_id'] + sp_port = core_plugin.get_port(context._plugin_context, sp_id) + sp_net = core_plugin.get_network(context._plugin_context, + sp_port['network_id']) + if sp_sid != sp_net['provider:segmentation_id']: + return True + + return False + def bind_port(self, context): """Attempt to bind a port. @@ -445,7 +464,6 @@ def bind_port(self, context): # of the links should be processed. if not self._is_link_valid(port, network): return - is_802_3ad = self._is_802_3ad(port) for link in local_link_information: port_id = link.get('port_id') @@ -458,15 +476,38 @@ def bind_port(self, context): segments = context.segments_to_bind # If segmentation ID is None, set vlan 1 segmentation_id = segments[0].get('segmentation_id') or 1 + trunk_details = port.get('trunk_details', {}) LOG.debug("Putting port %(port_id)s on %(switch_info)s " "to vlan: %(segmentation_id)s", {'port_id': port_id, 'switch_info': switch_info, 'segmentation_id': segmentation_id}) # Move port to network - if is_802_3ad and hasattr(switch, 'plug_bond_to_network'): - switch.plug_bond_to_network(port_id, segmentation_id) - else: - switch.plug_port_to_network(port_id, segmentation_id) + # START + try: + if trunk_details: + vtr = self._is_vlan_translation_required(trunk_details) + switch.plug_port_to_network_trunk( + port_id, segmentation_id, trunk_details, vtr) + elif is_802_3ad and hasattr(switch, 'plug_bond_to_network'): + switch.plug_bond_to_network(port_id, segmentation_id) + else: + switch.plug_port_to_network( + port_id, segmentation_id) + except ngs_exc.GenericSwitchNotSupported as e: + LOG.warning("Operation is not supported by " + "networking-generic-switch. %(err)s)", + {'err': e}) + raise e + except Exception as e: + LOG.error("Failed to bind port %(port_id)s in " + "segment %(segment_id)s on device " + "%(device)s due to error %(err)s", + {'port_id': port['id'], + 'device': switch_info, + 'segment_id': segmentation_id, + 'err': e}) + raise e + # END LOG.info("Successfully bound port %(port_id)s in segment " "%(segment_id)s on device %(device)s", {'port_id': port['id'], 'device': switch_info, diff --git a/networking_generic_switch/tests/unit/netmiko/test_arista_eos.py b/networking_generic_switch/tests/unit/netmiko/test_arista_eos.py index 69984376..5e17a360 100644 --- a/networking_generic_switch/tests/unit/netmiko/test_arista_eos.py +++ b/networking_generic_switch/tests/unit/netmiko/test_arista_eos.py @@ -14,6 +14,8 @@ from unittest import mock +from neutron.plugins.ml2 import driver_context + from networking_generic_switch.devices.netmiko_devices import arista from networking_generic_switch.tests.unit.netmiko import test_netmiko_base @@ -57,6 +59,43 @@ def test_delete_port(self, mock_exec): 'no switchport mode trunk', 'switchport trunk allowed vlan none']) + def test_get_trunk_port_cmds_no_vlan_translation(self): + mock_context = mock.create_autospec(driver_context.PortContext) + self.switch.ngs_config['vlan_translation_supported'] = False + trunk_details = {'trunk_id': 'aaa-bbb-ccc-ddd', + 'sub_ports': [{'segmentation_id': 130, + 'port_id': 'aaa-bbb-ccc-ddd', + 'segmentation_type': 'vlan', + 'mac_address': u'fa:16:3e:1c:c2:7e'}]} + mock_context.current = {'binding:profile': + {'local_link_information': + [ + { + 'switch_info': 'foo', + 'port_id': '2222' + } + ] + }, + 'binding:vnic_type': 'baremetal', + 'id': 'aaaa-bbbb-cccc', + 'trunk_details': trunk_details} + mock_context.network = mock.Mock() + mock_context.network.current = {'provider:segmentation_id': 123} + mock_context.segments_to_bind = [ + { + 'segmentation_id': 777, + 'id': 123 + } + ] + res = self.switch.get_trunk_port_cmds_no_vlan_translation( + '2222', 777, trunk_details) + self.assertEqual(['interface 2222', 'switchport mode trunk', + 'switchport trunk native vlan 777', + 'switchport trunk allowed vlan add 777', + 'interface 2222', + 'switchport trunk allowed vlan add 130'], + res) + def test__format_commands(self): cmd_set = self.switch._format_commands( arista.AristaEos.ADD_NETWORK, diff --git a/networking_generic_switch/tests/unit/netmiko/test_cisco_ios.py b/networking_generic_switch/tests/unit/netmiko/test_cisco_ios.py index 22705b66..0ffdb42f 100644 --- a/networking_generic_switch/tests/unit/netmiko/test_cisco_ios.py +++ b/networking_generic_switch/tests/unit/netmiko/test_cisco_ios.py @@ -14,6 +14,8 @@ from unittest import mock +from neutron.plugins.ml2 import driver_context + from networking_generic_switch.devices.netmiko_devices import cisco from networking_generic_switch.tests.unit.netmiko import test_netmiko_base @@ -56,6 +58,43 @@ def test_delete_port(self, mock_exec): ['interface 3333', 'no switchport access vlan 33', 'no switchport mode trunk', 'switchport trunk allowed vlan none']) + def test_get_trunk_port_cmds_no_vlan_translation(self): + mock_context = mock.create_autospec(driver_context.PortContext) + self.switch.ngs_config['vlan_translation_supported'] = True + trunk_details = {'trunk_id': 'aaa-bbb-ccc-ddd', + 'sub_ports': [{'segmentation_id': 130, + 'port_id': 'aaa-bbb-ccc-ddd', + 'segmentation_type': 'vlan', + 'mac_address': u'fa:16:3e:1c:c2:7e'}]} + mock_context.current = {'binding:profile': + {'local_link_information': + [ + { + 'switch_info': 'foo', + 'port_id': '2222' + } + ] + }, + 'binding:vnic_type': 'baremetal', + 'id': 'aaaa-bbbb-cccc', + 'trunk_details': trunk_details} + mock_context.network = mock.Mock() + mock_context.network.current = {'provider:segmentation_id': 123} + mock_context.segments_to_bind = [ + { + 'segmentation_id': 777, + 'id': 123 + } + ] + res = self.switch.get_trunk_port_cmds_no_vlan_translation( + '2222', 777, trunk_details) + self.assertEqual(['interface 2222', 'switchport mode trunk', + 'switchport trunk native vlan 777', + 'switchport trunk allowed vlan add 777', + 'interface 2222', + 'switchport trunk allowed vlan add 130'], + res) + def test__format_commands(self): cmd_set = self.switch._format_commands( cisco.CiscoIos.ADD_NETWORK, diff --git a/networking_generic_switch/tests/unit/netmiko/test_dell.py b/networking_generic_switch/tests/unit/netmiko/test_dell.py index 243cdfd9..6454d0f4 100644 --- a/networking_generic_switch/tests/unit/netmiko/test_dell.py +++ b/networking_generic_switch/tests/unit/netmiko/test_dell.py @@ -14,11 +14,58 @@ from unittest import mock +from neutron.plugins.ml2 import driver_context + from networking_generic_switch.devices.netmiko_devices import dell from networking_generic_switch import exceptions as exc from networking_generic_switch.tests.unit.netmiko import test_netmiko_base +class TestNetmikoDellOS10(test_netmiko_base.NetmikoSwitchTestBase): + + def _make_switch_device(self, extra_cfg={}): + device_cfg = {'device_type': 'netmiko_dell_os10'} + device_cfg.update(extra_cfg) + return dell.DellOS10(device_cfg) + + + def test_get_trunk_port_cmds_no_vlan_translation(self): + mock_context = mock.create_autospec(driver_context.PortContext) + self.switch.ngs_config['vlan_translation_supported'] = True + trunk_details = {'trunk_id': 'aaa-bbb-ccc-ddd', + 'sub_ports': [{'segmentation_id': 130, + 'port_id': 'aaa-bbb-ccc-ddd', + 'segmentation_type': 'vlan', + 'mac_address': u'fa:16:3e:1c:c2:7e'}]} + mock_context.current = {'binding:profile': + {'local_link_information': + [ + { + 'switch_info': 'foo', + 'port_id': '2222' + } + ] + }, + 'binding:vnic_type': 'baremetal', + 'id': 'aaaa-bbbb-cccc', + 'trunk_details': trunk_details} + mock_context.network = mock.Mock() + mock_context.network.current = {'provider:segmentation_id': 123} + mock_context.segments_to_bind = [ + { + 'segmentation_id': 777, + 'id': 123 + } + ] + res = self.switch.get_trunk_port_cmds_no_vlan_translation( + '2222', 777, trunk_details) + self.assertEqual(['interface 2222', 'switchport mode trunk', + 'switchport access vlan 777', + 'interface 2222', + 'switchport trunk allowed vlan 130'], + res) + + class TestNetmikoDellNos(test_netmiko_base.NetmikoSwitchTestBase): def _make_switch_device(self, extra_cfg={}): diff --git a/networking_generic_switch/tests/unit/netmiko/test_netmiko_base.py b/networking_generic_switch/tests/unit/netmiko/test_netmiko_base.py index 4d01b9ed..4c2c5da6 100644 --- a/networking_generic_switch/tests/unit/netmiko/test_netmiko_base.py +++ b/networking_generic_switch/tests/unit/netmiko/test_netmiko_base.py @@ -15,6 +15,8 @@ import re from unittest import mock +from neutron.plugins.ml2 import driver_context + import fixtures import netmiko import netmiko.base_connection @@ -411,3 +413,84 @@ def test_check_output_error(self): "fake op. Output: %s" % output) self.assertRaisesRegex(exc.GenericSwitchNetmikoConfigError, msg, self.switch.check_output, output, 'fake op') + + @mock.patch.object(netmiko_devices.netmiko, 'ConnectHandler') + @mock.patch.object(netmiko_devices.NetmikoSwitch, + 'send_commands_to_device') + @mock.patch.object(netmiko_devices.NetmikoSwitch, + 'get_trunk_port_cmds_no_vlan_translation') + @mock.patch.object(netmiko_devices.NetmikoSwitch, + 'get_trunk_port_cmds_vlan_translation') + def test_bind_port_trunk_no_vts(self, t_mock, nt_mock, sctd_mock, + nm_mock): + mock_context = mock.create_autospec(driver_context.PortContext) + connect_mock = mock.Mock() + nm_mock.return_value = connect_mock + self.switch.ngs_config['vlan_translation_supported'] = False + mock_context.current = {'binding:profile': + {'local_link_information': + [ + { + 'switch_info': 'foo', + 'port_id': '2222' + } + ] + }, + 'binding:vnic_type': 'baremetal', + 'id': 'aaaa-bbbb-cccc', + 'trunk_details': {'sub_ports': + [{'segmentation_id': 123}]}} + trunk_details = {'sub_ports': [{'segmentation_id': 123}]} + self.switch.plug_port_to_network_trunk('2222', None, trunk_details, vtr=False) + nt_mock.assert_called_once_with('2222', None, {'sub_ports': [{'segmentation_id': 123}]}) + self.assertFalse(t_mock.called) + + @mock.patch.object(netmiko_devices.netmiko, 'ConnectHandler') + @mock.patch.object(netmiko_devices.NetmikoSwitch, + 'send_commands_to_device') + @mock.patch.object(netmiko_devices.NetmikoSwitch, + 'get_trunk_port_cmds_no_vlan_translation') + @mock.patch.object(netmiko_devices.NetmikoSwitch, + 'get_trunk_port_cmds_vlan_translation') + def test_bind_port_trunk_vts(self, t_mock, nt_mock, sctd_mock, + nm_mock): + mock_context = mock.create_autospec(driver_context.PortContext) + connect_mock = mock.Mock() + nm_mock.return_value = connect_mock + self.switch.ngs_config['vlan_translation_supported'] = True + mock_context.current = {'binding:profile': + {'local_link_information': + [ + { + 'switch_info': 'foo', + 'port_id': '2222' + } + ] + }, + 'binding:vnic_type': 'baremetal', + 'id': 'aaaa-bbbb-cccc', + 'trunk_details': {'sub_ports': + [{'segmentation_id': 123}]}} + trunk_details = {'sub_ports': [{'segmentation_id': 123}]} + self.switch.plug_port_to_network_trunk('2222', None, trunk_details, vtr=False) + t_mock.assert_called_once_with('2222', None, {'sub_ports': [{'segmentation_id': 123}]}) + self.assertFalse(nt_mock.called) + + @mock.patch.object(netmiko_devices.netmiko, 'ConnectHandler') + @mock.patch.object(netmiko_devices.NetmikoSwitch, + 'send_commands_to_device') + @mock.patch.object(netmiko_devices.NetmikoSwitch, + 'get_trunk_port_cmds_no_vlan_translation') + @mock.patch.object(netmiko_devices.NetmikoSwitch, + 'get_trunk_port_cmds_vlan_translation') + def test_bind_port_trunk_no_vts_raise(self, t_mock, nt_mock, sctd_mock, + nm_mock): + connect_mock = mock.Mock() + mock_context = mock.create_autospec(driver_context.PortContext) + nm_mock.return_value = connect_mock + self.switch.ngs_config['vlan_translation_supported'] = False + trunk_details = {'sub_ports': [{'segmentation_id': 123}]} + self.assertRaises(exc.GenericSwitchNotSupported, + self.switch.plug_port_to_network_trunk, '2222', None, trunk_details, vtr=True) + self.assertFalse(t_mock.called) + self.assertFalse(nt_mock.called) diff --git a/networking_generic_switch/tests/unit/test_devices.py b/networking_generic_switch/tests/unit/test_devices.py index 7587837e..482dbd1f 100644 --- a/networking_generic_switch/tests/unit/test_devices.py +++ b/networking_generic_switch/tests/unit/test_devices.py @@ -218,3 +218,11 @@ def test__get_ssh_disabled_algorithms(self): "ciphers": ["blowfish-cbc", "3des-cbc"], } self.assertEqual(expected, algos) + + def test_driver_load_config_override(self): + device_cfg = {"device_type": 'netmiko_ovs_linux', + "vlan_translation_supported": True} + device = devices.device_manager(device_cfg) + self.assertIsInstance(device, devices.GenericSwitchDevice) + self.assertNotIn('vlan_translation_support', device.config) + self.assertTrue(device.ngs_config['vlan_translation_supported']) diff --git a/networking_generic_switch/tests/unit/test_generic_switch_mech.py b/networking_generic_switch/tests/unit/test_generic_switch_mech.py index 6c3356c9..6b1ae9f5 100644 --- a/networking_generic_switch/tests/unit/test_generic_switch_mech.py +++ b/networking_generic_switch/tests/unit/test_generic_switch_mech.py @@ -19,6 +19,7 @@ from neutron.db import provisioning_blocks from neutron.plugins.ml2 import driver_context from neutron_lib.callbacks import resources +from neutron_lib.plugins import directory from networking_generic_switch import exceptions from networking_generic_switch import generic_switch_mech as gsm @@ -512,10 +513,13 @@ def test_update_port_postcommit_complete_provisioning(self, m_pc, m_list): resources.PORT, 'GENERICSWITCH') + @mock.patch.object(gsm.GenericSwitchDriver, + '_is_vlan_translation_required', return_value=False) @mock.patch.object(provisioning_blocks, 'provisioning_complete') def test_update_portgroup_postcommit_complete_provisioning(self, m_pc, - m_list): + m_list, + m_ivtr): driver = gsm.GenericSwitchDriver() driver.initialize() mock_context = mock.create_autospec(driver_context.PortContext) From 443e65fc2a6b880dff932da9f20e05b33bc9000e Mon Sep 17 00:00:00 2001 From: Will Szumski Date: Fri, 25 Feb 2022 18:41:10 +0000 Subject: [PATCH 2/7] fixup! Add vlan aware VMs support (cherry picked from commit 10b1fbb9f8d5ecfdea496d5c7b399605f75f9ff1) (cherry picked from commit 81e4b13f2969605e74b6ff0567445134d7653bcc) (cherry picked from commit 4ba8c346b16efa3732f32ad428602e6e39d8b364) --- networking_generic_switch/devices/__init__.py | 6 ++- .../generic_switch_mech.py | 11 +--- .../tests/unit/test_generic_switch_mech.py | 50 +++++++++++++++++++ 3 files changed, 56 insertions(+), 11 deletions(-) diff --git a/networking_generic_switch/devices/__init__.py b/networking_generic_switch/devices/__init__.py index db2b4bf0..f38e8495 100644 --- a/networking_generic_switch/devices/__init__.py +++ b/networking_generic_switch/devices/__init__.py @@ -175,7 +175,11 @@ def del_network(self, segmentation_id, network_id): pass @abc.abstractmethod - def plug_port_to_network(self, port_id, segmentation_id, trunk_details=None, vtr=False): + def plug_port_to_network_trunk(self, port_id, segmentation_id, trunk_details=None, vtr=False): + pass + + @abc.abstractmethod + def plug_port_to_network(self, port_id, segmentation_id): pass @abc.abstractmethod diff --git a/networking_generic_switch/generic_switch_mech.py b/networking_generic_switch/generic_switch_mech.py index 0e223a20..3c76554b 100644 --- a/networking_generic_switch/generic_switch_mech.py +++ b/networking_generic_switch/generic_switch_mech.py @@ -400,16 +400,7 @@ def _is_vlan_translation_required(self, trunk_details): :returns: True if vlan translation is required, False otherwise. """ - core_plugin = directory.get_plugin() - for sub_port in trunk_details.get('sub_ports', {}): - sp_id = sub_port['port_id'] - sp_sid = sub_port['segmentation_id'] - sp_port = core_plugin.get_port(context._plugin_context, sp_id) - sp_net = core_plugin.get_network(context._plugin_context, - sp_port['network_id']) - if sp_sid != sp_net['provider:segmentation_id']: - return True - + # FIXME: removed for simplicity return False def bind_port(self, context): diff --git a/networking_generic_switch/tests/unit/test_generic_switch_mech.py b/networking_generic_switch/tests/unit/test_generic_switch_mech.py index 6b1ae9f5..51798569 100644 --- a/networking_generic_switch/tests/unit/test_generic_switch_mech.py +++ b/networking_generic_switch/tests/unit/test_generic_switch_mech.py @@ -787,6 +787,56 @@ def test_bind_port(self, m_apc, m_list): resources.PORT, 'GENERICSWITCH') + @mock.patch.object(gsm.GenericSwitchDriver, + '_is_vlan_translation_required', return_value=False) + @mock.patch.object(provisioning_blocks, 'add_provisioning_component') + def test_bind_port_trunk(self, m_apc, m_list, m_vlan): + driver = gsm.GenericSwitchDriver() + driver.initialize() + mock_context = mock.create_autospec(driver_context.PortContext) + mock_context._plugin_context = mock.MagicMock() + trunk_details = {'trunk_id': 'aaa-bbb-ccc-ddd', + 'sub_ports': [{'segmentation_id': 130, + 'port_id': 'aaa-bbb-ccc-ddd', + 'segmentation_type': 'vlan', + 'mac_address': u'fa:16:3e:1c:c2:7e'}]} + mock_context.network.current = { + 'provider:physical_network': 'physnet1' + } + mock_context.current = {'binding:profile': + {'local_link_information': + [ + { + 'switch_info': 'foo', + 'port_id': '2222' + } + ] + }, + 'binding:vnic_type': 'baremetal', + 'id': 'aaaa-bbbb-cccc', + 'trunk_details': trunk_details} + mock_context.network = mock.Mock() + mock_context.network.current = { + 'provider:segmentation_id': 123, + 'provider:physical_network': 'physnet1' + } + mock_context.segments_to_bind = [ + { + 'segmentation_id': 777, + 'id': 123 + } + ] + + driver.bind_port(mock_context) + self.switch_mock.plug_port_to_network_trunk.assert_called_once_with( + '2222', 777, trunk_details, False) + mock_context.set_binding.assert_called_with(123, 'other', {}) + m_apc.assert_called_once_with(mock_context._plugin_context, + mock_context.current['id'], + resources.PORT, + 'GENERICSWITCH') + + @mock.patch.object(provisioning_blocks, 'add_provisioning_component') def test_bind_portgroup(self, m_apc, m_list): driver = gsm.GenericSwitchDriver() From 350949b8bd79049cb599d9de2d0f359eb633c2e1 Mon Sep 17 00:00:00 2001 From: Will Szumski Date: Fri, 25 Feb 2022 19:35:49 +0000 Subject: [PATCH 3/7] fixup! Add vlan aware VMs support (cherry picked from commit d9409564012f932bd9ee91f11c2b8f3a51213b33) (cherry picked from commit e57a5c5e2aa23255fba828b69da8e8534f610c0f) (cherry picked from commit 0ae429654a3cf91cccf866c49ef0843333dce6bb) --- networking_generic_switch/devices/__init__.py | 1 - networking_generic_switch/devices/netmiko_devices/dell.py | 2 ++ networking_generic_switch/tests/unit/netmiko/test_dell.py | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/networking_generic_switch/devices/__init__.py b/networking_generic_switch/devices/__init__.py index f38e8495..94eee738 100644 --- a/networking_generic_switch/devices/__init__.py +++ b/networking_generic_switch/devices/__init__.py @@ -174,7 +174,6 @@ def add_network(self, segmentation_id, network_id): def del_network(self, segmentation_id, network_id): pass - @abc.abstractmethod def plug_port_to_network_trunk(self, port_id, segmentation_id, trunk_details=None, vtr=False): pass diff --git a/networking_generic_switch/devices/netmiko_devices/dell.py b/networking_generic_switch/devices/netmiko_devices/dell.py index d8150cea..2ce89323 100644 --- a/networking_generic_switch/devices/netmiko_devices/dell.py +++ b/networking_generic_switch/devices/netmiko_devices/dell.py @@ -72,6 +72,8 @@ class DellOS10(netmiko_devices.NetmikoSwitch): SET_NATIVE_VLAN = ( 'interface {port}', + # Clean all the old trunked vlans by switching to access mode first + 'switchport mode access', 'switchport mode trunk', 'switchport access vlan {segmentation_id}', ) diff --git a/networking_generic_switch/tests/unit/netmiko/test_dell.py b/networking_generic_switch/tests/unit/netmiko/test_dell.py index 6454d0f4..9501ef24 100644 --- a/networking_generic_switch/tests/unit/netmiko/test_dell.py +++ b/networking_generic_switch/tests/unit/netmiko/test_dell.py @@ -59,7 +59,8 @@ def test_get_trunk_port_cmds_no_vlan_translation(self): ] res = self.switch.get_trunk_port_cmds_no_vlan_translation( '2222', 777, trunk_details) - self.assertEqual(['interface 2222', 'switchport mode trunk', + self.assertEqual(['interface 2222', 'switchport mode access', + 'switchport mode trunk', 'switchport access vlan 777', 'interface 2222', 'switchport trunk allowed vlan 130'], From f630c0a6a2b0df28799e43025b67cbc3a01e2436 Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Wed, 8 Feb 2023 11:11:46 +0000 Subject: [PATCH 4/7] VLAN aware VMs: pep8 fix ups Change-Id: Ia8b2e2b8ea90830fbdbc60d8a3b64afe693224db (cherry picked from commit 9ad464f79835da339035c044b4fbdb72eb309e9a) (cherry picked from commit 8fa4b7b875bbc8a889650e9cde55705b6d3dc071) --- networking_generic_switch/devices/__init__.py | 3 ++- .../devices/netmiko_devices/__init__.py | 16 +++++++++----- .../generic_switch_mech.py | 22 +++++++++---------- .../tests/unit/netmiko/test_dell.py | 1 - .../tests/unit/netmiko/test_netmiko_base.py | 19 +++++++++------- .../tests/unit/test_generic_switch_mech.py | 2 -- 6 files changed, 34 insertions(+), 29 deletions(-) diff --git a/networking_generic_switch/devices/__init__.py b/networking_generic_switch/devices/__init__.py index 94eee738..15f48d5f 100644 --- a/networking_generic_switch/devices/__init__.py +++ b/networking_generic_switch/devices/__init__.py @@ -174,7 +174,8 @@ def add_network(self, segmentation_id, network_id): def del_network(self, segmentation_id, network_id): pass - def plug_port_to_network_trunk(self, port_id, segmentation_id, trunk_details=None, vtr=False): + def plug_port_to_network_trunk(self, port_id, segmentation_id, + trunk_details=None, vtr=False): pass @abc.abstractmethod diff --git a/networking_generic_switch/devices/netmiko_devices/__init__.py b/networking_generic_switch/devices/netmiko_devices/__init__.py index ca7d1c95..b50a6245 100644 --- a/networking_generic_switch/devices/netmiko_devices/__init__.py +++ b/networking_generic_switch/devices/netmiko_devices/__init__.py @@ -281,7 +281,8 @@ def del_network(self, segmentation_id, network_id): return self.send_commands_to_device(cmds) @check_output('plug port trunk') - def plug_port_to_network_trunk(self, port, segmentation_id, trunk_details=None, vtr=False): + def plug_port_to_network_trunk(self, port, segmentation_id, + trunk_details=None, vtr=False): cmd_set = [] vts = self.ngs_config.get('vlan_translation_supported', False) # NOTE(vsaienko) Always use vlan translation if it is supported. @@ -290,9 +291,9 @@ def plug_port_to_network_trunk(self, port, segmentation_id, trunk_details=None, port, segmentation_id, trunk_details)) else: if vtr: - msg = _("Cannot bind_port VLAN aware port as switch %s " - "doesn't support VLAN translation. " - "But it is required.") % self.config['ip'] + msg = ("Cannot bind_port VLAN aware port as switch %s " + "doesn't support VLAN translation. " + "But it is required.") % self.config['ip'] raise exc.GenericSwitchNotSupported(error=msg) else: cmd_set.extend( @@ -435,7 +436,9 @@ def check_output(self, output, operation): config=device_utils.sanitise_config(self.config), error=msg) - def get_trunk_port_cmds_no_vlan_translation(self, port_id, segmentation_id, trunk_details): + def get_trunk_port_cmds_no_vlan_translation(self, port_id, + segmentation_id, + trunk_details): cmd_set = [] cmd_set.extend( self._format_commands(self.SET_NATIVE_VLAN, @@ -448,5 +451,6 @@ def get_trunk_port_cmds_no_vlan_translation(self, port_id, segmentation_id, trun segmentation_id=sub_port['segmentation_id'])) return cmd_set - def get_trunk_port_cmds_vlan_translation(self, port_id, segmentation_id, trunk_details): + def get_trunk_port_cmds_vlan_translation(self, port_id, segmentation_id, + trunk_details): pass diff --git a/networking_generic_switch/generic_switch_mech.py b/networking_generic_switch/generic_switch_mech.py index 3c76554b..46945f9a 100644 --- a/networking_generic_switch/generic_switch_mech.py +++ b/networking_generic_switch/generic_switch_mech.py @@ -18,7 +18,6 @@ from neutron_lib.api.definitions import portbindings from neutron_lib.callbacks import resources from neutron_lib.plugins.ml2 import api -from neutron_lib.plugins import directory from oslo_log import log as logging from networking_generic_switch import config as gsw_conf @@ -479,25 +478,26 @@ def bind_port(self, context): vtr = self._is_vlan_translation_required(trunk_details) switch.plug_port_to_network_trunk( port_id, segmentation_id, trunk_details, vtr) - elif is_802_3ad and hasattr(switch, 'plug_bond_to_network'): + elif (is_802_3ad + and hasattr(switch, 'plug_bond_to_network')): switch.plug_bond_to_network(port_id, segmentation_id) else: switch.plug_port_to_network( port_id, segmentation_id) except ngs_exc.GenericSwitchNotSupported as e: LOG.warning("Operation is not supported by " - "networking-generic-switch. %(err)s)", + "networking-generic-switch. %(err)s)", {'err': e}) raise e except Exception as e: - LOG.error("Failed to bind port %(port_id)s in " - "segment %(segment_id)s on device " - "%(device)s due to error %(err)s", - {'port_id': port['id'], - 'device': switch_info, - 'segment_id': segmentation_id, - 'err': e}) - raise e + LOG.error("Failed to bind port %(port_id)s in " + "segment %(segment_id)s on device " + "%(device)s due to error %(err)s", + {'port_id': port['id'], + 'device': switch_info, + 'segment_id': segmentation_id, + 'err': e}) + raise e # END LOG.info("Successfully bound port %(port_id)s in segment " "%(segment_id)s on device %(device)s", diff --git a/networking_generic_switch/tests/unit/netmiko/test_dell.py b/networking_generic_switch/tests/unit/netmiko/test_dell.py index 9501ef24..f489ed4e 100644 --- a/networking_generic_switch/tests/unit/netmiko/test_dell.py +++ b/networking_generic_switch/tests/unit/netmiko/test_dell.py @@ -28,7 +28,6 @@ def _make_switch_device(self, extra_cfg={}): device_cfg.update(extra_cfg) return dell.DellOS10(device_cfg) - def test_get_trunk_port_cmds_no_vlan_translation(self): mock_context = mock.create_autospec(driver_context.PortContext) self.switch.ngs_config['vlan_translation_supported'] = True diff --git a/networking_generic_switch/tests/unit/netmiko/test_netmiko_base.py b/networking_generic_switch/tests/unit/netmiko/test_netmiko_base.py index 4c2c5da6..3f11b2d4 100644 --- a/networking_generic_switch/tests/unit/netmiko/test_netmiko_base.py +++ b/networking_generic_switch/tests/unit/netmiko/test_netmiko_base.py @@ -15,11 +15,10 @@ import re from unittest import mock -from neutron.plugins.ml2 import driver_context - import fixtures import netmiko import netmiko.base_connection +from neutron.plugins.ml2 import driver_context from oslo_config import fixture as config_fixture import paramiko import tenacity @@ -441,8 +440,10 @@ def test_bind_port_trunk_no_vts(self, t_mock, nt_mock, sctd_mock, 'trunk_details': {'sub_ports': [{'segmentation_id': 123}]}} trunk_details = {'sub_ports': [{'segmentation_id': 123}]} - self.switch.plug_port_to_network_trunk('2222', None, trunk_details, vtr=False) - nt_mock.assert_called_once_with('2222', None, {'sub_ports': [{'segmentation_id': 123}]}) + self.switch.plug_port_to_network_trunk('2222', None, trunk_details, + vtr=False) + nt_mock.assert_called_once_with( + '2222', None, {'sub_ports': [{'segmentation_id': 123}]}) self.assertFalse(t_mock.called) @mock.patch.object(netmiko_devices.netmiko, 'ConnectHandler') @@ -472,8 +473,10 @@ def test_bind_port_trunk_vts(self, t_mock, nt_mock, sctd_mock, 'trunk_details': {'sub_ports': [{'segmentation_id': 123}]}} trunk_details = {'sub_ports': [{'segmentation_id': 123}]} - self.switch.plug_port_to_network_trunk('2222', None, trunk_details, vtr=False) - t_mock.assert_called_once_with('2222', None, {'sub_ports': [{'segmentation_id': 123}]}) + self.switch.plug_port_to_network_trunk('2222', None, + trunk_details, vtr=False) + t_mock.assert_called_once_with( + '2222', None, {'sub_ports': [{'segmentation_id': 123}]}) self.assertFalse(nt_mock.called) @mock.patch.object(netmiko_devices.netmiko, 'ConnectHandler') @@ -486,11 +489,11 @@ def test_bind_port_trunk_vts(self, t_mock, nt_mock, sctd_mock, def test_bind_port_trunk_no_vts_raise(self, t_mock, nt_mock, sctd_mock, nm_mock): connect_mock = mock.Mock() - mock_context = mock.create_autospec(driver_context.PortContext) nm_mock.return_value = connect_mock self.switch.ngs_config['vlan_translation_supported'] = False trunk_details = {'sub_ports': [{'segmentation_id': 123}]} self.assertRaises(exc.GenericSwitchNotSupported, - self.switch.plug_port_to_network_trunk, '2222', None, trunk_details, vtr=True) + self.switch.plug_port_to_network_trunk, '2222', None, + trunk_details, vtr=True) self.assertFalse(t_mock.called) self.assertFalse(nt_mock.called) diff --git a/networking_generic_switch/tests/unit/test_generic_switch_mech.py b/networking_generic_switch/tests/unit/test_generic_switch_mech.py index 51798569..ebbdebd9 100644 --- a/networking_generic_switch/tests/unit/test_generic_switch_mech.py +++ b/networking_generic_switch/tests/unit/test_generic_switch_mech.py @@ -19,7 +19,6 @@ from neutron.db import provisioning_blocks from neutron.plugins.ml2 import driver_context from neutron_lib.callbacks import resources -from neutron_lib.plugins import directory from networking_generic_switch import exceptions from networking_generic_switch import generic_switch_mech as gsm @@ -836,7 +835,6 @@ def test_bind_port_trunk(self, m_apc, m_list, m_vlan): resources.PORT, 'GENERICSWITCH') - @mock.patch.object(provisioning_blocks, 'add_provisioning_component') def test_bind_portgroup(self, m_apc, m_list): driver = gsm.GenericSwitchDriver() From e690d067bdadac059633a62af0a0b1704ed62ffb Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Wed, 8 Feb 2023 11:59:19 +0000 Subject: [PATCH 5/7] fixup! Dell OS10 backport without trunk port strip patch Change-Id: I5d2f098d7633a23d60ae85d8b1e4cffb43182b4e (cherry picked from commit 56735ab351936f669eabfb27d0160190ac8a5350) (cherry picked from commit e16b831a9b0972e510e99f8d224eb71d3b9158d5) --- networking_generic_switch/tests/unit/netmiko/test_dell.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/networking_generic_switch/tests/unit/netmiko/test_dell.py b/networking_generic_switch/tests/unit/netmiko/test_dell.py index f489ed4e..8b16a140 100644 --- a/networking_generic_switch/tests/unit/netmiko/test_dell.py +++ b/networking_generic_switch/tests/unit/netmiko/test_dell.py @@ -191,7 +191,7 @@ def test_add_network(self, m_exec): @mock.patch('networking_generic_switch.devices.netmiko_devices.' 'NetmikoSwitch.send_commands_to_device') def test_add_network_with_trunk_ports(self, mock_exec): - switch = self._make_switch_device({'ngs_trunk_ports': 'port1, port2'}) + switch = self._make_switch_device({'ngs_trunk_ports': 'port1,port2'}) switch.add_network(33, '0ae071f5-5be9-43e4-80ea-e41fefe85b21') mock_exec.assert_called_with( ['interface vlan 33', @@ -212,7 +212,7 @@ def test_del_network(self, mock_exec): @mock.patch('networking_generic_switch.devices.netmiko_devices.' 'NetmikoSwitch.send_commands_to_device') def test_del_network_with_trunk_ports(self, mock_exec): - switch = self._make_switch_device({'ngs_trunk_ports': 'port1, port2'}) + switch = self._make_switch_device({'ngs_trunk_ports': 'port1,port2'}) switch.del_network(33, '0ae071f55be943e480eae41fefe85b21') mock_exec.assert_called_with( ['interface port1', 'no switchport trunk allowed vlan 33', 'exit', From b4a9c398feace7bc80e06ba0c46ca9d221d27599 Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Mon, 20 Feb 2023 20:09:50 +0000 Subject: [PATCH 6/7] fixup! Dell OS10 trunk port unit tests Change-Id: I46684ae1cdd8207833172e5ee04aa865eb965931 (cherry picked from commit 8aafe00013cde9231622bde81014377fc9f33bf4) (cherry picked from commit b8331f9a86cd43ae0e9a51f147a568498fe2f679) --- .../tests/unit/netmiko/test_dell.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/networking_generic_switch/tests/unit/netmiko/test_dell.py b/networking_generic_switch/tests/unit/netmiko/test_dell.py index 8b16a140..2cbbe791 100644 --- a/networking_generic_switch/tests/unit/netmiko/test_dell.py +++ b/networking_generic_switch/tests/unit/netmiko/test_dell.py @@ -285,6 +285,43 @@ def test__format_commands(self): 'no switchport trunk allowed vlan 33', 'exit']) + def test_get_trunk_port_cmds_no_vlan_translation(self): + mock_context = mock.create_autospec(driver_context.PortContext) + self.switch.ngs_config['vlan_translation_supported'] = True + trunk_details = {'trunk_id': 'aaa-bbb-ccc-ddd', + 'sub_ports': [{'segmentation_id': 130, + 'port_id': 'aaa-bbb-ccc-ddd', + 'segmentation_type': 'vlan', + 'mac_address': u'fa:16:3e:1c:c2:7e'}]} + mock_context.current = {'binding:profile': + {'local_link_information': + [ + { + 'switch_info': 'foo', + 'port_id': '2222' + } + ] + }, + 'binding:vnic_type': 'baremetal', + 'id': 'aaaa-bbbb-cccc', + 'trunk_details': trunk_details} + mock_context.network = mock.Mock() + mock_context.network.current = {'provider:segmentation_id': 123} + mock_context.segments_to_bind = [ + { + 'segmentation_id': 777, + 'id': 123 + } + ] + res = self.switch.get_trunk_port_cmds_no_vlan_translation( + '2222', 777, trunk_details) + self.assertEqual(['interface 2222', 'switchport mode access', + 'switchport mode trunk', + 'switchport access vlan 777', + 'interface 2222', + 'switchport trunk allowed vlan 130'], + res) + class TestNetmikoDellPowerConnect(test_netmiko_base.NetmikoSwitchTestBase): From 1704f7487a68f272f660a4a0f992203c67680416 Mon Sep 17 00:00:00 2001 From: Alex-Welsh Date: Mon, 17 Jul 2023 14:16:45 +0000 Subject: [PATCH 7/7] Fix backports (cherry picked from commit deb3bc7c6f439302fd73ceeee8fb780934fa2319) --- networking_generic_switch/devices/__init__.py | 1 + .../tests/unit/netmiko/test_dell.py | 45 ------------------- 2 files changed, 1 insertion(+), 45 deletions(-) diff --git a/networking_generic_switch/devices/__init__.py b/networking_generic_switch/devices/__init__.py index 15f48d5f..3eee2918 100644 --- a/networking_generic_switch/devices/__init__.py +++ b/networking_generic_switch/devices/__init__.py @@ -46,6 +46,7 @@ {'name': 'ngs_network_name_format', 'default': '{network_id}'}, # If false, ngs will not add and delete VLANs from switches {'name': 'ngs_manage_vlans', 'default': True}, + {'name': 'vlan_translation_supported', 'default': False}, # If False, ngs will skip saving configuration on devices {'name': 'ngs_save_configuration', 'default': True}, # When true try to batch up in flight switch requests diff --git a/networking_generic_switch/tests/unit/netmiko/test_dell.py b/networking_generic_switch/tests/unit/netmiko/test_dell.py index 2cbbe791..efb5b6b5 100644 --- a/networking_generic_switch/tests/unit/netmiko/test_dell.py +++ b/networking_generic_switch/tests/unit/netmiko/test_dell.py @@ -21,51 +21,6 @@ from networking_generic_switch.tests.unit.netmiko import test_netmiko_base -class TestNetmikoDellOS10(test_netmiko_base.NetmikoSwitchTestBase): - - def _make_switch_device(self, extra_cfg={}): - device_cfg = {'device_type': 'netmiko_dell_os10'} - device_cfg.update(extra_cfg) - return dell.DellOS10(device_cfg) - - def test_get_trunk_port_cmds_no_vlan_translation(self): - mock_context = mock.create_autospec(driver_context.PortContext) - self.switch.ngs_config['vlan_translation_supported'] = True - trunk_details = {'trunk_id': 'aaa-bbb-ccc-ddd', - 'sub_ports': [{'segmentation_id': 130, - 'port_id': 'aaa-bbb-ccc-ddd', - 'segmentation_type': 'vlan', - 'mac_address': u'fa:16:3e:1c:c2:7e'}]} - mock_context.current = {'binding:profile': - {'local_link_information': - [ - { - 'switch_info': 'foo', - 'port_id': '2222' - } - ] - }, - 'binding:vnic_type': 'baremetal', - 'id': 'aaaa-bbbb-cccc', - 'trunk_details': trunk_details} - mock_context.network = mock.Mock() - mock_context.network.current = {'provider:segmentation_id': 123} - mock_context.segments_to_bind = [ - { - 'segmentation_id': 777, - 'id': 123 - } - ] - res = self.switch.get_trunk_port_cmds_no_vlan_translation( - '2222', 777, trunk_details) - self.assertEqual(['interface 2222', 'switchport mode access', - 'switchport mode trunk', - 'switchport access vlan 777', - 'interface 2222', - 'switchport trunk allowed vlan 130'], - res) - - class TestNetmikoDellNos(test_netmiko_base.NetmikoSwitchTestBase): def _make_switch_device(self, extra_cfg={}):