forked from sonic-net/sonic-mgmt
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[dhcpv6_relay_test] test if dhcpv6_relay will set correct link addres…
…s in packet on multiple vlans host (sonic-net#14732) What is the motivation for this PR? This code is to fix test gap: sonic-net#9122 How did you do it? Configure multiple Vlans and dhcp servers Send dhcp requests Check dhcp packets relayed contains expected Vlan IP How did you verify/test it? Tested on mx, m0, dualtor and dualtor-aa.
- Loading branch information
1 parent
1ff7db8
commit 0fa8d2b
Showing
4 changed files
with
286 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,223 @@ | ||
import pytest | ||
import logging | ||
import ipaddress | ||
import json | ||
import re | ||
from math import log, ceil | ||
from tests.common.helpers.assertions import pytest_assert | ||
from tests.common.gcu_utils import create_checkpoint, rollback_or_reload, delete_checkpoint | ||
|
||
|
||
@pytest.fixture(scope="class") | ||
def setup_multiple_vlans_and_teardown(request, rand_selected_dut, rand_unselected_dut, tbinfo): | ||
''' | ||
This fixture will split the first vlan into multiple sub vlans and return the sub vlans info. | ||
The count of sub vlans is determined by the parameter vlan_count which is passed to the test. | ||
It will split prefix and members evenly to each sub vlan. | ||
''' | ||
vlan_count = request.param | ||
is_dualtor = 'dualtor' in tbinfo['topo']['name'] | ||
duthost = rand_selected_dut | ||
vlan_brief = duthost.get_vlan_brief() | ||
first_vlan_name = list(vlan_brief.keys())[0] | ||
first_vlan_info = list(vlan_brief.values())[0] | ||
running_config = duthost.get_running_config_facts() | ||
first_vlan_info['dhcp_servers'] = running_config['VLAN'][first_vlan_name].get('dhcp_servers', []) | ||
first_vlan_info['dhcp_relay'] = running_config['DHCP_RELAY'].get(first_vlan_name, {}).get('dhcp_servers', []) | ||
first_vlan_info['dhcpv6_servers'] = running_config['VLAN'][first_vlan_name].get('dhcpv6_servers', []) | ||
first_vlan_info['dhcpv6_relay'] = running_config['DHCP_RELAY'].get(first_vlan_name, {}).get('dhcpv6_servers', []) | ||
disabled_host_interfaces = tbinfo['topo']['properties']['topology'].get('disabled_host_interfaces', []) | ||
connected_ptf_ports_idx = [interface for interface in | ||
tbinfo['topo']['properties']['topology'].get('host_interfaces', []) | ||
if interface not in disabled_host_interfaces] | ||
if is_dualtor: | ||
pattern = r'0.(\d+)' | ||
connected_ptf_ports_idx = [int(re.findall(pattern, index)[0]) | ||
for index in connected_ptf_ports_idx if re.match(pattern, index)] | ||
dut_intf_to_ptf_index = duthost.get_extended_minigraph_facts(tbinfo)['minigraph_ptf_indices'] | ||
connected_dut_intf_to_ptf_index = {k: v for k, v in dut_intf_to_ptf_index.items() if v in connected_ptf_ports_idx} | ||
vlan_members = first_vlan_info['members'] | ||
vlan_member_with_ptf_idx = [(member, connected_dut_intf_to_ptf_index[member]) | ||
for member in vlan_members if member in connected_dut_intf_to_ptf_index] | ||
logging.info("The first_vlan_info before test is %s" % first_vlan_info) | ||
sub_vlans_info, config_patch = generate_sub_vlans_config_patch( | ||
first_vlan_name, | ||
first_vlan_info, | ||
vlan_member_with_ptf_idx, | ||
vlan_count | ||
) | ||
try: | ||
checkpoint_name = 'mutiple_vlans_test' | ||
create_checkpoint(duthost, checkpoint_name) | ||
|
||
logging.info("The patch for setup is %s" % config_patch) | ||
apply_config_patch(duthost, config_patch) | ||
logging.info("The sub_vlans_info after setup is %s" % sub_vlans_info) | ||
if is_dualtor: | ||
create_checkpoint(rand_unselected_dut, checkpoint_name) | ||
apply_config_patch(rand_unselected_dut, config_patch) | ||
|
||
yield sub_vlans_info | ||
finally: | ||
rollback_or_reload(duthost, checkpoint_name) | ||
delete_checkpoint(duthost, checkpoint_name) | ||
if is_dualtor: | ||
rollback_or_reload(rand_unselected_dut, checkpoint_name) | ||
delete_checkpoint(rand_unselected_dut, checkpoint_name) | ||
|
||
|
||
def generate_sub_vlans_config_patch(vlan_name, vlan_info, vlan_member_with_ptf_idx, count): | ||
pytest_assert(len(vlan_info['interface_ipv4']) > 0, "Expected at least one ipv4 address prefix") | ||
pytest_assert(len(vlan_info['interface_ipv6']) > 0, "Expected at least one ipv6 address prefix") | ||
pytest_assert(len(vlan_member_with_ptf_idx) >= count, "Expected member count greater or equal to sub vlan count") | ||
|
||
sub_vlans_info, config_patch = [], [] | ||
config_patch += remove_vlan_patch(vlan_name) \ | ||
+ remove_dhcpv6_relay_patch(vlan_name) \ | ||
+ [remove_vlan_ip_patch(vlan_name, ip)[0] for ip in vlan_info['interface_ipv4']] \ | ||
+ [remove_vlan_ip_patch(vlan_name, ip)[0] for ip in vlan_info['interface_ipv6']] \ | ||
+ [remove_vlan_member_patch(vlan_name, member)[0] for member in vlan_info['members']] | ||
|
||
vlan_prefix_v4 = vlan_info['interface_ipv4'][0] | ||
vlan_net_v4 = ipaddress.ip_network(address=vlan_prefix_v4, strict=False) | ||
vlan_nets_v4 = list(vlan_net_v4.subnets(prefixlen_diff=int(ceil(log(count, 2))))) | ||
vlan_prefix_v6 = vlan_info['interface_ipv6'][0] | ||
vlan_net_v6 = ipaddress.ip_network(address=vlan_prefix_v6, strict=False) | ||
vlan_nets_v6 = list(vlan_net_v6.subnets(prefixlen_diff=int(ceil(log(count, 2))))) | ||
member_count = len(vlan_member_with_ptf_idx)//count | ||
for i in range(count): | ||
sub_vlans_info.append( | ||
{ | ||
'vlan_name': 'Vlan90%s' % i, | ||
'interface_ipv4': str(next(vlan_nets_v4[i].hosts())) + '/' + str(vlan_nets_v4[i].prefixlen), | ||
'interface_ipv6': str(next(vlan_nets_v6[i].hosts())) + '/' + str(vlan_nets_v6[i].prefixlen), | ||
'members_with_ptf_idx': [(member, ptf_idx) for member, ptf_idx | ||
in vlan_member_with_ptf_idx[member_count*i:member_count*(i+1)]] | ||
} | ||
) | ||
|
||
for info in sub_vlans_info: | ||
new_vlan_name = info['vlan_name'] | ||
new_interface_ipv4 = info['interface_ipv4'] | ||
new_interface_ipv6 = info['interface_ipv6'] | ||
new_members_with_ptf_idx = info['members_with_ptf_idx'] | ||
config_patch += add_vlan_patch(new_vlan_name, vlan_info['dhcp_servers'], vlan_info['dhcpv6_servers']) \ | ||
+ add_dhcpv6_relay_patch(new_vlan_name, vlan_info['dhcpv6_relay']) \ | ||
+ add_vlan_ip_patch(new_vlan_name, new_interface_ipv4) \ | ||
+ add_vlan_ip_patch(new_vlan_name, new_interface_ipv6) \ | ||
+ [add_vlan_member_patch(new_vlan_name, member)[0] for member, _ in new_members_with_ptf_idx] | ||
|
||
return sub_vlans_info, config_patch | ||
|
||
|
||
def vlan_n2i(vlan_name): | ||
""" | ||
Convert vlan name to vlan id | ||
""" | ||
return vlan_name.replace("Vlan", "") | ||
|
||
|
||
def add_vlan_patch(vlan_name, dhcp_servers, dhcpv6_servers): | ||
patch = [ | ||
{ | ||
"op": "add", | ||
"path": "/VLAN/%s" % vlan_name, | ||
"value": { | ||
"vlanid": vlan_n2i(vlan_name) | ||
} | ||
}, | ||
{ | ||
"op": "add", | ||
"path": "/VLAN_INTERFACE/%s" % vlan_name, | ||
"value": {} | ||
} | ||
] | ||
if dhcp_servers: | ||
patch[0]["value"]["dhcp_servers"] = dhcp_servers | ||
if dhcpv6_servers: | ||
patch[0]["value"]["dhcpv6_servers"] = dhcpv6_servers | ||
return patch | ||
|
||
|
||
def remove_vlan_patch(vlan_name): | ||
patch = [ | ||
{ | ||
"op": "remove", | ||
"path": "/VLAN/%s" % vlan_name | ||
}, | ||
{ | ||
"op": "remove", | ||
"path": "/VLAN_INTERFACE/%s" % vlan_name | ||
} | ||
] | ||
return patch | ||
|
||
|
||
def add_dhcpv6_relay_patch(vlan_name, dhcpv6_servers): | ||
patch = [{ | ||
"op": "add", | ||
"path": "/DHCP_RELAY/%s" % vlan_name, | ||
"value": {} | ||
}] | ||
if dhcpv6_servers: | ||
patch[0]["value"]["dhcpv6_servers"] = dhcpv6_servers | ||
return patch | ||
|
||
|
||
def remove_dhcpv6_relay_patch(vlan_name): | ||
patch = [{ | ||
"op": "remove", | ||
"path": "/DHCP_RELAY/%s" % vlan_name | ||
}] | ||
return patch | ||
|
||
|
||
def add_vlan_member_patch(vlan_name, member_name): | ||
patch = [{ | ||
"op": "add", | ||
"path": "/VLAN_MEMBER/%s|%s" % (vlan_name, member_name), | ||
"value": { | ||
"tagging_mode": "untagged" | ||
} | ||
}] | ||
return patch | ||
|
||
|
||
def remove_vlan_member_patch(vlan_name, member_name): | ||
patch = [{ | ||
"op": "remove", | ||
"path": "/VLAN_MEMBER/%s|%s" % (vlan_name, member_name) | ||
}] | ||
return patch | ||
|
||
|
||
def add_vlan_ip_patch(vlan_name, ip): | ||
patch = [{ | ||
"op": "add", | ||
"path": "/VLAN_INTERFACE/%s|%s" % (vlan_name, ip.replace('/', '~1')), | ||
"value": {} | ||
}] | ||
return patch | ||
|
||
|
||
def remove_vlan_ip_patch(vlan_name, ip): | ||
patch = [{ | ||
"op": "remove", | ||
"path": "/VLAN_INTERFACE/%s|%s" % (vlan_name, ip.replace('/', '~1')) | ||
}] | ||
return patch | ||
|
||
|
||
def apply_config_patch(duthost, config_to_apply): | ||
logging.info("The config patch: %s" % config_to_apply) | ||
tmpfile = duthost.shell('mktemp')['stdout'] | ||
try: | ||
duthost.copy(content=json.dumps(config_to_apply, indent=4), dest=tmpfile) | ||
output = duthost.shell('config apply-patch {}'.format(tmpfile), module_ignore_errors=True) | ||
pytest_assert(not output['rc'], "Command is not running successfully") | ||
pytest_assert( | ||
"Patch applied successfully" in output['stdout'], | ||
"Please check if json file is validate" | ||
) | ||
finally: | ||
duthost.file(path=tmpfile, state='absent') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters