Skip to content

Commit ff0085a

Browse files
jumpojoyjuliakreger
authored andcommitted
Add vlan aware VMs support
With this patch ngs starts supporting attaching of trunk port to baremetal server. Only VLAN Neutron network is supported. Closes-Bug: #1653968 Co-Authored-By: Vasyl Saienko <vsaienko@mirantis.com> Co-Authored-By: Will Szumski <will@stackhpc.com> Co-Authored-By: Mark Goddard <mark@stackhpc.com> Co-Authored-By: Seunghun Lee <seunghun@stackhpc.com> Depends-On: https://review.opendev.org/c/openstack/ironic/+/941023 Change-Id: I978cb6b1ea8c049b40aaf1b305d0d0f033282299
1 parent d8261fd commit ff0085a

16 files changed

Lines changed: 1344 additions & 150 deletions

File tree

devstack/plugin.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ function ngs_configure_tempest {
220220
if [ $GENERIC_SWITCH_USER_MAX_SESSIONS -gt 0 ]; then
221221
iniset $TEMPEST_CONFIG ngs port_dlm_concurrency $(($GENERIC_SWITCH_USER_MAX_SESSIONS * 2))
222222
fi
223+
iniset $TEMPEST_CONFIG baremetal_feature_enabled trunks_supported True
223224
}
224225

225226
# check for service enabled

networking_generic_switch/devices/__init__.py

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,14 @@ def __init__(self, device_cfg, device_name=""):
112112

113113
self._validate_network_name_format()
114114

115+
@property
116+
def support_trunk_on_ports(self):
117+
return False
118+
119+
@property
120+
def support_trunk_on_bond_ports(self):
121+
return False
122+
115123
def _validate_network_name_format(self):
116124
"""Validate the network name format configuration option."""
117125
network_name_format = self.ngs_config['ngs_network_name_format']
@@ -223,17 +231,78 @@ def del_network(self, segmentation_id, network_id):
223231
pass
224232

225233
@abc.abstractmethod
226-
def plug_port_to_network(self, port_id, segmentation_id):
234+
def plug_port_to_network(self, port_id, segmentation_id,
235+
trunk_details=None):
236+
"""Plug port into network.
237+
238+
:param port_id: Then name of the switch interface
239+
:param segmentation_id: VLAN identifier of the network used as access
240+
or native VLAN for port.
241+
242+
:param trunk_details: trunk information if port is a part of trunk
243+
"""
227244
pass
228245

229246
@abc.abstractmethod
230-
def delete_port(self, port_id, segmentation_id):
247+
def delete_port(self, port_id, segmentation_id, trunk_details=None):
248+
"""Delete port from specific network.
249+
250+
:param port_id: Then name of the switch interface
251+
:param segmentation_id: VLAN identifier of the network used as access
252+
or native VLAN for port.
253+
254+
:param trunk_details: trunk information if port is a part of trunk
255+
"""
231256
pass
232257

233-
def plug_bond_to_network(self, bond_id, segmentation_id):
258+
def plug_bond_to_network(self, bond_id, segmentation_id,
259+
trunk_details=None):
260+
"""Plug bond port into network.
261+
262+
:param port_id: Then name of the switch interface
263+
:param segmentation_id: VLAN identifier of the network used as access
264+
or native VLAN for port.
265+
266+
:param trunk_details: trunk information if port is a part of trunk
267+
"""
268+
kwargs = {}
269+
if trunk_details:
270+
kwargs["trunk_details"] = trunk_details
234271
# Fall back to interface method.
235-
return self.plug_port_to_network(bond_id, segmentation_id)
272+
return self.plug_port_to_network(bond_id, segmentation_id, **kwargs)
236273

237-
def unplug_bond_from_network(self, bond_id, segmentation_id):
274+
def unplug_bond_from_network(self, bond_id, segmentation_id,
275+
trunk_details=None):
276+
"""Unplug bond port from network.
277+
278+
:param port_id: Then name of the switch interface
279+
:param segmentation_id: VLAN identifier of the network used as access
280+
or native VLAN for port.
281+
282+
:param trunk_details: trunk information if port is a part of trunk
283+
"""
284+
kwargs = {}
285+
if trunk_details:
286+
kwargs["trunk_details"] = trunk_details
238287
# Fall back to interface method.
239-
return self.delete_port(bond_id, segmentation_id)
288+
return self.delete_port(bond_id, segmentation_id, **kwargs)
289+
290+
def add_subports_on_trunk(self, binding_profile, port_id, subports):
291+
"""Allow subports on trunk
292+
293+
:param binding_profile: Binding profile of parent port
294+
:param port_id: The name of the switch port from
295+
Local Link Information
296+
:param subports: List with subports objects.
297+
"""
298+
pass
299+
300+
def del_subports_on_trunk(self, binding_profile, port_id, subports):
301+
"""Allow subports on trunk
302+
303+
:param binding_profile: Binding profile of parent port
304+
:param port_id: The name of the switch port from
305+
Local Link Information
306+
:param subports: List with subports objects.
307+
"""
308+
pass

