Skip to content

Commit d64fb94

Browse files
authored
Add support for set up virtual multi-asic chassis (sonic-net#16716)
What is the motivation for this PR? Currently, the virtual chassis setup in sonic-mgmt is very primitive and manual. How did you do it? Enhanced the ansible framework to support automatic topo-connect and minigraph generation of virtual multi-asic chassis How did you verify/test it? Set up the virtual chassis with 202205/202405-chassis VS images Any platform specific information? Only tested to work with 202205 and 202405 images so far.
1 parent c0c6e69 commit d64fb94

23 files changed

+815
-415
lines changed

ansible/config_sonic_basedon_testbed.yml

+8
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,19 @@
9393
delegate_to: localhost
9494
ignore_errors: true
9595

96+
- name: determine whether to sort port_alias by index
97+
set_fact:
98+
sort_by_index: false
99+
when: switch_type is defined and switch_type == "voq" and type is defined and type == "kvm"
100+
96101
- name: find interface name mapping and individual interface speed if defined from dut
97102
port_alias:
98103
hwsku: "{{ hwsku }}"
99104
card_type: "{{ card_type | default('fixed') }}"
100105
hostname: "{{ inventory_hostname | default('') }}"
101106
switchids: "{{ switchids | default([]) }}"
107+
num_asic: "{{ num_asics }}"
108+
sort_by_index: "{{ sort_by_index | default(true) }}"
102109
when: deploy is defined and deploy|bool == true
103110

104111
- name: find interface name mapping and individual interface speed if defined with local data
@@ -109,6 +116,7 @@
109116
hostname: "{{ inventory_hostname | default('') }}"
110117
switchids: "{{ switchids | default([]) }}"
111118
slotid: "{{ slot_num | default(None) }}"
119+
sort_by_index: "{{ sort_by_index | default(true) }}"
112120
delegate_to: localhost
113121
when: deploy is not defined or deploy|bool == false
114122

ansible/lab

+34
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,40 @@ sonic_a7260:
161161
"0x2d": "Arista Networks"
162162
"0x2e": "Aboot-norcal7-7.2.3-pcie2x4-12345678"
163163

164+
sonic_nokia_multi_asic_lc:
165+
vars:
166+
hwsku: Nokia-IXR7250E-36x400G
167+
iface_speed: 400000
168+
num_asics: 2
169+
start_topo_service: True
170+
frontend_asics: [0,1]
171+
card_type: linecard
172+
hosts:
173+
vlab-t2-03:
174+
ansible_host: 10.250.0.123
175+
ansible_hostv6: fec0::ffff:afa:13
176+
slot_num: slot1
177+
loopback4096_ip: [192.0.0.0/32, 192.0.0.1/32]
178+
loopback4096_ipv6: [2603:10e2:400::/128, 2603:10e2:400::1/128]
179+
vlab-t2-04:
180+
ansible_host: 10.250.0.124
181+
ansible_hostv6: fec0::ffff:afa:14
182+
slot_num: slot2
183+
loopback4096_ip: [192.0.0.3/32, 192.0.0.4/32]
184+
loopback4096_ipv6: [2603:10e2:400::3/128, 2603:10e2:400::4/128]
185+
186+
sonic_nokia_sup:
187+
vars:
188+
hwsku: Nokia-IXR7250E-SUP-10
189+
iface_speed: 400000
190+
start_topo_service: True
191+
card_type: supervisor
192+
hosts:
193+
vlab-t2-sup1:
194+
ansible_host: 10.250.0.125
195+
ansible_hostv6: fec0::ffff:afa:15
196+
slot_num: slot0
197+
164198
sonic_multi_asic:
165199
vars:
166200
hwsku: msft_multi_asic_vs

ansible/library/port_alias.py

+101-40
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,10 @@ def get_portconfig_path(self, slotid=None, asic_id=None):
9999
return None
100100

101101
def get_portmap(self, asic_id=None, include_internal=False,
102-
hostname=None, switchid=None, slotid=None):
102+
hostname=None, switchid=None, slotid=None, card_type=None):
103103
aliases = []
104+
front_panel_aliases = []
105+
inband_aliases = []
104106
portmap = {}
105107
aliasmap = {}
106108
portspeed = {}
@@ -109,7 +111,8 @@ def get_portmap(self, asic_id=None, include_internal=False,
109111
front_panel_asic_ifnames = {}
110112
front_panel_asic_id = {}
111113
# All asic names
112-
asic_if_names = []
114+
asic_if_names = {}
115+
asic_if_ids = {}
113116
sysports = []
114117
port_coreid_index = -1
115118
port_core_portid_index = -1
@@ -171,22 +174,27 @@ def get_portmap(self, asic_id=None, include_internal=False,
171174
else:
172175
alias = name
173176
add_port = False
177+
178+
if role == "Ext":
179+
front_panel_aliases.append(alias)
180+
if role == "Inb":
181+
inband_aliases.append(alias)
182+
174183
if role in {"Ext"} or (role in ["Int", "Inb", "Rec"] and include_internal):
175184
add_port = True
176185
aliases.append(
177186
(alias, -1 if port_index == -1 or len(mapping) <= port_index else mapping[port_index]))
178187
portmap[name] = alias
179188
aliasmap[alias] = name
180-
if role == "Ext" and (asic_name_index != -1) and (len(mapping) > asic_name_index):
189+
190+
if (asic_name_index != -1) and (len(mapping) > asic_name_index):
181191
asicifname = mapping[asic_name_index]
182-
# we only want following ASIC info in minigraph for multi-asic
183192
if asic_id is not None:
184-
front_panel_asic_ifnames[alias] = asicifname
185-
front_panel_asic_id[alias] = "ASIC" + \
186-
str(asic_id)
187-
if (asic_name_index != -1) and (len(mapping) > asic_name_index):
188-
asicifname = mapping[asic_name_index]
189-
asic_if_names.append(asicifname)
193+
asic_if_names[alias] = asicifname
194+
asic_if_ids[alias] = "ASIC" + str(asic_id)
195+
if role == "Ext":
196+
front_panel_asic_ifnames[alias] = asicifname
197+
front_panel_asic_id[alias] = "ASIC" + str(asic_id)
190198
if (speed_index != -1) and (len(mapping) > speed_index):
191199
speed = mapping[speed_index]
192200
sysport['speed'] = speed
@@ -201,13 +209,16 @@ def get_portmap(self, asic_id=None, include_internal=False,
201209
if (num_voq_index != -1) and (len(mapping) > num_voq_index):
202210
voq = mapping[num_voq_index]
203211
sysport['num_voq'] = voq
204-
sysport['name'] = name
205-
sysport['hostname'] = hostname
206-
sysport['asic_name'] = asic_name
207-
sysport['switchid'] = switchid
208212
sysports.append(sysport)
209213
if port_index != -1 and len(mapping) > port_index:
210214
indexmap[mapping[port_index]] = name
215+
216+
# Special handling for the Cpu port
217+
if include_internal and card_type == "linecard":
218+
aliases.append(("Cpu0/{}".format(asic_id if asic_id is not None else 0), -1))
219+
if asic_id is not None and card_type == "linecard":
220+
asic_if_names["Cpu0/{}".format(asic_id)] = "Cpu0"
221+
asic_if_ids["Cpu0/{}".format(asic_id)] = "ASIC" + str(asic_id)
211222
if len(sysports) > 0:
212223
sysport = {}
213224
sysport['name'] = 'Cpu0'
@@ -220,8 +231,9 @@ def get_portmap(self, asic_id=None, include_internal=False,
220231
sysport['hostname'] = hostname
221232
sysports.insert(0, sysport)
222233

223-
return (aliases, portmap, aliasmap, portspeed, front_panel_asic_ifnames, front_panel_asic_id, asic_if_names,
224-
sysports, indexmap)
234+
return (aliases, front_panel_aliases, inband_aliases, portmap, aliasmap, portspeed,
235+
front_panel_asic_ifnames, front_panel_asic_id,
236+
asic_if_names, asic_if_ids, sysports, indexmap)
225237

226238

227239
def main():
@@ -233,33 +245,46 @@ def main():
233245
card_type=dict(type='str', required=False),
234246
hostname=dict(type='str', required=False),
235247
switchids=dict(type='list', required=False),
236-
slotid=dict(type='str', required=False)
248+
slotid=dict(type='str', required=False),
249+
sort_by_index=dict(type='bool', required=False, default=True)
237250
),
238251
supports_check_mode=True
239252
)
240253
m_args = module.params
241254
try:
242-
aliases = []
243-
portmap = {}
244-
aliasmap = {}
245-
portspeed = {}
246-
sysports = []
247-
indexmap = {}
248-
# Map of ASIC interface names to front panel interfaces
249-
front_panel_asic_ifnames = {}
250-
front_panel_asic_ifs_asic_id = {}
251-
# { asic_name: [ asic interfaces] }
252-
asic_if_names = {}
255+
aliases = [] # list of port aliases
256+
front_panel_aliases = [] # list of (front panel port aliases, port indexes)
257+
portmap = {} # name to alias map
258+
aliasmap = {} # alias to name map
259+
portspeed = {} # alias to speed map
260+
sysports = [] # list of system ports
261+
indexmap = {} # index to port name map
262+
front_panel_asic_ifnames = {} # Map of interface aliases to interface names for front panel ports
263+
front_panel_asic_ifs_asic_id = {} # Map of interface aliases to asic ids for front panel ports
264+
asic_if_names = {} # Map of interface aliases to interface names for front panel ports
265+
asic_if_asic_ids = {} # Map of interface aliases to asic ids
266+
# Chassis related info
267+
midplane_port_alias = [] # list of (midplane port aliases, port indexes)
268+
inband_port_alias = [] # list of (inband port aliases, port indexes)
253269

254270
if 'card_type' in m_args and m_args['card_type'] == 'supervisor':
271+
midplane_port_alias.append(("Midplane", 0))
272+
if 'include_internal' in m_args and m_args['include_internal'] is True:
273+
aliases.append(("Midplane", -1))
274+
255275
module.exit_json(ansible_facts={'port_alias': aliases,
276+
'front_panel_port_alias': front_panel_aliases,
277+
'midplane_port_alias': midplane_port_alias,
278+
'inband_port_alias': inband_port_alias,
256279
'port_name_map': portmap,
257280
'port_alias_map': aliasmap,
258281
'port_speed': portspeed,
259282
'front_panel_asic_ifnames': [],
260283
'front_panel_asic_ids': [],
261-
'asic_if_names': asic_if_names,
262-
'sysports': sysports})
284+
'asic_if_names': [],
285+
'asic_if_asic_ids': [],
286+
'sysports': sysports,
287+
'port_index_map': indexmap})
263288
return
264289
allmap = SonicPortAliasMap(m_args['hwsku'])
265290
switchids = None
@@ -294,16 +319,32 @@ def main():
294319
hostname = ""
295320
if 'hostname' in m_args:
296321
hostname = m_args['hostname']
322+
card_type = None
323+
if 'card_type' in m_args:
324+
card_type = m_args['card_type']
325+
326+
if card_type == 'linecard':
327+
midplane_port_alias.append(("Midplane", 0)) # midplane port is always the first port (after the mgmt port)
328+
if include_internal:
329+
aliases.append(("Midplane", -1))
330+
331+
front_panel_aliases_set = set()
332+
inband_port_alias_set = set()
297333
for asic_id in range(num_asic):
298334
if switchids and asic_id is not None:
299335
switchid = switchids[asic_id]
300336
if num_asic == 1:
301337
asic_id = None
302-
(aliases_asic, portmap_asic, aliasmap_asic, portspeed_asic, front_panel_asic, front_panel_asic_ids,
303-
asicifnames_asic, sysport_asic, index_name) = allmap.get_portmap(
304-
asic_id, include_internal, hostname, switchid, slotid)
338+
(aliases_asic, front_panel_aliases_asic, inband_port_alias_asic, portmap_asic, aliasmap_asic,
339+
portspeed_asic, front_panel_asic, front_panel_asic_ids,
340+
asicifnames_asic, asicifids_asic, sysport_asic, indexmap_asic) = allmap.get_portmap(
341+
asic_id, include_internal, hostname, switchid, slotid, card_type)
305342
if aliases_asic is not None:
306343
aliases.extend(aliases_asic)
344+
if front_panel_aliases_asic is not None:
345+
front_panel_aliases_set.update(front_panel_aliases_asic)
346+
if inband_port_alias_asic is not None:
347+
inband_port_alias_set.update(inband_port_alias_asic)
307348
if portmap_asic is not None:
308349
portmap.update(portmap_asic)
309350
if aliasmap_asic is not None:
@@ -315,32 +356,52 @@ def main():
315356
if front_panel_asic_ids is not None:
316357
front_panel_asic_ifs_asic_id.update(front_panel_asic_ids)
317358
if asicifnames_asic is not None:
318-
asic = 'ASIC' + str(asic_id)
319-
asic_if_names[asic] = asicifnames_asic
359+
asic_if_names.update(asicifnames_asic)
360+
if asicifids_asic is not None:
361+
asic_if_asic_ids.update(asicifids_asic)
320362
if sysport_asic is not None:
321363
sysports.extend(sysport_asic)
322-
if index_name is not None:
323-
indexmap.update(index_name)
364+
if indexmap_asic is not None:
365+
indexmap.update(indexmap_asic)
324366

