From fe4929a05672d72800c4e211bb560101717166d0 Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Wed, 15 Aug 2018 11:44:51 +0100 Subject: [PATCH 1/5] Set branch name to stable/pike in tox_install.sh Change-Id: I20a78d7ff336dc337a5532637cd1efca55c4c79a --- tools/tox_install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/tox_install.sh b/tools/tox_install.sh index 30b6a805..48ece0b4 100755 --- a/tools/tox_install.sh +++ b/tools/tox_install.sh @@ -5,7 +5,7 @@ set -ex ZUUL_CLONER=/usr/zuul-env/bin/zuul-cloner -BRANCH_NAME=master +BRANCH_NAME=stable/pike CONSTRAINTS_FILE=$1 shift From 3df9a5db87361fd6f4ad887eb580c78e74c2d2fc Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Thu, 30 Aug 2018 15:09:23 +0100 Subject: [PATCH 2/5] Use stable/pike upper constraints file Change-Id: Iba0cd96565e51320a25aa597a36b38f99ca1a398 --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index fa92b965..948da021 100644 --- a/tox.ini +++ b/tox.ini @@ -6,7 +6,7 @@ envlist = py35,py27,pep8 [testenv] usedevelop = True install_command = - {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages} + {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/pike} {opts} {packages} setenv = VIRTUAL_ENV={envdir} PYTHONDONTWRITEBYTECODE = 1 PYTHONWARNINGS=default::DeprecationWarning From cd0fa3c288a4a4f2e441a6c8658dcb80ab42714e Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Thu, 30 Aug 2018 14:58:21 +0100 Subject: [PATCH 3/5] Add a TravisCI configuration file to test downstream changes Change-Id: I7911379d88305bb8d2c6ffccb1f56982156fdb3f --- .travis.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..2cbda291 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,34 @@ +--- +language: python +python: "2.7" + +# Run jobs in VMs - sudo is required by ansible tests. +sudo: required + +# Install ansible +addons: + apt: + packages: + - gcc + - python-apt + - python-virtualenv + - realpath + +# Create a build matrix for the different test jobs. +env: + matrix: + # Run python style checks. + - TOX_ENV=pep8 + # Build documentation. + - TOX_ENV=docs + # Run python2.7 unit tests. + - TOX_ENV=py27 + +install: + # Install tox in a virtualenv to ensure we have an up to date version. + - pip install -U pip + - pip install tox + +script: + # Run the tox environment. + - tox -e ${TOX_ENV} From e3731566ff04432a6425ae05a712db092fda20b8 Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Wed, 15 Aug 2018 11:45:09 +0100 Subject: [PATCH 4/5] Support disabling inactive links Currently, when a port is unbound by NGS, this is typically implemented by removing the VLAN association from the corresponding switch interface. This puts the interface on the default VLAN of the switch, and leaves it administratively up (active). This may be an issue in some scenarios, such as: * If there is more than one interface on the server, subsequent instances may choose not to attach a network to one or more interfaces. If these interfaces are active on a default VLAN this would be a security hole. * If configuration of an interface fails but is silently ignored by NGS, the interface will remain on the default VLAN. To avoid these issues this change adds support for administratively disabling ports when they are not in use. This behaviour is optional, since it might not be appropriate on all devices or in all scenarios. Configuration of the behaviour is controlled by a per-device config flag, 'ngs_disable_inactive_ports'. Change-Id: Ibdbb871d7f3e9ad0d3ade1049cc09a2da5e36fab Story: 2003391 Task: 24511 --- networking_generic_switch/devices/__init__.py | 11 ++++++++ .../devices/netmiko_devices/__init__.py | 25 +++++++++++++------ .../tests/unit/netmiko/test_netmiko_base.py | 18 ++++++++++++- 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/networking_generic_switch/devices/__init__.py b/networking_generic_switch/devices/__init__.py index 31c52f63..9e986273 100644 --- a/networking_generic_switch/devices/__init__.py +++ b/networking_generic_switch/devices/__init__.py @@ -33,6 +33,8 @@ {'name': 'ngs_ssh_connect_timeout', 'default': 60}, {'name': 'ngs_ssh_connect_interval', 'default': 10}, {'name': 'ngs_max_connections', 'default': 1}, + # If True, disable switch ports that are not in use. + {'name': 'ngs_disable_inactive_ports', 'default': False}, ] @@ -91,6 +93,15 @@ def _get_physical_networks(self): return [] return physnets.split(',') + @staticmethod + def _str_to_bool(value): + truthy = ('true', 'yes', '1') + return str(value).lower() in truthy + + def _disable_inactive_ports(self): + """Return whether inactive ports should be disabled.""" + return self._str_to_bool(self.ngs_config['ngs_disable_inactive_ports']) + @abc.abstractmethod def add_network(self, segmentation_id, network_id): pass diff --git a/networking_generic_switch/devices/netmiko_devices/__init__.py b/networking_generic_switch/devices/netmiko_devices/__init__.py index c1e246b7..2ae915a3 100644 --- a/networking_generic_switch/devices/netmiko_devices/__init__.py +++ b/networking_generic_switch/devices/netmiko_devices/__init__.py @@ -42,6 +42,10 @@ class NetmikoSwitch(devices.GenericSwitchDevice): DELETE_PORT = None + ENABLE_PORT = None + + DISABLE_PORT = None + PLUG_TRUNK_PORT_TO_NETWORK = None UNPLUG_TRUNK_PORT_FROM_NETWORK = None @@ -179,16 +183,21 @@ def del_network(self, segmentation_id, network_id): self.send_commands_to_device(cmds) def plug_port_to_network(self, port, segmentation_id): - self.send_commands_to_device( - self._format_commands(self.PLUG_PORT_TO_NETWORK, - port=port, - segmentation_id=segmentation_id)) + cmds = [] + if self._disable_inactive_ports() and self.ENABLE_PORT: + cmds += self._format_commands(self.ENABLE_PORT, port=port) + cmds += self._format_commands(self.PLUG_PORT_TO_NETWORK, + port=port, + segmentation_id=segmentation_id) + self.send_commands_to_device(cmds) def delete_port(self, port, segmentation_id): - self.send_commands_to_device( - self._format_commands(self.DELETE_PORT, - port=port, - segmentation_id=segmentation_id)) + cmds = self._format_commands(self.DELETE_PORT, + port=port, + segmentation_id=segmentation_id) + if self._disable_inactive_ports() and self.DISABLE_PORT: + cmds += self._format_commands(self.DISABLE_PORT, port=port) + self.send_commands_to_device(cmds) def send_config_set(self, net_connect, cmd_set): """Send a set of configuration lines to the device. 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 d8356f6c..31b08d0b 100644 --- a/networking_generic_switch/tests/unit/netmiko/test_netmiko_base.py +++ b/networking_generic_switch/tests/unit/netmiko/test_netmiko_base.py @@ -76,11 +76,27 @@ def test_plug_port_to_network(self, m_sctd): self.switch.plug_port_to_network(2222, 22) m_sctd.assert_called_with([]) + @mock.patch('networking_generic_switch.devices.netmiko_devices.' + 'NetmikoSwitch.send_commands_to_device') + def test_plug_port_to_network_disable_inactive(self, m_sctd): + switch = self._make_switch_device( + {'ngs_disable_inactive_ports': 'true'}) + switch.plug_port_to_network(2222, 22) + m_sctd.assert_called_with([]) + @mock.patch('networking_generic_switch.devices.netmiko_devices.' 'NetmikoSwitch.send_commands_to_device') def test_delete_port(self, m_sctd): self.switch.delete_port(2222, 22) - m_sctd.assert_called_with(None) + m_sctd.assert_called_with([]) + + @mock.patch('networking_generic_switch.devices.netmiko_devices.' + 'NetmikoSwitch.send_commands_to_device') + def test_delete_port_disable_inactive(self, m_sctd): + switch = self._make_switch_device( + {'ngs_disable_inactive_ports': 'true'}) + switch.delete_port(2222, 22) + m_sctd.assert_called_with([]) def test__format_commands(self): self.switch._format_commands( From 25fb79c2225ec6d8b677d6871ffc8567489c4f38 Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Thu, 30 Aug 2018 15:15:38 +0100 Subject: [PATCH 5/5] Support disabling inactive links for Juniper Adds the necessary code to the Juniper Junos device driver to support disabling inactive links. This feature is enabled by setting the per-device config flag 'ngs_disable_inactive_ports'. Change-Id: I636613d0c910d10601422ad094f835c17a606e37 Story: 2003391 Task: 24933 --- .../devices/netmiko_devices/juniper.py | 8 +++++++ .../tests/unit/netmiko/test_juniper.py | 24 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/networking_generic_switch/devices/netmiko_devices/juniper.py b/networking_generic_switch/devices/netmiko_devices/juniper.py index 9c3dde14..5ccacde5 100644 --- a/networking_generic_switch/devices/netmiko_devices/juniper.py +++ b/networking_generic_switch/devices/netmiko_devices/juniper.py @@ -55,6 +55,14 @@ class Juniper(netmiko_devices.NetmikoSwitch): 'vlan members', ) + ENABLE_PORT = ( + 'delete interface {port} disable', + ) + + DISABLE_PORT = ( + 'set interface {port} disable', + ) + PLUG_TRUNK_PORT_TO_NETWORK = ( 'set interface {port} unit 0 family ethernet-switching ' 'vlan members {segmentation_id}', diff --git a/networking_generic_switch/tests/unit/netmiko/test_juniper.py b/networking_generic_switch/tests/unit/netmiko/test_juniper.py index 2d599861..56ee092f 100644 --- a/networking_generic_switch/tests/unit/netmiko/test_juniper.py +++ b/networking_generic_switch/tests/unit/netmiko/test_juniper.py @@ -80,6 +80,19 @@ def test_plug_port_to_network(self, mock_exec): 'set interface 3333 unit 0 family ethernet-switching ' 'vlan members 33']) + @mock.patch('networking_generic_switch.devices.netmiko_devices.' + 'NetmikoSwitch.send_commands_to_device') + def test_plug_port_to_network_disable_inactive(self, m_sctd): + switch = self._make_switch_device( + {'ngs_disable_inactive_ports': 'true'}) + switch.plug_port_to_network(3333, 33) + m_sctd.assert_called_with( + ['delete interface 3333 disable', + 'delete interface 3333 unit 0 family ethernet-switching ' + 'vlan members', + 'set interface 3333 unit 0 family ethernet-switching ' + 'vlan members 33']) + @mock.patch('networking_generic_switch.devices.netmiko_devices.' 'NetmikoSwitch.send_commands_to_device') def test_delete_port(self, mock_exec): @@ -88,6 +101,17 @@ def test_delete_port(self, mock_exec): ['delete interface 3333 unit 0 family ethernet-switching ' 'vlan members']) + @mock.patch('networking_generic_switch.devices.netmiko_devices.' + 'NetmikoSwitch.send_commands_to_device') + def test_delete_port_disable_inactive(self, m_sctd): + switch = self._make_switch_device( + {'ngs_disable_inactive_ports': 'true'}) + switch.delete_port(3333, 33) + m_sctd.assert_called_with( + ['delete interface 3333 unit 0 family ethernet-switching ' + 'vlan members', + 'set interface 3333 disable']) + def test_send_config_set(self): connect_mock = mock.MagicMock(netmiko.base_connection.BaseConnection) connect_mock.send_config_set.return_value = 'fake output'