Browse Source

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
Mark Goddard 8 months ago
parent
commit
55a9efeffd

+ 12
- 1
networking_generic_switch/devices/__init__.py View File

@@ -34,7 +34,9 @@ NGS_INTERNAL_OPTS = [
34 34
     {'name': 'ngs_ssh_connect_timeout', 'default': 60},
35 35
     {'name': 'ngs_ssh_connect_interval', 'default': 10},
36 36
     {'name': 'ngs_max_connections', 'default': 1},
37
-    {'name': 'ngs_switchport_mode', 'default': 'access'}
37
+    {'name': 'ngs_switchport_mode', 'default': 'access'},
38
+    # If True, disable switch ports that are not in use.
39
+    {'name': 'ngs_disable_inactive_ports', 'default': False},
38 40
 ]
39 41
 
40 42
 
@@ -97,6 +99,15 @@ class GenericSwitchDevice(object):
97 99
             return []
98 100
         return physnets.split(',')
99 101
 
102
+    @staticmethod
103
+    def _str_to_bool(value):
104
+        truthy = ('true', 'yes', '1')
105
+        return str(value).lower() in truthy
106
+
107
+    def _disable_inactive_ports(self):
108
+        """Return whether inactive ports should be disabled."""
109
+        return self._str_to_bool(self.ngs_config['ngs_disable_inactive_ports'])
110
+
100 111
     @abc.abstractmethod
101 112
     def add_network(self, segmentation_id, network_id):
102 113
         pass

+ 8
- 0
networking_generic_switch/devices/netmiko_devices/__init__.py View File

@@ -73,6 +73,10 @@ class NetmikoSwitch(devices.GenericSwitchDevice):
73 73
 
74 74
     REMOVE_NETWORK_FROM_TRUNK = None
75 75
 
76
+    ENABLE_PORT = None
77
+
78
+    DISABLE_PORT = None
79
+
76 80
     SAVE_CONFIGURATION = None
77 81
 
78 82
     ERROR_MSG_PATTERNS = ()
@@ -217,6 +221,8 @@ class NetmikoSwitch(devices.GenericSwitchDevice):
217 221
     @check_output('plug port')
218 222
     def plug_port_to_network(self, port, segmentation_id):
219 223
         cmds = []
224
+        if self._disable_inactive_ports() and self.ENABLE_PORT:
225
+            cmds += self._format_commands(self.ENABLE_PORT, port=port)
220 226
         ngs_port_default_vlan = self._get_port_default_vlan()
221 227
         if ngs_port_default_vlan:
222 228
             cmds += self._format_commands(
@@ -244,6 +250,8 @@ class NetmikoSwitch(devices.GenericSwitchDevice):
244 250
                 self.PLUG_PORT_TO_NETWORK,
245 251
                 port=port,
246 252
                 segmentation_id=ngs_port_default_vlan)
253
+        if self._disable_inactive_ports() and self.DISABLE_PORT:
254
+            cmds += self._format_commands(self.DISABLE_PORT, port=port)
247 255
         return self.send_commands_to_device(cmds)
248 256
 
249 257
     def send_config_set(self, net_connect, cmd_set):

+ 24
- 0
networking_generic_switch/tests/unit/netmiko/test_netmiko_base.py View File

@@ -109,6 +109,18 @@ class TestNetmikoSwitch(NetmikoSwitchTestBase):
109 109
         m_sctd.assert_called_with([])
110 110
         m_check.assert_called_once_with('fake output', 'plug port')
111 111
 
112
+    @mock.patch('networking_generic_switch.devices.netmiko_devices.'
113
+                'NetmikoSwitch.send_commands_to_device',
114
+                return_value='fake output')
115
+    @mock.patch('networking_generic_switch.devices.netmiko_devices.'
116
+                'NetmikoSwitch.check_output')
117
+    def test_plug_port_to_network_disable_inactive(self, m_check, m_sctd):
118
+        switch = self._make_switch_device(
119
+            {'ngs_disable_inactive_ports': 'true'})
120
+        switch.plug_port_to_network(2222, 22)
121
+        m_sctd.assert_called_with([])
122
+        m_check.assert_called_once_with('fake output', 'plug port')
123
+
112 124
     @mock.patch('networking_generic_switch.devices.netmiko_devices.'
113 125
                 'NetmikoSwitch.send_commands_to_device',
114 126
                 return_value='fake output')
@@ -130,6 +142,18 @@ class TestNetmikoSwitch(NetmikoSwitchTestBase):
130 142
         m_sctd.assert_called_with([])
131 143
         m_check.assert_called_once_with('fake output', 'unplug port')
132 144
 
145
+    @mock.patch('networking_generic_switch.devices.netmiko_devices.'
146
+                'NetmikoSwitch.send_commands_to_device',
147
+                return_value='fake output')
148
+    @mock.patch('networking_generic_switch.devices.netmiko_devices.'
149
+                'NetmikoSwitch.check_output')
150
+    def test_delete_port_disable_inactive(self, m_check, m_sctd):
151
+        switch = self._make_switch_device(
152
+            {'ngs_disable_inactive_ports': 'true'})
153
+        switch.delete_port(2222, 22)
154
+        m_sctd.assert_called_with([])
155
+        m_check.assert_called_once_with('fake output', 'unplug port')
156
+
133 157
     def test__format_commands(self):
134 158
         self.switch._format_commands(
135 159
             netmiko_devices.NetmikoSwitch.ADD_NETWORK,

+ 7
- 0
releasenotes/notes/disable-inactive-ports-bd6c42ceb232aab2.yaml View File

@@ -0,0 +1,7 @@
1
+---
2
+features:
3
+  - |
4
+    Adds support for disabling inactive switch interfaces. This is configured
5
+    on a per-device basis using the ``ngs_disable_inactive_ports`` flag. See
6
+    `Story 2003391 <https://storyboard.openstack.org/#!/story/2003391>`__ for
7
+    details.

Loading…
Cancel
Save