325367
# Sort the Interface Name needed in multi-asic
326-
aliases.sort(key=lambda x: int(x[1]))
368+
if m_args['sort_by_index']:
369+
# Use the optional argument to enable opt out of sorting by index
370+
aliases.sort(key=lambda x: int(x[1]))
371+
327372
# Get ASIC interface names list based on sorted aliases
328373
front_panel_asic_ifnames_list = []
329374
front_panel_asic_ifs_asic_id_list = []
375+
asic_ifnames_list = []
376+
asic_ifs_asic_id_list = []
330377
for k in aliases:
331378
if k[0] in front_panel_asic_ifnames:
332379
front_panel_asic_ifnames_list.append(
333380
front_panel_asic_ifnames[k[0]])
334381
front_panel_asic_ifs_asic_id_list.append(
335382
front_panel_asic_ifs_asic_id[k[0]])
383+
if k[0] in asic_if_names:
384+
asic_ifnames_list.append(asic_if_names[k[0]])
385+
asic_ifs_asic_id_list.append(asic_if_asic_ids[k[0]])
386+
387+
# Get front panel and inband interface alias list based on sorted aliases
388+
for i, k in enumerate(aliases):
389+
if k[0] in front_panel_aliases_set:
390+
front_panel_aliases.append((k[0], i))
391+
if k[0] in inband_port_alias_set:
392+
inband_port_alias.append((k[0], i))
336393

