Skip to content

Commit d8f4ad1

Browse files
committed
[pytest] Add testcase to generate Spytest testbed file
* Add template file `tests/template/spytest_testbed.yaml.j2` as the testbed template. * Add a testcase: `tests/testbed_setup/test_gen_spy_testbed.py::test_gen_spy_testbed` to generate the testbed file for Spytest and save as `spytest/testbeds/spytest_testbed.yaml`. Signed-off-by: Longxiang Lyu <[email protected]>
1 parent 866825a commit d8f4ad1

File tree

2 files changed

+150
-0
lines changed

2 files changed

+150
-0
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
version: 2.0
2+
3+
services: {default: !include sonic_services.yaml}
4+
5+
params: !include sonic_params.yaml
6+
builds: !include sonic_builds.yaml
7+
speeds: !include sonic_speeds.yaml
8+
errors: !include sonic_errors.yaml
9+
instrument: !include sonic_instrument.yaml
10+
11+
configs:
12+
default: !include sonic_configs.yaml
13+
empty: {current: [], restore: []}
14+
15+
devices:
16+
{% for hostname, login_info in devices %}
17+
{{ hostname }}:
18+
device_type: DevSonic
19+
access: {{ login_info.login_access }}
20+
credentials: {{ login_info.login_credentials }}
21+
properties: {config: default, build: default, services: default, params: def_dut, speed: default, errors: default}
22+
{% endfor %}
23+
ptf-01:
24+
device_type: TGEN
25+
properties: {type: scapy, version: 1.0, ip: {{ testbed.ptf_ip }}, params: def_tg}
26+
27+
topology:
28+
ptf-01:
29+
interfaces:
30+
{% for ptf_conn in ptf_connections %}
31+
{{ ptf_conn.start_port }}: {{ "{" }}EndDevice: {{ ptf_conn.end_device }}, EndPort: {{ ptf_conn.end_port }}, params: def_tg_link{{ "}" }}
32+
{% endfor %}
33+
{% for hostname in testbed.duts %}
34+
{% if hostname in dev_connections %}
35+
{{ hostname }}:
36+
interfaces:
37+
{% for dev_conn in dev_connections[hostname] %}
38+
{{ dev_conn.start_port }}: {{ "{" }}EndDevice: {{ dev_conn.end_device }}, EndPort: {{ dev_conn.end_port }}, params: def_tg_link{{ "}" }}
39+
{% endfor %}
40+
{% endif %}
41+
{% endfor %}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import logging
2+
import json
3+
import os
4+
import pytest
5+
import yaml
6+
7+
from collections import defaultdict
8+
from jinja2 import Template
9+
10+
11+
TESTBED_TEMPLATE = "templates/spytest_testbed.yaml.j2"
12+
PTF_INTERFACE_TEMPLATE = "1/%d"
13+
14+
pytestmark = [
15+
pytest.mark.topology("util"),
16+
pytest.mark.sanity_check(skip_sanity=True)
17+
]
18+
19+
20+
@pytest.fixture(scope="function")
21+
def hostvars(duthosts):
22+
"""Return host variables dicts for DUTs defined in testbed."""
23+
if not duthosts:
24+
return {}
25+
var_manager = duthosts[-1].host.options["variable_manager"]
26+
hostvars_all = var_manager.get_vars()["hostvars"]
27+
return {duthost.hostname: hostvars_all[duthost.hostname]
28+
for duthost in duthosts}
29+
30+
31+
def test_gen_spy_testbed(conn_graph_facts_multi_duts, hostvars, testbed,
32+
pytestconfig):
33+
"""Generate spytest testbed file."""
34+
35+
def _interface_key(interface):
36+
"""Get interface key to sort."""
37+
return list(map(int, interface.lstrip("Ethernet").split("/")))
38+
39+
def _to_string(obj):
40+
"""Convert unicodes in obj to strings"""
41+
return yaml.safe_load(json.dumps(obj))
42+
43+
hostnames = testbed["duts"]
44+
device_conn = _to_string(conn_graph_facts_multi_duts["device_conn"])
45+
connections = dict(
46+
zip(hostnames, conn_graph_facts_multi_duts["device_conn"]))
47+
48+
# devices section
49+
devices = []
50+
for hostname in hostnames:
51+
hostvar = hostvars[hostname]
52+
login_info = {}
53+
login_info["login_access"] = \
54+
json.dumps(hostvar["login_access"]).replace('"', '')
55+
login_info["login_credentials"] = \
56+
json.dumps(hostvar["login_credentials"]).replace('"', '')
57+
devices.append((hostname, login_info))
58+
59+
# topology section
60+
ptf_connections = []
61+
intf = 1
62+
for hostname in hostnames:
63+
end_device = hostname
64+
conns = connections[hostname]
65+
end_ports = sorted(
66+
(_ for _ in conns if conns[_]['peerdevice'] not in connections),
67+
key=_interface_key)
68+
for end_port in end_ports:
69+
ptf_conn = {
70+
"start_port": PTF_INTERFACE_TEMPLATE % intf,
71+
"end_device": hostname,
72+
"end_port": end_port
73+
}
74+
ptf_connections.append(ptf_conn)
75+
conns.pop(end_port)
76+
intf += 1
77+
78+
dev_connections = defaultdict(list)
79+
for hostname in hostnames:
80+
conns = connections[hostname]
81+
for start_port in sorted(conns.keys(), key=_interface_key):
82+
end_device = conns[start_port]["peerdevice"]
83+
end_port = conns[start_port]["peerport"]
84+
dev_connections[hostname].append(
85+
{
86+
"start_port": start_port,
87+
"end_device": end_device,
88+
"end_port": end_port
89+
}
90+
)
91+
connections[end_device].pop(end_port)
92+
93+
# write to testbed dest file
94+
with open(TESTBED_TEMPLATE) as tmpl_fd:
95+
testbed_tmpl = Template(
96+
tmpl_fd.read(), trim_blocks=True, lstrip_blocks=True)
97+
testbed_file = os.path.join(str(pytestconfig.rootdir),
98+
"../spytest/testbeds/spytest_testbed.yaml")
99+
testbed_file = os.path.normpath(testbed_file)
100+
logging.info("testbed save path: %s", testbed_file)
101+
if os.path.exists(testbed_file):
102+
logging.warn("testbed file(%s) exists, overwrite!", testbed_file)
103+
testbed_stream = testbed_tmpl.stream(
104+
devices=devices,
105+
testbed=testbed,
106+
ptf_connections=ptf_connections,
107+
dev_connections=dev_connections
108+
)
109+
testbed_stream.dump(testbed_file)

0 commit comments

Comments
 (0)