Skip to content

Commit 8cf9da9

Browse files
authored
[testbed]: Support add topology for SONiC neighbor devices (#3324)
* temp Signed-off-by: Ze Gan <ganze718@gmail.com> * Polish variable name from sonic to vsonic Signed-off-by: Ze Gan <ganze718@gmail.com> * Add sonic vm topology Signed-off-by: Ze Gan <ganze718@gmail.com> * Polish code Signed-off-by: Ze Gan <ganze718@gmail.com> * Add neighbor timers in frr config file Signed-off-by: Ze Gan <ganze718@gmail.com> * Update Readme Signed-off-by: Ze Gan <ganze718@gmail.com> * Polish code Signed-off-by: Ze Gan <ganze718@gmail.com>
1 parent 7cf8492 commit 8cf9da9

13 files changed

+291
-14
lines changed

ansible/roles/sonic/handlers/main.yml

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Notify handlers are always run in the same order they are defined, not in the order listed in the notify-statement.
2+
# This is also the case for handlers using listen.
3+
4+
- name: SONiC update config db
5+
command: config reload -y
6+
become: yes
7+
listen: "Update config db"
8+
9+
- name: SONiC restart BGP service
10+
become: true
11+
service: name=bgp
12+
state=restarted
13+
listen: "Restart BGP service"

ansible/roles/sonic/tasks/main.yml

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
- debug: msg="{{ vm_type }}"
2+
- debug: msg="{{ vm_set_name }}"
3+
4+
- name: Load topo variables
5+
include_vars: "vars/topo_{{ topo }}.yml"
6+
7+
- name: Include server vars
8+
include_vars: "{{ host_var_file }}"
9+
10+
- name: Find current server group
11+
set_fact: current_server={{ group_names | extract_by_prefix('server_') }}
12+
13+
- name: Extract VM names from the inventory
14+
set_fact: VM_list={{ groups[current_server] | filter_by_prefix('VM') | sort }}
15+
16+
- name: Get VM host name
17+
set_fact: VM_host={{ groups[current_server] | difference(VM_list) }}
18+
19+
- name: Generate hostname for target VM
20+
set_fact: hostname={{ VM_list | extract_hostname(topology['VMs'], VM_base, inventory_hostname) }}
21+
when: topology['VMs'] is defined
22+
23+
- fail:
24+
msg: "cannot find {{ inventory_hostname }} in the topology"
25+
when: hostname == "hostname not found"
26+
27+
- name: Set properties list to default value, when properties are not defined
28+
set_fact: properties_list=[]
29+
when: configuration is not defined or configuration[hostname] is not defined or configuration[hostname]['properties'] is not defined
30+
31+
- name: Set properties list to values, when they're defined
32+
set_fact: properties_list="{{ configuration[hostname]['properties'] }}"
33+
when: configuration and configuration[hostname] and configuration[hostname]['properties'] is defined
34+
35+
- name: Expand {{ hostname }} properties into props
36+
set_fact: props="{{ configuration_properties[item] | combine(props | default({})) }}"
37+
with_items: "{{ properties_list }}"
38+
when: hostname in configuration and configuration_properties[item] is defined
39+
40+
- include_tasks: vsonic.yml
41+
when: vm_type == "vsonic"

ansible/roles/sonic/tasks/vsonic.yml

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
- name: Set ansible login user name and password
2+
set_fact: ansible_user="admin" ansible_password={{ sonic_password }}
3+
4+
- name: Get VM front panel interface number
5+
shell: virsh domiflist {{ inventory_hostname }} | grep -E "^{{ inventory_hostname }}-t" | wc -l
6+
register: fp_num
7+
delegate_to: "{{ VM_host[0] }}"
8+
become: yes
9+
10+
- name: Set SONiC backplane port name
11+
set_fact: bp_ifname="Ethernet{{ fp_num.stdout|int + 1 }}"
12+
13+
- set_fact:
14+
mgmt_ip: "{{ ansible_host }}/{{ mgmt_prefixlen }}"
15+
mgmt_gw: "{{ vm_mgmt_gw | default(mgmt_gw) }}"
16+
17+
- name: create mgmt config
18+
template: src="configdb-mgmt.j2"
19+
dest=config-mgmt.json
20+
when: hostname in configuration
21+
22+
- name: create device metadata config
23+
shell: >
24+
sonic-cfggen -H -k Force10-S6000 --preset empty
25+
| jq '.DEVICE_METADATA.localhost.hostname="{{ hostname }}"'
26+
| jq '.DEVICE_METADATA.localhost += {docker_routing_config_mode:"split"}'
27+
| jq '.DEVICE_METADATA.localhost.bgp_asn="{{ configuration[hostname]['bgp']['asn'] }}"'
28+
> config-metadata.json
29+
when: hostname in configuration
30+
31+
- name: create interface config
32+
shell: >
33+
sonic-cfggen -p /usr/share/sonic/device/x86_64-kvm_x86_64-r0/Force10-S6000/port_config.ini -k Force10-S6000 --print-data
34+
| jq '.PORT[].admin_status |= "up"'
35+
> config-port.json
36+
when: hostname in configuration
37+
38+
- name: create topo config
39+
template: src="configdb-{{ topo }}-{{ props.swrole }}.j2"
40+
dest=config-topo.json
41+
when: hostname in configuration
42+
43+
- name: create config db json
44+
shell: |
45+
sonic-cfggen -j config-mgmt.json -j config-metadata.json -j config-port.json -j config-topo.json --print-data > /etc/sonic/config_db.json
46+
rm -f config-mgmt.json config-metadata.json config-port.json config-topo.json
47+
become: yes
48+
when: hostname in configuration
49+
notify:
50+
- Update config db
51+
52+
- name: create frr config
53+
template: src="frr-{{ topo }}-{{ props.swrole }}.j2"
54+
dest=/etc/sonic/frr/bgpd.conf
55+
become: yes
56+
when: hostname in configuration
57+
notify:
58+
- Restart BGP service
59+
60+
- name: create zebra config
61+
template: src="zebra-{{ topo }}-{{ props.swrole }}.j2"
62+
dest=/etc/sonic/frr/zebra.conf
63+
become: yes
64+
when: hostname in configuration
65+
notify:
66+
- Restart BGP service
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"MGMT_INTERFACE": {
3+
"eth0|{{ mgmt_ip }}": {
4+
"gwaddr": "{{ mgmt_gw }}"
5+
}
6+
},
7+
"MGMT_PORT": {
8+
"eth0": {
9+
"alias": "eth0",
10+
"admin_status": "up"
11+
}
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{% set host = configuration[hostname] %}
2+
{
3+
{% for name, iface in host['interfaces'].items() %}
4+
{% if name.startswith('Port-Channel') %}
5+
"PORTCHANNEL": {
6+
"{{ name | replace("-", "") }}": {
7+
"admin_status": "up",
8+
"mtu": "9100"
9+
}
10+
},
11+
"PORTCHANNEL_INTERFACE": {
12+
"{{ name | replace("-", "") }}": {},
13+
{% if iface['ipv4'] is defined %}
14+
"{{ name | replace("-", "") }}|{{ iface['ipv4'] }}": {},
15+
{% endif %}
16+
{% if iface['ipv6'] is defined %}
17+
"{{ name | replace("-", "") }}|{{ iface['ipv6'] }}": {}
18+
{% endif %}
19+
},
20+
{% endif %}
21+
{% if iface['lacp'] is defined %}
22+
"PORTCHANNEL_MEMBER": {
23+
{% set index = name | replace("Ethernet", "") | int - 1 %}
24+
"PortChannel{{ iface['lacp'] }}|Ethernet{{ index * 4}}": {}
25+
},
26+
{% endif %}
27+
{% endfor %}
28+
{% for name, iface in host['interfaces'].items() %}
29+
{% if name.startswith('Loopback') %}
30+
"LOOPBACK_INTERFACE": {
31+
"{{ name }}": {},
32+
{% if iface['ipv4'] is defined %}
33+
"{{ name }}|{{ iface['ipv4'] }}": {},
34+
{% endif %}
35+
{% if iface['ipv6'] is defined %}
36+
"{{ name }}|{{ iface['ipv6'] }}": {}
37+
{% endif %}
38+
}
39+
{% endif %}
40+
{% endfor %}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{% set host = configuration[hostname] %}
2+
!
3+
hostname {{ hostname }}
4+
password zebra
5+
enable password zebra
6+
!
7+
log syslog informational
8+
log facility local4
9+
!
10+
router bgp {{ host['bgp']['asn'] }}
11+
no bgp ebgp-requires-policy
12+
bgp router-id {{ host['interfaces']['Loopback0']['ipv4'] | ipaddr('address') }}
13+
!
14+
{% for asn, remote_ips in host['bgp']['peers'].items() %}
15+
{% for remote_ip in remote_ips %}
16+
neighbor {{ remote_ip }} remote-as {{ asn }}
17+
neighbor {{ remote_ip }} description {{ asn }}
18+
neighbor {{ remote_ip }} timers 3 10
19+
{% if remote_ip | ipv6 %}
20+
address-family ipv6 unicast
21+
neighbor {{ remote_ip }} activate
22+
exit
23+
{% endif %}
24+
{% endfor %}
25+
{% endfor %}
26+
neighbor {{ props.nhipv4 }} remote-as {{ host['bgp']['asn'] }}
27+
neighbor {{ props.nhipv4 }} description exabgp_v4
28+
neighbor {{ props.nhipv6 }} remote-as {{ host['bgp']['asn'] }}
29+
neighbor {{ props.nhipv6 }} description exabgp_v6
30+
address-family ipv6
31+
neighbor {{ props.nhipv6 }} activate
32+
exit
33+
!
34+
{% for name, iface in host['interfaces'].items() if name.startswith('Loopback') %}
35+
{% if iface['ipv4'] is defined %}
36+
address-family ipv4 unicast
37+
network {{ iface['ipv4'] }}
38+
{% endif %}
39+
{% if iface['ipv6'] is defined %}
40+
address-family ipv6 unicast
41+
network {{ iface['ipv6'] }}
42+
{% endif %}
43+
{% endfor %}
44+
!
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{% set host = configuration[hostname] %}
2+
hostname {{ hostname }}
3+
password zebra
4+
enable password zebra
5+
!
6+
log syslog informational
7+
log facility local4
8+
!
9+
! end of template: common/daemons.common.conf.j2!
10+
!
11+
!
12+
! Enable link-detect (default disabled)
13+
{% for name, iface in host['interfaces'].items() %}
14+
interface {{ name }}
15+
link detect
16+
!
17+
{% endfor %}

ansible/roles/vm_set/library/vm_topology.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,7 @@ def main():
997997
topo=dict(required=False, type='dict'),
998998
vm_names=dict(required=True, type='list'),
999999
vm_base=dict(required=False, type='str'),
1000+
vm_type=dict(required=False, type='str'),
10001001
ptf_mgmt_ip_addr=dict(required=False, type='str'),
10011002
ptf_mgmt_ipv6_addr=dict(required=False, type='str'),
10021003
ptf_mgmt_ip_gw=dict(required=False, type='str'),
@@ -1064,6 +1065,7 @@ def main():
10641065
vm_base = module.params['vm_base']
10651066
else:
10661067
vm_base = None
1068+
vm_type = module.params['vm_type']
10671069

10681070
net.init(vm_set_name, vm_base, duts_fp_ports, duts_name)
10691071

@@ -1086,7 +1088,8 @@ def main():
10861088
if vms_exists:
10871089
net.add_injected_fp_ports_to_docker()
10881090
net.bind_fp_ports()
1089-
net.bind_vm_backplane()
1091+
if vm_type != "vsonic":
1092+
net.bind_vm_backplane()
10901093
net.add_bp_port_to_docker(ptf_bp_ip_addr, ptf_bp_ipv6_addr)
10911094

10921095
if hostif_exists:
@@ -1128,6 +1131,7 @@ def main():
11281131
vm_base = module.params['vm_base']
11291132
else:
11301133
vm_base = None
1134+
vm_type = module.params['vm_type']
11311135

11321136
net.init(vm_set_name, vm_base, duts_fp_ports, duts_name)
11331137

@@ -1137,7 +1141,8 @@ def main():
11371141
net.unbind_mgmt_port(dut_mgmt_port)
11381142

11391143
if vms_exists:
1140-
net.unbind_vm_backplane()
1144+
if vm_type != "vsonic":
1145+
net.unbind_vm_backplane()
11411146
net.unbind_fp_ports()
11421147

11431148
if hostif_exists:

ansible/roles/vm_set/tasks/add_topo.yml

+1
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@
186186
topo: "{{ topology }}"
187187
vm_names: "{{ VM_hosts }}"
188188
vm_base: "{{ VM_base }}"
189+
vm_type: "{{ vm_type }}"
189190
ptf_mgmt_ip_addr: "{{ ptf_ip }}"
190191
ptf_mgmt_ipv6_addr: "{{ ptf_ipv6 }}"
191192
ptf_mgmt_ip_gw: "{{ mgmt_gw }}"

ansible/roles/vm_set/tasks/main.yml

+9-10
Original file line numberDiff line numberDiff line change
@@ -210,18 +210,17 @@
210210
register: vm_list_paused
211211
become: true
212212

213-
- block:
213+
- name: Require VMs as VEOS by default
214+
set_fact:
215+
vm_type: "veos"
216+
when: vm_type is not defined
214217

215-
- name: Require VMs as VEOS by default
216-
set_fact:
217-
vm_type: "veos"
218-
when: vm_type is not defined
219-
220-
- name: Check VM type
221-
fail:
222-
msg: "Cannot support this VM type {{ vm_type }}"
223-
when: vm_type not in supported_vm_types
218+
- name: Check VM type
219+
fail:
220+
msg: "Cannot support this VM type {{ vm_type }}"
221+
when: vm_type not in supported_vm_types
224222

223+
- block:
225224
- name: Ensure {{ root_path }} exists
226225
file: path={{ root_path }} state=directory
227226

ansible/roles/vm_set/tasks/remove_topo.yml

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
topo: "{{ topology }}"
3434
vm_names: "{{ VM_hosts }}"
3535
vm_base: "{{ VM_base }}"
36+
vm_type: "{{ vm_type }}"
3637
duts_fp_ports: "{{ duts_fp_ports }}"
3738
duts_mgmt_port: "{{ duts_mgmt_port }}"
3839
duts_name: "{{ duts_name.split(',') }}"

ansible/testbed_add_vm_topology.yml

+8-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
# -e ptf_ipv6=fec0::ffff:afa:1/64 - the ipv6 address and prefix of ptf container mgmt interface
2828
# -e topo=t0 - the name of removed topo
2929
# -e ptf_imagename=docker-ptf - name of a docker-image which will be used for the ptf docker container
30-
# -e vm_type=veos|ceos
30+
# -e vm_type=veos|ceos|vsonic
3131

3232
- hosts: servers:&vm_host
3333
gather_facts: no
@@ -105,6 +105,11 @@
105105
- set_fact:
106106
base_topo: "{{ topo.split('_') | first }}"
107107

108+
- name: Require VMs as VEOS by default
109+
set_fact:
110+
vm_type: "veos"
111+
when: vm_type is not defined
112+
108113
- name: Check if it is a known topology
109114
fail: msg="Unknown topology {{ topo }}"
110115
when: base_topo not in topologies
@@ -132,4 +137,5 @@
132137
delegate_to: localhost
133138

134139
roles:
135-
- { role: eos, when: topology.VMs is defined and VM_targets is defined and inventory_hostname in VM_targets } # role eos will be executed in any case, and when will evaluate with every task
140+
- { role: eos, when: topology.VMs is defined and VM_targets is defined and inventory_hostname in VM_targets and (vm_type == "veos" or vm_type == "ceos" ) } # If the vm_type is eos based, role eos will be executed in any case, and when will evaluate with every task
141+
- { role: sonic, when: topology.VMs is defined and VM_targets is defined and inventory_hostname in VM_targets and (vm_type == "vsonic" ) } # If the vm_type is sonic based, role sonic will be executed in any case, and when will evaluate with every task

0 commit comments

Comments
 (0)