337394
module.exit_json(ansible_facts={'port_alias': [k[0] for k in aliases],
395+
'front_panel_port_alias': front_panel_aliases,
396+
'midplane_port_alias': midplane_port_alias,
397+
'inband_port_alias': inband_port_alias,
338398
'port_name_map': portmap,
339399
'port_alias_map': aliasmap,
340400
'port_speed': portspeed,
341401
'front_panel_asic_ifnames': front_panel_asic_ifnames_list,
342402
'front_panel_asic_ifs_asic_id': front_panel_asic_ifs_asic_id_list,
343-
'asic_if_names': asic_if_names,
403+
'asic_if_names': asic_ifnames_list,
404+
'asic_if_asic_ids': asic_ifs_asic_id_list,
344405
'sysports': sysports,
345406
'port_index_map': indexmap})
346407

@@ -349,7 +410,7 @@ def main():
349410
module.fail_json(msg=fail_msg)
350411
except Exception as e:
351412
fail_msg = "failed to find the correct port config for " + \
352-
m_args['hwsku'] + str(e)
413+
m_args['hwsku'] + "\n" + str(e)
353414
module.fail_json(msg=fail_msg)
354415

355416

ansible/roles/vm_set/library/kvm_port.py

+30-11
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,15 @@ def main():
2222

