diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst index aea0e2b9..a53c65d0 100644 --- a/doc/source/configuration.rst +++ b/doc/source/configuration.rst @@ -117,6 +117,16 @@ for the Dell OS10 device:: password = password secret = secret +for the Dell Enterprise SONiC CLI device:: + + [genericswitch:dell-hostname] + device_type = netmiko_dell_enterprise_sonic_cli + ngs_mac_address = + ip = + username = admin + password = password + secret = secret + for the Dell PowerConnect device:: [genericswitch:dell-hostname] diff --git a/networking_generic_switch/devices/netmiko_devices/dell.py b/networking_generic_switch/devices/netmiko_devices/dell.py index e221c5f6..2eb5fee0 100644 --- a/networking_generic_switch/devices/netmiko_devices/dell.py +++ b/networking_generic_switch/devices/netmiko_devices/dell.py @@ -133,6 +133,70 @@ class DellNos(netmiko_devices.NetmikoSwitch): ) +class DellEnterpriseSonicCli(netmiko_devices.NetmikoSwitch): + """Netmiko device driver for Dell Enterprise switches. + + Developed against SONiC-OS-4.2.3-Edge_Standard. + + This driver uses the sonic-cli rather than Linux userspace to + configure the switch. This is because LLDP advertises switch + ports based on the naming from the sonic-cli, which is not the + same as Linux userspace. + """ + + NETMIKO_DEVICE_TYPE = "dell_sonic_ssh" + + ADD_NETWORK = ( + 'interface Vlan {segmentation_id}', + ) + + DELETE_NETWORK = ( + 'no interface Vlan {segmentation_id}', + ) + + PLUG_PORT_TO_NETWORK = ( + 'interface {port}', + 'switchport access Vlan {segmentation_id}', + ) + + DELETE_PORT = ( + 'interface {port}', + 'no switchport access Vlan', + ) + + SAVE_CONFIGURATION = ( + 'copy running-configuration startup-configuration', + ) + + # TODO(dougszu): We need some typical failures to add here + ERROR_MSG_PATTERNS = [] + + def save_configuration(self, net_connect): + """Try to save the device's configuration. + + :param net_connect: a netmiko connection object. + """ + # NOTE(dougszu): We override this because the default + # method tries 'copy running-config startup-config' which + # is transformed by the switch to: + # 'copy running-configuration startup-configuration'. + for cmd in self.SAVE_CONFIGURATION: + net_connect.send_command(cmd) + + def send_config_set(self, net_connect, cmd_set): + """Send a set of configuration lines to the device. + + :param net_connect: a netmiko connection object. + :param cmd_set: a list of configuration lines to send. + :returns: The output of the configuration commands. + """ + net_connect.enable() + # NOTE(dougszu): We override this so that we wait for commands + # to run before moving on. + return net_connect.send_config_set(config_commands=cmd_set, + cmd_verify=True) + + class DellPowerConnect(netmiko_devices.NetmikoSwitch): """Netmiko device driver for Dell PowerConnect switches.""" diff --git a/networking_generic_switch/devices/netmiko_devices/fake.py b/networking_generic_switch/devices/netmiko_devices/fake.py index b754cd08..b939b50a 100644 --- a/networking_generic_switch/devices/netmiko_devices/fake.py +++ b/networking_generic_switch/devices/netmiko_devices/fake.py @@ -44,7 +44,7 @@ def send_config_set(self, config_commands, cmd_verify): if self.device.ngs_config.get('ngs_fake_failure_prob'): failure_prob = self.device.ngs_config['ngs_fake_failure_prob'] if random.random() < float(failure_prob): - raise Exception("Random failure!") + raise Exception("Random failure!") # noqa for cmd in config_commands: LOG.info("%s", cmd) diff --git a/networking_generic_switch/tests/unit/netmiko/test_dell.py b/networking_generic_switch/tests/unit/netmiko/test_dell.py index d529c9e1..1b2bd259 100644 --- a/networking_generic_switch/tests/unit/netmiko/test_dell.py +++ b/networking_generic_switch/tests/unit/netmiko/test_dell.py @@ -123,6 +123,46 @@ def test__format_commands(self): ['interface vlan 33', 'no tagged 3333', 'exit']) +class TestNetmikoDellEnterpriseSonicCli( + test_netmiko_base.NetmikoSwitchTestBase): + + def _make_switch_device(self, extra_cfg={}): + device_cfg = {'device_type': 'netmiko_dell_enterprise_sonic_cli'} + device_cfg.update(extra_cfg) + return dell.DellEnterpriseSonicCli(device_cfg) + + @mock.patch('networking_generic_switch.devices.netmiko_devices.' + 'NetmikoSwitch.send_commands_to_device', autospec=True) + def test_add_network(self, m_exec): + self.switch.add_network(33, '0ae071f5-5be9-43e4-80ea-e41fefe85b21') + m_exec.assert_called_with( + self.switch, + ['interface Vlan 33']) + + @mock.patch('networking_generic_switch.devices.netmiko_devices.' + 'NetmikoSwitch.send_commands_to_device', autospec=True) + def test_del_network(self, mock_exec): + self.switch.del_network(33, '0ae071f5-5be9-43e4-80ea-e41fefe85b21') + mock_exec.assert_called_with(self.switch, + ['no interface Vlan 33']) + + @mock.patch('networking_generic_switch.devices.netmiko_devices.' + 'NetmikoSwitch.send_commands_to_device', autospec=True) + def test_plug_port_to_network(self, mock_exec): + self.switch.plug_port_to_network(3333, 33) + mock_exec.assert_called_with( + self.switch, + ['interface 3333', 'switchport access Vlan 33']) + + @mock.patch('networking_generic_switch.devices.netmiko_devices.' + 'NetmikoSwitch.send_commands_to_device', autospec=True) + def test_delete_port(self, mock_exec): + self.switch.delete_port(3333, 33) + mock_exec.assert_called_with( + self.switch, + ['interface 3333', 'no switchport access Vlan']) + + class TestNetmikoDellOS10(test_netmiko_base.NetmikoSwitchTestBase): def _make_switch_device(self, extra_cfg={}): diff --git a/releasenotes/notes/add-dell-sonic-cli-driver-700eaaff3a431e8f.yaml b/releasenotes/notes/add-dell-sonic-cli-driver-700eaaff3a431e8f.yaml new file mode 100644 index 00000000..5ee33171 --- /dev/null +++ b/releasenotes/notes/add-dell-sonic-cli-driver-700eaaff3a431e8f.yaml @@ -0,0 +1,8 @@ +--- +features: + - | + Adds Dell Enterprise SONiC CLI driver. This differs from the existing Dell + SONiC driver in that it uses the `sonic-cli` to administer the switch. + This ensures that the port names advertised via LLDP from the switch can + be used without conversion to the different naming scheme used in the + Linux userspace environment. diff --git a/setup.cfg b/setup.cfg index 0356c921..6c046ea6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -38,6 +38,7 @@ generic_switch.devices = netmiko_huawei_vrpv8 = networking_generic_switch.devices.netmiko_devices.huawei_vrpv8:Huawei netmiko_arista_eos = networking_generic_switch.devices.netmiko_devices.arista:AristaEos netmiko_dell_os10 = networking_generic_switch.devices.netmiko_devices.dell:DellOS10 + netmiko_dell_enterprise_sonic_cli = networking_generic_switch.devices.netmiko_devices.dell:DellEnterpriseSonicCli netmiko_dell_force10 = networking_generic_switch.devices.netmiko_devices.dell:DellNos netmiko_dell_powerconnect = networking_generic_switch.devices.netmiko_devices.dell:DellPowerConnect netmiko_brocade_fastiron = networking_generic_switch.devices.netmiko_devices.brocade:BrocadeFastIron