networking_generic_switch/devices/netmiko_devices/__init__.py

Lines changed: 129 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
from networking_generic_switch.devices import utils as device_utils
3030
from networking_generic_switch import exceptions as exc
3131
from networking_generic_switch import locking as ngs_lock
32+
from networking_generic_switch import utils as ngs_utils
3233

3334
LOG = logging.getLogger(__name__)
3435
CONF = cfg.CONF
@@ -90,6 +91,22 @@ class NetmikoSwitch(devices.GenericSwitchDevice):
9091

9192
SAVE_CONFIGURATION = None
9293

94+
SET_NATIVE_VLAN = None
95+
96+
DELETE_NATIVE_VLAN = None
97+
98+
SET_NATIVE_VLAN_BOND = None
99+
100+
DELETE_NATIVE_VLAN_BOND = None
101+
102+
ADD_NETWORK_TO_TRUNK = None
103+
104+
REMOVE_NETWORK_FROM_TRUNK = None
105+
106+
ADD_NETWORK_TO_BOND_TRUNK = None
107+
108+
DELETE_NETWORK_ON_BOND_TRUNK = None
109+
93110
ERROR_MSG_PATTERNS = ()
94111
"""Sequence of error message patterns.
95112
@@ -145,6 +162,14 @@ def __init__(self, device_cfg, *args, **kwargs):
145162
self.locker.start()
146163
atexit.register(self.locker.stop)
147164

165+
@property
166+
def support_trunk_on_ports(self):
167+
return bool(self.ADD_NETWORK_TO_TRUNK)
168+
169+
@property
170+
def support_trunk_on_bond_ports(self):
171+
return bool(self.ADD_NETWORK_TO_BOND_TRUNK)
172+
148173
def _format_commands(self, commands, **kwargs):
149174
if not commands:
150175
return []
@@ -277,7 +302,7 @@ def del_network(self, segmentation_id, network_id):
277302
return self.send_commands_to_device(cmds)
278303

279304
@check_output('plug port')
280-
def plug_port_to_network(self, port, segmentation_id):
305+
def plug_port_to_network(self, port, segmentation_id, trunk_details=None):
281306
cmds = []
282307
if self._disable_inactive_ports() and self.ENABLE_PORT:
283308
cmds += self._format_commands(self.ENABLE_PORT, port=port)
@@ -287,18 +312,38 @@ def plug_port_to_network(self, port, segmentation_id):
287312
self.DELETE_PORT,
288313
port=port,
289314
segmentation_id=ngs_port_default_vlan)
290-
cmds += self._format_commands(
291-
self.PLUG_PORT_TO_NETWORK,
292-
port=port,
293-
segmentation_id=segmentation_id)
315+
316+
if trunk_details:
317+
cmds += self._format_commands(self.SET_NATIVE_VLAN,
318+
port=port,
319+
segmentation_id=segmentation_id)
320+
for sub_port in trunk_details.get('sub_ports', []):
321+
cmds += self._format_commands(
322+
self.ADD_NETWORK_TO_TRUNK, port=port,
323+
segmentation_id=sub_port['segmentation_id'])
324+
else:
325+
cmds += self._format_commands(
326+
self.PLUG_PORT_TO_NETWORK,
327+
port=port,
328+
segmentation_id=segmentation_id)
329+
294330
return self.send_commands_to_device(cmds)
295331

296332
@check_output('unplug port')
297-
def delete_port(self, port, segmentation_id):
333+
def delete_port(self, port, segmentation_id, trunk_details=None):
298334
cmds = self._format_commands(self.DELETE_PORT,
299335
port=port,
300336
segmentation_id=segmentation_id)
301337
ngs_port_default_vlan = self._get_port_default_vlan()
338+
if trunk_details:
339+
cmds += self._format_commands(self.DELETE_NATIVE_VLAN,
340+
port=port,
341+
segmentation_id=segmentation_id)
342+
for sub_port in trunk_details.get('sub_ports', []):
343+
cmds += self._format_commands(
344+
self.REMOVE_NETWORK_FROM_TRUNK, port=port,
345+
segmentation_id=sub_port['segmentation_id'])
346+
302347
if ngs_port_default_vlan:
303348
# NOTE(mgoddard): Pass network_id and segmentation_id for drivers
304349
# not yet using network_name.
@@ -315,14 +360,16 @@ def delete_port(self, port, segmentation_id):
315360
segmentation_id=ngs_port_default_vlan)
316361
if self._disable_inactive_ports() and self.DISABLE_PORT:
317362
cmds += self._format_commands(self.DISABLE_PORT, port=port)
363+
318364
return self.send_commands_to_device(cmds)
319365

320366
@check_output('plug bond')
321-
def plug_bond_to_network(self, bond, segmentation_id):
367+
def plug_bond_to_network(self, bond, segmentation_id, trunk_details=None):
322368
# Fallback to regular plug port if no specialist PLUG_BOND_TO_NETWORK
323369
# commands set
324370
if not self.PLUG_BOND_TO_NETWORK:
325-
return self.plug_port_to_network(bond, segmentation_id)
371+
return self.plug_port_to_network(bond, segmentation_id,
372+
trunk_details=trunk_details)
326373
cmds = []
327374
if self._disable_inactive_ports() and self.ENABLE_BOND:
328375
cmds += self._format_commands(self.ENABLE_BOND, bond=bond)
@@ -332,22 +379,44 @@ def plug_bond_to_network(self, bond, segmentation_id):
332379
self.UNPLUG_BOND_FROM_NETWORK,
333380
bond=bond,
334381
segmentation_id=ngs_port_default_vlan)
335-
cmds += self._format_commands(
336-
self.PLUG_BOND_TO_NETWORK,
337-
bond=bond,
338-
segmentation_id=segmentation_id)
382+
383+
if trunk_details:
384+
cmds += self._format_commands(self.SET_NATIVE_VLAN_BOND,
385+
bond=bond,
386+
segmentation_id=segmentation_id)
387+
for sub_port in trunk_details.get('sub_ports', []):
388+
cmds += self._format_commands(
389+
self.ADD_NETWORK_TO_BOND_TRUNK, bond=bond,
390+
segmentation_id=sub_port['segmentation_id'])
391+
else:
392+
cmds += self._format_commands(
393+
self.PLUG_BOND_TO_NETWORK,
394+
bond=bond,
395+
segmentation_id=segmentation_id)
396+
339397
return self.send_commands_to_device(cmds)
340398

341399
@check_output('unplug bond')
342-
def unplug_bond_from_network(self, bond, segmentation_id):
400+
def unplug_bond_from_network(self, bond, segmentation_id,
401+
trunk_details=None):
343402
# Fallback to regular port delete if no specialist
344403
# UNPLUG_BOND_FROM_NETWORK commands set
345404
if not self.UNPLUG_BOND_FROM_NETWORK:
346-
return self.delete_port(bond, segmentation_id)
405+
return self.delete_port(bond, segmentation_id,
406+
trunk_details=trunk_details)
347407
cmds = self._format_commands(self.UNPLUG_BOND_FROM_NETWORK,
348408
bond=bond,
349409
segmentation_id=segmentation_id)
350410
ngs_port_default_vlan = self._get_port_default_vlan()
411+
if trunk_details:
412+
cmds += self._format_commands(self.DELETE_NATIVE_VLAN_BOND,
413+
bond=bond,
414+
segmentation_id=segmentation_id)
415+
for sub_port in trunk_details.get('sub_ports', []):
416+
cmds += self._format_commands(
417+
self.ADD_NETWORK_TO_BOND_TRUNK, bond=bond,
418+
segmentation_id=sub_port['segmentation_id'])
419+
351420
if ngs_port_default_vlan:
352421
# NOTE(mgoddard): Pass network_id and segmentation_id for drivers
353422
# not yet using network_name.
@@ -364,6 +433,7 @@ def unplug_bond_from_network(self, bond, segmentation_id):
364433
segmentation_id=ngs_port_default_vlan)
365434
if self._disable_inactive_ports() and self.DISABLE_BOND:
366435
cmds += self._format_commands(self.DISABLE_BOND, bond=bond)
436+
367437
return self.send_commands_to_device(cmds)
368438

369439
def send_config_set(self, net_connect, cmd_set):
@@ -417,3 +487,48 @@ def check_output(self, output, operation):
417487
raise exc.GenericSwitchNetmikoConfigError(
418488
config=device_utils.sanitise_config(self.config),
419489
error=msg)
490+
491+
def add_subports_on_trunk(self, binding_profile, port_id, subports):
492+
"""Allow subports on trunk
493+
494+
:param binding_profile: Binding profile of parent port
495+
:param port_id: The name of the switch port from
496+
Local Link Information
497+
:param subports: List with subports objects.
498+
"""
499+
cmds = []
500+
is_802_3ad = ngs_utils.is_802_3ad(binding_profile)
501+
502+
for sub_port in subports:
503+
if is_802_3ad:
504+
cmds += self._format_commands(
505+
self.ADD_NETWORK_TO_BOND_TRUNK, bond=port_id,
506+
segmentation_id=sub_port['segmentation_id'])
507+
else:
508+
cmds += self._format_commands(
509+
self.ADD_NETWORK_TO_TRUNK, port=port_id,
510+
segmentation_id=sub_port['segmentation_id'])
511+
return self.send_commands_to_device(cmds)
512+
513+
def del_subports_on_trunk(self, binding_profile, port_id, subports):
514+
"""Allow subports on trunk
515+
516+
:param binding_profile: Binding profile of parent port
517+
:param port_id: The name of the switch port from
518+
Local Link Information
519+
:param subports: List with subports objects.
520+
"""
521+
522+
cmds = []
523+
is_802_3ad = ngs_utils.is_802_3ad(binding_profile)
524+
525+
for sub_port in subports:
526+
if is_802_3ad:
527+
cmds += self._format_commands(
528+
self.DELETE_NETWORK_ON_BOND_TRUNK, bond=port_id,
529+
segmentation_id=sub_port['segmentation_id'])
530+
else:
531+
cmds += self._format_commands(
532+
self.REMOVE_NETWORK_FROM_TRUNK, port=port_id,
533+
segmentation_id=sub_port['segmentation_id'])
534+
return self.send_commands_to_device(cmds)

networking_generic_switch/devices/netmiko_devices/arista.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,26 @@ class AristaEos(netmiko_devices.NetmikoSwitch):
3737
'no switchport mode trunk',
3838
'switchport trunk allowed vlan none'
3939
)
40+
41+
SET_NATIVE_VLAN = (
42+
'interface {port}',
43+
'switchport mode trunk',
44+
'switchport trunk native vlan {segmentation_id}',
45+
'switchport trunk allowed vlan add {segmentation_id}'
46+
)
47+
48+
DELETE_NATIVE_VLAN = (
49+
'interface {port}',
50+
'no switchport trunk native vlan {segmentation_id}',
51+
'switchport trunk allowed vlan remove {segmentation_id}',
52+
)
53+
54+
ADD_NETWORK_TO_TRUNK = (
55+
'interface {port}',
56+
'switchport trunk allowed vlan add {segmentation_id}'
57+
)
58+
59+
REMOVE_NETWORK_FROM_TRUNK = (
60+
'interface {port}',
61+
'switchport trunk allowed vlan remove {segmentation_id}'
62+
)

networking_generic_switch/devices/netmiko_devices/cisco.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,31 @@ class CiscoIos(netmiko_devices.NetmikoSwitch):
3939
'switchport trunk allowed vlan none'
4040
)
4141

42+
SET_NATIVE_VLAN = (
43+
'interface {port}',
44+
'switchport mode trunk',
45+
'switchport trunk native vlan {segmentation_id}',
46+
'switchport trunk allowed vlan add {segmentation_id}',
47+
)
48+
49+
DELETE_NATIVE_VLAN = (
50+
'interface {port}',
51+
'no switchport mode trunk',
52+
'no switchport trunk native vlan {segmentation_id}',
53+
'switchport trunk allowed vlan remove {segmentation_id}',
54+
)
55+
56+
ADD_NETWORK_TO_TRUNK = (
57+
'interface {port}',
58+
'switchport mode trunk',
59+
'switchport trunk allowed vlan add {segmentation_id}',
60+
)
61+
62+
REMOVE_NETWORK_FROM_TRUNK = (
63+
'interface {port}',
64+
'switchport trunk allowed vlan remove {segmentation_id}',
65+
)
66+
4267

4368
class CiscoNxOS(netmiko_devices.NetmikoSwitch):
4469
"""Netmiko device driver for Cisco Nexus switches running NX-OS."""

0 commit comments

Comments
 (0)