2323
module = AnsibleModule(argument_spec=dict(
2424
vmname=dict(required=True),
25+
front_panel_port_aliases=dict(required=True, type=list),
26+
midplane_port_aliases=dict(required=False, type=list, default=[]),
27+
inband_port_aliases=dict(required=False, type=list, default=[]),
2528
))
2629

2730
vmname = module.params['vmname']
31+
fp_port_aliases = module.params['front_panel_port_aliases']
32+
midplane_port_aliases = module.params['midplane_port_aliases']
33+
inband_port_aliases = module.params['inband_port_aliases']
2834

2935
try:
3036
output = subprocess.check_output(
@@ -36,24 +42,37 @@ def main():
3642

3743
mgmt_port = None
3844
fp_ports = {}
39-
cur_fp_idx = 0
45+
midplane_ports = []
46+
inband_ports = []
4047

41-
for msg in output.split('\n'):
42-
fds = re.split(r'\s+', msg.lstrip())
48+
lines = output.split('\n')[2:] # the first two lines are table headers
49+
eth_interfaces = []
50+
for line in lines:
51+
fds = re.split(r'\s+', line.lstrip())
4352
if len(fds) != 5:
4453
continue
4554
if fds[1] == "ethernet":
46-
if mgmt_port is None:
47-
mgmt_port = fds[0]
48-
else:
49-
fp_ports[cur_fp_idx] = fds[0]
50-
cur_fp_idx = cur_fp_idx + 1
55+
eth_interfaces.append(fds[0])
56+
57+
if len(eth_interfaces) < 1 + len(fp_port_aliases) + len(midplane_port_aliases) + len(inband_port_aliases):
58+
module.fail_json(msg="No enough ethernet ports for {}\n{}\n{}\n{}".format(
59+
vmname, fp_port_aliases, midplane_port_aliases, inband_port_aliases))
5160

52-
if mgmt_port is None:
53-
module.fail_json(msg="failed to find mgmt port")
61+
# extract mgmt port, fp_ports, midplane_ports(optional), inband_ports(optional)
62+
mgmt_port = eth_interfaces[0]
63+
eth_interfaces = eth_interfaces[1:]
64+
cur_fp_idx = 0
65+
for portinfo in fp_port_aliases:
66+
fp_ports[cur_fp_idx] = eth_interfaces[portinfo[1]]
67+
cur_fp_idx += 1
68+
for portinfo in midplane_port_aliases:
69+
midplane_ports.append(eth_interfaces[portinfo[1]])
70+
for portinfo in inband_port_aliases:
71+
inband_ports.append(eth_interfaces[portinfo[1]])
5472

5573
module.exit_json(changed=False, ansible_facts={
56-
'dut_mgmt_port': mgmt_port, 'dut_fp_ports': fp_ports})
74+
'dut_mgmt_port': mgmt_port, 'dut_fp_ports': fp_ports,
75+
'dut_midplane_ports': midplane_ports, 'dut_inband_ports': inband_ports})
5776

5877

5978
if __name__ == "__main__":

0 commit comments

Comments
 (0)