diff --git a/spytest/ansible/ansible.cfg b/spytest/ansible/ansible.cfg new file mode 100644 index 00000000000..c08194152a7 --- /dev/null +++ b/spytest/ansible/ansible.cfg @@ -0,0 +1,9 @@ +[defaults] +interpreter_python = auto_legacy_silent +host_key_checking = False +ssh_args = -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no + +[ssh_connection] +pipelining = True +control_path = /tmp/ansible-ssh-%%h-%%p-%%r + diff --git a/spytest/ansible/ping.yml b/spytest/ansible/ping.yml new file mode 100644 index 00000000000..c1435c95b98 --- /dev/null +++ b/spytest/ansible/ping.yml @@ -0,0 +1,7 @@ +--- +- hosts: all + remote_user: admin + gather_facts: False + tasks: + - name: test connection + ping: diff --git a/spytest/apis/__init__.py b/spytest/apis/__init__.py new file mode 100644 index 00000000000..297316aad73 --- /dev/null +++ b/spytest/apis/__init__.py @@ -0,0 +1 @@ +__all__ = ['common', 'routing', 'switching', 'system'] diff --git a/spytest/apis/common/__init__.py b/spytest/apis/common/__init__.py new file mode 100644 index 00000000000..bfb924330ef --- /dev/null +++ b/spytest/apis/common/__init__.py @@ -0,0 +1 @@ +__all__ = ['checks', 'hooks', 'init' ] \ No newline at end of file diff --git a/spytest/apis/common/asic.py b/spytest/apis/common/asic.py new file mode 100644 index 00000000000..eab93f8e726 --- /dev/null +++ b/spytest/apis/common/asic.py @@ -0,0 +1,26 @@ +def dump_l3_alpm(dut): + pass + +def dump_l2(dut): + pass + +def dump_vlan(dut): + pass + +def dump_ports_info(dut): + pass + +def dump_trunk(dut): + pass + +def dump_counters(dut): + pass + +def clear_counters(dut): + pass + +def bcmcmd_show_ps(dut): + pass + +def dump_threshold_info(dut, test, platform, mode): + pass diff --git a/spytest/apis/common/checks.py b/spytest/apis/common/checks.py new file mode 100644 index 00000000000..d037f4b8bbc --- /dev/null +++ b/spytest/apis/common/checks.py @@ -0,0 +1,278 @@ + +from spytest import st, tgapi, tgapi +import utilities.common as utils +import apis.system.port as portapi + +def log_info(fmt, *args): + st.log(fmt % args) + +def warn(fmt, *args): + st.warn(fmt % args) + +def trace(dut, local, partner, remote, status): + #print(dut, local, partner, remote, status) + pass + +def wait(): + st.wait(5) + +def check_status(s1, s2, s3, s4): + #print(s1, s2, s3, s4) + if not s1 or not s3: + return False + if s1.lower() != s2.lower(): + return False + if s3.lower() != s4.lower(): + return False + return True + +def get_link_status(tg, ph): + return tg.tg_interface_control(mode="check_link", desired_status='up', + port_handle=ph) + +def verify_topology(check_type, threads=True): + if check_type in ["status", "status2", "status3", "status4"]: + return links_status(threads, check_type) + + retval = True + results = [] + header = ['DUT', 'Local', "Partner", "Remote", "Status"] + check_oneway = True + exclude = [] + for dut in st.get_dut_names(): + alias = st.get_device_alias(dut) + for local, partner, remote in st.get_dut_links(dut): + palias = st.get_device_alias(partner) + + # check if the port is verified from other direction + skip = False + for ex in exclude: + #print("CMP", dut, local, ex[0], ex[1]) + if dut == ex[0] and local == ex[1]: + skip = True + break + if skip: + log_info("{}/{} is already verified".format(alias, local)) + continue + + result = [alias, local, palias, remote, "Fail"] + + # shutdown local link and get remote link stats in partner + portapi.shutdown(dut, [local]) + wait() + status1 = portapi.get_interface_status(partner, remote) + trace(alias, local, palias, remote, status1) + + # noshutdown local link and get remote link stats in partner + portapi.noshutdown(dut, [local]) + wait() + status2 = portapi.get_interface_status(partner, remote) + trace(alias, local, palias, remote, status2) + + # log the result on fail + if not check_status(status1, "down", status2, "up"): + warn("1. port %s/%s is not connected to %s/%s\n", + alias, local, palias, remote) + results.append(result) + exclude.append([partner, remote]) + retval = False + continue + + if not check_oneway: + # shutdown remote link and get local link status + portapi.shutdown(partner, [remote]) + wait() + status3 = portapi.get_interface_status(dut, local) + trace(alias, local, palias, remote, status3) + + # noshutdown remote link and get local link status + portapi.noshutdown(partner, [remote]) + wait() + status4 = portapi.get_interface_status(dut, local) + trace(alias, local, palias, remote, status4) + + # log the result on fail + if not check_status(status3, "down", status4, "up"): + warn("2. port %s/%s is not connected to %s/%s\n", + alias, local, palias, remote) + results.append(result) + retval = False + continue + + # log the result on pass + result[4] = "OK" + results.append(result) + exclude.append([partner, remote]) + + for local, partner, remote in st.get_tg_links(dut): + palias = st.get_device_alias(partner) + (tg, ph) = tgapi.get_handle_byname(None, tg=partner, port=remote) + + result = [alias, local, palias, remote, "Fail"] + + tgen_link_status_supported = False + if tgen_link_status_supported: + # shutdown local link and get remote link stats in partner + portapi.shutdown(dut, [local]) + wait() + status1 = get_link_status(tg, ph) + trace(alias, local, palias, remote, status1) + + # no shutdown local link and get remote link stats in partner + portapi.noshutdown(dut, [local]) + wait() + status2 = get_link_status(tg, ph) + trace(alias, local, palias, remote, status2) + + # log the result on fail + if tgen_link_status_supported and (status1 or not status2): + warn("3. port %s/%s is not connected to %s/%s\n", + alias, local, palias, remote) + results.append(result) + retval = False + continue + + # shutdown remote link and get local link status + tg.tg_interface_control(mode="break_link", port_handle=ph) + wait() + status3 = portapi.get_interface_status(dut, local) + trace(alias, local, palias, remote, status3) + + # noshutdown remote link and get local link status + tg.tg_interface_control(mode="restore_link", port_handle=ph) + wait() + status4 = portapi.get_interface_status(dut, local) + trace(alias, local, palias, remote, status4) + + # log the result on fail + if not check_status(status3, "down", status4, "up"): + warn("4. port %s/%s is not connected to %s/%s\n", + alias, local, palias, remote) + results.append(result) + retval = False + continue + + # log the result on pass + result[4] = "OK" + results.append(result) + + return [retval, header, results] + +def fill_alias(): + alias = dict() + for dut in st.get_dut_names(): + alias[dut] = st.get_device_alias(dut) + for tg in st.get_tg_names(): + alias[tg] = st.get_device_alias(tg) + return alias + +def links_status(threads, check_type): + header = ['DUT', 'Local', "LStatus (A/O)", "Partner", "Remote", "RStatus (A/O)"] + funcs = [ + [tg_links_status, check_type], + [duts_links_status, threads] + ] + [[v1, v2], [e1, e2]] = utils.exec_all(threads, funcs, True) + if v1 is None or v2 is None or e1 is not None or e2 is not None: + print(v1, v2, e1, e2) + return [True, header, []] + + v1_default = "?2?" if v1 else "NA" + (results, exclude, alias) = ([], [], fill_alias()) + for dut in st.get_dut_names(): + for local, partner, remote in st.get_tg_links(dut): + res = [] + res.append(alias.get(dut, "?")) + res.append(local) + res.append(v2.get("{}--{}".format(dut, local), "?1?")) + res.append(alias.get(partner, "?")) + res.append(remote) + res.append(v1.get("{}--{}".format(partner, remote), v1_default)) + results.append(res) + for local, partner, remote in st.get_dut_links(dut): + name = "{}--{}".format(dut, local) + if name in exclude: + continue + res = [] + res.append(alias.get(dut, "?")) + res.append(local) + res.append(v2.get("{}--{}".format(dut, local), "?3?")) + res.append(alias.get(partner, "?")) + res.append(remote) + res.append(v2.get("{}--{}".format(partner, remote), "?4?")) + exclude.append("{}--{}".format(partner, remote)) + results.append(res) + return [True, header, results] + +def tg_links_status_1(): + results = dict() + for dut in st.get_dut_names(): + for local, partner, remote in st.get_tg_links(dut): + (tg, ph) = tgapi.get_handle_byname(None, tg=partner, port=remote) + name = "{}--{}".format(partner, remote) + results[name] = get_link_status(tg, ph) + return results + +def tg_links_status_0(): + # build port list per tgen + tg_port_dict = {} + for dut in st.get_dut_names(): + for local, partner, remote in st.get_tg_links(dut): + tg_port_dict.setdefault(partner, []).append(remote) + + results = dict() + for partner, port_list in tg_port_dict.items(): + # get tgen handle using first port + (tg, ph) = tgapi.get_handle_byname(None, tg=partner, port=port_list[0]) + # get all ports status + rv = tg.get_port_status(port_list) + # fill the results + for port in port_list: + name = "{}--{}".format(partner, port) + results[name] = rv[port] + + return results + +def tg_links_status(check_type): + if check_type in ["status3"]: + return dict() + try: + return tg_links_status_0() + except: + return tg_links_status_1() + +def duts_links_status(threads): + results = dict() + [rvs, exs] = utils.exec_foreach(threads, st.get_dut_names(), dut_links_status) + for rv in rvs: + if rv: + results.update(rv) + return results + +def dut_links_status(dut): + local_list = [] + for local, partner, remote in st.get_dut_links(dut): + local_list.append(local) + for local, partner, remote in st.get_tg_links(dut): + local_list.append(local) + output = portapi.get_status(dut, ",".join(local_list)) + + results = dict() + for local, partner, remote in st.get_dut_links(dut): + match = {"interface": local} + entries = utils.filter_and_select(output, ["admin","oper"], match) + name = "{}--{}".format(dut, local) + if entries: + results[name] = "{}/{}".format(entries[0]["admin"], entries[0]["oper"]) + else: + results[name] = "----" + for local, partner, remote in st.get_tg_links(dut): + match = {"interface": local} + entries = utils.filter_and_select(output, ["admin","oper"], match) + name = "{}--{}".format(dut, local) + if entries: + results[name] = "{}/{}".format(entries[0]["admin"], entries[0]["oper"]) + else: + results[name] = "----" + return results + diff --git a/spytest/apis/common/hooks.py b/spytest/apis/common/hooks.py new file mode 100644 index 00000000000..c179b14f223 --- /dev/null +++ b/spytest/apis/common/hooks.py @@ -0,0 +1,57 @@ +import os + +# this file is imported from framework and hence +# we can't import framework API globally here +# import them with in functions +#from spytest import st + +def get_vars(dut): + from spytest import st + from apis.system.basic import show_version + retval = dict() + retval["constants"] = st.get_datastore(dut, "constants") + retval["vervars"] = st.get_datastore(dut, "vervars") + version_data = show_version(dut) + if not version_data and st.is_dry_run(): return retval + retval["product"] = version_data['product'] + retval["hwsku"] = version_data['hwsku'] + retval["version"] = version_data['version'] + for version in ["3.0.x.0", "3.0.1"]: + if version in retval["version"]: + retval["vervars"] = st.get_datastore(dut, "vervars", version) + if retval["vervars"]: + for name, val in retval["vervars"].items(): + st.log("VERVARS ({}) - {} = {}".format(dut, name, val)) + return retval + +def ensure_upgrade(dut): + from apis.system.basic import ensure_hwsku_config + from apis.system.basic import ensure_certificate + from apis.system.ntp import ensure_ntp_config + ensure_hwsku_config(dut) + if os.getenv("SPYTEST_NTP_CONFIG_INIT", "0") != "0": + ensure_ntp_config(dut) + if os.getenv("SPYTEST_GENERATE_CERTIFICATE", "0") != "0": + ensure_certificate(dut) + +def api_hooks_init(): + from spytest.dicts import SpyTestDict + from apis.system.port import shutdown, noshutdown, get_interfaces_all + from apis.system.port import get_interface_status + from apis.system.basic import get_swver, get_sysuptime, get_system_status + from apis.common.checks import verify_topology + from apis.common.verifiers import get_verifiers + hooks = SpyTestDict() + hooks.port_shutdown = shutdown + hooks.port_noshutdown = noshutdown + hooks.get_swver = get_swver + hooks.get_sysuptime = get_sysuptime + hooks.get_interfaces_all = get_interfaces_all + hooks.get_interface_status = get_interface_status + hooks.get_system_status = get_system_status + hooks.verify_topology = verify_topology + hooks.get_vars = get_vars + hooks.verifiers = get_verifiers + hooks.ensure_upgrade = ensure_upgrade + return hooks + diff --git a/spytest/apis/common/init.py b/spytest/apis/common/init.py new file mode 100644 index 00000000000..0f904f7ab0b --- /dev/null +++ b/spytest/apis/common/init.py @@ -0,0 +1,70 @@ +import os + +# this file is imported from framework and hence +# we can't import framework API globally here +# import them with in functions +#from spytest import st + +def _api_common_session_begin(): + from spytest import st, utils + st.debug ("---- common session begin ----") + if not os.getenv("SPYTEST_SKIP_INIT_COMMANDS"): + def f(dut): + if st.get_device_type(dut) in ["sonic", "vsonic"]: + st.show(dut, "show version", skip_error_check=True, + skip_tmpl=True) + st.show(dut, "show runningconfiguration all", + skip_error_check=True, skip_tmpl=True) + if not st.is_community_build(): + st.show(dut, "show system status", skip_error_check=True, + skip_tmpl=True) + utils.exec_foreach(True, st.get_dut_names(), f) + +def apis_register(): + from apis.common.hooks import api_hooks_init + return api_hooks_init() + +def apis_common_init(scope, ref=None): + """ + + :param scope: + :type scope: + :param ref: + :type ref: + :return: + :rtype: + """ + from spytest import st, utils + if scope == "session": + return _api_common_session_begin() + + if scope == "module": + st.debug ("---- common module {} begin ----".format(ref)) + elif scope == "function": + st.debug ("---- common test {} begin ----".format(ref)) + if not os.getenv("SPYTEST_SKIP_INIT_COMMANDS"): + def f(dut): + if st.get_device_type(dut) in ["sonic", "vsonic"]: + if not st.is_community_build(): + st.show(dut, "show system status", skip_error_check=True, + skip_tmpl=True) + utils.exec_foreach(True, st.get_dut_names(), f) + +def apis_common_clean(scope, ref=None): + """ + + :param scope: + :type scope: + :param ref: + :type ref: + :return: + :rtype: + """ + from spytest import st + if scope == "session": + st.debug ("---- common session end ----") + elif scope == "module": + st.debug ("---- common module end ----") + elif scope == "function": + st.debug ("---- common test {} end ----".format(ref)) + diff --git a/spytest/apis/common/verifiers.py b/spytest/apis/common/verifiers.py new file mode 100644 index 00000000000..3298f2a1d56 --- /dev/null +++ b/spytest/apis/common/verifiers.py @@ -0,0 +1,16 @@ +from spytest import st + +def na_verifier(): + st.log("na_verifier") + return True + +def l2_verifier(): + st.log("l2_verifier") + return False + +def get_verifiers(): + verifiers = dict() + verifiers["NA"] = na_verifier + verifiers["L2"] = l2_verifier + return verifiers + diff --git a/spytest/apis/common/wait.py b/spytest/apis/common/wait.py new file mode 100644 index 00000000000..79f8c7d40a3 --- /dev/null +++ b/spytest/apis/common/wait.py @@ -0,0 +1,18 @@ +from spytest import st + +def _wait(dut, secs): + if st.is_vsonic(dut): + st.wait(secs) + +def mac_learn(dut=None, secs=10): + _wait(dut, secs) + +def tgen_stats(dut=None, secs=15): + _wait(dut, secs) + +def tgen_send(dut=None, secs=15): + _wait(dut, secs) + +def nat_stats(dut=None, secs=10): + _wait(dut, secs) + diff --git a/spytest/apis/qos/__init__.py b/spytest/apis/qos/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/spytest/apis/qos/acl.py b/spytest/apis/qos/acl.py new file mode 100644 index 00000000000..065667a0b27 --- /dev/null +++ b/spytest/apis/qos/acl.py @@ -0,0 +1,534 @@ +# This file contains the list of API's which performs ACL operations. +# Author : Chaitanya Vella (Chaitanya-vella.kumar@broadcom.com) and Prudvi Mangadu (prudvi.mangadu@broadcom.com) + +from spytest import st +import json +import tempfile +import utilities.utils as util_obj +from spytest.utils import filter_and_select +import os +import re + + +def create_acl_table(dut, skip_verify=True, **kwargs): + """ + Create the ACL table + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + + :param dut: + :param kwargs: + :param skip_verify: True(Default) / False + :return: + + Ex: Create_acl_table(1, name="DATAACL", stage = "INGRESS", type="L3", description="Testing", + ports=["Ethernet0", "Ethernet2"]) + """ + cli_type = kwargs.get("cli_type","click") + table_name = kwargs.get("table_name", None) + if cli_type == "click": + st.log("Creating ACL table ...") + acl_data = kwargs + if not acl_data: + st.error("ACL table creation failed because of invalid data ..") + acl_table_data = dict() + acl_table = dict() + acl_table[acl_data["name"]] = dict() + acl_table[acl_data["name"]]["type"] = acl_data["type"] if 'type' in acl_data else '' + acl_table[acl_data["name"]]["policy_desc"] = acl_data["description"] if 'description' in acl_data else '' + acl_table[acl_data["name"]]["ports"] = acl_data["ports"] if 'ports' in acl_data else [] + acl_table[acl_data["name"]]["stage"] = acl_data["stage"] if 'stage' in acl_data else '' + acl_table_data["ACL_TABLE"] = acl_table + acl_table_data = json.dumps(acl_table_data) + json.loads(acl_table_data) + st.apply_json2(dut, acl_table_data) + # reboot.config_save(dut) + if not skip_verify: + if not verify_acl_table(dut, acl_data["name"]): + return False + else: + if not table_name: + st.log("Mandatory parameter table name not passed") + return False + commands = list() + commands.append("ip access-list {}".format(table_name)) + commands.append("exit") + st.config(dut, commands, type=cli_type, skip_error_check=skip_verify) + return True + + +def create_acl_rule(dut, skip_verify=True, type1=None, type2=None, **kwargs): + """ + Create the ACL rule + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param kwargs: + :param skip_verify: True(Default) / False + :return: + + create_acl_rule(1, table_name="DATAACL",rule_name = "DATARULE", ) + REF: https://github.com/Azure/SONiC/wiki/ACL-High-Level-Design -- Follow the rule attributes names from this link + """ + cli_type = kwargs.get("cli_type","click") + table_name = kwargs.get("table_name", None) + rule_name = kwargs.get("rule_name", None) + packet_action = kwargs.get("packet_action", "deny") + l4_protocol = kwargs.get("l4_protocol", None) + src_ip = kwargs.get("SRC_IP", "any") + dst_ip = kwargs.get("DST_IP","any") + dscp_value = kwargs.get("dscp_value") + tcp_flag = kwargs.get("tcp_flag", None) + + st.log("Creating ACL rule ...") + if cli_type == "click": + acl_rule_data = kwargs + if not acl_rule_data: + st.error("ACL rule creation failed because of invalid data") + return False + acl_table_rules = dict() + acl_rules = dict() + excluded_keys = ["table_name", "rule_name"] + acl_rules["{}|{}".format(acl_rule_data["table_name"], acl_rule_data["rule_name"])] = dict() + for key, value in acl_rule_data.items(): + if key not in excluded_keys: + acl_rules["{}|{}".format(acl_rule_data["table_name"], acl_rule_data["rule_name"])][key.upper()] = value + acl_table_rules["ACL_RULE"] = acl_rules + acl_table_rules = json.dumps(acl_table_rules) + json.loads(acl_table_rules) + st.apply_json2(dut, acl_table_rules) + # reboot.config_save(dut) + if not skip_verify: + if not verify_acl_table_rule(dut, acl_rule_data["table_name"], acl_rule_data["rule_name"]): + return False + else: + if not (table_name and l4_protocol): + st.log("Mandatory parameters like table name and/or rule name and/or l4 protocol not passed") + return False + commands = list() + commands.append("ip access-list {}".format(table_name)) + if rule_name: + rule_seq = int(re.findall(r'\d+', rule_name)[0]) + if type1 and type2: + command = "seq {} {} {} {} {} {} {}".format(rule_seq, packet_action, l4_protocol, type1, src_ip, type2, + dst_ip) + elif type1: + command = "seq {} {} {} {} {} {}".format(rule_seq, packet_action, l4_protocol, type1, src_ip, dst_ip) + elif type2: + command = "seq {} {} {} {} {} {}".format(rule_seq, packet_action, l4_protocol, src_ip, type2, dst_ip) + else: + command = "seq {} {} {} {} {}".format(rule_seq, packet_action, l4_protocol, src_ip, dst_ip) + if tcp_flag: + if not l4_protocol == "tcp": + st.log("l4 protocol should be tcp") + return False + command += " tcp_flag".format(tcp_flag) + if dscp_value: + command += " dscp {}".format(dscp_value) + commands.append(command) + commands.append('exit') + st.config(dut, commands, type=cli_type, skip_error_check=skip_verify) + return True + + +def show_acl_table(dut, acl_table=""): + """ + Get the ACL table + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + + :param dut: + :param acl_table: + :return: + """ + st.log("Showing ACL table data ...") + command = "show acl table" + if acl_table: + command += " {}".format(acl_table) + acl_table_data = st.show(dut, command) + if acl_table_data: + bindings = dict() + data = list() + for table_data in acl_table_data: + if table_data["name"] not in bindings.keys(): + bindings[table_data["name"]] = {"binding": "", "description": "", "type": ""} + bindings[table_data["name"]]["binding"] += table_data["binding"] + " " + bindings[table_data["name"]]["description"] = table_data["description"] + bindings[table_data["name"]]["type"] = table_data["type"] + data.append(bindings) + return data + else: + st.log("ACL table data not found, hence returning empty data..") + return acl_table_data + + +def show_acl_rule(dut, acl_table=None, acl_rule=None): + """ + Get the ACL rule + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :param acl_table: + :param acl_rule: + :return: + """ + st.log("Showing ACL rule table data ..") + command = "show acl rule" + if acl_table: + command += " {}".format(acl_table) + acl_rule_data = st.show(dut, command) + if not acl_rule_data: + st.log("ACL table data not found, hence returning empty data..") + return acl_rule_data + + final_data = {} + for each in acl_rule_data: + key = "{}|{}".format(each['table'], each['rule']) + if key not in final_data.keys(): + final_data[key] = {'priority': each['priority'], 'match': [each['match']], 'action': each['action'], + 'table': each['table'], 'rule': each['rule']} + else: + final_data[key]['match'].append(each['match']) + if not acl_rule: + return final_data + else: + return final_data["{}|{}".format(acl_table, acl_rule)] if "{}|{}".format(acl_table, + acl_rule) in final_data else {} + + +def get_acl_rule_count(dut): + """ + Get the ACL table vs rule count + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :return: + """ + output = show_acl_rule(dut) + count = {} + for each in output: + table = each.split("|")[0] + if table not in count: + count[table] = 1 + else: + count[table] += 1 + return count + + +def verify_acl_table(dut, acl_table): + """ + Verify the ACL table + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + + :param dut: + :param acl_table: + :return: + """ + st.log("Verifying ACL table {} data ...".format(acl_table)) + if not show_acl_table(dut, acl_table): + st.error("ACL table {} not found ....".format(acl_table)) + return False + return True + + +def verify_acl_table_rule(dut, acl_table, acl_rule): + """ + Get the ACL table and rule + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + + :param dut: + :param acl_table: + :param acl_rule: + :return: + """ + st.log("Verifying ACL table {} and ACL rule {} data ...".format(acl_table, acl_rule)) + if not show_acl_rule(dut, acl_table, acl_rule): + st.error("ACL table {} and ACL rule {} not found ....".format(acl_table, acl_rule)) + return False + return True + + +def show_acl_counters(dut, acl_table=None, acl_rule=None): + """ + Get the ACL table + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + + :param dut: + :param acl_table: + :param acl_rule: + :return: + """ + command = "aclshow" + if acl_table: + command += " -t {}".format(acl_table) + if acl_rule: + command += " -r {}".format(acl_rule) + return st.show(dut, command) + + +def delete_acl_rule_via_acl_loader(dut, acl_table, acl_rule): + """ + Delete the ACL rule + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + + :param dut: + :param acl_table: + :param acl_rule: + :return: + """ + command = "acl-loader delete {} {}".format(acl_table, acl_rule) + st.config(dut, command) + + +def clear_acl_counter(dut, acl_table=None, acl_rule=None): + """ + Clear ACl counters + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + + :param dut: + :param acl_table: + :param acl_rule: + :return: + """ + command = "aclshow -c" + if acl_table: + command += " -t {}".format(acl_table) + if acl_rule: + command += " -r {}".format(acl_rule) + st.config(dut, command) + return True + + +def config_acl_loader_update(dut, type_name, json_data, config_type="acl_loader"): + """ + Config ACL loader update and add. + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + + :param dut: + :param type_name: + :param json_data: + :param config_type: acl_loader and config_update + :return: + """ + try: + if type_name not in ["full", "incremental", "add"]: + st.log("Invalid type - {}".format(type_name)) + return False + temp_file_path = tempfile.gettempdir() + current_datetime = util_obj.get_current_datetime() + file_name = "sonic_{}.json".format(current_datetime) + file_path = "{}/{}".format(temp_file_path, file_name) + file_path = util_obj.write_to_json_file(json_data, file_path) + st.upload_file_to_dut(dut, file_path, file_name) + if config_type == "acl_loader" and type_name != 'add': + command = "acl-loader update {} {}".format(type_name, file_path) + elif config_type == "acl_loader" and type_name == 'add': + command = "acl-loader {} {}".format(type_name, file_path) + else: + if type_name == "add": + command = "config acl {} {}".format(type_name, file_path) + else: + command = "config acl update {} {}".format(type_name, file_path) + st.config(dut, command) + os.remove(file_path) + return True + except ValueError as e: + st.log(e) + return False + + +def delete_acl_table(dut, acl_table_name=None, cli_type="click"): + """ + API to delete the ACL table from DUT + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param acl_table_name: table name can be a string or list + :return: + """ + st.log("Deleting ACL table ...") + if cli_type == "click": + command = "config acl table delete" + if st.is_community_build(): + command = "config acl remove table" + if acl_table_name: + table_name = list([str(e) for e in acl_table_name]) if isinstance(acl_table_name, list) \ + else [acl_table_name] + for acl_table in table_name: + command = "{} {}".format(command, acl_table) + st.config(dut, command) + else: + st.config(dut, command) + else: + if not acl_table_name: + st.report_fail("acl_table_name_missing") + command = "no ip access-list {}".format(acl_table_name) + output = st.config(dut, command, type=cli_type, skip_error_check=True) + if "Entry not found" in output: + st.log("acl_table_not_found") + + +def delete_acl_rule(dut, acl_table_name=None, acl_rule_name=None, cli_type="click"): + """ + API to delete ACL rule of an ACL table + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param acl_table_name: + :param acl_rule_name: Rule can be a string or list + :return: + """ + st.log("Deleting ACL rule ...") + if acl_table_name: + if cli_type == "click": + command = "config acl rule delete {}".format(acl_table_name) + if acl_rule_name: + rule_name = list([str(e) for e in acl_rule_name]) if isinstance(acl_rule_name, list) else [acl_rule_name] + for acl_rule in rule_name: + command = "config acl rule delete {} {}".format(acl_table_name, acl_rule) + st.config(dut, command) + else: + st.config(dut, command) + else: + if acl_rule_name: + commands = list() + commands.append("ip access-list {}".format(acl_table_name)) + rule_seq = int(re.findall(r'\d+', acl_rule_name)[0]) + commands.append("no seq {}".format(rule_seq)) + commands.append("exit") + else: + commands = "no ip access-list {}".format(acl_table_name) + output = st.config(dut, commands, type=cli_type, skip_error_check=True) + if "Entry not found" in output: + st.report_fail("acl_rule_table_not_found") + else: + st.report_fail("acl_table_name_missing") + + +def clear_acl_config(dut, acl_table_name=None): + """ + API to clear ACL configuration from DUT + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param acl_table_name: + :return: + """ + if acl_table_name: + delete_acl_rule(dut, acl_table_name) + delete_acl_table(dut) + + +def verify_acl_stats(dut, table_name, rule_name, packet_count=None, bindpoint=None): + """ + API to verify acl stats + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param table_name: + :param rule_name: + :param packet_count: + :param bindpoint: + :return: + """ + result = True + acl_stats = show_acl_counters(dut, table_name, rule_name) + if packet_count: + match = {"rulename": rule_name, "tablename": table_name, "packetscnt": packet_count} + if not filter_and_select(acl_stats, ["packetscnt"], match): + result = False + if bindpoint: + match = {"rulename": rule_name, "tablename": table_name, "bindpoint": bindpoint} + if not filter_and_select(acl_stats, ["bindpoint"], match): + result = False + return result + + +def poll_for_acl_counters(dut, acl_table=None, acl_rule=None, itr=5, delay=2): + """ + Author:kesava-swamy.karedla@broadcom.com + :param dut: + :param acl_table: + :param acl_rule: + :param itr: + :param delay: + :return: + """ + i = 1 + while True: + result = show_acl_counters(dut, acl_table, acl_rule) + if result: + return result + if i >= itr: + return None + st.wait(delay) + i += 1 + + +def config_hw_acl_mode(dut, **kwargs): + """ + config hardware access-list modes + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param :dut: + :param :counter: + :param :lookup: + :return: + """ + command = "config hardware access-list" + if kwargs.get('counter'): + command += " -c {}".format(kwargs['counter']) + elif kwargs.get('loockup'): + command += " -l {}".format(kwargs['loockup']) + st.config(dut, command) + + +def verify_hw_acl_mode(dut, **kwargs): + """ + verify config hardware access-list modes + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param kwargs: lookup_configured, lookup_active, counter_configured, counter_active + :return: + """ + output = st.show(dut, 'show hardware access-list') + result = True + for each in kwargs: + entries = filter_and_select(output, None, {each: kwargs[each]}) + if not entries: + st.log("{} and {} is not match ".format(each, kwargs[each])) + result = False + return result + +def config_access_group(dut, **kwargs): + """ + API to map interface to Access-list + Author : Pradeep Bathula (pradeep.b@broadcom.com) + :param dut: + :type dut: + :param cli_type: + :type cli_type:klish + :param table_name: + :type table_name: + :param port: + :type interface: + :param access_group_action: + :type access_group_action:in|out + :return: + """ + cli_type = kwargs.get("cli_type", "klish") + table_name = kwargs.get("table_name", None) + port = kwargs.get("port", None) + access_group_action = kwargs.get("access_group_action", None) + skip_error_check = kwargs.get("skip_error_check", True) + config = kwargs.get("config", "yes") + mode = "" if config == "yes" else "no " + + + st.log("Assigning Access-group action on interface") + if not cli_type == "klish": + st.log("Unsupported CLI type {} provided, required klish".format(cli_type)) + return False + if not (table_name and port and access_group_action and config ): + st.log("Mandatory parameters like table_name and/or port and/or access_group_action and/or config not passed") + return False + interface_details = util_obj.get_interface_number_from_name(port) + commands = list() + commands.append("interface {} {}".format(interface_details.get("type"), interface_details.get("number"))) + commands.append("{}ip access-group {} {}".format(mode,table_name,access_group_action)) + commands.append("exit") + st.config(dut, commands, type=cli_type, skip_error_check=skip_error_check) + return True + + diff --git a/spytest/apis/qos/cos.py b/spytest/apis/qos/cos.py new file mode 100644 index 00000000000..23e242bc305 --- /dev/null +++ b/spytest/apis/qos/cos.py @@ -0,0 +1,64 @@ +import json +import sys +from spytest import st + +def config_port_qos_map(dut,obj_name,interface): + st.log(sys._getframe( ).f_code.co_name.replace('_',' ')) + + final_data = dict() + temp_data = dict() + if not obj_name or not interface: + st.log("Please provide obj_name like 'AZURE' and interface like 'Ethernet0,Ethernet1'") + return False + else: + cos_specific_dict = {"tc_to_queue_map": "[TC_TO_QUEUE_MAP|" + obj_name + "]", "dscp_to_tc_map": "[DSCP_TO_TC_MAP|" + obj_name + "]" } + temp_data[interface] = cos_specific_dict + final_data['PORT_QOS_MAP'] = temp_data + final_data = json.dumps(final_data) + st.apply_json(dut, final_data) + return True + +def config_tc_to_queue_map(dut,obj_name,tc_to_queue_map_dict): + st.log(sys._getframe( ).f_code.co_name.replace('_',' ')) + + final_data = dict() + temp_data = dict() + if not tc_to_queue_map_dict or not obj_name: + st.log("Please provide traffic class to queue map dict. For example - {'0':'0', '1':'1', ...} and Object name like 'AZURE'") + return False + else: + temp_data[obj_name] = tc_to_queue_map_dict + final_data['TC_TO_QUEUE_MAP'] = temp_data + final_data = json.dumps(final_data) + st.apply_json(dut, final_data) + return True + +def config_dscp_to_tc_map(dut,obj_name,dscp_to_tc_map_dict): + st.log(sys._getframe( ).f_code.co_name.replace('_',' ')) + + final_data = dict() + temp_data = dict() + if not dscp_to_tc_map_dict or not obj_name: + st.log("Please provide dscp value to traffic priority value map dict. For example - {'0':'0', '1':'1', ...} and Object name like 'AZURE'") + return False + else: + temp_data[obj_name] = dscp_to_tc_map_dict + final_data['DSCP_TO_TC_MAP'] = temp_data + final_data = json.dumps(final_data) + st.apply_json(dut, final_data) + return True + +def config_tc_to_pg_map(dut,obj_name,tc_to_pg_map_dict): + st.log(sys._getframe( ).f_code.co_name.replace('_',' ')) + + final_data = dict() + temp_data = dict() + if not tc_to_pg_map_dict or not obj_name: + st.log("Please provide traffic class to priority group map dict. For example - {'0':'0', '1':'1', ...} and Object name like 'AZURE'") + return False + else: + temp_data[obj_name] = tc_to_pg_map_dict + final_data['TC_TO_PRIORITY_GROUP_MAP'] = temp_data + final_data = json.dumps(final_data) + st.apply_json(dut, final_data) + return True diff --git a/spytest/apis/qos/qos.py b/spytest/apis/qos/qos.py new file mode 100644 index 00000000000..c4158600f29 --- /dev/null +++ b/spytest/apis/qos/qos.py @@ -0,0 +1,125 @@ +import re +from spytest.utils import filter_and_select +from spytest import st +import json + +def verify_qos_queue_counters(dut,port,queue_name,param_list,val_list,tol_list): + ''' + verifies QOS queue counters in the CLI show qos queue counters + :param dut: Device name where the command to be executed + :type dut: string + :param port: interface name to be checked + :type dut: string + :param queue_name: queue name to be checked + :type dut: string + :param param_list: list of params to be verified; example ['pkts_count', 'pkts_drop'] + :param val_list: list of expected values for the params specified; example ['10000','5000'] + :param tol_list: tolerence value for each param while comparing; for example ['1000', '500'] + :return: True/False True - success case; False - Failure case + + usage: verify_qos_queue_counters(dut1,'Ethernet0','UC0',['pkts_count', 'pkts_drop'], + ['10000','5000'],['1000', '500']) + verify_qos_queue_counters(dut1,'Ethernet0','UC0',['pkts_count'],['10000'],['1000']) + + Created by: Julius =int(val)-int(tol): + st.log('obtained value: {} is in the range b/w {} and {} as expected for param: {}' + 'in queue: {}'.format(int(fil_out[param]),int(val)-int(tol), + int(val)+int(tol),param,queue_name)) + else: + st.error('obtained value: {} is NOT in the range b/w {} and {} for param: {}' + 'in queue: {}'.format(int(fil_out[param]), int(val) - int(tol), + int(val) + int(tol), param, queue_name)) + success = False + return True if success else False + +def clear_qos_queue_counters(dut): + ''' + :param dut: DUT name where CLI to be executed + :type dut: string + :return: True/False True - Success ; False - Failure + usage: + clear_qos_queue_counters(dut1) + + Created by: Julius iterations: + st.log("Reached max iteration count, Exiting ...") + return False + i += 1 + st.wait(delay) + else: + st.log("Required values not found ....") + return False + + +def show_ip_bgp_route(dut, family='ipv4'): + """ + API for show ip bgp + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :return: + """ + command = "show bgp {}".format(family) + return st.show(dut, command, type='vtysh') + +def fetch_ip_bgp_route(dut, family='ipv4', match=None, select=None): + entries = dict() + output = show_ip_bgp_route(dut, family=family) + #match = {'network': network} + entries = filter_and_select(output, select, match) + return entries + +def get_ip_bgp_route(dut, family='ipv4', **kwargs): + output = show_ip_bgp_route(dut, family=family) + st.debug(output) + for each in kwargs.keys(): + match = {each: kwargs[each]} + get_list = ["network", "as_path"] + entries = filter_and_select(output, get_list, match) + if not entries: + st.log("Could not get bgp route info") + return False + return entries + +def verify_ip_bgp_route(dut, family='ipv4', **kwargs): + """ + + EX; verify_ip_bgp_route(vars.D1, network= '11.2.1.2/24') + """ + output = show_ip_bgp_route(dut, family=family) + for each in kwargs.keys(): + match = {each: kwargs[each]} + entries = filter_and_select(output, None, match) + if not entries: + st.log("{} and {} is not match ".format(each, kwargs[each])) + return False + return True + + +def verify_ip_bgp_route_network_list(dut, family='ipv4', nw_list=[]): + + output = show_ip_bgp_route(dut, family=family) + for network in nw_list: + match = {'network': network} + entries = filter_and_select(output, None, match) + if not entries: + st.log("BGP Network {} is not matching ".format(network)) + return False + return True + + +def check_bgp_config_in_startupconfig(dut, config_list): + """ + API to check the configuration in startup config + :param dut: + :param config_list: list of configuration commands to check in statup config + :return: + """ + cmd = "show startupconfiguration bgp" + output = st.show(dut, cmd, skip_error_check=True) + output_list = output.splitlines() + for config in config_list: + if config not in output_list: + return False + return True + +def show_bgp_ipvx_prefix(dut, prefix, masklen, family='ipv4'): + """ + API for show bgp ipv4 prefix + + :param dut: + :param prefix: (ip address) + :param masklen: length of mask (e.g. 24) + :param family: ipv4/ipv6 + EX: show_bgp_ipvx_prefix(dut1, prefix="40.1.1.1", masklen=32, family='ipv4') + :return: + """ + + entries = dict() + command = "show bgp {} {}/{}".format(family, prefix, masklen) + entries = st.show(dut, command, type='vtysh') + st.log(entries) + return entries + + +def show_bgp_ip_prefix(dut, ip_prefix, family='ipv4'): + """ + API for show bgp ipv4 prefix + + :param dut: + :param prefix: ip address with or without subnet / + :param family: ipv4/ipv6 + EX: show_bgp_ipvx_prefix(dut1, prefix="40.1.1.1/32", family='ipv4') + :return: + """ + + if family != 'ipv4' and family != 'ipv6' : + return {} + + command = "show bgp {} {}".format(family, ip_prefix) + entries = st.show(dut, command, type='vtysh') + return entries + +def activate_bgp_neighbor(dut, local_asn, neighbor_ip, family="ipv4", config='yes',vrf='default', **kwargs): + """ + + :param dut: + :param local_asn: + :param neighbor_ip: + :param family: + :param config: + :param vrf: + :return: + """ + + st.log("Activate BGP neigbor") + cli_type = kwargs.get('cli_type', st.get_ui_type()) + skip_error_check = kwargs.get('skip_error_check', True) + remote_asn = kwargs.get('remote_asn', '') + if config.lower() == 'yes': + mode = "" + else: + mode = 'no' + if family !='ipv4' and family != 'ipv6': + return False + cmd = '' + if cli_type == 'vtysh' or cli_type == 'click': + if vrf != 'default': + cmd = cmd + 'router bgp {} vrf {}\n'.format(local_asn, vrf) + else: + cmd = cmd + 'router bgp {}\n'.format(local_asn) + cmd = cmd + 'address-family {} unicast\n'.format(family) + cmd = cmd + '{} neighbor {} activate\n'.format(mode, neighbor_ip) + cmd = cmd + '\n end' + st.config(dut, cmd, type='vtysh', skip_error_check=skip_error_check) + return True + elif cli_type == "klish": + if vrf != 'default': + cmd = cmd + 'router bgp {} vrf {}\n'.format(local_asn, vrf) + else: + cmd = cmd + 'router bgp {}\n'.format(local_asn) + cmd = cmd + 'neighbor {}\n'.format(neighbor_ip) + cmd = cmd + 'remote-as {}\n'.format(remote_asn) + cmd = cmd + 'address-family {} unicast\n'.format(family) + cmd = cmd + 'activate\n' + cmd = cmd + 'exit\nexit\nexit\n' + st.config(dut, cmd, type=cli_type, skip_error_check=skip_error_check, conf = True) + return True + else: + st.log("Unsupported CLI TYPE - {}".format(cli_type)) + return False + +def bgp_debug_config(dut, **kwargs): + """ + API to enable BGP zebra logs + :param dut: + :param prefix: (ip address) + :param message: eg update + """ + + command = "debug bgp zebra\n" + if "prefix" in kwargs: + command += "debug bgp zebra prefix {}\n".format(kwargs["prefix"]) + if "message" in kwargs: + if kwargs["message"] == "updates": + command += "debug bgp updates\n" + command += "debug bgp update-groups\n" + command += "log stdout\n" + st.config(dut, command, type='vtysh') + +class ASPathAccessList: + """ + Usage: + aspath_access_list = ASPathAccessList("testaspath") + aspath_access_list.add_match_permit_sequence(['_65001', '65002', '65003']) + aspath_access_list.add_match_deny_sequence(['_1^', '_2$', '_3*']) + aspath_access_list.add_match_permit_sequence(['_65100^']) + aspath_access_list.execute_command(dut, config='yes') + cmd_str = aspath_access_list.config_command_string() + aspath_access_list.execute_command(dut, config='no') + """ + + def __init__(self, name): + self.name = name + self.match_sequence = [] + self.cmdkeyword = 'bgp as-path access-list' + + def add_match_permit_sequence(self, as_path_regex_list): + self.match_sequence.append(('permit', as_path_regex_list)) + + def add_match_deny_sequence(self, as_path_regex_list): + self.match_sequence.append(('deny', as_path_regex_list)) + + def config_command_string(self): + command = '' + for v in self.match_sequence: + command += '{} {} {}'.format(self.cmdkeyword, self.name, v[0]) + for as_path_regex in list(v[1]): + command += ' {}'.format(as_path_regex) + command += '\n' + return command + + def unconfig_command_string(self): + command = 'no {} {}\n'.format(self.cmdkeyword, self.name) + return command + + def execute_command(self, dut, config='yes'): + if config == 'no': + command = self.unconfig_command_string() + else: + command = self.config_command_string() + st.config(dut, command, type='vtysh') + + diff --git a/spytest/apis/routing/ip.py b/spytest/apis/routing/ip.py new file mode 100644 index 00000000000..20ab00848e7 --- /dev/null +++ b/spytest/apis/routing/ip.py @@ -0,0 +1,1574 @@ +# This file contains the list of API's which performs IP,Ping related operations. +# Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + +import re +from spytest import st +import socket +import ipaddress +import subprocess +from utilities.utils import get_interface_number_from_name +import utilities.common as utils + +def is_valid_ipv4_address(address): + """ + Validate ipv4 address. + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + + :param address: + :return: + """ + try: + socket.inet_pton(socket.AF_INET, address) + except AttributeError: # no inet_pton here, sorry + try: + socket.inet_aton(address) + except socket.error: + return False + return address.count('.') == 3 + except socket.error: # not a valid address + return False + return True + + +def is_valid_ipv6_address(address): + """ + Validate ipv6 address. + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + + :param address: + :return: + """ + try: + socket.inet_pton(socket.AF_INET6, address) + except socket.error: # not a valid address + return False + return True + + +def is_valid_ip_address(address, family, subnet=None): + """ + Validate ip address. + Author: Naveena Suvarna (naveen.suvarna@broadcom.com) + + :param address: + :param family + :param subnet + :return: + """ + + if not address or not family: + st.error("Parameter Family or address is Null") + return False + + if family == "ipv4": + if not is_valid_ipv4_address(address): + st.error("Invalid IPv4 address {} ".format(address)) + return False + if subnet: + subnet = int(subnet) + if subnet < 1 or subnet > 32: + st.error("Invalid IPv4 subnet {}".format(subnet)) + return False + elif family == "ipv6": + if not is_valid_ipv6_address(address): + st.error("Invalid IPv6 address {} ".format(address)) + return False + if subnet: + subnet = int(subnet) + if subnet < 1 or subnet > 128: + st.error("Invalid IPv6 subnet {}".format(subnet)) + return False + else: + st.error("Invalid address family {} ".format(family)) + return False + + return True + + +def config_ipv6(dut, action='disable'): + """ + To globally disable or enabled Ipv6 + :param dut: + :param action: Can be 'disable' or 'enable'. + :return: + """ + if not st.is_community_build(): + command = "config ipv6 {}".format(action) + st.config(dut, command) + else: + st.config(dut, "sysctl -w net.ipv6.conf.all.disable_ipv6=1") + st.config(dut, "sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1") + st.config(dut, "sysctl -w net.ipv6.conf.lo.disable_ipv6=1") + +def show_ipv6(dut): + """ + Display global Ipv6 state + :param dut: + :return: + """ + command = "show ipv6 brief" + st.show(dut, command, skip_tmpl=True) + + +def ping(dut, addresses, family='ipv4', **kwargs): + """ + To Perform ping to ipv4 or ipv6 address. + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param :dut: + :param :addresses: + :param :family: ipv4|ipv6 + :param :count: 3(default) + :param :timeout: + :param :interface: + :param :packetsize: + :param :external: True | False (Default: False) # Used True for Ping from external server. (Ex: VDI) + :return: + """ + ping_pattern = r'(\d+)\s+packets\s+transmitted,\s+(\d+)\s+received,(.*)\s+(\d+)%\s+packet\s+loss,\s+time\s+(\d+)ms' + external = kwargs.get("external", False) + + if 'count' not in kwargs: + kwargs['count'] = 3 + + command = "ping -4 {} -c {} ".format(addresses, kwargs['count']) + if external: + command = "ping {} -c {} ".format(addresses, kwargs['count']) + + if family.lower() == "ipv6": + command = "ping -6 {} -c {} ".format(addresses, kwargs['count']) + if external: + command = "ping6 {} -c {} ".format(addresses, kwargs['count']) + + if 'timeout' in kwargs: + timeout = utils.integer_parse(kwargs['timeout']) + else: + timeout = None + + if st.is_vsonic(dut): + if not timeout or timeout < 7: + timeout = 7 + + if timeout: + command = command + "-W {} ".format(timeout) + + if 'interface' in kwargs: + command = command + "-I {} ".format(kwargs['interface']) + if 'packetsize' in kwargs: + command = command + "-s {} ".format(kwargs['packetsize']) + + if external: + st.log(command) + p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, + universal_newlines=True) + rv, err = p.communicate() + st.log(rv) + st.log(err) + else: + rv = st.config(dut, command) + out = re.findall(ping_pattern, rv) + if not out: + st.error("Failed to get the ping output.") + return False + if '0' < out[0][3] <= '100': + st.error("Ping failed with packet loss.") + return False + return True + +def config_ip_addr_interface(dut, interface_name='', ip_address='', subnet='', family="ipv4", config='add', skip_error = False, **kwargs): + """ + Config ip address to interface + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + + :param dut: + :param interface_name: + :param ip_address: + :param subnet: + :param family: ipv4|ipv6 + :param config: add | remove + :return: + """ + cli_type = kwargs.pop('cli_type', st.get_ui_type(dut)) + if cli_type == 'click': + if config == 'add': + try: + if not interface_name: + st.error("Please provide interface name..") + return False + if not ip_address: + st.error("Please provide ip|ipv6 address..") + return False + + if family == "ipv4": + if not is_valid_ipv4_address(ip_address): + st.error("Invalid IP address.") + return False + elif family == "ipv6": + if not is_valid_ipv6_address(ip_address): + st.error("Invalid IPv6 address.") + return False + command = "config interface ip add {} {}/{}".format(interface_name, ip_address, subnet) + st.config(dut, command, skip_error_check=skip_error) + except Exception as e: + st.log(e) + return False + return True + elif config == 'remove': + return delete_ip_interface(dut, interface_name, ip_address, subnet, family, cli_type=cli_type) + else: + st.error("Invalid config used - {}.".format(config)) + return False + elif cli_type == 'klish': + if config == 'add': + if interface_name =='eth0': + command = "interface Management 0" + command = command + "\n" + "ip address {}/{}".format(ip_address, subnet) + else: + regex = re.compile(r'(\d+|\s+)') + intf = regex.split(interface_name) + command = "interface {} {}".format(intf[0], intf[1]) + fam = "ip" if family=='ipv4' else 'ipv6' + command = command + "\n" + "{} address {}/{}".format(fam, ip_address, subnet) + command = command + "\n" + "exit" + output = st.config(dut, command, skip_error_check=skip_error, type="klish", conf=True) + if "Could not connect to Management REST Server" in output: + st.error("klish mode not working.") + return False + return True + elif config == 'remove': + return delete_ip_interface(dut, interface_name, ip_address, subnet, family, cli_type=cli_type) + else: + st.error("Invalid cli_type for this API - {}.".format(cli_type)) + return False + +def delete_ip_interface(dut, interface_name, ip_address, subnet="32", family="ipv4", skip_error=False, **kwargs): + """ + Deleting ip address to interface + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + + :param dut: + :param interface_name: + :param ip_address: + :param subnet: + :param skip_error: + :param family: ipv4|ipv6 + :return: + """ + if family == "ipv4": + if not is_valid_ipv4_address(ip_address): + st.error("Invalid IP address.") + return False + elif family == "ipv6": + if not is_valid_ipv6_address(ip_address): + st.error("Invalid IPv6 address.") + return False + cli_type = kwargs.pop('cli_type', st.get_ui_type(dut)) + if cli_type == 'click': + command = "config interface ip remove {} {}/{}".format(interface_name, ip_address, subnet) + st.config(dut, command, skip_error_check=skip_error) + return True + elif cli_type == 'klish': + regex = re.compile(r'(\d+|\s+)') + intf = regex.split(interface_name) + command = "interface {} {}".format(intf[0], intf[1]) + fam = "ip" if family=='ipv4' else 'ipv6' + # Subnet not required while removing IP/IPv6 address. + command = command + "\n" + "no {} address {}".format(fam, ip_address) + command = command + "\n" + "exit" + output = st.config(dut, command, skip_error_check=skip_error, type="klish", conf=True) + if "Could not connect to Management REST Server" in output: + st.error("klish mode not working.") + return False + return True + +def get_interface_ip_address(dut, interface_name=None, family="ipv4"): + """ + To Get ip address on interface + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + + :param dut: + :param interface_name: + :param family: ipv4 | ipv6 + :return: + """ + command = "show ip interface" + if family == "ipv6": + command = "show ipv6 interface" + output = st.show(dut, command) + result = output if family == "ipv4" else prepare_show_ipv6_interface_output(output) + if interface_name: + match = {"interface": interface_name} + output = utils.filter_and_select(result, None, match) + return output + + +def verify_interface_ip_address(dut, interface_name, ip_address, family="ipv4", vrfname='', flags = ''): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param interface_name: + :param ip_address: + :param family: + :param vrfname: + :param flags: + :return: + """ + command = "show ip interface" + if family == "ipv6": + command = "show ipv6 interface" + output = st.show(dut, command) + result = output if family == "ipv4" else prepare_show_ipv6_interface_output(output) + match = {"interface": interface_name, "vrf": vrfname, "ipaddr": ip_address, "flags": flags} + entries = utils.filter_and_select(result, ["interface"], match) + return True if entries else False + + +def create_static_route(dut, next_hop=None, static_ip=None, shell="vtysh", family='ipv4', interface = None, vrf = None): + """ + To create static route + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :param next_hop: + :param static_ip: + :param shell: sonic|vtysh + :param family: ipv4|ipv6 + :return: + """ + if not static_ip: + st.log("Provide static_ip") + return False + if shell == "vtysh": + if family.lower() == "ipv4" or family.lower() == "": + if next_hop: + command = "ip route {} {}".format(static_ip, next_hop) + else: + command = "ip route {}".format(static_ip) + elif family.lower() == "ipv6": + command = "ipv6 route {} {}".format(static_ip, next_hop) + if interface: + command +=" {}".format(interface) + if vrf: + command +=" vrf {}".format(vrf) + st.config(dut, command, type='vtysh') + else: + if family.lower() == "ipv4" or family.lower() == "": + if next_hop: + command = "ip route add {} via {}".format(static_ip, next_hop) + else: + command = "ip route add {}".format(static_ip) + elif family.lower() == "ipv6": + if next_hop: + command = "ip -6 route add {} via {}".format(static_ip, next_hop) + else: + command = "ip -6 route add {}".format(static_ip) + if interface: + command +=" dev {}".format(interface) + st.config(dut, command) + + +def delete_static_route(dut, next_hop, static_ip, family='ipv4', shell="vtysh", interface = None, vrf = None): + """ + To delete static route + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :param next_hop: + :param static_ip: + :param family: ipv4|ipv6 + :param shell: sonic|vtysh + :return: + """ + + if shell == "vtysh": + if family.lower() == "ipv4" or family.lower() == "": + if next_hop == None: + command = "no ip route {}".format(static_ip) + else: + command = "no ip route {} {}".format(static_ip, next_hop) + elif family.lower() == "ipv6": + command = "no ipv6 route {} {}".format(static_ip, next_hop) + if interface: + command +=" {}".format(interface) + if vrf: + command +=" vrf {}".format(vrf) + st.config(dut, command, type='vtysh') + else: + if family.lower() == "ipv4" or family.lower() == "": + if next_hop != None: + command = "ip route del {} via {}".format(static_ip, next_hop) + else: + command = "ip route del {}".format(static_ip) + elif family.lower() == "ipv6": + if next_hop != None: + command = "ip -6 route del {} via {}".format(static_ip, next_hop) + else: + command = "ip -6 route del {}".format(static_ip) + if interface: + command +=" dev {}".format(interface) + st.config(dut, command) + + +def show_ip_route(dut, family="ipv4", shell="sonic", vrf_name=None): + """ + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + show_ip_route(dut1) + show_ip_route(dut1,shell="vtysh") + show_ip_route(dut1,vrf_name='Vrf-101') + To get static route + + :param dut: + :param family: ipv4|ipv6 + :param shell: sonic|vtysh + :param vrf_name + :type vrf_name + :return: + """ + if vrf_name: + cmd = "show ip route vrf " + vrf_name + else: + cmd = "show ip route" + + if family == "ipv6": + if vrf_name: + cmd = "show ipv6 route vrf " + vrf_name + else: + cmd = "show ipv6 route" + + if shell == "vtysh": + output = st.show(dut, cmd, type='vtysh') + else: + output = st.show(dut, cmd) + return output + + +def verify_ip_route(dut, family="ipv4", shell="sonic", vrf_name=None, **kwargs): + """ + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + verify_ip_route(dut1,family='ipv6',vrf_name='Vrf-101') + verify_ip_route(dut1,family='ipv6',shell='vtysh',vrf_name='Vrf-101') + verify_ip_route(dut1,family='ipv6') + verify_ip_route(dut1,vrf_name='Vrf-101',type='B',nexthop='1.0.1.2',interface='Vlan1') + verify_ip_route(dut1,vrf_name='Vrf-101') + To verify static route + :param :dut: + :param :family: ipv4|ipv6 + :param :shell: sonic|vtysh + :param :type: + :param :selected: + :param :fib: + :param :ip_address: + :param :interface: + :param :duration: + :param :nexthop: + :param :distance: + :param :cost: + :param :vrf_name + :type :vrf_name + :return: + """ + + cli_type = kwargs.pop('cli_type', st.get_ui_type(dut)) + cli_type = "vtysh" if cli_type == 'click' else "klish" + + if family == "ipv6": + if vrf_name: + cmd = "show ipv6 route vrf " + vrf_name + else: + cmd = "show ipv6 route" + else: + if vrf_name: + cmd = "show ip route vrf " + vrf_name + else: + cmd = "show ip route" + + result = st.show(dut, cmd, type=cli_type) + if cli_type == "klish" and "interface" in kwargs: + del kwargs['interface'] + + ret_val = False + for rlist in result: + count = 0 + for key in kwargs: + if rlist[key] == kwargs[key]: + count = count + 1 + if len(kwargs) == count: + ret_val = True + for key in kwargs: + st.log("Match: Match key {} found => {} : {}".format(key, kwargs[key], rlist[key])) + break + else: + for key in kwargs: + if rlist[key] == kwargs[key]: + st.log("Match: Match key {} found => {} : {}".format(key, kwargs[key], rlist[key])) + else: + st.log("No-Match: Match key {} NOT found => {} : {}".format(key, kwargs[key], rlist[key])) + st.log("\n") + + if not ret_val: + st.log("Fail: Not Matched all args in passed dict {} from parsed dict".format(kwargs)) + return ret_val + + +def fetch_ip_route(dut, family="ipv4", shell="sonic", vrf_name=None, match=None, select=None): + """ + + :param dut: + :param family: + :param shell: + :param vrf_name: + :param match: + :param select: + :return: + """ + if family == "ipv4": + result = show_ip_route(dut, family, shell, vrf_name) + else: + result = show_ip_route(dut, family, shell, vrf_name) + entries = utils.filter_and_select(result, select, match) + return entries + + +def increment_ip_addr(ipaddr, increment_type,family = "ipv4"): + """ + Author: Ramprakash Reddy + :param ipaddr: + :type ipaddr: + :param increment_type: + :type increment_type: + :param family: + :type family: + :return: + :rtype: + """ + # remove the netmask portion as it doesnt render well when passed to ipaddress + if family == "ipv4": + ip_split = re.split(r'(.*)\.(.*)\.(.*)\.(.*)/(.*)', ipaddr) + netmask = ip_split[5] + ip_split = "{}.{}.{}.{}".format(ip_split[1], ip_split[2], ip_split[3], ip_split[4]) + else: + ip_split = ipaddr.split('/')[0] + netmask = int(ipaddr.split('/')[1]) + if not utils.is_unicode(ip_split): + ip_split = ip_split.decode('utf-8', 'ignore') + temp_ip = ipaddress.ip_address(ip_split) + try: + temp_ip = ipaddress.ip_address(ip_split) + valid_option = True + except Exception as e: + st.log(e) + valid_option = False + if valid_option: + if increment_type == "host": + temp_ip += 1 + elif increment_type == "network": + if family=="ipv4": + temp_ip += 256 + else: + temp_ip += 2 ** (128 - netmask) + # repack the netmask to the IP address and return + retval = str(temp_ip) + "/{}".format(netmask) + return valid_option, retval + + +def reset_host_ip_to_start(ipaddr): + """ + + :param ipaddr: + :type ipaddr: + :return: + :rtype: + """ + # remove the netmask portion as it doesnt render well when passed to ipaddress + ip_split = re.split(r'(.*)\.(.*)\.(.*)\.(.*)/(.*)', ipaddr) + netmask = ip_split[5] + ip_split[4] = 1 + ip_split = "{}.{}.{}.{}".format(ip_split[1], ip_split[2], ip_split[3], ip_split[4]) + # repack the netmask to the IP address and return + retval = str(ip_split) + "/{}".format(netmask) + return retval + + +def _clear_ip_configuration_helper(dut_list, family="ipv4"): + """ + Find and clear ip address in DUT + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut_list: + :param family: ipv4|ipv6|all + :return: + """ + dut_li = list(dut_list) if isinstance(dut_list, list) else [dut_list] + + if family == "ipv4": + family_li = ['ipv4'] + elif family == "ipv6": + family_li = ['ipv6'] + else: + family_li = ['ipv4', 'ipv6'] + + for dut in dut_li: + for each_af in family_li: + st.log("############## {} : {} Address Cleanup ################".format(dut, each_af.upper())) + output = get_interface_ip_address(dut, family=each_af) + for each_ip in output: + if each_ip['interface'].startswith("Ethernet") or each_ip['interface'].startswith("Vlan") or \ + each_ip['interface'].startswith("PortChannel") or each_ip['interface'].startswith("Loopback"): + ip, subnet = each_ip['ipaddr'].split('/') + if not each_ip['ipaddr'].startswith('fe80::'): + delete_ip_interface(dut, each_ip['interface'], ip, subnet, family=each_af) + else: + ip_link_local, interface = ip.split('%') + delete_ip_interface(dut, each_ip['interface'], ip_link_local, subnet, family=each_af) + return True + + +def clear_ip_configuration(dut_list, family='ipv4', thread=True): + """ + Find and clear ip address in the list of DUTs + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut_list: + :param family: ipv4 (Default) / ipv6 + :param thread: True (Default) / False + :return: + """ + dut_li = list(dut_list) if isinstance(dut_list, list) else [dut_list] + [out, exceptions] = utils.exec_foreach(thread, dut_li, _clear_ip_configuration_helper, family) + st.log(exceptions) + return False if False in out else True + + +def get_loopback_interfaces(dut): + """ + To get list of loopback interfaces. + """ + output = st.show(dut, "show interfaces loopback") + st.log("### loopbacks={}".format(output)) + loopbacks = [] + for entry in output: + if entry['ifname']: + loopbacks.append(entry['ifname']) + return loopbacks + + +def _clear_loopback_config_helper(dut_list): + """ + Helper internal function to find and clear loopback interfaces. + """ + dut_li = list(dut_list) if isinstance(dut_list, list) else [dut_list] + + for dut in dut_li: + st.log("############## {} : Loopback intfs Cleanup ################".format(dut)) + output = get_loopback_interfaces(dut) + for intf in output: + configure_loopback(dut,loopback_name=intf,config="no") + return True + + +def clear_loopback_interfaces(dut_list, thread=True): + """ + Find and delete all loopback interfaces. + + :param dut_list + :return: + """ + dut_li = list(dut_list) if isinstance(dut_list, list) else [dut_list] + [out, exceptions] = utils.exec_foreach(thread, dut_li, _clear_loopback_config_helper) + st.log(exceptions) + return False if False in out else True + + +def get_network_addr(ipaddr): + """ + Get network address from an ip address + Author: Lakshminarayana D (lakshminarayana.d@broadcom.com) + :param ipaddr: + :type ipaddr: + :return: + :rtype: + """ + if not utils.is_unicode(ipaddr): + ipaddr = ipaddr.decode('utf-8', 'ignore') + try: + iface = ipaddress.ip_interface(ipaddr) + network = iface.network + valid_option = True + # netmask = iface.netmask + # net_add = iface.network.network_address + except ValueError: + network = "" + valid_option = False + return valid_option, network + + +def clear_ip_route(dut, **kwargs): + """ + Clear ip route + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param :dut: + :param :not_installed: + :param :prefix_mask: prefix/mask + :return: + """ + + command = "clear ip route" + if 'not_installed' in kwargs: + command += " {}".format('not_installed') + if 'prefix_mask' in kwargs: + command += " {}".format(kwargs['prefix_mask']) + st.config(dut, command, type='vtysh') + return True + + +def verify_ip_route_not_installed(dut, family="ipv4", **kwargs): + """ + To verify static route not installed + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param :dut: + :param :family: ipv4|ipv6 + :param :type: + :param :selected: + :param :fib: + :param :ip_address: + :param :interface: + :param :duration: + :param :nexthop: + :param :distance: + :param :cost: + :return: + """ + if family == "ipv4": + command = "show ip route not-installed" + result = st.show(dut, command, type='vtysh') + else: + command = "show ipv6 route not-installed" + result = st.show(dut, command, type='vtysh') + + for each in kwargs.keys(): + match = {each: kwargs[each]} + entries = utils.filter_and_select(result, None, match) + if not entries: + st.log("{} and {} is not match ".format(each, kwargs[each])) + return False + return True + + +def create_neighbor(dut, neigh, mac, interface, family='ipv4'): + """ + Author: Amit Kaushik (amit.kaushik@broadcom.com) + :param dut: + :param neigh: + :param mac: + :param interface: + :param family: + :return: + """ + command = '' + if family.lower() == "ipv4": + command = "ip neigh replace {} lladdr {} dev {}".format(neigh, mac, interface) + elif family.lower() == "ipv6": + command = "ip -6 neigh replace {} lladdr {} dev {}".format(neigh, mac, interface) + st.config(dut, command) + + +def delete_neighbor(dut, neigh, mac, interface, family='ipv4'): + """ + Author: Amit Kaushik (amit.kaushik@broadcom.com) + :param dut: + :param neigh: + :param mac: + :param interface: + :param family: + :return: + """ + command = '' + if family.lower() == "ipv4": + command = "ip neigh del {} lladdr {} dev {}".format(neigh, mac, interface) + elif family.lower() == "ipv6": + command = "ip -6 neigh del {} lladdr {} dev {}".format(neigh, mac, interface) + st.config(dut, command) + + +def traceroute(dut, addresses, family='ipv4', vrf_name=None, timeout=None, gateway=None, external=False): + """ + traceroute(dut1,addresses='10.75.224.184') + traceroute(dut1,addresses='10.75.224.184',vrf_name ='Vrf-RED') + :param dut: + :param addresses: + :param family: + :param vrf_name: + :param timeout: + :param gateway: + :param external: True | False (Default: False) # Used True for traceroute from external server. (Ex: VDI) + :return: + """ + trace_route1 = r'(.*)\s+\(' + addresses + r'\)\s+(\d+\.\d+)\s+ms\s+(\d+\.\d+)\s+ms\s+(\d+\.\d+)\s+ms' + trace_route2 = r'(\d+)\s+(' + addresses + r')\s+(\d+\.\d+)\s*ms\s+(\d+\.\d+)\s*ms\s+(\d+\.\d+)\s*ms' + trace_route = r"{}|{}".format(trace_route1, trace_route2) + command = "traceroute -4 {}".format(addresses) + if external: + command = "traceroute {}".format(addresses) + if family.lower() == "ipv6": + command = "traceroute -6 {}".format(addresses) + if external: + command = "traceroute6 {}".format(addresses) + if vrf_name: + command = command + " -i {} ".format(vrf_name) + if timeout: + command = command + " -w {} ".format(timeout) + if gateway: + command = command + " -g {} ".format(gateway) + + if external: + st.log(command) + p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, + universal_newlines=True) + rv, err = p.communicate() + st.log(rv) + st.log(err) + else: + rv = st.config(dut, command) + result = re.findall(trace_route, str(rv)) + if result: + st.log("Traceroute to destination address " + addresses + " Passed") + return True + else: + st.log("Traceroute to destination address " + addresses + " Failed") + return False + + +def config_linux_static_route(dut, **kwargs): + """ + Author: Priyanka Gupta + + :param :dut: + :param :next_hop: + :param :static_ip: + :param :family: ipv4|ipv6 + :return: + """ + command = '' + if kwargs["action"] == "add": + if kwargs["family"] == "ipv4": + command = "ip route add {} nexthop via {}".format(kwargs["route"], kwargs["nhp"]) + if "nhp1" in kwargs: + command += " nexthop via {}".format(kwargs["nhp1"]) + elif kwargs["family"].lower() == "ipv6": + command = "ip -6 route add {} via {}".format(kwargs["route"], kwargs["nhp"]) + + elif kwargs["action"] == "delete": + if kwargs["family"] == "ipv4": + command = "ip route delete {}".format(kwargs["route"]) + elif kwargs["family"] == "ipv6": + command = "ip -6 route delete {}".format(kwargs["route"]) + st.config(dut, command) + + +def config_route_map(dut, route_map, config='yes', **kwargs): + """ + :param :dut: + :param :route-map: name of the route map + :param :community: name of the community + :EX: config_route_map(dut, route_map='rmap1', config='yes', sequence='10', community='100:100') + config_route_map(dut, route_map='rmap1', config='no') + config_route_map(dut, route_map='rmap1', config='no', sequence='10') + :Caution: while creating the route-map (config='yes'), sequence number must be mentioned and it should be + the first parameter of the variable argument, because other arguments have newline appended. + :return: + """ + if config == 'yes': + cmd = "route-map {}".format(route_map) + if kwargs['sequence']: + cmd += " permit {}".format(kwargs['sequence']) + if 'metric' in kwargs: + cmd += "\n set metric {}".format(kwargs['metric']) + if 'community' in kwargs: + cmd += "\n set community {}".format(kwargs['community']) + cmd += "\n" + st.config(dut, cmd, type='vtysh') + else: + cmd = "no route-map {}".format(route_map) + if 'sequence' in kwargs: + cmd += " permit {}".format(kwargs['sequence']) + cmd += "\n" + st.config(dut, cmd, type='vtysh') + + +def config_route_map_global_nexthop(dut, route_map='route_map_next_hop_global', sequence='10', config='yes'): + """ + :Author: sooriya.gajendrababu@broadcom.com + :param dut: + :param route_map: + :param sequence: + :param config: + :return: + """ + if config == 'yes': + cmd = "route-map {} permit {} \n set ipv6 next-hop prefer-global".format(route_map, sequence) + st.config(dut, cmd, type='vtysh') + else: + cmd = "no route-map {} permit {}\n".format(route_map, sequence) + st.config(dut, cmd, type='vtysh') + + +def config_static_route_vrf(dut, dest, dest_subnet, next_hop, family='ipv4', vrf_name=None, config=''): + """ + Author: Manisha Joshi + :param dut: + :param dest: + :param dest_subnet: + :param next_hop: + :param family: + :param vrf_name: + :param config: + :return: + """ + my_cmd = '' + if family.lower() == "ipv4" or family.lower() == "": + my_cmd = "{} ip route {}/{} {} vrf {}".format(config, dest, dest_subnet, next_hop, vrf_name) + st.config(dut, my_cmd, type='vtysh') + elif family.lower() == "ipv6": + my_cmd = "{} ipv6 route {}/{} {} vrf {}".format(config, dest, dest_subnet, next_hop, vrf_name) + st.config(dut, my_cmd, type='vtysh') + +def create_static_route_nexthop_vrf(dut, next_hop, static_ip, shell="vtysh", family='ipv4',vrf_name="", nhopvrf="", + config="yes"): + """ + To create static route with nexthop as vrf + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :param next_hop: + :param static_ip: + :param shell: sonic|vtysh + :param family: ipv4|ipv6 + :param config: yes|no + :return: + """ + + if shell == "vtysh": + if config == "no": + command = "no " + else: + command = "" + if family.lower() == "ipv4" or family.lower() == "": + if vrf_name and nhopvrf: + command += "ip route {} {} vrf {} nexthop-vrf {}".format(static_ip, next_hop,vrf_name,nhopvrf) + st.config(dut, command, type='vtysh') + if vrf_name and nhopvrf=="": + command += "ip route {} {} vrf {}".format(static_ip, next_hop,vrf_name) + st.config(dut, command, type='vtysh') + if vrf_name == "" and nhopvrf: + command += "ip route {} {} nexthop-vrf {}".format(static_ip, next_hop,nhopvrf) + st.config(dut, command, type='vtysh') + if vrf_name =="" and nhopvrf=="": + command += "ip route {} {} ".format(static_ip, next_hop) + st.config(dut, command, type='vtysh') + + elif family.lower() == "ipv6": + if vrf_name and nhopvrf: + command += "ipv6 route {} {} vrf {} nexthop-vrf {}".format(static_ip, next_hop,vrf_name,nhopvrf) + st.config(dut, command, type='vtysh') + if vrf_name and nhopvrf=="": + command += "ipv6 route {} {} vrf {}".format(static_ip, next_hop,vrf_name) + st.config(dut, command, type='vtysh') + if vrf_name == "" and nhopvrf: + command += "ipv6 route {} {} nexthop-vrf {}".format(static_ip, next_hop,nhopvrf) + st.config(dut, command, type='vtysh') + if vrf_name == "" and nhopvrf=="": + command += "ipv6 route {} {} ".format(static_ip, next_hop) + st.config(dut, command, type='vtysh') + else: + if family.lower() == "ipv4" or family.lower() == "": + command = "ip route add {} via {}".format(static_ip, next_hop) + st.config(dut, command) + elif family.lower() == "ipv6": + command = "ip -6 route add {} via {}".format(static_ip, next_hop) + st.config(dut, command) + + + +def config_route_map_mode(dut, tag, operation, sequence, config='yes'): + """ + :param dut: + :param tag: route-map name + :param operation: deny/permit + :param sequence + :param config + :return: + """ + if config.lower() == 'yes': + mode = "" + else: + mode = 'no' + + command = "{} route-map {} {} {}\n".format(mode, tag, operation, sequence) + + st.config(dut, command, type='vtysh') + + +def config_route_map_match_ip_address(dut, tag, operation, sequence, value, family = 'ipv4'): + """ + :param dut: + :param tag: route-map name + :param operation: deny/permit + :param sequence + :param value: access_list / prefix-list/ prefix-len + :return: + """ + config_route_map_mode(dut, tag, operation, sequence) + if family == 'ipv6': + command = "match ipv6 address {}\n".format(value) + else: + command = "match ip address {}\n".format(value) + + command += "exit\n" + st.config(dut, command, type='vtysh') + + +def config_route_map_set_aspath(dut, tag, operation, sequence, value, option='prepend'): + """ + :param dut: + :param tag: route-map name + :param operation: deny/permit + :param sequence + :param option : exclude/prepend + :param value: as-path + :return: + """ + config_route_map_mode(dut, tag, operation, sequence) + command = "set as-path {} {}\n".format(option, value) + command += "exit\n" + st.config(dut, command, type='vtysh') + + +def config_access_list(dut, name, ipaddress, mode='permit', config='yes', family='ipv4'): + """ + :param dut: + :param name: access-list name + :param ipaddress: address/prefix + :param mode: deny/permit + :param config: 'yes' + :return: + """ + if config.lower() == 'yes': + config = "" + else: + config = 'no' + if family == 'ipv6': + command = "{} ipv6 access-list {} {} {}\n".format(config, name, mode, ipaddress) + else: + command = "{} access-list {} {} {}\n".format(config, name, mode, ipaddress) + st.config(dut, command, type='vtysh') + + +def configure_loopback(dut, **kwargs): + """ + Author:gangadhara.sahu@broadcom.com + :param :loopback_name: + :type :loopback_name: + :param :config: + :type :config: + :param :dut: + :type :dut: + :return: + :rtype: + + usage: + configure_loopback(dut1,loopback_name="Loopback1",config="yes") + configure_loopback(dut1,loopback_name="Loopback1",config="no") + """ + my_cmd = '' + if 'loopback_name' not in kwargs: + st.error("Mandatory parameter - loopback_name not found") + return False + cli_type = kwargs.pop('cli_type', st.get_ui_type(dut)) + if cli_type == 'click': + if 'config' in kwargs and kwargs['config'] == 'yes': + my_cmd = 'config loopback add {}'.format(kwargs['loopback_name']) + elif 'config' not in kwargs: + my_cmd = 'config loopback add {}'.format(kwargs['loopback_name']) + elif 'config' in kwargs and kwargs['config'] == 'no': + my_cmd = 'config loopback del {}'.format(kwargs['loopback_name']) + st.config(dut, my_cmd) + elif cli_type == 'klish': + regex = re.compile(r'(\d+|\s+)') + intf = regex.split(kwargs['loopback_name']) + if 'config' in kwargs and kwargs['config'] == 'yes': + command = "interface {} {}".format(intf[0], intf[1]) + command = command + "\n" + "exit" + elif 'config' not in kwargs: + command = "interface {} {}".format(intf[0], intf[1]) + command = command + "\n" + "exit" + elif 'config' in kwargs and kwargs['config'] == 'no': + command = "no interface {} {}".format(intf[0], intf[1]) + output = st.config(dut, command, type="klish", conf=True) + if "Could not connect to Management REST Server" in output: + st.error("klish mode not working.") + return False + + +def config_unconfig_interface_ip_addresses(dut, if_data_list=[], config='add'): + """ + Configure IP addresses on multiple interfaces + Author: Naveena Suvarna (naveen.suvarna@broadcom.com) + + :param dut: + :param if_data_list: + :param config: + :return: + """ + if config != 'add' and config != 'remove': + st.error("Invalid config type {}".format(config)) + return False + + command = '' + for if_data in if_data_list: + if not if_data['name']: + st.error("Please provide interface name in {} ".format(if_data)) + return False + + if not is_valid_ip_address(if_data['ip'], if_data['family'], if_data['subnet']): + st.error("Invalid IP address or family or subnet {} ".format(if_data)) + return False + + command += "sudo config interface ip {} {} {}/{} ; ".format(config, + if_data['name'], if_data['ip'], if_data['subnet']) + + if command != '': + try: + st.config(dut, command) + except Exception as e: + st.log(e) + return False + + return True + + +def config_unconfig_static_routes(dut, route_data_list=[], shell="vtysh", config='add'): + """ + Configure multiple static route entries + Author: Naveena Suvarna (naveen.suvarna@broadcom.com) + + :param dut: + :param route_data_list + :param shell + :param config + :return: + """ + if config != 'add' and config != 'remove': + st.error("Invalid config type {}".format(config)) + return False + + command = '' + for rt_data in route_data_list: + if not is_valid_ip_address(rt_data['ip'], rt_data['family'], rt_data['subnet']): + st.error("Invalid IP address or family or subnet {} ".format(rt_data)) + return False + + if not rt_data['nexthop']: + st.error("Please provide nexthop in {} ".format(rt_data)) + return False + + if shell == "vtysh": + cfg_mode = '' if config == 'add' else 'no' + family = 'ip' if rt_data['family'] == 'ipv4' else 'ipv6' + command += " {} {} route {}/{} {} \n ".format(cfg_mode, family, + rt_data['ip'], rt_data['subnet'], rt_data['nexthop']) + else: + family = '' if rt_data['family'] == 'ipv4' else '-6' + command += " sudo ip {} route {} {}/{} via {} ; ".format(family, config, + rt_data['ip'], + rt_data['subnet'], rt_data['nexthop']) + + if command != '': + try: + if shell == "vtysh": + st.config(dut, command, type='vtysh') + else: + st.config(dut, command) + except Exception as e: + st.log(e) + return False + return True + + +class PrefixList: + """ + Usage: + prefix_list = PrefixList("mylist") + prefix_list.add_description("This_is_my_prefix_list") + prefix_list.add_match_permit_sequence('0.0.0.0/0', seq_num='10') + prefix_list.add_match_permit_sequence('1.1.1.1/32') + prefix_list.add_match_deny_sequence('2.2.2.0/24', seq_num='30', ge='26') + prefix_list.add_match_permit_sequence('3.3.3.0/24', seq_num='40', ge='26', le='30') + prefix_list.execute_command(dut, config='yes') + cmd_string = prefix_list.config_command_string() + prefix_list.execute_command(dut, config='no') + """ + + def __init__(self, name, family='ipv4'): + self.name = name + self.description = '' + self.family = family + self.match_sequence = [] + if self.family == 'ipv6': + self.cmdkeyword = 'ipv6 prefix-list' + else: + self.cmdkeyword = 'ip prefix-list' + + def add_description(self, description): + self.description = description + + def add_match_permit_sequence(self, prefix, ge='', le='', seq_num=''): + self.match_sequence.append((seq_num, 'permit', prefix, ge, le)) + + def add_match_deny_sequence(self, prefix, ge='', le='', seq_num=''): + self.match_sequence.append((seq_num, 'deny', prefix, ge, le)) + + def config_command_string(self): + command = '' + if self.description != '': + command += '{} {} description {}\n'.format(self.cmdkeyword, self.name, self.description) + for v in self.match_sequence: + if v[0] != '': + command += '{} {} seq {} {} {}'.format(self.cmdkeyword, self.name, v[0], v[1], v[2]) + else: + command += '{} {} {} {}'.format(self.cmdkeyword, self.name, v[1], v[2]) + if v[3] != '': + command += ' ge {}'.format(v[3]) + if v[4] != '': + command += ' le {}'.format(v[4]) + command += '\n' + return command + + def unconfig_command_string(self): + command = 'no {} {}\n'.format(self.cmdkeyword, self.name) + return command + + def execute_command(self, dut, config='yes'): + if config == 'no': + command = self.unconfig_command_string() + else: + command = self.config_command_string() + st.config(dut, command, type='vtysh') + + +class AccessList: + """ + Usage: + access_list = AccessList("aclcfg") + access_list.add_description("This_is_my_access_list") + access_list.add_match_permit_sequence('0.0.0.0/0') + access_list.add_match_permit_sequence('1.1.1.1/32') + access_list.add_match_deny_sequence('2.2.2.2/24', exact_match="true") + access_list.add_match_permit_sequence('3.3.3.3/24') + access_list.execute_command(dut, config='yes') + cmd_string = access_list.config_command_string() + access_list.execute_command(dut, config='no') + """ + + def __init__(self, name, family='ipv4'): + self.name = name + self.description = '' + self.family = family + self.match_sequence = [] + if self.family == 'ipv6': + self.cmdkeyword = 'ipv6 access-list' + else: + self.cmdkeyword = 'access-list' + + def add_description(self, description): + self.description = description + + def add_match_permit_sequence(self, prefix, exact_match='false'): + self.match_sequence.append(('permit', prefix, exact_match)) + + def add_match_deny_sequence(self, prefix, exact_match='false'): + self.match_sequence.append(('deny', prefix, exact_match)) + + def config_command_string(self): + command = '' + if self.description: + command += '{} {} remark {}\n'.format(self.cmdkeyword, self.name, self.description) + for v in self.match_sequence: + command += '{} {} {} {}'.format(self.cmdkeyword, self.name, v[0], v[1]) + if v[2] != 'false': + command += ' exact-match' + command += '\n' + return command + + def unconfig_command_string(self): + command = 'no {} {}\n'.format(self.cmdkeyword, self.name) + return command + + def execute_command(self, dut, config='yes'): + if config == 'no': + command = self.unconfig_command_string() + else: + command = self.config_command_string() + st.config(dut, command, type='vtysh') + + +def get_link_local_addresses(dut, interface): + """ + To get the Link local address on port. + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :param interface: + :return: + """ + output = get_interface_ip_address(dut, interface, family="ipv6") + ipv6_list = utils.dicts_list_values(output, 'ipaddr') + return [each.split("%")[0] for each in ipv6_list if '%' in each] + +def config_interface_ip6_link_local(dut, interface_list, action='enable'): + """ + Configure IPv6 link local on multiple interfaces + Author: Kesava Swamy (kesava-swamy.karedla@broadcom.com) + + :param dut: + :param interface_list: + :param action: + :return: + """ + if action != 'enable' and action != 'disable': + st.error("Invalid config type {}".format(action)) + return False + interfaces = list(interface_list) if isinstance(interface_list, list) else [interface_list] + command = '' + for interfaces in interfaces: + if not interfaces: + st.error("Please provide interface name in {} ".format(interfaces)) + return False + command += "sudo config interface ipv6 {} use-link-local-only {} ; ".format(action,interfaces) + + if command != '': + try: + st.config(dut, command) + except Exception as e: + st.log(e) + return False + + return True + + +def config_interface_ip_addresses(dut, if_data_list={}, config='yes'): + + if config == 'yes' or config == 'add': + config = 'add' + elif config == 'no' or config == 'del': + config = 'remove' + else : + st.error("Invalid config type {}".format(config)) + return False + + command = [] + for if_name, if_data in if_data_list.items(): + if not if_data['name']: + st.error("Please provide interface name in {} ".format(if_data)) + return False + + if not is_valid_ip_address(if_data['ip'], if_data['family'], if_data['subnet']): + st.error("Invalid IP address or family or subnet {} ".format(if_data)) + return False + + cmd_str = "sudo config interface ip {} {} {}/{} ".format(config, + if_data['name'], if_data['ip'], if_data['subnet']) + command.append(cmd_str) + + try: + st.config(dut, command) + except Exception as e: + st.log(e) + return False + + return True + +def config_unnumbered_interface(dut, **kwargs): + """ + API to config / unconfig unnumbered interface + :param dut: + :param kwargs: + :return: + """ + cli_type = kwargs.get('cli_type', st.get_ui_type(dut)) + family = kwargs.get("family","ipv4") + action = kwargs.get("action","add") + interface = kwargs.get("interface", None) + loop_back = kwargs.get("loop_back", None) + skip_error = kwargs.get('skip_error', False) + intf_name = get_interface_number_from_name(interface) + + if cli_type == "click": + commands = list() + if action not in ["add","del"]: + st.log("Unsupported action provided") + return False + if not interface: + st.log("Please provide interface") + return False + if not loop_back and action == "add": + st.log("Please provide loopback interface") + return False + if family == "ipv4": + if action == "add": + command = "config interface ip unnumbered add {} {}".format(interface, loop_back) + else: + command = "config interface ip unnumbered del {}".format(interface) + commands.append(command) + elif cli_type == "klish": + commands = list() + if interface and loop_back and family == "ipv4": + command = "interface {} {}".format(intf_name["type"],intf_name["number"]) + commands.append(command) + if action == "add": + command = "ip unnumbered {}".format(loop_back) + elif action == "del": + command = "no ip unnumbered" + commands.append(command) + else: + st.log("Please provide interface, loop_back and family as ipv4") + return False + + if commands: + if skip_error: + try: + out = st.config(dut, commands, skip_error_check=skip_error, type=cli_type) + return True + except: + st.log("Error handled..by API") + return False + else: + out = st.config(dut, commands, skip_error_check=skip_error, type=cli_type) + return True + + +def prepare_show_ipv6_interface_output(data): + """ + Helper function to prepare show ipv6 interface output + :param data: + :return: + """ + output = dict() + result = list() + for ip_data in data: + if output.get(ip_data["interface"]): + output[ip_data["interface"]].append(ip_data) + else: + output[ip_data["interface"]] = [ip_data] + if output: + ip_keys = ["status", "neighborip", "ipaddr", "flags", "vrf", "neighbor", "interface"] + for key, value in output.items(): + result.append(value[0]) + if len(value) > 1: + for attr in ip_keys: + value[1][attr] = value[1][attr] if value[1][attr] else value[0][attr] + result.append(value[1]) + return result + +def config_ip_prefix_list(dut, prefix_list, ip_addr, family="ipv4", action="permit", cli_type="klish", skip_error_check=True): + """ + API to cpnfigure IP Prefix list + :param dut: + :param prefix_list: + :param family: + :param action: + :param ip_addr: + :param cli_type: + :return: + """ + if cli_type == "klish": + commands = list() + if family == "ipv4": + ip_address = "0.0.0.0/0" if ip_addr == "any" else ip_addr + else: + ip_address = "0::/64" if ip_addr == "any" else ip_addr + ip_cmd = "ipv6" if family == "ipv6" else "ip" + commands.append("{} prefix-list {} {} {}".format(ip_cmd, prefix_list, action, ip_address)) + st.config(dut, commands, type=cli_type, skip_error_check=skip_error_check) + return True + + +def ping_poll(dut, addresses, family='ipv4', iter=1, delay=1, **kwargs): + """ + To Perform ping to ipv4 or ipv6 address using poll. + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param addresses: + :param family: + :param iter: + :param delay: + :param kwargs: + :return: + """ + i = 1 + while True: + if not ping(dut, addresses, family=family, **kwargs): + if i == iter: + return False + i += 1 + st.wait(delay, 'for next try...') + else: + return True + + +def dump_mgmt_connectivity_info(dut): + """ + To dump MGMT route info + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :return: + """ + utils.exec_foreach(True, utils.make_list(dut), st.config, "sudo route -n") + + +def kill_dhclient_on_interface(dut, interface): + """ + API to kill the dhclient on an interface using the process ID + :param dut: + :param interface: + :return: + """ + command = "cat /var/run/dhclient.{}.pid".format(interface) + pid = utils.remove_last_line_from_string(st.config(dut, command)) + if pid: + cmd = "kill -9 {}".format(pid) + st.config(dut, cmd) + return True + else: + st.error("PID not found") + return False + +def config_loopback_interfaces(dut, **kwargs): + """ + :param :loopback_name: + :type :loopback_name: + :param :config: + :type :config: + :param :dut: + :type :dut: + :return: + :rtype: + + usage: + configure_loopback(dut1,loopback_name="Loopback1",config="yes") + configure_loopback(dut1,loopback_name="Loopback1",config="no") + """ + my_cmd = '' + if not kwargs.get('loopback_name'): + st.error("Mandatory parameter - loopback_name not found") + return False + config = kwargs.get("config", "yes") + loopback_interface = kwargs.get("loopback_name") if isinstance(kwargs.get("loopback_name"), list) else [kwargs.get("loopback_name")] + for intf in loopback_interface: + if config == 'yes': + my_cmd += 'sudo config loopback add {};'.format(intf) + elif config == 'no': + my_cmd += 'sudo config loopback del {};'.format(intf) + st.config(dut, my_cmd) + diff --git a/spytest/apis/routing/route_map.py b/spytest/apis/routing/route_map.py new file mode 100644 index 00000000000..9f1c1cdb2e4 --- /dev/null +++ b/spytest/apis/routing/route_map.py @@ -0,0 +1,251 @@ +# This file contains API to configure route-maps. +# It provides a route-map config object where user can add all match +# and set statements +# User can then execute the full route-map in one go +# or +# obtain the configuration string so that it can be concatenated with other commands +# for faster execution of CLIs +# @author : Sayed Saquib (sayed.saquib@broadcom.com) + +from spytest import st + +class RouteMap: + """ + Usage: + rmap = RouteMap("testroutemap") + + rmap.add_permit_sequence('10') + seq='10' + rmap.add_sequence_description(seq, 'This is permit seq 10') + #match + rmap.add_sequence_match_prefix_list(seq, 'myprefixlist') + rmap.add_sequence_match_prefix_length(seq, '32') + rmap.add_sequence_match_ip_access_list(seq, '22') + rmap.add_sequence_match_bgp_aspath_list(seq, 'mybgpaspathlist') + #set + rmap.add_sequence_set_metric(seq, '444') + rmap.add_sequence_set_local_preference(seq, '6400') + rmap.add_sequence_set_ipv4_next_hop(seq, '3.3.3.3') + rmap.add_sequence_set_ipv6_next_hop_local(seq, 'fe80:1::1:1') + rmap.add_sequence_set_ipv6_next_hop_global(seq, '2:2::2:2') + rmap.add_sequence_set_ipv6_next_hop_peer_address(seq) + rmap.add_sequence_set_ipv6_next_hop_prefer_global(seq) + rmap.add_sequence_set_as_path_exclude(seq, ['65101', '65003']) + rmap.add_sequence_set_as_path_prepend(seq, ['65102', '65004']) + rmap.add_sequence_set_as_path_prepend_last_as(seq, '5') + rmap.add_sequence_set_community_none(seq) + + rmap.add_deny_sequence('20') + rmap.add_sequence_description('20', 'This is deny seq 20') + rmap.add_sequence_match_community('20', '44:4444') + rmap.add_sequence_set_community('20', ['44:4444', '55:5555', 'no-export']) + """ + + def __init__(self, name): + self.name = name + self.stanza = {} + self.cmdkeyword = 'route-map' + + def add_permit_sequence(self, seq): + stanza = dict() + stanza['mode'] = 'permit' + stanza['match'] = [] + stanza['set'] = [] + self.stanza[seq] = stanza + + def add_deny_sequence(self, seq): + stanza = dict() + stanza['mode'] = 'deny' + stanza['match'] = [] + stanza['set'] = [] + self.stanza[seq] = stanza + + def add_sequence_description(self, seq, description): + self.stanza[seq]['description'] = description + + def add_sequence_match_prefix_length(self, seq, length, family='ipv4'): + matchstmt = dict() + matchstmt['type'] = 'prefix-len' + matchstmt['family'] = family + matchstmt['length'] = length + self.stanza[seq]['match'].append(matchstmt) + + def add_sequence_match_prefix_list(self, seq, prefix_list_name, family='ipv4'): + matchstmt = dict() + matchstmt['type'] = 'prefix-list' + matchstmt['family'] = family + matchstmt['name'] = prefix_list_name + self.stanza[seq]['match'].append(matchstmt) + + def add_sequence_match_ip_access_list(self, seq, access_list_name_number, family='ipv4'): + matchstmt = dict() + matchstmt['type'] = 'access-list' + matchstmt['family'] = family + matchstmt['name'] = access_list_name_number + self.stanza[seq]['match'].append(matchstmt) + + def add_sequence_match_bgp_aspath_list(self, seq, bgp_as_path_list_name): + matchstmt = dict() + matchstmt['type'] = 'aspath-access-list' + matchstmt['name'] = bgp_as_path_list_name + self.stanza[seq]['match'].append(matchstmt) + + def add_sequence_match_community(self, seq, community_number_name, exact_match=False): + matchstmt = dict() + matchstmt['type'] = 'community' + matchstmt['community'] = community_number_name + matchstmt['exact_match'] = exact_match + self.stanza[seq]['match'].append(matchstmt) + + def add_sequence_set_metric(self, seq, metric): + setstmt = dict() + setstmt['type'] = 'metric' + setstmt['metric'] = metric + self.stanza[seq]['set'].append(setstmt) + + def add_sequence_set_local_preference(self, seq, local_pref): + setstmt = dict() + setstmt['type'] = 'localpref' + setstmt['value'] = local_pref + self.stanza[seq]['set'].append(setstmt) + + def add_sequence_set_ipv4_next_hop(self, seq, nhop): + setstmt = dict() + setstmt['type'] = 'ipv4nexthop' + setstmt['value'] = nhop + self.stanza[seq]['set'].append(setstmt) + + def add_sequence_set_ipv6_next_hop_local(self, seq, nhop): + setstmt = dict() + setstmt['type'] = 'ipv6nexthoplocal' + setstmt['value'] = nhop + self.stanza[seq]['set'].append(setstmt) + + def add_sequence_set_ipv6_next_hop_global(self, seq, nhop): + setstmt = dict() + setstmt['type'] = 'ipv6nexthopglobal' + setstmt['value'] = nhop + self.stanza[seq]['set'].append(setstmt) + + def add_sequence_set_ipv6_next_hop_peer_address(self, seq): + setstmt = dict() + setstmt['type'] = 'ipv6nexthoppeeraddress' + self.stanza[seq]['set'].append(setstmt) + + def add_sequence_set_ipv6_next_hop_prefer_global(self, seq): + setstmt = dict() + setstmt['type'] = 'ipv6nexthoppreferglobal' + self.stanza[seq]['set'].append(setstmt) + + def add_sequence_set_as_path_exclude(self, seq, as_path_list): + setstmt = dict() + setstmt['type'] = 'aspathexclude' + setstmt['as_path_list'] = as_path_list + self.stanza[seq]['set'].append(setstmt) + + def add_sequence_set_as_path_prepend(self, seq, as_path_list): + setstmt = dict() + setstmt['type'] = 'aspathprepend' + setstmt['as_path_list'] = as_path_list + self.stanza[seq]['set'].append(setstmt) + + def add_sequence_set_as_path_prepend_last_as(self, seq, number): + setstmt = dict() + setstmt['type'] = 'aspathprependlastas' + setstmt['value'] = number + self.stanza[seq]['set'].append(setstmt) + + def add_sequence_set_community(self, seq, community_list): + setstmt = dict() + setstmt['type'] = 'community' + setstmt['community_list'] = community_list + self.stanza[seq]['set'].append(setstmt) + + def add_sequence_set_community_none(self, seq): + setstmt = dict() + setstmt['type'] = 'communitynone' + self.stanza[seq]['set'].append(setstmt) + + def config_command_string(self): + command = '' + for v in self.stanza.keys(): + command += '{} {} {} {}\n'.format(self.cmdkeyword, self.name, self.stanza[v]['mode'], v) + if 'description' in self.stanza[v]: + command += 'description {}\n'.format(self.stanza[v]['description']) + + for items in self.stanza[v]['match']: + if items['type'] == 'prefix-len': + if items['family'] == 'ipv4': + command += 'match ip address prefix-len {}\n'.format(items['length']) + else: + command += 'match ipv6 address prefix-len {}\n'.format(items['length']) + elif items['type'] == 'prefix-list': + if items['family'] == 'ipv4': + command += 'match ip address prefix-list {}\n'.format(items['name']) + else: + command += 'match ipv6 address prefix-list {}\n'.format(items['name']) + elif items['type'] == 'access-list': + if items['family'] == 'ipv4': + command += 'match ip address {}\n'.format(items['name']) + else: + command += 'match ipv6 address {}\n'.format(items['name']) + elif items['type'] == 'aspath-access-list': + command += 'match as-path {}\n'.format(items['name']) + elif items['type'] == 'community': + command += 'match community {}'.format(items['community']) + if items['exact_match'] == True: + command += ' exact-match\n' + else: + command += '\n' + else: + st.error("Invalid type({}) in match statement".format(items['type'])) + + for items in self.stanza[v]['set']: + if items['type'] == 'metric': + command += 'set metric {}\n'.format(items['metric']) + elif items['type'] == 'localpref': + command += 'set local-preference {}\n'.format(items['value']) + elif items['type'] == 'ipv4nexthop': + command += 'set ip next-hop {}\n'.format(items['value']) + elif items['type'] == 'ipv6nexthoplocal': + command += 'set ipv6 next-hop local {}\n'.format(items['value']) + elif items['type'] == 'ipv6nexthopglobal': + command += 'set ipv6 next-hop global {}\n'.format(items['value']) + elif items['type'] == 'ipv6nexthoppeeraddress': + command += 'set ipv6 next-hop peer-address\n' + elif items['type'] == 'ipv6nexthoppreferglobal': + command += 'set ipv6 next-hop prefer-global\n' + elif items['type'] == 'aspathexclude': + command += 'set as-path exclude' + for v in items['as_path_list']: + command = command + ' ' + v + command += '\n' + elif items['type'] == 'aspathprepend': + command += 'set as-path prepend' + for v in items['as_path_list']: + command = command + ' ' + v + command += '\n' + elif items['type'] == 'aspathprependlastas': + command += 'set as-path prepend last-as {}\n'.format(items['value']) + elif items['type'] == 'community': + command += 'set community' + for v in items['community_list']: + command = command + ' ' + v + command += '\n' + elif items['type'] == 'communitynone': + command += 'set community none\n' + else: + st.error("Invalid type({}) in set statement".format(items['type'])) + return command + + def unconfig_command_string(self): + command = 'no {} {}\n'.format(self.cmdkeyword, self.name) + return command + + def execute_command(self, dut, config='yes'): + if config == 'no': + command = self.unconfig_command_string() + else: + command = self.config_command_string() + st.config(dut, command, type='vtysh') + diff --git a/spytest/apis/routing/vrf.py b/spytest/apis/routing/vrf.py new file mode 100644 index 00000000000..0a22a3dc21f --- /dev/null +++ b/spytest/apis/routing/vrf.py @@ -0,0 +1,260 @@ +import re +from spytest import st, utils +from spytest.utils import filter_and_select + +def verify_vrf(dut,**kwargs): + st.log("verify show vrf output") + """ + verify_vrf(dut1,vrfname="Vrf-103") + """ + cmd = "show vrf" + output = st.show(dut,cmd) + if 'vrfname' in kwargs: + if not isinstance(kwargs['vrfname'],list): + vname_list = [kwargs['vrfname']] + else: + vname_list = kwargs['vrfname'] + else: + st.log("Mandatory parameter vrfname is not found") + return False + for vname in vname_list: + match = {"vrfname":vname} + entries = filter_and_select(output, ["vrfname"], match) + if not bool(entries): + return bool(entries) + return True + +def verify_vrf_verbose(dut,vrfname,interface): + st.log("verify show vrf --verbose output") + """ + verify_vrf_verbose(dut1,vrfname="Vrf-103",interface='Ethernet2') + """ + cmd = "show vrf --verbose" + output = st.show(dut,cmd) + if not isinstance(vrfname,list): + vrfname = [vrfname] + for vname,intf in zip(vrfname,interface): + match = {"vrfname":vname,"interfaces": intf} + entries = filter_and_select(output,["vrfname"],match) + print("entries") + if not bool(entries): + return bool(entries) + return True + + +def get_vrf_verbose(dut, **kwargs): + st.log("get show vrf --verbose output") + """ + get_vrf_verbose(dut1,vrfname="Vrf-1") + """ + cmd = "show vrf --verbose" + output = st.show(dut, cmd) + if len(output) == 0: + st.error("OUTPUT is Empty") + return [] + match_dict = {} + if 'vrfname' in kwargs: + match_dict['vrfname'] = kwargs['vrfname'] + else: + st.error("Mandatory parameter peeraddress is not found") + return False + entries = filter_and_select(output, None, match_dict) + return entries[0] + +def config_vrf(dut, **kwargs): + """ + #Sonic cmd: Config vrf + eg: config_vrf(dut = dut1, vrf_name = 'Vrf-test', config = 'yes') + eg: config_vrf(dut = dut1, vrf_name = 'Vrf-test', config = 'no') + """ + st.log('Config VRF API') + if kwargs.has_key('config'): + config = kwargs['config'] + else: + config = 'yes' + if 'vrf_name' in kwargs: + if not isinstance(kwargs['vrf_name'],list): + vrf_name = [kwargs['vrf_name']] + else: + vrf_name = kwargs['vrf_name'] + else: + st.log("Mandatory parameter vrfname is not found") + if kwargs.has_key('skip_error'): + skip_error = kwargs['skip_error'] + else: + skip_error = False + cli_type = kwargs.pop('cli_type', st.get_ui_type(dut)) + if cli_type == 'click': + my_cmd = '' + if config.lower() == 'yes': + for vrf in vrf_name: + my_cmd += 'sudo config vrf add {}\n'.format(vrf) + else: + for vrf in vrf_name: + my_cmd += 'sudo config vrf del {}\n'.format(vrf) + if skip_error: + try: + out = st.config(dut, my_cmd) + return True + except: + st.log("Error handled..by API") + return False + else: + out = st.config(dut, my_cmd) + return True + elif cli_type == 'klish': + command = '' + if config.lower() == 'yes': + for vrf in vrf_name: + command = command + "\n" + "ip vrf {}".format(vrf) + else: + for vrf in vrf_name: + command = command + "\n" + "no ip vrf {}".format(vrf) + output = st.config(dut, command, skip_error_check=skip_error, type="klish", conf=True) + if "Could not connect to Management REST Server" in output: + st.error("klish mode not working.") + return False + return True + + +def bind_vrf_interface(dut, **kwargs): + """ + #Sonic cmd: Config interface + eg: bind_vrf_interface(dut = dut1, vrf_name = 'Vrf-test', intf_name ='Ethernet8', config = 'no') + """ + st.log('API to bind interface to VRF') + if kwargs.has_key('config'): + config = kwargs['config'] + else: + config = 'yes' + if 'vrf_name' in kwargs: + if not isinstance(kwargs['vrf_name'],list): + vrf_name = [kwargs['vrf_name']] + else: + vrf_name = kwargs['vrf_name'] + else: + st.log("Mandatory parameter vrfname is not found") + if 'intf_name' in kwargs: + if not isinstance(kwargs['intf_name'],list): + intf_name = [kwargs['intf_name']] + else: + intf_name = kwargs['intf_name'] + else: + st.log("Mandatory parameter intf_name is not found") + if kwargs.has_key('skip_error'): + skip_error = kwargs['skip_error'] + else: + skip_error = False + + cli_type = kwargs.pop('cli_type', st.get_ui_type(dut)) + if cli_type == 'click': + my_cmd = '' + if config.lower() == 'yes': + for vrf,intf in zip(vrf_name,intf_name): + if 'Loopback' in intf: + my_cmd += 'sudo config loopback add {}\n'.format(intf) + my_cmd += 'sudo config interface vrf bind {} {}\n'.format(intf, vrf) + else: + my_cmd += 'sudo config interface vrf bind {} {}\n'.format(intf, vrf) + else: + for vrf,intf in zip(vrf_name,intf_name): + if 'Loopback' in intf: + my_cmd += 'sudo config interface vrf unbind {} {}\n'.format(intf, vrf) + my_cmd += 'sudo config loopback del {}\n'.format(intf) + else: + my_cmd += 'config interface vrf unbind {} {}\n'.format(intf, vrf) + if skip_error: + out = st.config(dut, my_cmd, skip_error_check=True) + return True + else: + out = st.config(dut, my_cmd) + return True + elif cli_type == 'klish': + regex = re.compile(r'(\d+|\s+)') + command = '' + if config.lower() == 'yes': + for vrf,intf in zip(vrf_name,intf_name): + intfv = regex.split(intf) + command = command + "\n" + "interface {} {}".format(intfv[0], intfv[1]) + command = command + "\n" + "ip vrf forwarding {}".format(vrf) + command = command + "\n" + "exit" + else: + for vrf,intf in zip(vrf_name,intf_name): + intfv = regex.split(intf) + command = command + "\n" + "interface {} {}".format(intfv[0], intfv[1]) + command = command + "\n" + "no ip vrf forwarding {}".format(vrf) + command = command + "\n" + "exit" + if 'Loopback' in intf: + command = command + "\n" + "no interface {} {}".format(intfv[0], intfv[1]) + output = st.config(dut, command, skip_error_check=skip_error, type="klish", conf=True) + if "Could not connect to Management REST Server" in output: + st.error("klish mode not working.") + return False + return True + + + + +def config_vrfs(dut, vrf_data_list={}, config='yes'): + + if config == 'yes' or config == 'add': + config = 'add' + elif config == 'no' or config == 'del': + config = 'del' + else : + st.error("Invalid config type {}".format(config)) + return False + + command = [] + for vrf_name, vrf_data in vrf_data_list.items(): + vrf = vrf_data['name'] + cmd_str = "sudo config vrf {} {} ".format(config, vrf) + command.append(cmd_str) + + try: + st.config(dut, command) + except Exception as e: + st.log(e) + return False + + return True + + +def _clear_vrf_config_helper(dut_list): + """ + Helper routine to cleanup VRF config from devices. + """ + dut_li = list(dut_list) if isinstance(dut_list, list) else [dut_list] + for dut in dut_li: + st.log("############## {} : VRF Config Cleanup ################".format(dut)) + output = st.show(dut, "show vrf") + st.log("##### VRF : {}".format(output)) + if len(output) == 0: + continue + + for entry in output: + if not entry['vrfname']: + continue + + vrfname = entry['vrfname'] + if type(entry['interfaces']) is list: + for intf in entry['interfaces']: + bind_vrf_interface(dut, vrf_name=vrfname, intf_name=intf, config='no') + + config_vrf(dut, vrf_name=vrfname, config='no') + + return True + + +def clear_vrf_configuration(dut_list, thread=True): + """ + Find and cleanup all VRF configuration. + + :param dut_list + :return: + """ + dut_li = list(dut_list) if isinstance(dut_list, list) else [dut_list] + [out, exceptions] = utils.exec_foreach(thread, dut_li, _clear_vrf_config_helper) + st.log(exceptions) + return False if False in out else True + diff --git a/spytest/apis/security/__init__.py b/spytest/apis/security/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/spytest/apis/security/radius.py b/spytest/apis/security/radius.py new file mode 100644 index 00000000000..8503e198764 --- /dev/null +++ b/spytest/apis/security/radius.py @@ -0,0 +1,203 @@ +# This file contains the list of API's which performs RADIUS operations. +# @author : Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + +from spytest import st +import utilities.utils as utils +import utilities.common as common_utils + +debug = False + + +def config_server(dut, no_form=False, skip_error_check=False, **kwargs): + """ + Config / Unconfig radius server using provided parameters + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param family: + :param no_form: + :param kwargs: + :return: + """ + st.log("Configuring RADIUS SERVER Parameters ...") + cli_type = kwargs["cli_type"] if kwargs.get("cli_type") else "klish" + if "ip_address" not in kwargs: + st.log("IP Address not provided") + return False + ipaddress_li = common_utils.make_list(kwargs["ip_address"]) + for each_ip in ipaddress_li: + if cli_type == "klish": + cmd = "radius-server host {}".format(each_ip) + if "auth_type" in kwargs: + cmd += " auth-type {}".format(kwargs["auth_type"]) + if "auth_port" in kwargs: + cmd += " auth-port {}".format(kwargs["auth_port"]) + if "key" in kwargs: + cmd += " key {}".format(kwargs["key"]) + if "priority" in kwargs: + cmd += " priority {}".format(kwargs["priority"]) + if "timeout" in kwargs: + cmd += " timeout {}".format(kwargs["timeout"]) + if "use_mgmt_vrf" in kwargs: + cmd += " vrf {}".format(kwargs.get("use_mgmt_vrf")) + if "retransmit" in kwargs: + cmd += " re-transmit {}".format(kwargs["retransmit"]) + command = "no {}".format(cmd) if no_form else cmd + st.cli_config(dut, command, "mgmt-config", skip_error_check) + elif cli_type == "click": + action = kwargs["action"] if "action" in kwargs else "add" + command = "config radius {} {}".format(action, each_ip) + if "retransmit" in kwargs: + command += " -r {}".format(kwargs["retransmit"]) + if "auth_type" in kwargs: + command += " -a {}".format(kwargs["auth_type"]) + if "timeout" in kwargs: + command += " -t {}".format(kwargs["timeout"]) + if "key" in kwargs: + command += " -k {}".format(kwargs["key"]) + if "auth_port" in kwargs: + command += " -o {}".format(kwargs["auth_port"]) + if "priority" in kwargs: + command += " -p {}".format(kwargs["priority"]) + if "use_mgmt_vrf" in kwargs: + command += " -m" + st.config(dut, command, skip_error_check=skip_error_check) + + +def config_global_server_params(dut, skip_error_check=False, params=dict(), cli_type="klish"): + """ + Config / Unconfig global server params using provided parameters + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param params: {"source_ip":{"value":"10.20.3.1", "no_form": False}, "key":{"value":"ABCD", "no_form": True}, + "auth_port":{"value":"56", "no_form": False}, "timeout":{"value":"30", "no_form": True}} + :return: + """ + st.log("Configuring GLOBAL SERVER Parameters ...") + if not params: + st.log("Invalid parameters provided ..") + return False + count = 0 + if cli_type == "klish": + cmd = "radius-server" + fields = {"source_ip": "source-ip", "key": "key", "auth_type": "auth-type", "timeout": "timeout", + "retransmit": "retransmit"} + for key, value in params.items(): + if value.get("no_form"): + command = "no {} {}".format(cmd, fields[key]) + else: + command = "{} {} {}".format(cmd, fields[key], value["value"]) + output = st.cli_config(dut, command, "mgmt-config", skip_error_check) + if "Syntax error: Illegal parameter" in utils.remove_last_line_from_string(output): + st.log(utils.remove_last_line_from_string(output)) + return False + count += 1 + elif cli_type == "click": + cmd = "config radius" + fields = {"source_ip":"sourceip","key":"passkey","auth_type":"authtype","timeout":"timeout","retransmit":"retransmit"} + for key, value in params.items(): + if value.get("no_form"): + if key == "source_ip": + command = "{} default {} {}".format(cmd, fields[key], value["value"]) + else: + command = "{} default {}".format(cmd, fields[key]) + else: + command = "{} {} {}".format(cmd, fields[key], value["value"]) + output = st.config(dut, command, skip_error_check=skip_error_check) + if "Valid chars are" in utils.remove_last_line_from_string(output): + st.log(utils.remove_last_line_from_string(output)) + return False + count += 1 + if count > 0: + return True + st.log("Returning False as the command execution is not happened with the provided parameters .. ") + return False + +def show_config(dut, search_string="", cli_type="klish"): + ''' + API to show the configured radius parameters + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :return: + {'globals': [{global_auth_type': 'pap (default)','global_source_ip': '10.25.36.25','global_passkey': 'abcd (default)', + 'global_timeout': '5 (default)'}], + 'servers': [{'auth_type': '', 'passkey': '', 'auth_port': '1815', 'priority': '1', 'timeout': '', 'address': '1.1.1.5'}, + {'auth_type': '', 'passkey': '', 'auth_port': '1812', 'priority': '1', 'timeout': '', 'address': '1.1.1.1'}]} + ''' + st.log("Showing radius configuration ...") + result = {"globals": [], "servers": []} + if cli_type == "klish": + command = "show radius-server" + output = st.cli_show(dut, command, "mgmt-user") + global_out = dict() + for k, v in output[0].items(): + if "global" in k: + global_out[k] = v + if global_out and utils.check_empty_values_in_dict(global_out): + result["globals"].append(global_out) + for d in output[1:]: + server_out = dict() + for k, v in d.items(): + if not "global" in k: + server_out[k] = v + if server_out and utils.check_empty_values_in_dict(server_out): + result["servers"].append(server_out) + elif cli_type == "click": + command = "show radius | grep -w {}".format(search_string) if search_string else "show radius" + output = st.show(dut, command) + for d in output: + global_out = dict() + server_out = dict() + for k, v in d.items(): + if "global" in k: + global_out[k] = v + else: + server_out[k] = v + if global_out and not utils.check_empty_values_in_dict(global_out): + result["globals"].append(global_out) + if server_out and not utils.check_empty_values_in_dict(server_out): + result["servers"].append(server_out) + return result + +def verify_config(dut, params, cli_type="klish"): + """ + API to verify the Radius Parameters + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param params: {"globals":{"global_auth_type":"pap", "global_source_ip":'10.25.36.25'}, + "servers":[{'auth_port': '1815', 'priority': '1', 'address': '1.1.1.5'}, + {'auth_port': '1812', 'priority': '1', 'address': '1.1.1.1'}]} + :return: + """ + if not isinstance(params, dict): + st.log("Unsupported data format provided...") + return False + + output = show_config(dut, cli_type=cli_type) + if not output: + st.log("Identified empty radius output ..") + return False + if "globals" in params and params["globals"]: + for key, value in params["globals"].items(): + if str(value) != output["globals"][0][key]: + st.log("Verification of radius global parameters {} with {} values is failed".format(key, value)) + return False + if "servers" in params and params["servers"]: + for details in params["servers"]: + is_found = 0 + for data in output["servers"]: + for key, value in details.items(): + if str(value) != data[key]: + st.log("Verifications of {} with {} values is failed".format(key, value)) + is_found = 0 + else: + st.log("Radius server Key Value verification success") + is_found += 1 + if is_found == len(details): + st.log("Already found, hence breaking the iteration ..") + break + if is_found != len(details): + st.log("Verification of radius server parameter verification failed..") + return False + if "globals" in params and params["globals"] or "servers" in params and params["servers"]: + st.log("Verification of radius server parameters SUCCESS ..") + return True diff --git a/spytest/apis/security/rbac.py b/spytest/apis/security/rbac.py new file mode 100644 index 00000000000..b99517f09e8 --- /dev/null +++ b/spytest/apis/security/rbac.py @@ -0,0 +1,212 @@ +# This file contains the list of RBAC APIs. +# Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + +from spytest import st +from utilities.utils import banner_log +from apis.system.gnmi import gnmi_get, gnmi_set +from apis.system.rest import rest_call, get_jwt_token + + +def ssh_call(dut, remote_dut=None, **kwargs): + """ + Call to test SSH session using diff users w.r.t RBAC. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param remote_dut: + :param kwargs: + :return: + """ + banner_log('Performing SSH call using - {}'.format(kwargs)) + for each in ['login_type', 'username', 'password', 'mode']: + if not kwargs.get(each): + st.error("Mandatory argument is not found - {}".format(each)) + return False + + dut_ip = st.get_mgmt_ip(dut) + result = {'show': True, 'config': True} + result2 = True + username = kwargs.get('username') + password = kwargs.get('password') + login_type = kwargs.get('login_type') + mode = kwargs.get('mode') + + if login_type == 'cred': + ssh_out = st.exec_ssh(dut, username, password, + ['show vlan config', 'sudo config vlan add 100\n{}'.format(password)]) + st.log(ssh_out) + elif login_type == 'pubkey': + show_out = st.exec_ssh_remote_dut(remote_dut, dut_ip, username, password, 'show vlan config') + config_out = st.exec_ssh_remote_dut(remote_dut, dut_ip, username, password, + 'sudo config vlan add 100\n{}'.format(password)) + ssh_out = show_out + "\n" + config_out + else: + st.error("Invalid 'login_type' used = {}".format(login_type)) + return False + + if not ssh_out: + st.report_fail('rbac_call_fail', "SSH", mode, login_type) + + if 'Sorry, user {} is not allowed to execute'.format(username) in ssh_out or \ + "no askpass program specified" in ssh_out: + result['config'] = False + if 'VID' not in ssh_out: + result['show'] = False + st.log(result) + + msg = 'Failed to execute show command using ssh session with mode- {mode}, type- {login_type}' + if mode == 'rw' and not all(result.values()): + st.error(msg.format(**kwargs)) + result2 = False + if mode == 'ro' and not (result['show'] and not result['config']): + st.error(msg.format(**kwargs)) + result2 = False + + if not result2: + st.report_fail('rbac_test_status', 'Fail', mode, 'SSH', login_type, result) + st.report_pass('rbac_test_status', 'Pass', mode, 'SSH', login_type, result) + + +def gnmi_call(dut, **kwargs): + """ + Call to test gnmi session using diff users w.r.t RBAC. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param kwargs: + :return: + """ + banner_log('Performing gnmi operations using - {}'.format(kwargs)) + + for each in ['login_type', 'username', 'password', 'mode']: + if not kwargs.get(each): + st.error("Mandatory argument is not found - {}".format(each)) + return False + + dut_ip = "127.0.0.1" + result = {'gnmi_get_out': True, 'gnmi_set_out': True} + result2 = True + username = kwargs.get('username') + password = kwargs.get('password') + cert = kwargs.get('cert') + login_type = kwargs.get('login_type') + mode = kwargs.get('mode') + port = st.get_free_ports(dut)[0] + insecure = kwargs.get('insecure', '') + xpath = '/openconfig-interfaces:interfaces/interface[name={}]/config/description'.format(port) + json_content = {"openconfig-interfaces:description": "Eth"} + + if login_type == 'cred': + gnmi_set_out = gnmi_set(dut, xpath, json_content=json_content, ip_address=dut_ip, username=username, + password=password) + st.log(gnmi_set_out) + gnmi_get_out = gnmi_get(dut, xpath, ip_address=dut_ip, username=username, password=password) + st.log(gnmi_get_out) + + elif login_type == 'cert': + gnmi_set_out = gnmi_set(dut, xpath, json_content=json_content, ip_address=dut_ip, cert=cert, + username=None, password=None, insecure='none', target_name='admin') + st.log(gnmi_set_out) + gnmi_get_out = gnmi_get(dut, xpath, ip_address=dut_ip, cert=cert, username=None, password=None, insecure='none', + target_name='admin') + st.log(gnmi_get_out) + + else: + st.error("Invalid 'login_type' used = {}".format(login_type)) + return False + + if mode == 'rw': + if not (gnmi_set_out and gnmi_get_out): + st.report_fail('rbac_call_fail', "gNMI", mode, login_type) + else: + if not (not gnmi_set_out and gnmi_get_out): + st.report_fail('rbac_call_fail', "gNMI", mode, login_type) + + msg = 'Failed to execute set command using gNMI session with mode- {mode}, type- {login_type}' + if mode == 'rw' and "op: UPDATE" not in gnmi_set_out and "description" not in str(gnmi_get_out): + st.error(msg.format(**kwargs)) + result2 = False + if mode == 'ro' and not gnmi_set_out and "description" not in str(gnmi_get_out): + st.error(msg.format(**kwargs)) + result2 = False + + if not result2: + st.report_fail('rbac_test_status', 'Fail', mode, 'gNMI', login_type, result) + st.report_pass('rbac_test_status', 'Pass', mode, 'gNMI', login_type, result) + + +def rest_rbac_call(dut, **kwargs): + """ + Call to test REST sessions using diff users w.r.t RBAC. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param kwargs: + :return: + """ + banner_log('Performing REST call using - {}'.format(kwargs)) + for each in ['login_type', 'username', 'password', 'mode']: + if not kwargs.get(each): + st.error("Mandatory argument is not found - {}".format(each)) + return False + + result = {'get': True, 'put': True} + result2 = True + pass_status = [200, 204] + fail_status = [401, 403] + username = kwargs.get('username') + password = kwargs.get('password') + login_type = kwargs.get('login_type') + mode = kwargs.get('mode') + operation_down = {"sonic-port:admin_status": "down"} + operation_up = {"sonic-port:admin_status": "up"} + port = st.get_free_ports(dut)[0] + device_ip = st.get_mgmt_ip(dut) + cert = kwargs.get("cert") + url = 'restconf/data/sonic-port:sonic-port/PORT/PORT_LIST={}/admin_status'.format(port) + + if login_type == 'cred': + headers1 = {'Content-Type': 'application/yang-data+json', 'Accept': 'application/yang-data+json'} + rest_get_out = rest_call(dut, headers=headers1, username=username, password=password, url=url, call_type='get') + st.log(rest_get_out) + rest_put_out = rest_call(dut, headers=headers1, username=username, password=password, url=url, call_type='put', + data=operation_down) + st.log(rest_put_out) + + elif login_type == 'token': + headers2 = {'Content-Type': 'application/yang-data+json', 'Accept': 'application/yang-data+json', + 'Authorization': 'Bearer {}'} + tocken = get_jwt_token(dut, username=username, password=password) + if not tocken: + st.report_fail('rbac_test_jwt_token_fail', mode, login_type) + headers2['Authorization'] = headers2['Authorization'].format(tocken) + rest_get_out = rest_call(dut, headers=headers2, url=url, call_type='get') + st.log(rest_get_out) + rest_put_out = rest_call(dut, headers=headers2, url=url, call_type='put', data=operation_up) + st.log(rest_put_out) + + elif login_type == 'cert': + get_curl = 'curl --key {} --cert {} ' \ + '"https://localhost/restconf/data/sonic-port:sonic-port/PORT/PORT_LIST={}/admin_status"' \ + ' -k'.format(cert[0], cert[1], port) + out = st.show(dut, get_curl, skip_tmpl=True, skip_error_check=True) + rest_get_out = {'status': 401} + rest_put_out = {'status': 200} + if 'sonic-port:admin_status' in out: + rest_get_out = {'status': 200} + + else: + st.error("Invalid 'login_type' used = {}".format(login_type)) + return False + + if not (rest_get_out and rest_put_out): + st.report_fail('rbac_call_fail', "REST", mode, login_type) + + msg = 'Failed to authenticate using rest session with mode- {mode}, type- {login_type}' + if mode == 'rw' and not (rest_get_out["status"] in pass_status and rest_put_out["status"] in pass_status): + st.error(msg.format(**kwargs)) + result2 = False + if mode == 'ro' and not (rest_get_out["status"] in pass_status and rest_put_out["status"] in fail_status): + st.error(msg.format(**kwargs)) + result2 = False + + if not result2: + st.report_fail('rbac_test_status', 'Fail', mode, 'REST', login_type, result) + st.report_pass('rbac_test_status', 'Pass', mode, 'REST', login_type, result) diff --git a/spytest/apis/security/tacacs.py b/spytest/apis/security/tacacs.py new file mode 100644 index 00000000000..a3024592e5f --- /dev/null +++ b/spytest/apis/security/tacacs.py @@ -0,0 +1,183 @@ +# This file contains the list of API's which performs TACSCS operations. +# @author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + +from spytest.utils import filter_and_select +from spytest import st + +def set_aaa_authentication_properties(dut,property,value): + ''' + Configuring aaa authentication properties. + ''' + command = "config aaa authentication {} {}".format(property,value) + rv = st.config(dut,command) + return True + + +def set_tacacs_properties(dut,property,value): + ''' + Configuring tacacs properties. + ''' + command = "config tacacs {} {}".format(property,value) + rv = st.config(dut,command) + return True + + +def set_tacacs_server(dut,mode,address,tcp_port=None,timeout=None,passkey=None,auth_type=None,priority=None,use_mgmt_vrf= False): + ''' + Configuring tacacs server properties. + ''' + sub_opts = [] + if mode.lower() == 'add': + command = "config tacacs {} {} ".format('add',address) + if tcp_port: + sub_opts.append('{} {}'.format('-o',tcp_port)) + if timeout: + sub_opts.append('{} {}'.format('-t',timeout)) + if passkey: + sub_opts.append('{} {}'.format('-k',passkey)) + if auth_type: + sub_opts.append('{} {}'.format('-a',auth_type)) + if priority: + sub_opts.append('{} {}'.format('-p',priority)) + if use_mgmt_vrf: + sub_opts.append('{}'.format('-m')) + command = command + ' '.join(sub_opts) + + elif mode.lower() == 'delete': + command = "config tacacs {} {} ".format('delete',address) + + rv = st.config(dut,command) + return True + +def show_aaa(dut): + ''' + To get the show aaa command output as list of dict + ''' + command = "show aaa" + return st.show(dut,command) + +def show_tacacs(dut): + ''' + To get the show tacacs command output as list of dict + ''' + command = "show tacacs" + out = st.show(dut,command) + ## Dividing show tacacs output as global and server + rv ={'global':[],'servers':[]} + rv['global']= [{'auth_type':out[0]['global_auth_type']\ + ,'passkey':out[0]['global_passkey']\ + ,'timeout':out[0]['global_timeout']}] + for each_dict in out: + if each_dict['address'] != '' and each_dict['priority'] != '': + temp_dic = { + 'address':each_dict['address']\ + ,'priority':each_dict['priority']\ + ,'tcp_port':each_dict['tcp_port'] \ + ,'passkey': each_dict['passkey']\ + ,'auth_type':each_dict['auth_type']\ + ,'timeout':each_dict['timeout'] + } + + rv['servers'].append(temp_dic) + return rv + +def verify_aaa(dut,login=None,failthrough=None,fallback=None): + ''' + To verify the 'show aaa' parameters + ''' + output = show_aaa(dut) + if login and not filter_and_select(output, ['login'], {"login": login}): + st.error("Provided and Configured login values are not match.") + return False + if failthrough and not filter_and_select(output, ['failthrough'], {"failthrough": failthrough}): + st.error("Provided and Configured failthrough values are not match.") + return False + if fallback and not filter_and_select(output, ['fallback'], {"fallback": fallback}): + st.error("Provided and Configured fallback values are not match.") + return False + return True + + +def verify_tacacs_global(dut,auth_type=None,timeout=None,passkey=None): + ''' + To verify the 'show tacacs' global parameters + ''' + output = show_tacacs(dut) + output = output['global'] + if auth_type and not filter_and_select(output, ['auth_type'], {"auth_type": auth_type}): + st.error("Global:Provided and Configured auth_type values are not match.") + return False + if timeout and not filter_and_select(output, ['timeout'], {"timeout": timeout}): + st.error("Global:Provided and Configured timeout values are not match.") + return False + if passkey and not filter_and_select(output, ['passkey'], {"passkey": passkey}): + st.error("Global:Provided and Configured passkey values are not match.") + return False + return True + + +def verify_tacacs_server(dut,address,tcp_port=None,timeout=None,passkey=None,auth_type=None,priority=None): + ''' + To verify the 'show tacacs' server parameters + ''' + output = show_tacacs(dut) + output = output['servers'] + if address and not filter_and_select(output, ['address'], {"address": address}): + st.error("Provided and configured address values are not matching.") + return False + if tcp_port and not filter_and_select(output, ['tcp_port'], {"tcp_port": tcp_port}): + st.error("Provided and configured tcp_port values are not matching.") + return False + if priority and not filter_and_select(output, ['priority'], {"priority": priority}): + st.error("Provided and configured priority values are not matching.") + return False + if timeout and not filter_and_select(output, ['timeout'], {"timeout": timeout}): + st.error("Provided and configured timeout values are not matching.") + return False + if passkey and not filter_and_select(output, ['passkey'], {"passkey": passkey}): + st.error("Provided and configured passkey values are not matching.") + return False + if auth_type and not filter_and_select(output, ['auth_type'], {"auth_type": auth_type}): + st.error("Provided and configured auth_type values are not matching.") + return False + return True + + +def verify_tacacs_details(dut, tacacs_params): + """ + API to verify the tacacs details + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param tacacs_params: + :return: + """ + if tacacs_params: + output = show_tacacs(dut) + if output and "servers" in output: + output = output['servers'] + for params in tacacs_params: + if params["ip"] and not filter_and_select(output, ['address'], {"address": params["ip"]}): + st.error("Provided and configured address values are not matching.") + return False + if params["tcp_port"] and not filter_and_select(output, ['tcp_port'], {"tcp_port": params["tcp_port"]}): + st.error("Provided and configured tcp_port values are not matching.") + return False + if params["priority"] and not filter_and_select(output, ['priority'], {"priority": params["priority"]}): + st.error("Provided and configured priority values are not matching.") + return False + if params["timeout"] and not filter_and_select(output, ['timeout'], {"timeout": params["timeout"]}): + st.error("Provided and configured timeout values are not matching.") + return False + if params["passkey"] and not filter_and_select(output, ['passkey'], {"passkey": params["passkey"]}): + st.error("Provided and configured passkey values are not matching.") + return False + if params["auth_type"] and not filter_and_select(output, ['auth_type'], {"auth_type": params["auth_type"]}): + st.error("Provided and configured auth_type values are not matching.") + return False + return True + else: + st.log("servers index not found in output ...") + return False + else: + st.log("tacacs params not provided ...") + return False \ No newline at end of file diff --git a/spytest/apis/security/user.py b/spytest/apis/security/user.py new file mode 100644 index 00000000000..f8a022136f6 --- /dev/null +++ b/spytest/apis/security/user.py @@ -0,0 +1,152 @@ +# This file contains the list of API's which performs User operations. +# Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + +from spytest import st +from utilities.common import filter_and_select, make_list + + +def config_user(dut, username, mode='add'): + """ + Add/Delete the user name to the device. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param username: + :param mode: add|del + :return: + """ + username = username.strip() + if mode == 'add': + command = "useradd {}".format(username) + rv = st.config(dut, command) + if "already exists" in rv: + st.error("User '{}' already exists".format(username)) + return False + else: + command = "userdel {}".format(username) + rv = st.config(dut, command) + if "does not exist" in rv: + st.log("User '{}' does not exist".format(username)) + return False + return True + + +def show(dut, *argv, **kwargs): + """ + Generic show API. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param argv: + :param kwargs: + :Usage: + show(vars.D1, 'logged_users') + show(vars.D1, 'user_list') + show(vars.D1, 'group_list') + show(vars.D1, user_group='admin') + """ + cli_type = kwargs.get("cli_type", "click") + if 'logged_users' in argv: + return st.show(dut, "show users", type=cli_type) + if 'user_list' in argv: + command = "cat /etc/passwd | awk -F: '{ print $1}'" + return st.show(dut, command, skip_tmpl=True, skip_error_check=True, faster_cli=False).split('\n')[:-1] + if 'group_list' in argv: + command = "getent group | awk -F: '{ print $1}'" + return st.show(dut, command, skip_tmpl=True, skip_error_check=True, faster_cli=False).split('\n')[:-1] + if kwargs.get('user_group'): + return st.show(dut, "id {}".format(kwargs['user_group']), type=cli_type) + + +def verify(dut, *argv, **kwargs): + """ + Generic verify API. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param argv: + :param kwargs: + :Usage: + verify(vars.D1, 'logged_users', verify_list=[{'user':'admin'}, {'user':'test1}]) + verify(vars.D1, 'user_list', verify_list=['admin', 'test1']) + verify(vars.D1, 'group_list', verify_list=['admin','operator']) + verify(vars.D1, user_group='admin', verify_list=[{'group':'admin'}]) + """ + result = True + if not kwargs.get('verify_list'): + st.error("Mandatory parameter -verify_list is missing") + return False + out = show(dut, *argv, **kwargs) + if 'logged_users' in argv or kwargs.get('user_group'): + for each in make_list(kwargs.get('verify_list')): + if not filter_and_select(out, None, each): + st.log("{} - {} is not match".format(each, out)) + result = False + + if 'user_list' in argv or 'group_list' in argv: + for each in make_list(kwargs.get('verify_list')): + if each not in out: + st.log("{} - is not found in {}".format(each, out)) + result = False + + return result + + +def config(dut, **kwargs): + """ + Add/Delete username with password and role to the device. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param :dut: + :param :username: + :param :password: + :param :role: admin | operator + :param :group: + :param :cli_type: click | klish + :param :no_form: 0[False] | 1[True] + + :Usage: + config(vars.D1, username='test', password='test123', role='operator', cli_type='kilsh') + config(vars.D1, username='test', cli_type='kilsh', no_form=True) + config(vars.D1, username='test', password='test123', role='admin', cli_type='click', no_form=0) + config(vars.D1, username='test', password_update='test1234', cli_type='click', no_form=0) + config(vars.D1, group='admin_test', cli_type='click', no_form=0) + config(vars.D1, group='admin_test', cli_type='click', no_form=1) + config(vars.D1, username='test', password='test123', role='admin', cli_type='click', no_form=1) + """ + cli_type = kwargs.get("cli_type", "klish") + no_form = kwargs.get("no_form", False) + + if cli_type == "click": + if not no_form: + if kwargs.get('group'): + st.config(dut, "groupadd {}".format(kwargs['group'])) + if kwargs.get('username'): + command = "useradd {} -m".format(kwargs['username']) + if kwargs.get('role'): + command += " -g {}".format(kwargs['role']) + st.config(dut, command) + if kwargs.get('username') and kwargs.get('password'): + st.change_passwd(dut, kwargs['username'], kwargs['password']) + if kwargs.get('username') and kwargs.get('append_role'): + st.config(dut, "usermod -aG {} {}".format(kwargs['append_role'], kwargs['username'])) + else: + if kwargs.get('username') and kwargs.get('role'): + st.config(dut, "gpasswd -d {} {}".format(kwargs['username'], kwargs['role'])) + if kwargs.get('group'): + st.config(dut, "groupdel {}".format(kwargs['group'])) + if kwargs.get('username'): + st.config(dut, "userdel {} -r".format(kwargs['username'])) + + elif cli_type == "klish": + if not kwargs.get('username'): + st.error("Mandatory parameter 'username' is missing") + if not no_form: + if not kwargs.get("password") and not kwargs.get('role'): + st.error("Mandatory parameter 'password' and 'role' is missing") + return False + command = "username {} password {} role {}".format(kwargs['username'], kwargs['password'], kwargs['role']) + if no_form: + command = "no username {} ".format(kwargs['username']) + st.config(dut, command, type=cli_type, skip_error_check=True) + + else: + return False + + return True diff --git a/spytest/apis/switching/__init__.py b/spytest/apis/switching/__init__.py new file mode 100644 index 00000000000..d8757bd94aa --- /dev/null +++ b/spytest/apis/switching/__init__.py @@ -0,0 +1,2 @@ +__all__ = ['mac', 'portchannel', 'stp', 'vlan'] + diff --git a/spytest/apis/switching/mac.py b/spytest/apis/switching/mac.py new file mode 100644 index 00000000000..9e1646f8c9f --- /dev/null +++ b/spytest/apis/switching/mac.py @@ -0,0 +1,321 @@ +from spytest.utils import filter_and_select +from spytest import st +from spytest.utils import exec_all +import apis.system.basic as basic_obj +import utilities.utils as utils_obj + + +def get_mac(dut,**kwargs): + """ + :param dut: + :type dut: + :return: + :rtype: + """ + cli_type = kwargs.get("cli_type") if kwargs.get("cli_type") else "click" + if cli_type == "click": + return st.show(dut, "show mac") + elif cli_type == "klish": + response = dict() + attrs = ["address", "interface", "type", "vlan", "count"] + command = "show mac address-table" + if kwargs.get("count"): + command += " count" + output = st.show(dut, command, type=cli_type) + if output: + for data in output: + for key, value in data.items(): + if value: + response[key] = value + return response + else: + for attr in attrs: + if kwargs.get(attr): + if attr not in ["type", "count"]: + if attr == "interface": + interface_number = utils_obj.get_interface_number_from_name(kwargs.get(attr)) + if interface_number: + command += " {} {} {}".format(attr, interface_number["type"], interface_number["number"]) + elif attr == "type": + command += " {} {}".format(attr, kwargs.get(attr)) + else: + command += " {}".format(kwargs.get(attr)) + return st.show(dut, command, type=cli_type) + else: + st.log("Invalid cli type") + return False + + + +def get_mac_all_intf(dut, intf): + """ + :param dut: + :type dut: + :return: + :rtype: + """ + output = get_mac(dut) + retval = [] + entries = filter_and_select(output, ["macaddress"], {'port': str(intf)}) + for ent in entries: + retval.append(ent["macaddress"]) + return retval + + +def get_mac_all(dut, vlan, cli_type="click"): + """ + :param dut: + :type dut: + :return: + :rtype: + """ + output = get_mac(dut, cli_type=cli_type) + retval = [] + entries = filter_and_select(output, ["macaddress"], {'vlan': str(vlan)}) + for ent in entries: + retval.append(ent["macaddress"]) + return retval + + +def get_mac_entries_by_mac_address(dut, mac_address): + command="show mac | grep {}".format(mac_address) + mac_entries = st.show(dut, command) + if not mac_entries: + return list + return mac_entries + + +def get_mac_count(dut): + """ + To get the MAC count using - 'show mac count' command. + :param dut: + :type dut: + :return: + :rtype: + """ + output = st.show(dut, "show mac count") + count = int(output[0]['mac_count']) + return count + + +def get_mac_address_count(dut, vlan=None, port=None, type=None, mac_search=None): + """ + To verify the MAC count after applying given filters vlan/port/type/mac_pattern + :param dut: + :param vlan: vlan id which needs to be filtered + :param port: port which needs to be filtered like Ethernet4/PortChannel1 + :param type: mac type to be filtered, Values can be Static/Dynamic + :param mac_search: mac_pattern to be grepped from show mac output + :return: + """ + dec_flag = 0 + if mac_search: + entries = get_mac_entries_by_mac_address(dut, mac_search) + else: + entries = get_mac(dut) + ###Decrement by 1 as output has "Total number of entries" as one list element + dec_flag = 1 + if entries == list or entries == None: + ### If entries is null, no need to apply filter, return 0 + return 0 + + if vlan: + entries = filter_and_select(entries, None, {"vlan": str(vlan)}) + dec_flag = 0 + if port: + entries = filter_and_select(entries, None, {"port": port}) + dec_flag = 0 + if type: + entries = filter_and_select(entries, None, {"type": type}) + dec_flag = 0 + return len(entries)-1 if dec_flag==1 else len(entries) + + +def verify_mac_address(dut, vlan, mac_addr): + """ + + :param dut: + :param vlan: + :param mac_addr: + :return: + """ + + if basic_obj.is_vsonic_device(dut): + st.log("For vSONiC device, waiting for 10 sec") + st.wait(10) + + st.log("Checking provided mac entries are present in mac table under specified vlan") + mac_address_all = get_mac_all(dut, vlan) + mac_addr_list = [mac_addr] if type(mac_addr) is str else mac_addr + return set(mac_addr_list).issubset(set(mac_address_all)) + + +def get_sbin_intf_mac(dut, interface): + """ + This proc is to return the mac address of the interface from the ifconfig o/p. + :param dut: DUT Number + :param interface: Interface number + :return: + """ + my_cmd = "/sbin/ifconfig {}".format(interface) + output = st.show(dut, my_cmd) + output = dict(output[0]) + mac = output['mac'] + return mac + + +def clear_mac(dut,port=None,vlan=None,**kwargs): + """ + This proc is to clear mac address/fdb entries of the dut. + :param dut: DUT Number + :return: + """ + cli_type = kwargs.get('cli_type', st.get_ui_type(dut)) + if cli_type == "click": + if port: + command = "sonic-clear fdb port {}".format(port) + elif vlan: + command = "sonic-clear fdb vlan Vlan{}".format(vlan) + else: + command = "sonic-clear fdb all" + elif cli_type == "klish": + if kwargs.has_key('address'): + command = "clear mac address-table address {}".format(kwargs['address']) + elif kwargs.has_key('vlan'): + command = "clear mac address-table dynamic Vlan {}".format(kwargs['vlan']) + else: + command = "clear mac address-table dynamic all" + + st.config(dut, command,cli_type=cli_type) + return True + + +def config_mac(dut, mac, vlan, intf): + """ + :param dut: + :type dut: + :return: + :rtype: + """ + #st.log("config mac add ") + command = "sudo config mac add {} {} {}".format(mac, vlan, intf) + st.config(dut, command) + return True + + +def delete_mac(dut, mac, vlan): + """ + :param dut: + :type dut: + :return: + :rtype: + """ + #st.log("config mac del ") + command = "sudo config mac del {} {}".format(mac, vlan) + st.config(dut, command) + return True + + +def config_mac_agetime(dut, agetime): + """ + :param dut: + :type dut: + :return: + :rtype: + """ + #st.log("config mac aging_time") + command = "sudo config mac aging_time {}".format(agetime) + st.config(dut, command) + return True + + +def config_fdb_aging_time(dut, agetime): + """ + This proc is to clear mac address/fdb entries of the dut. + :param dut: DUT Number + :param agetime: fdg age time in seconds + :return: + """ + st.config(dut, "config mac aging_time {}".format(agetime)) + return True + + +def get_mac_agetime(dut): + """ + :param dut: + :type dut: + :return: + :rtype: + """ + output = st.show(dut, "show mac aging_time") + retval = output[0]["aging_time"] + return int(retval) + + +def get_mac_address_list(dut, mac=None, vlan=None, port=None, type=None): + """ + + :param dut: + :param mac: + :param vlan: + :param port: + :param type: + :return: + """ + entries = get_mac(dut) + if mac: + entries = filter_and_select(entries, None, {"macaddress": str(mac)}) + if vlan: + entries = filter_and_select(entries, None, {"vlan": str(vlan)}) + if port: + entries = filter_and_select(entries, None, {"port": port}) + if type: + entries = filter_and_select(entries, None, {"type": type}) + return [ent["macaddress"] for ent in filter_and_select(entries, ['macaddress'], None)] + + +def verify_mac_address_table(dut, mac_addr, vlan=None, port=None, type=None, dest_ip=None): + """ + To verify the MAC parameters + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param mac_addr: + :param vlan: + :param port: + :param type: + :param dest_ip: + :return: + """ + + if basic_obj.is_vsonic_device(dut): + st.log("For vSONiC device, waiting for 10 sec") + st.wait(10) + + output = get_mac(dut) + entries = filter_and_select(output, None, {"macaddress": mac_addr}) + if not entries: + st.log("Provided MAC {} entry is not exist in table".format(mac_addr)) + return False + if vlan and not filter_and_select(entries, None, {"vlan": str(vlan)}): + st.log("Provided VLAN {} is not exist in table with MAC {}".format(vlan, mac_addr)) + return False + if port and not filter_and_select(entries, None, {"port": port}): + st.log("Provided Port {} is not exist in table with MAC {}".format(port, mac_addr)) + return False + if type and not filter_and_select(entries, None, {"type": type}): + st.log("Provided Type {} is not exist in table with MAC {}".format(type, mac_addr)) + return False + if dest_ip and not filter_and_select(entries, None, {"dest_ip": dest_ip}): + st.log("Provided DEST_IP {} is not exist in table with MAC {}".format(dest_ip, mac_addr)) + return False + return True + + +def get_mac_all_dut(dut_list, thread=True): + st.log("Displaying mac ..in all dut") + dut_li = list([str(e) for e in dut_list]) if isinstance(dut_list, list) else [dut_list] + params = list() + for dut in dut_li: + params.append([get_mac, dut]) + if params: + exec_all(thread, params) diff --git a/spytest/apis/switching/portchannel.py b/spytest/apis/switching/portchannel.py new file mode 100644 index 00000000000..3a24edc275a --- /dev/null +++ b/spytest/apis/switching/portchannel.py @@ -0,0 +1,945 @@ +# This file contains the list of API's which performs Port channel operations. +# Author : Chaitanya Vella (Chaitanya-vella.kumar@broadcom.com) +import re +from spytest import st +import utilities.common as utils +import utilities.utils as uutils +import utilities.parallel as putils + +def create_portchannel(dut, portchannel_list=[], fallback=False, min_link="", static=False, cli_type=""): + """ + API to Create port channel with the provided data + :param dut: + :type dut: + :param portchannel_list: + :type portchannel_list: + :return: + :rtype: + """ + if not cli_type: + cli_type = st.get_ui_type(dut) + + st.log("Creating port channel {} ..".format(portchannel_list)) + if cli_type == "click": + for portchannel_name in utils.make_list(portchannel_list): + if not fallback: + if not min_link: + static_flag = "--static=true" if static else "" + command = "config portchannel add {} {}".format(portchannel_name, static_flag).strip() + else: + command = "config portchannel add {} --min-links {}".format(portchannel_name, min_link) + else: + if static: + return False + if not min_link: + command = "config portchannel add {} --fallback=true".format(portchannel_name) + else: + command = "config portchannel add {} --fallback=true --min-links {}".format(portchannel_name,min_link) + st.config(dut, command, skip_error_check=True) + return True + elif cli_type == "klish": + commands = list() + for portchannel_name in utils.make_list(portchannel_list): + intf_data = uutils.get_interface_number_from_name(portchannel_name) + if not static: + commands.append("interface PortChannel {}".format(intf_data["number"])) + if min_link: + commands.append("minimum-links {}".format(min_link)) + if fallback: + commands.append("fallback enable") + else: + commands.append("interface PortChannel {} mode on".format(intf_data["number"])) # This is incomplete, will add support once this defect SONIC-15643 is closed. + commands.append("no shutdown") + commands.append("exit") + if commands: + st.config(dut, commands, type=cli_type, skip_error_check=True) + return True + return False + else: + st.log("Unsupported CLI type") + return False + + +def delete_portchannel(dut, portchannel_list, cli_type=""): + """ + API to Delete port channel with the provided data + :param dut: + :type dut: + :param portchannel_list: + :type portchannel_list: + :return: True The Portchannel was successfully deleted. + :return: False The Portchannel was not successfully deleted. + :return: False Error in parameter passed. + :rtype: + """ + if not cli_type: + cli_type = st.get_ui_type(dut) + + st.log("Deleting port channel {} ..".format(portchannel_list)) + commands = list() + try: + for portchannel_name in utils.make_list(portchannel_list): + if cli_type=="click": + command = "config portchannel del {}".format(portchannel_name) + response = st.config(dut, command) + if "Error" in response: + st.log(response) + return False + else: + if not get_portchannel(dut, portchannel_name): + st.log("Portchannel {} deleted successfully ..".format(portchannel_name)) + return True + else: + return False + elif cli_type == "klish": + commands.append("no interface PortChannel {}".format(portchannel_name.replace("PortChannel", ""))) + else: + st.log("Unsupported CLI type") + return False + if commands: + st.config(dut, commands, type=cli_type) + return True + except Exception as e: + st.error("ERROR: DELETE port channel {} ".format(str(e))) + return False + + +def delete_all_portchannels(dut, cli_type="click"): + """ + API to Delete ALL port channels. + :param dut: + :type dut: + :return: True The Portchannel(s) was successfully deleted. + :return: False The Portchannel(s) was not successfully deleted. + :return: False Error in parameter passed. + :rtype: + """ + available_portchannels = list() + st.log("Deleting all availabe port channels...") + for portchannel in get_portchannel_list(dut, cli_type=cli_type): + if cli_type == "click": + available_portchannels.append(portchannel["teamdev"]) + elif cli_type == "klish": + available_portchannels.append(portchannel["name"]) + else: + st.log("Unsupported CLI type") + return False + response = delete_portchannel(dut, available_portchannels, cli_type=cli_type) + return response + + +def get_portchannel(dut, portchannel_name="", cli_type=""): + """ + This API is used to get the portchannel details + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param portchannel_name: + :return: + FOR KLISH : [{u'group': '1', u'name': 'PortChannel1', u'state': 'D', + 'members': [{u'port_state': 'D', u'port': 'Ethernet56'}, {u'port_state': 'U', u'port': 'Ethernet60'}], + u'protocol': 'LACP', u'type': 'Eth'}, {u'group': '10', u'name': 'PortChannel10', u'state': 'U', + 'members': [{u'port_state': 'D', u'port': 'Ethernet40'}], u'protocol': 'LACP', u'type': 'Eth'}, + {u'group': '111', u'name': 'PortChannel111', u'state': 'D', 'members': [], u'protocol': 'LACP', u'type': 'Eth'}] + """ + if not cli_type: + cli_type = st.get_ui_type(dut) + + st.log("Getting port channel {} details ...".format(portchannel_name)) + result = dict() + try: + if cli_type == "click": + if portchannel_name: + command = "show interfaces portchannel | grep -w {}".format(portchannel_name) + rv = st.show(dut, command) + return rv + else: + return False + elif cli_type == "klish": + command = "show PortChannel summary" + output = st.show(dut, command, type=cli_type) + if portchannel_name: + output = utils.filter_and_select(output, match={'name': portchannel_name}) + if output: + for data in output: + portchannel_data = dict() + portchannel_data["members"] = list() + members = dict() + for key, value in data.items(): + if key not in ["port","port_state"]: + portchannel_data[key] = value + else: + if value: + members[key] = value + if members: + portchannel_data["members"].append(members) + if portchannel_data: + if not portchannel_data["name"] in result: + result[portchannel_data["name"]] = portchannel_data + else: + result[portchannel_data["name"]]["members"].append(members) + response = list() + if result: + for pc_name, pc_data in result.items(): + response.append(pc_data) + return response + else: + st.log("Unsupported CLI type") + return False + except Exception as e: + st.error("ERROR: Get PortChannel {}".format(str(e))) + return False + +def delete_portchannels(dut, portchannel_list, cli_type="click"): + """ + API to Delete port channel with the provided data + :param dut: + :type dut: + :param portchannel_list: + :type portchannel_list: + :return: True The Portchannel was successfully deleted. + :return: False The Portchannel was not successfully deleted. + :return: False Error in parameter passed. + :rtype: + """ + st.log("Deleting port channel {} ..".format(portchannel_list)) + commands = list() + try: + for portchannel_name in utils.make_list(portchannel_list): + if cli_type=="click": + command = "config portchannel del {}".format(portchannel_name) + response = st.config(dut, command) + if "Error" in response: + st.log(response) + return False + elif cli_type == "klish": + commands.append("no interface PortChannel {}".format(portchannel_name.replace("PortChannel", ""))) + else: + st.log("Unsupported CLI type") + return False + if commands: + st.config(dut, commands, type=cli_type) + return True + except Exception as e: + st.error("ERROR: DELETE port channel {} ".format(str(e))) + return False + + +def verify_portchannel(dut, portchannel_name, cli_type=""): + """ + This API is used to verify the portchannel. + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param dut: + :param portchannel_name: + :return: + """ + if not cli_type: + cli_type = st.get_ui_type(dut) + + st.log("Verifying port channel {} ...".format(portchannel_name)) + details = get_portchannel(dut, portchannel_name, cli_type=cli_type) + return False if not details else True + + +def get_portchannel_list(dut, cli_type=""): + """ + This API is used to get the list of portchannel details + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param dut: + :return: + """ + if not cli_type: + cli_type = st.get_ui_type(dut) + + if dut: + st.log("Getting all the list of port channels ..") + if cli_type == "click": + command = "show interfaces portchannel" + response = st.show(dut, command) + return response + elif cli_type == "klish": + return get_portchannel(dut, cli_type=cli_type) + else: + st.log("Unsupported CLI type") + return False + else: + st.error("Get PortChannel List Invalid DUT object") + return False + + +def add_portchannel_member(dut, portchannel="", members=[], cli_type=""): + """ + This API is used to add the members to portchannel + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param dut: + :param portchannel: + :param members: + :return: + """ + if not cli_type: + cli_type = st.get_ui_type(dut) + + return add_del_portchannel_member(dut, portchannel, members, flag="add", cli_type=cli_type) + + +def get_portchannel_members(dut, portchannel, with_state=False, cli_type="click"): + """ + This API is used to get the members of portchannel + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param dut: + :param portchannel: + :param with_state: + :return: + """ + st.log("Getting portchannel members ...") + if not portchannel: + st.error("PortChannel Member GET Error: Missing portchannel name") + return False + portchannel_details = get_portchannel(dut, portchannel, cli_type=cli_type) + if not portchannel_details: + st.error("Port Channel Members GET: PortChannel {} not found".format(portchannel)) + return False + else: + if cli_type == "click": + if 'ports' in portchannel_details[0]: + members = re.findall(r'Ethernet\d+', portchannel_details[0]['ports']) + if with_state: + return portchannel_details[0]['ports'].split(" ") + return members + else: + st.error("Members not found in mentioned portchannel") + return False + elif cli_type == "klish": + members = list() + if "members" in portchannel_details[0]: + for member in portchannel_details[0]["members"]: + if not with_state: + members.append(member.get("port")) + else: + members.append("{}({})".format(member.get("port"),member.get("port_state"))) + return members + else: + st.log("Members not found") + return False + + +def verify_portchannel_member(dut, portchannel, members, flag='add', cli_type="click"): + """ + This API is used to verify the members of portchannel + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param dut: + :param portchannel: + :param members: + :param flag: + :return: + """ + st.log("Verifying port channel members ...") + portchannel_members = get_portchannel_members(dut, portchannel, cli_type=cli_type) + if flag == 'add': + if not portchannel_members: + st.error("ERROR in port channel members") + return False + for member in utils.make_list(members): + if member not in portchannel_members: + return False + return True + elif flag == 'del': + for member in utils.make_list(members): + if member in portchannel_members: + return False + return True + + +def add_del_portchannel_member(dut, portchannel, members, flag="add", skip_verify=True, cli_type="", skip_err_check=False): + """ + This API is used to add or delete the members of the portchannel + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param dut: + :param portchannel: + :param members: + :param flag: + :param skip_verify: + :return: + """ + if not cli_type: + cli_type = st.get_ui_type(dut) + + if flag != "add" and flag != "del": + st.error("Invalid input to add del portchannel member internal api call...") + return False + action = "Adding" if flag == "add" else "Deleting" + st.log("{} port channel member ...".format(action)) + if not portchannel: + st.error("Port Channel Member {} Error: Missing portchannel name".format(action)) + return False + if cli_type == "click": + if not skip_verify: + portchannel_details = get_portchannel(dut, portchannel) + if not portchannel_details: + st.error("Port Channel Members {}: PortChannel {} not found".format(action, portchannel)) + return False + for member in utils.make_list(members): + command = "config portchannel member {} {} {}".format(flag, portchannel, member) + st.config(dut, command, skip_error_check=skip_err_check) + if not skip_verify: + if flag == 'add': + if not verify_portchannel_member(dut, portchannel, member, flag): + st.error("Member {} not present in port channel member list {}".format(member, portchannel)) + return False + else: + if not verify_portchannel_member(dut, portchannel, member, flag): + st.error("Member {} present in port channel member list {}".format(member, portchannel)) + return False + elif cli_type == "klish": + commands = list() + for member in utils.make_list(members): + intf_details = uutils.get_interface_number_from_name(member) + if not intf_details: + st.log("Interface data not found for {} ".format(member)) + commands.append("interface {} {}".format(intf_details["type"], intf_details["number"])) + if flag == "add": + channel_number = uutils.get_interface_number_from_name(portchannel)["number"] + commands.append("channel-group {}".format(channel_number)) + else: + commands.append("no channel-group") + commands.append("exit") + if commands: + st.config(dut, commands, type=cli_type, skip_error_check=skip_err_check) + else: + st.log("add_del_portchannel_member : Unsupported CLI type") + return False + return True + + +def delete_portchannel_member(dut, portchannel, members, cli_type=""): + """ + This API is used to delete the member of the portchannel + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param portchannel: + :param members: + :return: + """ + if not cli_type: + cli_type = st.get_ui_type(dut) + + return add_del_portchannel_member(dut, portchannel, members, flag="del", cli_type=cli_type) + + +def verify_portchannel_state(dut, portchannel, state="up", error_msg=True,cli_type="click"): + """ + This API is used to verify the portchannel state + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param portchannel: + :param state: + :return: + """ + st.log("Verifying portchannel state with provided state {}...".format(state.capitalize())) + supported_states = ['up', 'down'] + if state not in supported_states: + st.error("Invalid states provided in verify portchannel state") + return False + if not portchannel: + st.error("Port Channel state verification Error: Missing portchannel name") + return False + portchannel_details = get_portchannel(dut, portchannel, cli_type=cli_type) + if not portchannel_details: + st.error("PortChannel {} not found".format(portchannel)) + return False + if cli_type == "click": + state = "Up" if state == "up" else "Dw" + state_match = r'LACP\(A\)\({}\)|NONE\(A\)\({}\)'.format(state.capitalize(), state.capitalize()) if cli_type == "click" else r'LACP|NONE' + for details in portchannel_details: + if 'protocol' in details: + if not re.match(state_match, details['protocol']): + st.log("Portchannel state is {} ...".format(state.capitalize())) + if error_msg: + st.error("Portchannel state verification failed with state {}".format(state.capitalize())) + return False + else: + st.log("Portchannel state is {} ...".format(state.capitalize())) + if error_msg: + st.log("Portchannel state verification passed with state {}".format(state.capitalize())) + elif cli_type == "klish": + state = "U" if state == "up" else "D" + for details in portchannel_details: + if details["name"] != portchannel: + st.log("Portchannel name is not matching") + return False + if details["state"] != state: + st.log("Mismatch in portchannel state with {} and expecting {}".format(state, details["state"])) + return False + else: + st.log("UNSUPPORTED CLI Type") + return False + return True + +def poll_for_portchannel_status(dut, portchannel, state="up", iteration=90, delay=1, cli_type="click"): + """ + API to poll for portchannel state + :param dut: + :param portchannel: + :param state: + :param iteration: + :param delay: + :return: + """ + i=0 + while True: + if verify_portchannel_state(dut, portchannel, state, False,cli_type=cli_type): + st.log("Observed port channel {} with state as {}".format(portchannel, state)) + return True + if i > iteration: + st.log("Max iteration count reached {}".format(i)) + return False + i+=1 + st.wait(delay) + + +def verify_portchannel_member_state(dut, portchannel, members_list, state='up', cli_type="click"): + """ + This API is used to verify the member state of a portchannel + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param dut: + :param portchannel: + :param members_list: + :param state: + :return: + """ + st.log("Verifying portchannel state with provided state ...") + supported_states = ['up', 'down'] + if state not in supported_states: + st.error("Invalid states provided in verify portchannel state") + return False + if not portchannel: + st.error("Port Channel state verification Error: Missing portchannel name") + return False + members = get_portchannel_members(dut, portchannel, True, cli_type=cli_type) + if not members: + st.error("PortChannel {} members {} not found".format(portchannel, members)) + return False + if cli_type == "click": + member_state = 'S' if state == 'up' else 'D' + elif cli_type == "klish": + member_state = 'U' if state == 'up' else 'D' + member_list = [members_list] if type(members_list) is str else members_list + for member in member_list: + member_state_match = '{}({})'.format(member, member_state) + if member_state_match not in members: + st.error("Portchannel member {} state verification failed with state {}".format(member, state.capitalize())) + return False + st.log("Portchannel all member state verification successful with state {}".format(state.capitalize())) + return True + + +def verify_portchannel_fallback(dut, portchannel): + """ + This API is used to verify the portchannel fallback functionality + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param dut: + :param portchannel: + :return: + """ + command = "teamdctl {} config dump".format(portchannel) + response = st.config(dut, command) + + +def _clear_portchannel_configuration_helper(dut_list,cli_type="click"): + """ + This is the helper function to clear the portchannel configuration + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param dut_list: + :return: + """ + dut_li = list(dut_list) if isinstance(dut_list, list) else [dut_list] + for dut in dut_li: + st.log("############## {} : Port Channel Cleanup ################".format(dut)) + portchannel_list = get_portchannel_list(dut, cli_type=cli_type) + if portchannel_list: + for portchannel in portchannel_list: + portchannel_name = portchannel["teamdev"] if cli_type == "click" else portchannel["name"] + portchannel_member = get_portchannel_members(dut, portchannel_name) + if portchannel_member: + if not delete_portchannel_member(dut, portchannel_name, portchannel_member): + st.log("Error while deleting portchannel members") + return False + if not delete_portchannel(dut, portchannel_name): + st.log("Portchannel deletion failed {}".format(portchannel_name)) + return False + return True + + +def clear_portchannel_configuration(dut_list, thread=True, cli_type="click"): + """ + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut_list: + :param thread: True (Default) / False + :return: + """ + dut_li = list(dut_list) if isinstance(dut_list, list) else [dut_list] + [out, exceptions] = utils.exec_foreach(thread, dut_li, _clear_portchannel_configuration_helper, cli_type=cli_type) + putils.ensure_no_exception(exceptions) + return False if False in out else True + + +def verify_portchannel_and_member_status(dut, portchannel, members, iter_count=6, iter_delay=1, state='up', cli_type="click"): + """ + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param portchannel: + :param members: + :param iter_count: + :param iter_delay: + :param state: + :return: + """ + if not verify_portchannel(dut, portchannel, cli_type=cli_type): + st.log("Port channel {} not present".format(portchannel)) + return False + + if not verify_portchannel_member(dut, portchannel, members, 'add', cli_type=cli_type): + st.log("Members are not added to the Port channel {}".format(portchannel)) + return False + + i = 1 + while True: + st.log("Checking iteration {}".format(i)) + if verify_portchannel_member_state(dut, portchannel, members, state, cli_type=cli_type): + st.log("All members of port channel are {}".format(state)) + return True + if i > iter_count: + st.log("Exiting from the loop.. iter_count reaches max {}".format(i)) + return False + i += 1 + st.wait(iter_delay) + +def config_portchannel_min_link(dut, **kwargs): + """ + Author:gangadhara.sahu@broadcom.com + :param portchannel: + :type portchannel-number: + :param min_link: + :type min_link_no: + :param dut: + :type dut: + :return: + :rtype: + + usage: + """ + if 'portchannel' not in kwargs: + st.error("Mandatory parameter - portchannel not found") + return False + elif 'min_link' not in kwargs: + st.error("Mandatory parameter - min_link not found") + return False + st.log("Configuring the --min-link for LACP..") + if 'portchannel' in kwargs and "min_link" in kwargs: + st.config(dut, "config portchannel add {} --min-links {}".format(kwargs['portchannel'], kwargs['min_link'])) + + +def _config_portchannel(dut, portchannel_name, members, config='add', cli_type="click"): + """ + Configure the port channel between 2 devices and add member to it. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param portchannel_name: + :param members: + :param config: + :return: + """ + if config == "add": + create_portchannel(dut, portchannel_name,cli_type=cli_type) + if not add_portchannel_member(dut, portchannel_name, members,cli_type=cli_type): + return False + else: + delete_portchannel_member(dut, portchannel_name, members,cli_type=cli_type) + if not delete_portchannel(dut, portchannel_name,cli_type=cli_type): + return False + return True + + +def config_portchannel(dut1, dut2, portchannel_name, members_dut1, members_dut2, config='add', thread=True,cli_type="click"): + """ + Configure the port channel between 2 devices and add member to it. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut1: + :param dut2: + :param portchannel_name: + :param members_dut1: member or list of members + :param members_dut2: member or list of members + :param config: add | del + :param thread: True | False + :return: + """ + [out, exceptions] = utils.exec_all(thread, [[_config_portchannel, dut1, portchannel_name, members_dut1, config,cli_type], + [_config_portchannel, dut2, portchannel_name, members_dut2, config,cli_type]]) + st.log(exceptions) + return False if False in out else True + +def verify_lacp_fallback(dut,**kwargs): + """ + Author: Chandra Sekhar Reddy + email: chandra.vedanaparthi@broadcom.com + Verify show interfaces portchannel fallback output + :param dut: + :param kwargs: Parameters can be + :param kwargs:port_channel_name is mandatory + :return: + + Usage: + + verify_lacp_fallback(dut1,port_channel_name='PortChannel10", fallback_config='True', fallback_oper_status='Disabled') + verify_lacp_fallback(dut1,port_channel_name='PortChannel10", fallback_config='True', fallback_oper_status='Enabled') + verify_lacp_fallback(dut1,port_channel_name='PortChannel10", fallback_config='False', fallback_oper_status='Disabled') + + """ + ret_val = True + cli_type = kwargs.get('cli_mode', 'click') + cmd = '' + if 'port_channel_name' in kwargs: + port_channel_name = kwargs['port_channel_name'] + del kwargs['port_channel_name'] + cmd += 'show interfaces portchannel {} fallback'.format(port_channel_name) + else: + st.error("Mandatory argument Port Channel name Not Found") + return False + + output = st.show(dut,cmd,type=cli_type,config="false",skip_error_check="True") + + if len(output) == 0: + st.error("Output is Empty") + return False + + for key in kwargs: + if str(kwargs[key]) != str(output[0][key]): + st.error("Match NOT FOUND for {} : Expected -<{}> Actual-<{}> ".format(key, kwargs[key], output[0][key])) + ret_val = False + else: + st.log("Match FOUND for {} : Expected -<{}> Actual-<{}> ".format(key, kwargs[key], output[0][key])) + return ret_val + +def verify_portchannel_fallback_status(dut, portchannel, members_list, iter_count=10, iter_delay=1, state='up', static=False, cli_type="click"): + if cli_type == "click": + proto = "NONE" if static else "LACP" + if state.lower() == 'up': + portchannel_state = '{}(A)(Up)'.format(proto) + portchannel_member_list = [] + for member in members_list: + portchannel_member_list.append(str(member)+'(S)') + elif state.lower() == 'down': + portchannel_state = '{}(A)(Dw)'.format(proto) + else: + st.log("Invalid LAG status provided as input to verify") + return False + i = 1 + while i <= iter_count: + st.log("Checking iteration {}".format(i)) + st.wait(iter_delay) + output_dict = get_portchannel(dut, portchannel_name=portchannel)[0] + ports_list = (output_dict['ports'].strip()).split(' ') + if output_dict['teamdev'] == portchannel: + if (output_dict['protocol'] == portchannel_state): + for portchannel_member in portchannel_member_list: + if portchannel_member in ports_list: + return True + else: + st.log("The Portchannel-{} is not found".format(portchannel)) + return False + i += 1 + elif cli_type == "klish": + channel_number = portchannel.replace("PortChannel","") + portchannel_details = get_interface_portchannel(dut, channel_number=channel_number, cli_type=cli_type) + if not portchannel_details: + st.log("PortChannel Details not found -- {}".format(portchannel_details)) + return False + if state == "up": + if portchannel_details[0]["fallback"] != "Enabled" or portchannel_details[0]["state"] != "up": + st.log("Fallback state is not matching -- Expecting Enabled but it is {}".format(portchannel_details[0]["fallback"])) + return False + elif portchannel_details[0]["fallback"] == "Enabled" and portchannel_details[0]["state"] != "up": + st.log("Portchannel state is not up eventhough, fallback mode is enabled.") + return False + else: + return True + return False + +def config_properties(dut, params, cli_type="click"): + """ + API to configure portchannel properties like mtu, no mtu, no fallback and no min links + :param dut: + :param params: [{"portchannel_name":"PortChannel001","mtu":"9000", "no_mtu":True/False, + "no_min_links" : True, min_links:1, "fallback":True/False, "v4_address":"2.2.2.2","v4_subnet":24 + "v6_address":"2001::1", "v6_subnet":64, "no_v6":True/False, "no_v4":True/False,"shutdown":True/False}] + :param cli_type: click, klish + :return: + """ + commands = list() + if params: + for param in params: + if not param.get("portchannel_name"): + st.log("PortChannel Name not provided") + return False + if cli_type == "klish": + commands.append("interface PortChannel {}".format(param.get("portchannel_name").replace("PortChannel",""))) + if not param.get("no_mtu"): + if param.get("mtu"): + commands.append("mtu {}".format(param.get("mtu"))) + else: + commands.append("no mtu") + if not param.get("no_min_links"): + if param.get("min_links"): + commands.append("minimum-links {}".format(param.get("min_links"))) + else: + commands.append("no minimum-links") + if param.get("fallback"): + commands.append("fallback enable") + else: + commands.append("no fallback") + if param.get("shutdown"): + commands.append("shutdown") + else: + commands.append("no shutdown") + if param.get("v4_address") and param.get("v4_subnet"): + commands.append("ip address {}/{}".format(param.get("v4_address"), param.get("v4_subnet"))) + if param.get("v4_address") and param.get("no_v4"): + commands.append("no ip address {}".format(param.get("v4_address"))) + if param.get("v6_address") and param.get("v6_subnet"): + commands.append("ipv6 address {}/{}".format(param.get("v6_address"), param.get("v6_subnet"))) + if param.get("v6_address") and param.get("no_v6"): + commands.append("no ipv6 address {}".format(param.get("v6_address"))) + commands.append("exit") + elif cli_type == "click": + if not param.get("fallback"): + if not param.get("min_links"): + command = "config portchannel add {}".format(param.get("portchannel_name")) + else: + command = "config portchannel add {} --min-links {}".format(param.get("portchannel_name"), param.get("min_links")) + else: + if not param.get("min_links"): + command = "config portchannel add {} --fallback=true".format(param.get("portchannel_name")) + else: + command = "config portchannel add {} --fallback=true --min-links {}".format( + param.get("portchannel_name"), param.get("min_links")) + commands.append(command) + else: + st.log("config_portchannel_properties : Unsupported CLI type") + return False + if commands: + st.config(dut, commands, type=cli_type) + return True + else: + st.log("config_portchannel_properties : PARAMS not provided") + return False + +def config_port_mode(dut, channel_number, interface, mode="active", cli_type="klish"): + """ + API to configure port mode for the given channel group + :param dut: + :param channel_number: + :param mode: + :param cli_type: + :return: + """ + if mode not in ["active","on"]: + st.log("Unsupported mode") + return False + if cli_type == "klish": + commands = list() + commands.append("interface {}".format(interface)) + commands.append("channel-group {} mode {}".format(channel_number, mode)) + commands.append("exit") + st.config(dut, commands, type=cli_type) + return True + else: + st.log("PORT MODE CONFIGURATION IS NOT SUPPORTED IN {} CLI".format(cli_type.upper())) + return False + +def get_interface_portchannel(dut, channel_number=None, cli_type="klish"): + """ + API to execute show interface PortChannel {id} and get the result + :param dut: + :param channel_number: + :param cli_type: + :return: + """ + result = dict() + if cli_type == "klish": + stats_index = ["pkts","octets","multicasts","broadcasts","unicasts","errors","discards"] + command = "show interface PortChannel" + if channel_number: + command += " {}".format(channel_number) + output = st.show(dut, command, type=cli_type) + if output: + for key,value in output[0].items(): + if key not in stats_index: + result[key] = value + for key,value in output[1].items(): + if key in stats_index: + result["input_{}".format(key)] = value + for key,value in output[2].items(): + if key in stats_index: + result["output_{}".format(key)] = value + return result + +def verify_interface_portchannel(dut,**kwargs): + """ + API to verify interface portchannel + :param dut: + :param kwargs: {u'lacp_mode': 'active', u'partner_mac': '00:00:00:00:00:00', 'input_broadcasts': '3369', + 'output_pkts': '74169', u'min_links': '1', 'input_errors': '0', u'ip_mtu': '1500', 'output_multicasts': '70186', + 'input_pkts': '6224', 'input_multicasts': '2855', u'priority': '65535', u'state': 'up', u'partner_port': '0', + 'output_unicasts': '0', 'output_discards': '0', u'actor_port': '56', 'output_errors': '0', + u'is_selected': 'True', 'output_octets': '10678293', u'members': 'Ethernet56', u'protocol_state': 'down', + 'output_broadcasts': '3983', u'actor_mac': '90:b1:1c:f4:a8:7e', 'input_unicasts': '0', u'mtu': '1532', + u'channel_number': '1', u'mode': 'LACP', 'input_discards': '3', u'fallback': 'Enabled', + 'input_octets': '1787177', u'pc_mac_address': '90:b1:1c:f4:a8:7e'} + :param cli_type: + :return: + """ + if not kwargs: + st.log("Parameters not provided") + return False + cli_type = kwargs.get("cli_type", "klish") + if kwargs.get("channel_number"): + output = get_interface_portchannel(dut,channel_number=kwargs.get("channel_number"),cli_type=cli_type) + else: + output = get_interface_portchannel(dut,cli_type=cli_type) + if not output: + st.log("Empty output") + return False + kwargs.pop("cli_type") + for key,value in kwargs.items(): + if str(output[key]) != str(value): + st.log("Mismatch in {} with value {} but expecting {}".format(key, output[key], value)) + return False + return True + +def config_multiple_portchannels(dut, data, config="add"): + """ + :param dut: + :param data: {'G7': {'PortChannel4': ['Ethernet48', 'Ethernet34'],'PortChannel5': 'Ethernet36', 'PortChannel2': ['Ethernet13', 'Ethernet14'],'PortChannel3': ['Ethernet32', 'Ethernet33']}, 'G6': {'PortChannel5':['Ethernet27', 'Ethernet36']}, 'G4': {'PortChannel4': ['Ethernet10','Ethernet24'], 'PortChannel2': ['Ethernet2', 'Ethernet3'], 'PortChannel3':['Ethernet8', 'Ethernet9'], 'PortChannel1': ['Ethernet0', 'Ethernet1']},'G3': {'PortChannel4': ['Ethernet18', 'Ethernet40'], 'PortChannel5':'Ethernet112', 'PortChannel2': ['Ethernet1', 'Ethernet2'], 'PortChannel3':['Ethernet16', 'Ethernet17']}, 'G8': {'PortChannel4': ['Ethernet120','Ethernet94'], 'PortChannel2': ['Ethernet126', 'Ethernet127'],'PortChannel3': ['Ethernet92', 'Ethernet93'], 'PortChannel1':['Ethernet124', 'Ethernet125']}} + :param config: add | del + :return: True | False + """ + if data.get(dut): + for portchannel_name, members in data[dut].items(): + if config == "add": + create_portchannel(dut, portchannel_name) + if not add_portchannel_member(dut, portchannel_name, members): + return False + else: + delete_portchannel_member(dut, portchannel_name, members) + if not delete_portchannel(dut, portchannel_name): + return False + return True + return False diff --git a/spytest/apis/switching/pvst.py b/spytest/apis/switching/pvst.py new file mode 100644 index 00000000000..f73f0310040 --- /dev/null +++ b/spytest/apis/switching/pvst.py @@ -0,0 +1,1124 @@ +import re +import utilities.utils as utils +from spytest import st +from spytest.utils import filter_and_select +from spytest.utils import exec_foreach, exec_all +import utilities.common as utility +import apis.switching.portchannel as portchannel +import apis.system.basic as basic +from utilities.parallel import ensure_no_exception +from datetime import datetime,timedelta + +debug_log_path = r"/var/log/stplog" +SHOW_STP_VLAN = "show spanning_tree vlan {}" +BLOCKING_STATE = "BLOCKING" + + +def config_spanning_tree(dut, feature="pvst", mode="enable", vlan=None, cli_type='click'): + """ + + :param dut: + :param feature: + :param mode: + :param vlan: + :param cli_type: + :return: + """ + command = '' + no_form = 'no' + if mode == 'enable': + no_form = '' + + st.log("{} spanning_tree {}".format(mode, feature)) + if cli_type == 'click': + if vlan: + command = "config spanning_tree vlan {} {}".format(mode, vlan) + else: + command = "config spanning_tree {} {}".format(mode, feature) + elif cli_type == 'klish': + if mode == 'disable': + feature = '' + if vlan: + command = "{} spanning-tree vlan {}".format(no_form, vlan) + else: + command = "{} spanning-tree mode {}".format(no_form, feature) + + st.config(dut, command, type=cli_type) + + +def config_stp_parameters(dut, cli_type='click', no_form='', **kwargs): + """ + + :param dut: + :param cli_type: + :param no_form: + :param kwargs: + :return: + """ + no_form = 'no' if no_form else '' + + for each_key in kwargs.keys(): + if cli_type == 'click': + command = "config spanning_tree {} {}".format(each_key, kwargs[each_key]) + elif cli_type == 'klish': + command = "{} spanning-tree {} {}".format(no_form, each_key, kwargs[each_key]) + else: + st.error("Invalid CLI type - {}".format(cli_type)) + return + st.config(dut, command, type=cli_type) + + +def config_stp_vlan_parameters(dut, vlan, **kwargs): + """ + + :param dut: + :param vlan: + :param kwargs: + :return: + """ + cli_type = kwargs.setdefault('cli_type', 'click') + no_form = 'no' if kwargs.setdefault('no_form', False) else '' + del kwargs['cli_type'] + del kwargs['no_form'] + click_2_klish = {'forward_delay': 'forward-time', 'hello': 'hello-time', 'max_age': 'max-age'} + + for each_key, value in kwargs.items(): + if cli_type == 'click': + command = "config spanning_tree vlan {} {} {}".format(each_key, vlan, value) + elif cli_type == 'klish': + each_key1 = click_2_klish.get(each_key, each_key) + command = "{} spanning-tree vlan {} {} {}".format(no_form, vlan, each_key1, value) + else: + st.error("Invalid CLI type - {}".format(cli_type)) + return + st.config(dut, command, type=cli_type) + + +def config_stp_vlan_parameters_parallel(dut_list, thread=True, **kwargs): + """ + Author : chaitanya lohith bollapragada + This will configure the "config_stp_vlan_parameters" in parallel to all DUTs mentioned. + :param dut_list: + :param vlan: list of vlans + :param priority: list of STP priorities + :param thread: True | False + :return: + """ + st.log("Configuring STP vlan parameters in paraller on all DUT's ... ") + dut_li = list(dut_list) if isinstance(dut_list, list) else [dut_list] + vlan_li = list(kwargs['vlan']) if isinstance(kwargs['vlan'], list) else [kwargs['vlan']] + priority_li = list(kwargs['priority']) if isinstance(kwargs['priority'], list) else [kwargs['priority']] + if not len(dut_li) == len(vlan_li) == len(priority_li): + return False + params = list() + for i,each in enumerate(dut_list): + params.append(utility.ExecAllFunc(config_stp_vlan_parameters, each, vlan_li[i], priority=priority_li[i])) + [out, exceptions] = exec_all(thread, params) + st.log(exceptions) + return False if False in out else True + + +def config_stp_vlan_interface(dut, vlan, iface, value, mode='cost', **kwargs): + """ + + :param dut: + :param vlan: + :param iface: + :param value: + :param mode: + :return: + """ + cli_type = kwargs.get('cli_type', 'click') + no_form = 'no' if kwargs.get('no_form') else '' + + if mode in ['cost', 'priority']: + if cli_type == 'click': + command = "config spanning_tree vlan interface {} {} {} {} ".format(mode, vlan, iface, value) + elif cli_type == 'klish': + if mode == 'priority': + mode = 'port-priority' + interface_data = utils.get_interface_number_from_name(iface) + command = ['interface {} {}'.format(interface_data["type"], interface_data["number"]), + '{} spanning-tree vlan {} {} {}'.format(no_form, vlan, mode, value), "exit"] + else: + st.error("Invalid CLI type - {}".format(cli_type)) + return + else: + st.log("Invalid mode = {}".format(mode)) + return + st.config(dut, command, type=cli_type) + + +def config_stp_enable_interface(dut, iface, mode="enable"): + """ + + :param dut: + :param iface: + :param mode: + :return: + """ + command = "config spanning_tree interface {} {}".format(mode, iface) + st.config(dut, command) + +def config_stp_interface_params(dut, iface, **kwargs): + """ + + :param dut: + :param iface: + :param cli_type: + :param kwargs: + :return: + """ + cli_type = kwargs.setdefault('cli_type', 'click') + del kwargs['cli_type'] + + click_2_klish = {"root_guard": " guard root", "bpdu_guard": "bpduguard ", "portfast": "portfast", + "uplink_fast": "uplinkfast"} + + if cli_type == 'click': + for each_key in kwargs.keys(): + if each_key == "priority" or each_key == "cost": + command = "config spanning_tree interface {} {} {}".format(each_key, iface, kwargs[each_key]) + elif each_key == "bpdu_guard_action": + command = "config spanning_tree interface bpdu_guard enable {} {}".format(iface, kwargs[each_key]) + else: + command = "config spanning_tree interface {} {} {}".format(each_key, kwargs[each_key], iface) + st.config(dut, command) + + elif cli_type == 'klish': + interface_data = utils.get_interface_number_from_name(iface) + command = ['interface {} {}'.format(interface_data["type"], interface_data["number"])] + for each_key in kwargs.keys(): + no_form = 'no' if kwargs[each_key] == 'disable' else '' + if each_key == "priority" or each_key == "cost": + command.append('spanning-tree {} {}'.format(each_key, kwargs[each_key])) + elif each_key == "bpdu_guard_action": + command.append('{} spanning-tree bpduguard port-shutdown'.format(no_form)) + else: + command.append("{} spanning-tree {}".format(no_form, click_2_klish[each_key])) + command.append('exit') + st.config(dut, command, type=cli_type) + + +def config_stp_interface(dut, iface, mode="enable"): + """ + + :param dut: + :param iface: + :param mode: + :return: + """ + command = "config spanning_tree interface {} {} ".format(mode, iface) + st.config(dut, command) + +def show_stp(dut, **kwargs): + """ + + :param dut: + :return: + """ + cli_type = kwargs.get("cli_type", 'click') + command = "show spanning_tree" + if 'sub_cmd' in kwargs: + command = "show spanning_tree {}".format(kwargs['sub_cmd']) + return st.show(dut, command, type=cli_type) + +def show_stp_vlan(dut, vlan, cli_type="click"): + """ + + :param dut: + :param vlan: + :param cli_type: + :return: + """ + st.log("show spanning_tree vlan ") + command = SHOW_STP_VLAN.format(vlan) + return st.show(dut, command, type=cli_type) + + +def show_stp_vlan_iface(dut, vlan, iface, cli_type="click"): + """ + + :param dut: + :param vlan: + :param iface: + :return: + """ + if cli_type == "click": + command = "show spanning_tree vlan interface {} {}".format(vlan, iface) + elif cli_type == "klish": + command = "show spanning_tree vlan {} interface {}".format(vlan, iface) + else: + st.log("Unsupported CLI type {}".format(cli_type)) + return list() + return st.show(dut, command, type="cli_type") + +def show_stp_stats(dut): + """ + + :param dut: + :return: + """ + command = "show spanning_tree statistics" + return st.show(dut, command) + +def show_stp_stats_vlan(dut, vlan): + """ + + :param dut: + :param vlan: + :return: + """ + command = "show spanning_tree statistics vlan {} ".format(vlan) + return st.show(dut, command) + +def debug_stp(dut, *argv): + """ + + :param dut: + :param argv: + :return: + + Usage: + debug_stp(dut) + debug_stp(dut, "reset") + debug_stp(dut, "vlan 100", "interface Ethernet0") + debug_stp(dut, "vlan 100 -d", "interface Ethernet0 -d") + """ + command = 'debug spanning_tree' + if not argv: + st.config(dut, command) + for each in argv: + command2 = "{} {}".format(command, each) + st.config(dut, command2) + return True + +def get_debug_stp_log(dut, filter_list=[]): + """" + + :param dut: + :param filter_list: + :return: + """ + if isinstance(filter_list, list): + filter_list = list(filter_list) + else: + filter_list = [filter_list] + command = "cat {}".format(debug_log_path) + for each_filter in filter_list: + command += " | grep '{}'".format(each_filter) + output = st.show(dut, command, skip_tmpl=True, skip_error_check=True) + reg_output = utils.remove_last_line_from_string(output) + out_list = reg_output.split('\n') + return out_list + +def clear_debug_stp_log(dut): + """ + :param dut: + :return: + """ + command = "dd if=/dev/null of={}".format(debug_log_path) + st.config(dut, command) + return True + +def verify_stp_vlan_iface(dut, **kwargs): + """ + + :param dut: + :param kwargs: + :return: + """ + output = show_stp_vlan_iface(dut, kwargs["vlan"], kwargs["iface"]) + for each in kwargs.keys(): + match = {each: kwargs[each]} + entries = filter_and_select(output, None, match) + if not entries: + st.log("{} and {} is not match ".format(each, kwargs[each])) + return False + return True + +def verify_stp_statistics_vlan(dut, **kwargs): + """ + + :param dut: + :param kwargs: + :return: + """ + output = show_stp_stats_vlan(dut, kwargs["vlan"]) + for each in kwargs.keys(): + match = {each: kwargs[each]} + entries = filter_and_select(output, None, match) + if not entries: + st.log("{} and {} is not match ".format(each, kwargs[each])) + return False + return True + +def check_dut_is_root_bridge_for_vlan(dut, vlanid): + """ + + :param dut: + :param vlanid: + :return: + """ + cmd = SHOW_STP_VLAN.format(vlanid) + stp_output = st.show(dut, cmd) + root_bridge=stp_output[0]["rt_id"] + dut_bridge_id=stp_output[0]["br_id"] + return (root_bridge == dut_bridge_id) and stp_output[0]["rt_port"] == "Root" + +def get_stp_bridge_param(dut, vlanid, bridge_param): + """ + This is used to provide value of the bridge_param for given dut and vlanid + :param dut: + :param vlanid: + :param bridge_param: should be one of the below strings + + stp_mode Returns STP mode + vid Returns vlanid + inst Returns STP intance id + br_id Returns Bridge id + br_maxage Returns Bridge max age + br_hello Returns Bridge Hello timer value + br_fwddly Returns Bridge Forward Delay + br_hold Returns Bridge Hold Timer value + rt_id Returns Root Bridge id + rt_pathcost Returns RootPath Cost + rt_desigbridgeid Returns DesignatedBridge id + rt_port Returns Root + rt_maxage Returns Root max age + rt_hello Returns Root Bridge Hello Timer value + rt_fwddly Returns Root Bridge Forward Delay + + :return: Returns value of the bridge_param for given dut and vlanid + """ + stp_bridge_param_list = ['stp_mode', + 'vid', + 'inst', + 'br_id', + 'br_maxage', + 'br_hello', + 'br_fwddly', + 'br_hold', + 'br_lasttopo', + 'br_topoch', + 'rt_id', + 'rt_pathcost', + 'rt_desigbridgeid', + 'rt_port', + 'rt_maxage', + 'rt_hello', + 'rt_fwddly'] + + if bridge_param not in stp_bridge_param_list: + st.error("Please provide the valid stp bridge parameter") + return + cmd = SHOW_STP_VLAN.format(vlanid) + stp_output = st.show(dut, cmd) + return stp_output[0][bridge_param] + +def get_stp_port_param(dut, vlanid, ifname, ifparam): + """ + This is used to provide value of the bridge_param for given dut and vlanid + :param dut: + :param vlanid: + :param bridge_param: should be one of the below strings + + port_name Returns Port Name + port_priority Returns Port Priority + port_pathcost Returns Port pathcost + port_portfast Returns Portfast Enabled(Y) or Not(N) + port_uplinkfast Returns Uplinkfast is Enabled(Y) or Not(N) + port_state Returns Port state + port_desigcost Returns Port Designated cost + port_desigrootid Returns Port Designated Root id + port_desigbridgeid Returns Port Designated Bridge id + :return: + """ + stp_port_param_list = ['port_name', + 'port_priority', + 'port_pathcost', + 'port_portfast', + 'port_uplinkfast', + 'port_state', + 'port_desigcost', + 'port_desigrootid', + 'port_desigbridgeid'] + + if ifparam not in stp_port_param_list: + st.error("Please provide the valid stp port parameter") + return + + cmd = SHOW_STP_VLAN.format(vlanid)+" interface {}".format(ifname) + stp_output = st.show(dut, cmd) + return None if len(stp_output) == 0 else stp_output[0][ifparam] + +def get_default_root_bridge(dut_list): + """ + This is used to get the root bridge with default config + :param vars : Testbed Vars + :return: Returns root bridge like D1 or D2 + """ + duts_mac_list = basic.get_dut_mac_address_thread(dut_list) + if duts_mac_list: + min_mac_addr = min(duts_mac_list.values()) + root_bridge = [dut for dut, mac_addr in duts_mac_list.items() if mac_addr == min_mac_addr][0] + return [dut for dut in dut_list if dut==root_bridge][0] + else: + return None + +def get_duts_mac_address(duts): + """ + This is used to get the Duts and its mac addresses mapping + :param duts: List of DUTs + :return : Duts and its mac addresses mapping + + """ + duts_mac_addresses = {} + cmd = "show platform syseeprom" + for dut in duts: + if st.is_vsonic(dut): + mac = basic.get_ifconfig_ether(dut) + duts_mac_addresses[dut] = mac + continue + eeprom_details = st.show(dut, cmd, skip_error_check=True) + if not eeprom_details: + iteration=3 + for i in range(1, iteration+1): + st.wait(2) + eeprom_details = st.show(dut, cmd, skip_error_check=True) + if eeprom_details: + break + if not eeprom_details and i >= iteration + 1: + st.log("EEPROM data not found for {}".format(dut)) + st.report_fail("eeprom_data_not_found", dut) + st.log("EEPROM DETAILS -- {}".format(eeprom_details)) + if eeprom_details: + for data in eeprom_details: + if "tlv_name" in data and data["tlv_name"] == "Base MAC Address": + duts_mac_addresses[dut] = data["value"].replace(":","") + st.log("DUT MAC ADDRESS -- {}".format(duts_mac_addresses)) + return duts_mac_addresses + +def _get_duts_list_in_order(vars): + """ + This is used to get the DUTs and their mac addresses in ascending order of Mac addresses + :param duts: List of DUTs + :return : Duts and its mac addresses mapping + + """ + duts_mac_addresses = get_duts_mac_address(vars["dut_list"]) + + return sorted(zip(duts_mac_addresses.values(), duts_mac_addresses.keys())) + +def get_ports_based_on_state(vars, vlanid, port_state, dut=None, cli_type='click'): + """ + This is used to get the blocked ports on none-root bridge + :param duts: List of DUTs + :return : Duts and its mac addresses mapping + + """ + + selected_non_root = "" + if dut is None: + duts_list = _get_duts_list_in_order(vars) + dut_with_max_mac_address = duts_list[len(duts_list) - 1][1] + selected_non_root = [dut_key for dut_key, dut_value in vars.items() if dut_value == dut_with_max_mac_address][0] + else: + selected_non_root = [dut_key for dut_key, dut_value in vars.items() if dut_value == dut][0] + stp_output = show_stp_vlan(vars[selected_non_root], vlanid, cli_type=cli_type) + ports_list = [row["port_name"] for row in stp_output if + row["port_state"] == port_state and int(row["vid"]) == vlanid] + + return ports_list + +def poll_for_root_switch(dut, vlanid, iteration=20, delay=1): + """ + API to poll for root switch + + :param dut: + :param vlanid: + :param iteration: + :param delay: + :return: + """ + + i = 1 + while True: + if check_dut_is_root_bridge_for_vlan(dut, vlanid): + st.log("Observed dut is root bridge {} iteration".format(i)) + return True + if i > iteration: + st.log("Max iterations {} reached".format(i)) + return False + i += 1 + st.wait(delay) + +def poll_for_stp_status(dut, vlanid, interface, status, iteration=20, delay=1): + """ + API to poll for stp stauts for an interface + + :param dut: + :param vlanid: + :param iteration: + :param delay: + :return: + """ + i = 1 + while True: + if get_stp_port_param(dut, vlanid, interface, "port_state") == status: + st.log("Port status is changed to {} after {} sec".format(status, i)) + return True + if i > iteration: + st.log("Max iterations {} reached".format(i)) + return False + i += 1 + st.wait(delay) + +def get_root_guard_details(dut, vlan=None, ifname=None , rg_param="rg_timeout"): + """ + API will return Root Guard timeout if vlan and interface won't provide , otherwise Root Guard state will return + :param dut: + :param vlan: + :param ifname: + :return: + """ + cmd = "show spanning_tree root_guard" + output = st.show(dut, cmd) + if vlan is None and ifname is None: + rg_value = int(output[0][rg_param]) + else: + rg_value = [row[rg_param] for row in output if row["rg_ifname"] == ifname and int(row["rg_vid"]) == vlan][0] + return rg_value + +def check_rg_current_state(dut, vlan, ifname): + """ + API will check the Root Guard status for given interface and vlan + :param dut: + :param vlan: + :param ifname: + :return: + """ + rg_status = get_root_guard_details(dut, vlan, ifname, "rg_status") + #show_stp_config_using_klish(dut, "root_guard", vlan) + return rg_status == "Consistent state" + +def check_bpdu_guard_action(dut, ifname, **kwargs): + """ + API will check the BPDU Guard action config and it's operational status + :param dut: + :param ifname: + :param kwargs: + config_shut : BPDU shutdown configuration + opr_shut : status of the port shut due to BPDU Guard + :return: + """ + cmd = "show spanning_tree bpdu_guard" + show_out = st.show(dut, cmd) + #show_stp_config_using_klish(dut, "bpdu_guard") + if_out = [row for row in show_out if row['bg_ifname'] == ifname][0] + config_shut, opr_shut = if_out['bg_cfg_shut'], if_out['bg_oper_shut'] + return kwargs['config_shut'] == config_shut and kwargs['opr_shut'] == opr_shut + +def stp_clear_stats(dut, **kwargs): + """ + + :param dut: + :param kwargs: + vlan :vlan id + interface : interface name + :return: + """ + cmd = "sonic-clear spanning_tree statistics" + if 'vlan' in kwargs and 'interface' not in kwargs: + cmd += ' vlan {}'.format(kwargs['vlan']) + if 'vlan' in kwargs and 'interface' in kwargs: + cmd += ' vlan-interface {} {}'.format(kwargs['vlan'], kwargs['interface']) + output = st.config(dut, cmd) + +def get_stp_stats(dut, vlan, interface, param): + """ + + :param dut: + :param vlan: + :param interface: + :param param: + tx_bpdu : BPDU Transmission count + rx_bpdu : BPDU Receive count + tx_tcn : TCN Transmission count + rx_tcn : TCN Receive count + + :return: + """ + output = show_stp_stats_vlan(dut, vlan) + #show_stp_config_using_klish(dut, 'statistics', vlan) + value_list = [row[param] for row in output if int(row['st_vid']) == vlan and row['st_portno'] == interface] + utils.banner_log(value_list) + return None if len(output) == 0 else int(value_list[0]) + +def verify_stp_ports_by_state(dut, vlan, port_state, port_list, cli_type='click'): + """ + API Will check the port state in the VLAN. + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param vlan: + :param state: + :param port_list: + :param cli_type: + :return: + """ + port_li = list(port_list) if isinstance(port_list, list) else [port_list] + stp_output = show_stp_vlan(dut, vlan, cli_type=cli_type) + ports_list = [row["port_name"] for row in stp_output if + row["port_state"] == port_state and int(row["vid"]) == vlan] + + result = True + for each_port in port_li: + if each_port not in ports_list: + st.log("{} is not {} state ".format(each_port, port_state)) + result = False + else: + st.log("{} is {} state ".format(each_port, port_state)) + return result + +def get_stp_port_list(dut, vlan, exclude_port=[], cli_type='click'): + """ + API will return all ports of VLAN instance. + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param vlan: + :param exclude_port: + :param cli_type: + :return: + """ + ex_port_li = list(exclude_port) if isinstance(exclude_port, list) else [exclude_port] + stp_output = show_stp_vlan(dut, vlan, cli_type=cli_type) + ports_list = [row["port_name"] for row in stp_output] + for each_int in ex_port_li: + if each_int in ports_list: + ports_list.remove(each_int) + st.log("{} is excluded".format(each_int)) + return ports_list + +def get_stp_root_port(dut, vlan, cli_type='click'): + """ + API will return Root/Forwarding port of the device in the VLAN. + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param vlan: + :param cli_type: + :return: + """ + out = show_stp_vlan(dut, vlan, cli_type=cli_type) + if not out: + st.error("No Root/Forwarding port found") + return False + if out[0]['rt_port'] == "Root": + st.error("Given device is ROOT Bridge.") + return False + return out[0]['rt_port'] + +def get_stp_next_root_port(dut, vlan, cli_type='click'): + """ + API will return Next possible Root/Forwarding port of the device in the VLAN. + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param vlan: + :param cli_type: + :return: + """ + + partner = None + next_root_port = None + sort_list = lambda list1, list2: [x for _, x in sorted(zip(list2, list1))] + + out = show_stp_vlan(dut, vlan, cli_type=cli_type) + if not out: + st.error("No Initial Root/Forwarding port found") + return next_root_port + + if out[0]['rt_port'] == "Root": + st.error("Given device is ROOT Bridge.") + return next_root_port + + partner_ports = st.get_dut_links(dut) + root_port = out[0]['rt_port'] + root_cost = int(filter_and_select(out, ['port_pathcost'], {'port_name': root_port})[0]['port_pathcost']) + st.log('root_port : {}, root_cost: {}'.format(root_port, root_cost)) + + # Finding the Root port connected partner + for each in partner_ports: + if not partner: + if root_port == each[0]: + partner = each[1] + st.log("partner : {}".format(partner)) + + if not partner: + st.error("No Partner found for Root/Forwarding Port.") + return next_root_port + + # Dut Partner port mapping + dut_partner_ports = st.get_dut_links(dut, partner) + dut_partner_ports_map = {all[0]: all[2] for all in dut_partner_ports} + dut_partner_ports_map_rev = {all[2]: all[0] for all in dut_partner_ports} + st.log('dut_partner_ports_map : {}'.format(str(dut_partner_ports_map))) + st.log('dut_partner_ports_map_rev : {}'.format(str(dut_partner_ports_map_rev))) + + # Preparing DATA to process and find the next Root/Forwarding port. + cut_data = {} + pc_list = [each['teamdev'] for each in portchannel.get_portchannel_list(partner)] + for each in out: + port = each['port_name'] + if "Ethernet" in port and port in dut_partner_ports_map: + port = dut_partner_ports_map[each['port_name']] + ifindex = int(re.findall(r'\d+', port)[0]) + cut_data[port] = [ifindex, each['port_state'], int(each['port_pathcost'])] + elif port in pc_list: + ifindex = int(re.findall(r'\d+', port)[0]) + cut_data[port] = [ifindex, each['port_state'], int(each['port_pathcost'])] + else: + pass + st.log('cut_data == {}'.format(str(cut_data))) + + cost_vs_port = {} + for each in cut_data: + if each != dut_partner_ports_map[root_port]: + if 'Ethernet' in each: + if cut_data[each][2] not in cost_vs_port: + cost_vs_port[cut_data[each][2]] = [[each], []] + else: + cost_vs_port[cut_data[each][2]][0].append(each) + else: + if cut_data[each][2] not in cost_vs_port: + cost_vs_port[cut_data[each][2]] = [[], [each]] + else: + cost_vs_port[cut_data[each][2]][1].append(each) + + sorted_cost = sorted(cost_vs_port.keys()) + st.log("cost_vs_port : {}".format(cost_vs_port)) + st.log("sorted_cost : {}".format(sorted_cost)) + + # Logic to find next Root/Forwarding port + if root_cost in cost_vs_port and (len(cost_vs_port[root_cost][0]) or len(cost_vs_port[root_cost][1])): + st.debug("When 2 or more ports has configured with same root port cost.") + if len(cost_vs_port[root_cost][0]): + port_list = cost_vs_port[root_cost][0] + port_index_li = [cut_data[e][0] for e in port_list] + next_root_port = sort_list(port_list, port_index_li)[0] + return dut_partner_ports_map_rev[next_root_port] + else: + port_list = cost_vs_port[root_cost][1] + port_index_li = [cut_data[e][0] for e in port_list] + next_root_port = sort_list(port_list, port_index_li)[0] + return next_root_port + + elif len(sorted_cost): + st.debug("When NO 2 or more ports has root port cost configured. So checking next larger cost ports") + next_root_cost = sorted_cost[0] + if len(cost_vs_port[next_root_cost][0]): + port_list = cost_vs_port[next_root_cost][0] + port_index_li = [cut_data[e][0] for e in port_list] + next_root_port = sort_list(port_list, port_index_li)[0] + return dut_partner_ports_map_rev[next_root_port] + else: + port_list = cost_vs_port[next_root_cost][1] + port_index_li = [cut_data[e][0] for e in port_list] + next_root_port = sort_list(port_list, port_index_li)[0] + return next_root_port + else: + st.error("No Match") + return next_root_port + +def config_stp_in_parallel(dut_list, feature="pvst", mode="enable", vlan=None, thread=True): + """ + API to configure stp in parallel on all the provided DUT's + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut_list: + :param feature: + :param mode: + :param vlan: + :param thread: + :return: + """ + st.log("Configuring {} on all the DUT's with mode as {}".format(feature.capitalize(), mode)) + dut_li = list([str(e) for e in dut_list]) if isinstance(dut_list, list) else [dut_list] + params = list() + for dut in dut_li: + params.append([config_spanning_tree, dut, feature, mode, vlan]) + if params: + exec_all(thread, params) + +def show_stp_in_parallel(dut_list, thread=True, cli_type='click'): + """ + API to show the stp configuration in parallel in all the provided DUT's + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut_list: + :param thread: + :param cli_type: + :return: + """ + st.log("Displaying STP result on all the DUT's in parallel ....") + dut_li = utility.make_list(dut_list) + exec_foreach(thread, dut_li, show_stp, cli_type=cli_type) + +def get_root_bridge_for_vlan(dut_vlan_data, thread=True): + params = list() + result = dict() + for dut, vlan in dut_vlan_data.items(): + params.append([check_dut_is_root_bridge_for_vlan, dut, vlan]) + if params: + [out, exceptions] = exec_all(thread, params) + utils.banner_log("Getting root bridge details") + for i,response in enumerate(out): + result[params[i][1]] = response + print(result) + return result + +def check_for_single_root_bridge_per_vlan(dut_list, vlan_list, dut_vlan_data, cli_type='click'): + """ + API to check for single root bridge per VLAN + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param vlanid: + :param cli_type: + :return: + """ + st.log("Verifying the single root bridge per vlan ...") + dut_li = list([str(e) for e in dut_list]) if isinstance(dut_list, list) else [dut_list] + vlan_li = list([str(e) for e in vlan_list]) if isinstance(vlan_list, list) else [vlan_list] + if len(vlan_list) != len(dut_list): + st.log("Invalid data provided to check the root bridge per vlan ...") + st.report_fail("invalid_data_for_root_bridge_per_vlan") + for vlan in vlan_li: + root_count = 0 + params = list() + for dut in dut_li: + params.append([show_stp_vlan, dut, vlan, cli_type]) + stp_output, exceptions = exec_all(True, params) + st.log(stp_output) + st.log(exceptions) + for value in exceptions: + st.log("Exceptions observed {}".format(value)) + if value is not None: + st.log("Exception occured {}".format(value)) + return False + if not stp_output: + st.log("STP output not found on {} for {} instance".format(dut_li, vlan)) + st.report_fail("stp_output_not_found", dut_li, vlan) + for index, stp_out in enumerate(stp_output): + if len(stp_out) <= 0: + st.log("STP OUTPUT IS NOT OBSERVED --- {}".format(stp_out)) + st.report_fail("stp_output_not_found") + root_bridge = stp_out[0]["rt_id"] + dut_bridge_id = stp_out[0]["br_id"] + if root_bridge == dut_bridge_id and stp_out[0]["rt_port"] == "Root": + if dut_vlan_data[dut_li[index]] != int(vlan.strip()): + st.log("Expected DUT {} is not root for {} instance".format(dut_li[index], vlan)) + st.report_fail("expected_dut_not_root", dut_li[index], vlan) + root_count += 1 + if root_count > 1: + st.log("Observed more than 1 root bridge per {} instance".format(vlan)) + st.report_fail("observed_more_than_1_root_bridge", vlan) + return True + +def verify_root_bridge_interface_state(dut, vlan, interface_list, cli_type='click'): + """ + API to verify the root bridge interface state to be forwarded + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param vlan: + :param interface_list: + :param cli_type: + :return: + """ + fail_states = ["BLOCKING", "DISABLED", "DISCARDING"] + pass_states = ["FORWARDING"] + forwarding_counter = 0 + result = show_stp_vlan(dut, vlan, cli_type=cli_type) + if result: + for data in result: + if data["port_name"] not in interface_list: + st.log("Interface {} not found in expected list ...".format(data["port_name"])) + if data["port_state"] in fail_states: + st.log("Observed that interface {} state is {} for root bridge".format(data["port_name"],fail_states)) + if data["port_state"] in pass_states: + forwarding_counter+=1 + if forwarding_counter != len(interface_list): + return False + else: + return True + else: + st.log("No STP data found for {} and {} instance".format(dut, vlan)) + return False + +def poll_root_bridge_interfaces(dut_vlan_list, interfaces_list, iteration=30, delay=1): + """ + API to get the root bridge interfaces to be forwarded + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut_vlan_list: + :param interfaces_list: + :param iteration: + :param delay: + :return: + """ + st.log("Polling for root bridge interfaces ...") + if dut_vlan_list and interfaces_list: + no_of_duts = len(dut_vlan_list) + check=0 + for dut, vlan in dut_vlan_list.items(): + i=1 + while True: + if verify_root_bridge_interface_state(dut, vlan, interfaces_list[dut]): + st.log("Root bridge interface verification succeeded.") + check+=1 + break + if i > iteration: + st.log("Max iteration limit reached.") + break + i+=1 + st.wait(delay) + if check != no_of_duts: + st.log("Number of root DUTs check failed ...") + return False + return True + else: + st.log("Empty DUT VLAN LIST dut_vlan_list AND INTERFACE LIST interfaces_list") + return False + +def verify_root_bridge_on_stp_instances(dut_list, vlan, bridge_identifier): + """ + API to verify the bridge identifier with root bridge identifier + :param dut_list: + :param vlan: + :param bridge_identifier: + :return: + """ + dut_li = list(dut_list) if isinstance(dut_list, list) else [dut_list] + params = list() + for dut in dut_li: + params.append([get_stp_bridge_param, dut, vlan, "rt_id"]) + if params: + [out, exceptions] = exec_all(True, params) + st.log("#########OUTPUT###########") + st.log(out) + st.log(exceptions) + for value in exceptions: + st.log("Exceptions observed {}".format(value)) + if value is not None: + return False + for identifier in out: + st.log("Comparing ROOT bridge ID {} with Provided ID {}".format(identifier, bridge_identifier)) + if identifier != bridge_identifier: + st.log("Mismatch in root and bridge identifiers") + return False + else: + st.log("Root Bridge Identifier {} is matched with provided identifier {}".format(identifier, bridge_identifier)) + return True + return False + +def config_bpdu_filter(dut, **kwargs): + """ + API to config BPDU filter for global and interface level + Usage: + ====== + Interface level config: + ========================= + config_bpdu_filter(dut, interface="Ethernet8", action="enable", cli_type="klish") + config_bpdu_filter(dut, interface="Ethernet8", no_form=True, cli_type="klish") + + Global level config: + ==================== + config_bpdu_filter(dut, cli_type="klish") + config_bpdu_filter(dut, ,no_form=True, cli_type="klish") + + :param dut: + :param kwargs: + :return: + """ + cli_type = kwargs.get("cli_type", "klish") + interface=kwargs.get("interface",None) + no_form=kwargs.get("no_form", None) + action=kwargs.get("action", "enable") + commands = list() + if not interface: + command = "spanning-tree edge-port bpdufilter default" + if no_form: + command = "no {}".format(command) + commands.append(command) + else: + interface_details = utils.get_interface_number_from_name(interface) + if not interface_details: + st.log("Interface details not found {}".format(interface_details)) + return False + commands.append("interface {} {}".format(interface_details.get("type"), interface_details.get("number"))) + command = "spanning-tree bpdufilter" + if no_form: + command = "no {}".format(command) + elif action: + command = "{} {}".format(command, action) + else: + command = "" + if command: + commands.append(command) + if commands: + st.config(dut, commands, type=cli_type) + return True + return False + +def config_stp_root_bridge_by_vlan(stp_data): + """ + :param stp_data: {dut1: {"vlan":10, "priority": "0"}, dut2: {"vlan":20, "priority": "0"}, dut3: {"vlan":30, "priority": "0"}} + """ + params = list() + for dut, data in stp_data.items(): + params.append(utility.ExecAllFunc(config_stp_vlan_parameters, dut, data["vlan"], priority=data["priority"])) + [out, exceptions] = exec_all(True, params) + ensure_no_exception(exceptions) + +def config_port_type(dut, interface, stp_type="rpvst", port_type="edge", no_form=False, cli_type="klish"): + """ + API to config/unconfig the port type in RPVST + :param dut: + :param port_type: + :param no_form: + :return: + """ + commands = list() + command = "spanning-tree port type {}".format(port_type) if not no_form else "no spanning-tree port type" + interface_details = utils.get_interface_number_from_name(interface) + if not interface_details: + st.log("Interface details not found {}".format(interface_details)) + return False + commands.append("interface {} {}".format(interface_details.get("type"), interface_details.get("number"))) + commands.append(command) + commands.append('exit') + st.config(dut, commands, type=cli_type) + return True + +def show_stp_config_using_klish(dut, type="", vlan="", intf=""): + if type == 'statistics': + command = "show spanning-tree counters vlan {}".format(vlan) + elif type == 'root_guard': + command = "show spanning-tree inconsistentports vlan {}".format(vlan) + elif type == 'bpdu_guard': + command = "show spanning-tree bpdu-guard" + elif type == "vlan_intf": + command = "show spanning-tree vlan {} interface {}".format(vlan, intf) + # elif type == "vlan": + # command = "show spanning-tree vlan {}".format(vlan) + st.show(dut, command, type="klish", skip_tmpl=True) + + +def verify_stp_intf_status(dut, vlanid, interface, status): + """ + API to poll for stp stauts for an interface + + :param dut: + :param vlanid: + :param interface: + :param status: + :return: + """ + if get_stp_port_param(dut, vlanid, interface, "port_state") == status: + st.log("Port status is changed to {}".format(status)) + return True + return False \ No newline at end of file diff --git a/spytest/apis/switching/stp.py b/spytest/apis/switching/stp.py new file mode 100644 index 00000000000..8acdb451af0 --- /dev/null +++ b/spytest/apis/switching/stp.py @@ -0,0 +1,540 @@ +from collections import OrderedDict +from spytest import st + +def config_bridge_priority(dut, port, prio): + """ + + :param dut: + :type dut: + :param port: + :type port: + :param prio: + :type prio: + :return: + :rtype: + """ + st.log("TODO: config_bridge_priority {}/{} {}".format(dut, port, prio)) + + +def read_all_props(dut, port): + """ + + :param dut: + :type dut: + :param port: + :type port: + :return: + :rtype: + """ + st.log("TODO: read_all_props {}/{}".format(dut, port)) + retval = OrderedDict() + retval[dut] = OrderedDict() + retval[dut][port] = OrderedDict() + retval[dut][port]["prop1"] = "v1" + retval[dut][port]["prop2"] = "v2" + return retval + + +CFG_STP = "config spanning_tree" +CFG_STP_INTF = "config spanning_tree interface" +CFG_STP_VLAN = "config spanning_tree vlan" +SHOW_STP = "show spanning_tree" + +def config_stp_intf_param(dut, cfgdictionary={}): + """ + config spanning_tree interface + cfgdictionary={'' : 'enable', 'ifname'} + cfgdictionary={'' : 'disable', 'ifname'} + + config spanning_tree interface priority + cfgdictionary={'priority' : 'prio', 'ifname'} + + config spanning_tree interface cost + cfgdictionary={'cost' : 'cost', 'ifname'} + + config spanning_tree interface portfast + cfgdictionary={'portfast' : 'enable', 'ifname'} + cfgdictionary={'portfast' : 'disable', 'ifname'} + + config spanning_tree interface bpdu_guard + config spanning_tree interface root_guard + config spanning_tree interface uplink_fast + """ + + for key in cfgdictionary: + values = cfgdictionary[key] + cmd = CFG_STP_INTF+" "+key + for value in values: + cmd += " "+value + rv = st.config(dut, cmd, skip_error_check=True) + if "Error" in rv: + st.log(rv) + return False + # no output expected on console for success + st.wait(.200) # add 200 ms delay after config to avoid timing issues + st.log("success : " + cmd) + return True + + +def config_stp_vlan_param(dut, cfgdictionary={}): + """ + config spanning_tree vlan + cfgdictionary={'' : ['enable', 'vlanid']} + cfgdictionary={'' : ['disable', 'vlanid']} + + config spanning_tree vlan forward_delay + cfgdictionary={'forward_delay' : ['vid', 'forward_delay']} + + config spanning_tree vlan hello + cfgdictionary={'hello' : ['vid', 'hello_interval']} + + config spanning_tree vlan max_age + config spanning_tree vlan priority + + config spanning_tree vlan interface priority + cfgdictionary={'interface' : ['priority', 'vid', 'ifname', 'prio']} + cfgdictionary={'interface' : ['cost', 'vid', 'ifname', 'cost_val']} + + """ + + for key in cfgdictionary: + values = cfgdictionary[key] + cmd = CFG_STP_VLAN+" "+key + for value in values: + cmd += " "+value + rv = st.config(dut, cmd, skip_error_check=True) + if "Error" in rv: + st.log(rv) + return False + # no output expected on console for success + st.wait(.200) # add 200 ms delay after config to avoid timing issues + st.log("success : " + cmd) + return True + + + +def config_stp_param(dut, cfgdictionary={}): + """ + config spanning_tree disable pvst + config spanning_tree enable pvst + config spanning_tree priority + config spanning_tree hello + config spanning_tree forward_delay + config spanning_tree max_age + config spanning_tree root_guard_timeout + """ + for key in cfgdictionary: + value = cfgdictionary[key] + cmd = CFG_STP+" "+key+" "+str(value) + rv = st.config(dut, cmd, skip_error_check=True) + if "Error" in rv: + st.log(rv) + return False + # no output expected on console for success + st.wait(.200) # add 200 ms delay after config to avoid timing issues + st.log("success : " + cmd) + return True + + +""" Wrapper CLI-functions for ease of use""" + +def config_stp_enable_pvst(dut): + return config_stp_param(dut, {'enable':'pvst'}) + +def config_stp_enable_rpvst(dut): + return config_stp_param(dut, {'enable':'rpvst'}) + +def config_stp_disable_pvst(dut): + return config_stp_param(dut, {'disable':'pvst'}) + +def config_stp_disable_rpvst(dut): + return config_stp_param(dut, {'disable':'rpvst'}) + +def config_stp_global_hello(dut, hello): + return config_stp_param(dut, {'hello':hello}) + + +def config_stp_global_priority(dut, priority): + return config_stp_param(dut, {'priority':priority}) + + +def config_stp_global_maxage(dut, maxage): + return config_stp_param(dut, {'max_age':maxage}) + + +def config_stp_global_rootguardtimeout(dut, rg_timeout): + return config_stp_param(dut, {'root_guard_timeout':rg_timeout}) + + +def config_stp_global_fwddly(dut, fwddly): + return config_stp_param(dut, {'forward_delay':fwddly}) + + +def config_stp_vlan_enable(dut, vlanid): + return config_stp_vlan_param(dut, cfgdictionary={'' : ['enable', str(vlanid)]}) + + +def config_stp_vlan_disable(dut, vlanid): + return config_stp_vlan_param(dut, cfgdictionary={'' : ['disable', str(vlanid)]}) + + +def config_stp_vlan_hello(dut, vlanid, hello): + return config_stp_vlan_param(dut, cfgdictionary={'hello' : [str(vlanid), str(hello)]}) + + +def config_stp_vlan_fwddly(dut, vlanid, fwddly): + return config_stp_vlan_param(dut, cfgdictionary={'fwddly' : [str(vlanid), str(fwddly)]}) + + +def config_stp_vlan_maxage(dut, vlanid, maxage): + return config_stp_vlan_param(dut, cfgdictionary={'maxage' : [str(vlanid), str(maxage)]}) + + +def config_stp_vlan_priority(dut, vlanid, priority): + return config_stp_vlan_param(dut, cfgdictionary={'priority' : [str(vlanid), str(priority)]}) + + +def config_stp_vlan_intf_cost(dut, vlanid, ifname, cost): + return config_stp_vlan_param(dut, cfgdictionary={'interface' : ['cost', str(vlanid), ifname, str(cost)]}) + + +def config_stp_vlan_intf_priority(dut, vlanid, ifname, priority): + return config_stp_vlan_param(dut, cfgdictionary={'interface' : [str(vlanid), ifname, str(priority)]}) + + +def config_stp_intf_enable(dut, ifname): + return config_stp_intf_param(dut, cfgdictionary={'' : ['enable', ifname]}) + + +def config_stp_intf_disable(dut, ifname): + return config_stp_intf_param(dut, cfgdictionary={'' : ['disable', ifname]}) + + +def config_stp_intf_cost(dut, ifname, cost): + return config_stp_intf_param(dut, cfgdictionary={'cost' : [ifname, str(cost)]}) + + +def config_stp_intf_portfast(dut, ifname, action): + if action not in ['enable', 'disable']: + st.log("Invalid param: action = {}".format(action)) + return + return config_stp_intf_param(dut, cfgdictionary={'portfast' : [action, ifname]}) + + +def config_stp_intf_uplinkfast(dut, ifname, action): + if action not in ['enable', 'disable']: + st.log("Invalid param: action = {}".format(action)) + return + return config_stp_intf_param(dut, cfgdictionary={'uplink_fast' : [action, ifname]}) + + +def config_stp_intf_priority(dut, ifname, priority): + return config_stp_intf_param(dut, cfgdictionary={'priority' : [ifname, str(priority)]}) + + +def config_stp_intf_bpduguard(dut, ifname, cfg_shut=False, action='enable'): + if action not in ['enable', 'disable']: + st.log("Invalid param: action = {}".format(action)) + return + if cfg_shut and action == 'enable': + return config_stp_intf_param(dut, cfgdictionary={'bpdu_guard' : [action, '-s', ifname]}) + + return config_stp_intf_param(dut, cfgdictionary={'bpdu_guard' : [action, ifname]}) + + +def config_stp_intf_rootguard(dut, ifname, action): + if action not in ['enable', 'disable']: + st.log("Invalid param: action = {}".format(action)) + return + return config_stp_intf_param(dut, cfgdictionary={'root_guard' : [action, ifname]}) + + +""" SHOW commands """ +def show_spanning_tree(dut, params=[]): + cmd = SHOW_STP + + for param in params: + cmd += " "+param + + return st.show(dut, cmd) + + +""" Wrapper CLI-functions for ease of use""" +def show_spanning_tree_vlan(dut, vlanid): + return show_spanning_tree(dut, ['vlan', str(vlanid)]) + + +def show_spanning_tree_vlan_intf(dut, vlanid, ifname): + return show_spanning_tree(dut, ['vlan', str(vlanid), 'interface', ifname]) + + +def show_spanning_tree_statistics(dut, vlanid=0): + if vlanid == 0: + return show_spanning_tree(dut, ['statistics']) + else: + return show_spanning_tree(dut, ['statistics', str(vlanid)]) + + +def get_stp_mode(dut): + rows = show_spanning_tree(dut) + if not rows: + return None + return rows[0]['mode'] + + +def _get_stp_vlan_row(dut, vlanid): + rows = show_spanning_tree_vlan(dut, vlanid) + if not rows: + return None + + for row in rows: + if row['vid'] == vlanid: + return row + return None + + +def get_stp_vlan_entry(dut, vlanid): + return _get_stp_vlan_row(dut, vlanid) + + +def get_stp_vlan_instance(dut, vlanid): + matched_row = _get_stp_vlan_row(dut, vlanid) + return matched_row['inst'] if matched_row else None + + +def get_stp_vlan_br_id(dut, vlanid): + matched_row = _get_stp_vlan_row(dut, vlanid) + return matched_row['br_id'] if matched_row else None + + +def get_stp_vlan_br_maxage(dut, vlanid): + matched_row = _get_stp_vlan_row(dut, vlanid) + return matched_row['br_maxage'] if matched_row else None + + +def get_stp_vlan_br_hello(dut, vlanid): + matched_row = _get_stp_vlan_row(dut, vlanid) + return matched_row['br_hello'] if matched_row else None + + +def get_stp_vlan_br_fwddly(dut, vlanid): + matched_row = _get_stp_vlan_row(dut, vlanid) + return matched_row['br_fwddly'] if matched_row else None + + +def get_stp_vlan_br_hold(dut, vlanid): + matched_row = _get_stp_vlan_row(dut, vlanid) + return matched_row['br_hold'] if matched_row else None + + +def get_stp_vlan_br_lasttopo(dut, vlanid): + matched_row = _get_stp_vlan_row(dut, vlanid) + return matched_row['br_lasttopo'] if matched_row else None + + +def get_stp_vlan_br_topoch(dut, vlanid): + matched_row = _get_stp_vlan_row(dut, vlanid) + return matched_row['br_topoch'] if matched_row else None + + +def get_stp_vlan_rt_id(dut, vlanid): + matched_row = _get_stp_vlan_row(dut, vlanid) + return matched_row['rt_id'] if matched_row else None + + +def get_stp_vlan_rt_pathcost(dut, vlanid): + matched_row = _get_stp_vlan_row(dut, vlanid) + return matched_row['rt_pathcost'] if matched_row else None + + +def get_stp_vlan_rt_desigbridgeid(dut, vlanid): + matched_row = _get_stp_vlan_row(dut, vlanid) + return matched_row['rt_desigbridgeid'] if matched_row else None + + +def get_stp_vlan_rt_port(dut, vlanid): + matched_row = _get_stp_vlan_row(dut, vlanid) + return matched_row['rt_port'] if matched_row else None + + +def get_stp_vlan_rt_maxage(dut, vlanid): + matched_row = _get_stp_vlan_row(dut, vlanid) + return matched_row['rt_maxage'] if matched_row else None + + +def get_stp_vlan_rt_hello(dut, vlanid): + matched_row = _get_stp_vlan_row(dut, vlanid) + return matched_row['rt_hello'] if matched_row else None + + +def get_stp_vlan_rt_fwddly(dut, vlanid): + matched_row = _get_stp_vlan_row(dut, vlanid) + return matched_row['rt_fwddly'] if matched_row else None + + +def _get_stp_vlan_port_row(dut, vlanid, ifname): + rows = show_spanning_tree_vlan(dut, vlanid) + for row in rows: + if (row['vid'] == vlanid) and (row['port_name'] == ifname): + return row + return None + + +def get_stp_vlan_port_entry(dut, vlanid, ifname): + return _get_stp_vlan_port_row(dut, vlanid, ifname) + + +def get_stp_vlan_port_priority(dut, vlanid, ifname): + matched_row = _get_stp_vlan_port_row(dut, vlanid, ifname) + return matched_row['port_priority'] if matched_row else None + + +def get_stp_vlan_port_pathcost(dut, vlanid, ifname): + matched_row = _get_stp_vlan_port_row(dut, vlanid, ifname) + return matched_row['port_pathcost'] if matched_row else None + + +def get_stp_vlan_port_portfast(dut, vlanid, ifname): + matched_row = _get_stp_vlan_port_row(dut, vlanid, ifname) + return matched_row['port_portfast'] if matched_row else None + + +def get_stp_vlan_port_uplinkfast(dut, vlanid, ifname): + matched_row = _get_stp_vlan_port_row(dut, vlanid, ifname) + return matched_row['port_uplinkfast'] if matched_row else None + + +def get_stp_vlan_port_state(dut, vlanid, ifname): + matched_row = _get_stp_vlan_port_row(dut, vlanid, ifname) + return matched_row['port_state'] if matched_row else None + + +def get_stp_vlan_port_desigcost(dut, vlanid, ifname): + matched_row = _get_stp_vlan_port_row(dut, vlanid, ifname) + return matched_row['port_desigcost'] if matched_row else None + + +def get_stp_vlan_port_desigroot(dut, vlanid, ifname): + matched_row = _get_stp_vlan_port_row(dut, vlanid, ifname) + return matched_row['port_desigroot'] if matched_row else None + + +def get_stp_vlan_port_desigbridgeid(dut, vlanid, ifname): + matched_row = _get_stp_vlan_port_row(dut, vlanid, ifname) + return matched_row['port_desigbridgeid'] if matched_row else None + + +def _get_stp_bpdu_guard_row(dut, ifname): + rows = show_spanning_tree(dut, ['bpdu_guard']) + for row in rows: + if row['bg_ifname'] == ifname: + return row + return None + + +def get_stp_bg_entry(dut, ifname): + return _get_stp_bpdu_guard_row(dut, ifname) + +def get_stp_bg_cfg_shut(dut, ifname): + matched_row = _get_stp_bpdu_guard_row(dut, ifname) + return matched_row['bg_cfg_shut'] if matched_row else None + + +def get_stp_bg_oper_shut(dut, ifname): + matched_row = _get_stp_bpdu_guard_row(dut, ifname) + return matched_row['bg_oper_shut'] if matched_row else None + + +def _get_stp_root_guard_row(dut, ifname): + rows = show_spanning_tree(dut, ['root_guard']) + if not rows: + return None + + if ifname is None: + return rows[0] + else: + for row in rows: + if row['rg_ifname'] == ifname: + return row + return None + + +def get_stp_rg_timeout(dut): + matched_row = _get_stp_root_guard_row(dut, None) + return matched_row['rg_timeout'] if matched_row else None + + +def get_stp_rg_entry(dut, ifname): + matched_row = _get_stp_root_guard_row(dut, ifname) + return matched_row + + +def get_stp_rg_vid(dut, ifname): + matched_row = _get_stp_root_guard_row(dut, ifname) + return matched_row['rg_vid'] if matched_row else None + + +def get_stp_rg_status(dut, ifname): + matched_row = _get_stp_root_guard_row(dut, ifname) + return matched_row['rg_status'] if matched_row else None + + +def _get_stp_stats_row(dut, vlanid, ifname): + rows = show_spanning_tree_statistics(dut, vlanid) + if not rows: + return None + + for row in rows: + if (row['st_vid'] == vlanid) and (row['st_portno'] == ifname): + return row + return None + + +def get_stp_stats_entry(dut, vlanid, ifname): + return _get_stp_stats_row(dut, vlanid, ifname) + + +def get_stp_stats_bpdurx(dut, vlanid, ifname): + matched_row = _get_stp_stats_row(dut, vlanid, ifname) + return matched_row['st_bpdurx'] if matched_row else None + + +def get_stp_stats_bpdutx(dut, vlanid, ifname): + matched_row = _get_stp_stats_row(dut, vlanid, ifname) + return matched_row['st_bpdutx'] if matched_row else None + + +def get_stp_stats_tcnrx(dut, vlanid, ifname): + matched_row = _get_stp_stats_row(dut, vlanid, ifname) + return matched_row['st_tcnrx'] if matched_row else None + + +def get_stp_stats_tcntx(dut, vlanid, ifname): + matched_row = _get_stp_stats_row(dut, vlanid, ifname) + return matched_row['st_tcntx'] if matched_row else None + + +def get_stpctl_vlan_port(dut, vlanid, ifname): + output = st.show(dut, 'stpctl port {} {}'.format(vlanid, ifname)) + if len(output) != 1: + return None + return output[0] + +""" + +if __name__== "__main__": + config_stp_param("dut1", {'enable':'pvst', 'forward_delay': '20'}) + config_stp_intf_param('dut2', + {'':['enable', 'Eth1'], + 'cost': ['2', 'Eth0'], + 'portfast' : ['disable', 'Eth2'], + 'portfast' : ['enable', 'Eth3']}) + + config_stp_vlan_param('dut3', + {'interface':['cost', '100', 'Ethernet1', '20']}) + + show_spanning_tree('dut1') + show_spanning_tree('dut1', ['vlan']) + show_spanning_tree('dut1', ['vlan', '100']) + show_spanning_tree('dut1', ['vlan', '100', 'interface', 'Eth0']) +""" diff --git a/spytest/apis/switching/vlan.py b/spytest/apis/switching/vlan.py new file mode 100644 index 00000000000..01e511969d0 --- /dev/null +++ b/spytest/apis/switching/vlan.py @@ -0,0 +1,657 @@ +# This file contains the list of API's which performs VLAN operations. +# Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + +from spytest import st +from utilities.common import random_vlan_list, filter_and_select, exec_foreach +from utilities.utils import get_interface_number_from_name +from utilities.parallel import ensure_no_exception + +def create_vlan(dut, vlan_list, cli_type=""): + """ + To create list of VLANs. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :param vlan_list: + :param cli_type: + :return: + """ + if not cli_type: + cli_type=st.get_ui_type(dut) + + st.log("Creating vlan") + vlan_li = map(str, vlan_list) if isinstance(vlan_list, list) else [vlan_list] + commands = list() + for each_vlan in vlan_li: + if cli_type == "click": + commands.append("config vlan add {}".format(each_vlan)) + else: + commands.append("interface Vlan {}".format(each_vlan)) + commands.append('exit') + st.config(dut, commands, type=cli_type) + return True + + +def delete_vlan(dut, vlan_list, cli_type=""): + """ + To delete list of VLANs. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :param vlan_list: + :param cli_type: + :return: + """ + if not cli_type: + cli_type=st.get_ui_type(dut) + + st.log("Delete vlan") + vlan_li = map(str, vlan_list) if isinstance(vlan_list, list) else [vlan_list] + commands = list() + try: + for each_vlan in vlan_li: + if cli_type == "click": + commands.append("config vlan del {}".format(each_vlan)) + else: + commands.append("no interface Vlan {}".format(each_vlan)) + st.config(dut, commands, skip_error_check=True, type=cli_type) + if cli_type == "click": + vlan_list = get_vlan_list(dut, cli_type="click") + for each_vlan in vlan_li: + if each_vlan in vlan_list: + st.error(" Vlan{} is not deleted".format(each_vlan)) + return False + return True + except Exception as e1: + st.log(e1) + st.error(" Vlan is not deleted due to other reasons") + return False + + +def delete_all_vlan(dut, cli_type="click"): + """ + To delete All VLANs. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :param cli_type: + :return: + """ + st.log("delete all vlan") + if cli_type == "click": + try: + config_vlan_range(dut, '1 4093', config='del', skip_verify=True) + return True + except Exception as e: + st.log(e) + st.error("Failed to Delete VLAN(s)") + return False + if cli_type == "klish": + return delete_vlan(dut, get_vlan_list(dut, cli_type=cli_type), cli_type=cli_type) + + +def show_vlan_config(dut, vlan_id=None, cli_type="click"): + """ + To get vlan config from 'show vlan config' + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param vlan_id: + :param cli_type: + :return: + """ + if cli_type == "click": + st.log("show vlan config") + command = "show vlan config" + else: + command = "show Vlan" + if vlan_id: + command += " {}".format(vlan_id) + return st.show(dut, command, type=cli_type) + + +def show_vlan_brief(dut, vlan_id=None, cli_type="click"): + """ + To get vlan config from 'show vlan brief' + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :param vlan_id: + :param cli_type: + :return: + """ + if cli_type == "click": + st.log("show vlan brief") + command = "show vlan brief" + else: + command = "show Vlan" + if vlan_id: + command += " {}".format(vlan_id) + return st.show(dut, command, type=cli_type) + + +def get_vlan_count(dut): + """ + To get the Vlan count using - 'show vlan count' command. + :param dut: + :type dut: + :return: + :rtype: + """ + output = st.show(dut, "show vlan count") + count = int(output[0]['vlan_count']) + return count + + +def get_vlan_list(dut, cli_type="click"): + """ + Get list of VLANs + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :param cli_type: + :return: + """ + st.log("show vlan to get vlan list") + rv = show_vlan_config(dut, cli_type=cli_type) + vlan_list = list(set([eac['vid'] for eac in rv])) + return vlan_list + + +def add_vlan_member(dut, vlan, port_list, tagging_mode=False, skip_error=False, no_form=False, cli_type=""): + """ + Add Members to VLAN + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param vlan: + :param port_list: + :param tagging_mode: + :param skip_error: + :param no_form: + :param cli_type: + :return: + """ + if not cli_type: + cli_type=st.get_ui_type(dut) + + st.log("Add member to the VLAN") + port_li = list(port_list) if isinstance(port_list, list) else [port_list] + commands = list() + for each_port in port_li: + if cli_type == "click": + if tagging_mode: + command = "config vlan member add {} {}".format(vlan, each_port) + else: + command = "config vlan member add {} {} -u ".format(vlan, each_port) + + # Here handling the error while adding interface to vlan + out = st.config(dut, command, skip_error_check=True) + + if "is already a member of Vlan{}".format(vlan) in out: + st.error("{} is already a member of Vlan{}".format(each_port, vlan)) + return False + if "Vlan{} doesn't exist".format(vlan) in out: + st.error(" Vlan{} doesn't exist".format(vlan)) + return False + if "has ip address configured" in out: + st.error("Error: {} has ip address configured".format(each_port)) + return False + else: + interface_details = get_interface_number_from_name(each_port) + if not interface_details: + st.log("Interface details not found {}".format(interface_details)) + return False + commands.append("interface {} {}".format(interface_details.get("type"), interface_details.get("number"))) + participation_mode = "trunk" if tagging_mode else "access" + if participation_mode == "trunk": + command = "switchport trunk allowed Vlan {}".format(vlan) + commands.append("no {}".format(command) if no_form else "{}".format(command)) + elif participation_mode == "access": + command = "switchport access Vlan" + commands.append("no {}".format(command) if no_form else "{} {}".format(command, vlan)) + commands.append("exit") + if commands: + out = st.config(dut, commands, type=cli_type, skip_error_check=True) + if "Invalid VLAN:" in out: + st.log("Vlan{} doesn't exist".format(vlan)) + return False + return True + + +def delete_vlan_member(dut, vlan, port_list, participation_mode="trunk", cli_type="", skip_error_check=False): + """ + Delete Members in VLAN + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :param vlan: + :param port_list: + :param participation_mode: + :param cli_type: + :return: + """ + if not cli_type: + cli_type=st.get_ui_type(dut) + + st.log("Delete member from the VLAN") + port_li = list(port_list) if isinstance(port_list, list) else [port_list] + commands = list() + for each_port in port_li: + if cli_type == "click": + command = "config vlan member del {} {}".format(vlan, each_port) + out = st.config(dut, command, skip_error_check=skip_error_check) + if "is not a member of Vlan{}".format(vlan) in out: + st.error("{} is not a member of Vlan{}".format(each_port, vlan)) + return False + if "Vlan{} doesn't exist".format(vlan) in out: + st.error("Vlan{} doesn't exist".format(vlan)) + return False + else: + interface_details = get_interface_number_from_name(each_port) + if not interface_details: + st.log("Interface details not found {}".format(interface_details)) + return False + commands.append("interface {} {}".format(interface_details.get("type"), interface_details.get("number"))) + if participation_mode == "trunk": + command = "switchport trunk allowed Vlan {}".format(vlan) + commands.append("no {}".format(command)) + elif participation_mode == "access": + command = "switchport access Vlan".format(vlan) + commands.append("no {}".format(command)) + commands.append("exit") + if commands: + st.config(dut, commands, type=cli_type, skip_error_check=skip_error_check) + return True + + +def get_vlan_member(dut, vlan_list=[], cli_type="click"): + """ + To Get VLANs vs list of Members. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :param vlan_list: + :param cli_type: + :return: + """ + vlan_val = {} + vlan_li = map(str, vlan_list) if isinstance(vlan_list, list) else [vlan_list] + out = show_vlan_config(dut, cli_type=cli_type) + if vlan_li: + temp = [] + for each in list(set(vlan_li)): + temp += filter_and_select(out, None, {"vid": each}) + out = temp + + for each in out: + if each['member']: + if each['vid'] not in vlan_val: + vlan_val[each['vid']] = [each['member']] + else: + vlan_val[each['vid']].append(each['member']) + return vlan_val + + +def get_member_vlan(dut, interface_list=[], cli_type="click"): + """ + To Get Members vs list of VLANs. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :param interface_list: + :param cli_type: + :return: + """ + member_val = {} + interface_li = list(interface_list) if isinstance(interface_list, list) else [interface_list] + out = show_vlan_config(dut, cli_type=cli_type) + if interface_li: + temp = [] + for each in list(set(interface_li)): + temp += filter_and_select(out, None, {"member": each}) + out = temp + + for each in out: + if each['member']: + if each['member'] not in member_val: + member_val[each['member']] = [each['vid']] + else: + member_val[each['member']].append(each['vid']) + return member_val + + +def verify_vlan_config(dut, vlan_list, tagged=None, untagged=None, name=None): + """ + Verify vlan config using 'show vlan config' + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :param vlan_list: + :param tagged: + :param untagged: + :param name: + :return: + """ + + vlan_li = map(str, vlan_list) if isinstance(vlan_list, list) else [vlan_list] + + output = show_vlan_config(dut) + for each_vlan in vlan_li: + entries = filter_and_select(output, None, {"vid": each_vlan}) + if not entries: + st.log("Provided VLAN {} entry is not exist in table".format(each_vlan)) + return False + if tagged: + interface_list = list(tagged) if isinstance(tagged, list) else [tagged] + for each_intr in interface_list: + if not filter_and_select(entries, None, {"member": each_intr, "mode": "tagged"}): + st.log("Provided interface {} is not a tagged member of Vlan {}".format(each_intr, each_vlan)) + return False + if untagged: + interface_list = list(untagged) if isinstance(untagged, list) else [untagged] + for each_intr in interface_list: + if not filter_and_select(entries, None, {"member": each_intr, "mode": "untagged"}): + st.log("Provided interface {} is not a untagged member of Vlan {}".format(each_intr, each_vlan)) + return False + if name and not filter_and_select(entries, None, {"name": name}): + st.log("Provided and configured VLAN {} name in not match".format(each_vlan)) + return False + return True + + +def verify_vlan_brief(dut, vid, tagged=None, untagged=None, ip_address=None, dhcp_helper_add=None): + """ + Verify vlan config using 'show vlan brief' + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :param vid: + :param tagged: + :param untagged: + :param ip_address: + :param dhcp_helper_add: + :return: + """ + vid = str(vid) + output = show_vlan_brief(dut) + entries = filter_and_select(output, None, {"vid": vid}) + if not entries: + st.log("Provided VLAN {} entry is not exist in table".format(vid)) + return False + if tagged and not filter_and_select(entries, None, {"ports": tagged, "porttagging": "tagged"}): + st.log("Provided interface {} is not a tagged member of Valn {}".format(tagged, vid)) + return False + if untagged and not filter_and_select(entries, None, {"ports": untagged, "porttagging": "untagged"}): + st.log("Provided interface {} is not a untagged member of Valn {}".format(untagged, vid)) + return False + if dhcp_helper_add and not filter_and_select(entries, None, {"vid": vid, "dhcphelperadd": dhcp_helper_add}): + st.log("Provided and configured vlan {} DHCPHelperAdd in not match".format(vid, dhcp_helper_add)) + return False + if ip_address and not filter_and_select(entries, None, {"vid": vid, "ipadd": ip_address}): + st.log("Provided and configured vlan {} IpAdd in not match".format(vid, ip_address)) + return False + return True + + +def _clear_vlan_configuration_helper(dut_list, cli_type="click"): + """ + Find and clear VLAN and its Members. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut_list: + :return: + """ + dut_li = list(dut_list) if isinstance(dut_list, list) else [dut_list] + for dut in dut_li: + st.log("############## {} : VLAN Cleanup ################".format(dut)) + if cli_type == 'click': + output = show_vlan_config(dut) + + if st.is_community_build(): + (vlans, commands) = ({}, []) + for eac in output: + (vid, member) = (eac['vid'], eac['member']) + if vid: + vlans[vid] = 1 + if member: + command = "config vlan member del {} {}".format(vid, member) + commands.append(command) + for vid in vlans.keys(): + command = "config vlan del {}".format(vid) + commands.append(command) + st.config(dut, commands) + continue + + # Get Vlan list + vlan_list = list(set([eac['vid'] for eac in output])) + # Get interface list + member_list = list(set([eac['member'] for eac in output if eac['member'] != ''])) + if member_list: + if not config_vlan_range_members(dut, '1 4093', member_list, config='del', skip_verify=True): + st.log("VLAN all member delete failed") + return False + if vlan_list: + if not delete_all_vlan(dut, cli_type=cli_type): + st.log("VLAN all delete failed") + return False + if cli_type == 'klish': + return delete_all_vlan(dut, cli_type=cli_type) + + return True + + +def clear_vlan_configuration(dut_list, thread=True, cli_type="click"): + """ + Find and clear VLAN and its Members on list of DUTs + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut_list: + :param thread: True(Default) / False + :param cli_type: + :return: + """ + dut_li = list(dut_list) if isinstance(dut_list, list) else [dut_list] + [out, exceptions] = exec_foreach(thread, dut_li, _clear_vlan_configuration_helper, cli_type=cli_type) + ensure_no_exception(exceptions) + return False if False in out else True + + +def create_vlan_and_add_members(vlan_data, cli_type="click"): + """ + Create VLAN and Add members to VLAN across DUTs + Author : Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + + :param vlan_data: List of dictionaries with dut, vlan_id and tagged / untagged members list. + Sample vlan_data -- [{"dut": [vars.D1, vars.D2], "vlan_id":"100","tagged":["Ethernet0", "Ethernet1"], + "untagged":["Ethernet3", "Ethernet4"]},{"dut": [vars.D2], "vlan_id":"200", + "tagged":["Ethernet5", "Ethernet6"], "untagged":["Ethernet7", "Ethernet8"]}] + :param cli_type: + :return: True / False + """ + if not isinstance(vlan_data, list): + return False + for data in vlan_data: + if "dut" not in data: + return False + dut_list = list(data["dut"]) if isinstance(data["dut"], list) else [data["dut"]] + for dut in dut_list: + if "vlan_id" in data: + create_vlan(dut, data["vlan_id"], cli_type=cli_type) + if "tagged" in data and data["tagged"]: + add_vlan_member(dut, data["vlan_id"], data["tagged"], tagging_mode=True, cli_type=cli_type) + if "untagged" in data and data["untagged"]: + add_vlan_member(dut, data["vlan_id"], data["untagged"], tagging_mode=False, cli_type=cli_type) + else: + return False + return True + + +def get_non_existing_vlan(dut, count=1, cli_type="click"): + """ + This API will provide number of non existing vlans + Author : Chaitanya Vella (chaitanya-vella.kumar@broadcom.com)s + :param dut: + :param count: + :return: + """ + vlan_list = get_vlan_list(dut, cli_type=cli_type) + return random_vlan_list(count, vlan_list) + +def _check_config_vlan_output(rv): + if "VLANs already existing" in rv: + st.error("VLANs already existing were tried to be configured") + return False + if "Non-existent VLANs" in rv: + st.error("Non-existent VLANs were tried to be deleted") + return False + return True + +def config_vlan_range(dut, vlan_range, config="add", skip_verify=False): + """ + Author: sneha.mathew@broadcom.com + Creates/Deletes range of VLANs given as input. + :param dut: + :param vlan_range: range of VLAN IDs in string format, separator space: "first-VLAN last_VLAN". + can be list of strings. + :param config: flag which specifies to configure or unconfigure the cli. add for config, del for unconfig. + Default:add + :param skip_verify: True | False + :return True: The VLANs were successfully created or deleted. + :return False: Error in parameter passed. + """ + if config == "del": + st.log("Deleting range of vlans {}".format(vlan_range)) + else: + st.log("Creating range of vlans {}".format(vlan_range)) + + vlan_range_list = list(vlan_range) if isinstance(vlan_range, list) else [vlan_range] + + commands = [] + if st.is_community_build(): + for vrange in vlan_range_list: + [range_min, range_max] = [int(vid) for vid in vrange.split()] + for vid in range(range_min, range_max+1): + commands.append("config vlan {} {}".format(config, vid)) + rv = st.config(dut, commands) + return _check_config_vlan_output(rv) + + for vrange in vlan_range_list: + commands.append("config vlan range {} {}".format(config, vrange)) + + ver_flag = True + for command in commands: + # -w option displays warning, turned on so that existing vlans message can be checked + if not skip_verify: command += " -w" + rv = st.config(dut, command) + if not _check_config_vlan_output(rv): + ver_flag = False + + return ver_flag + +def config_vlan_range_members(dut, vlan_range, port, config="add", skip_verify=False, skip_error=False): + """ + Author: sneha.mathew@broadcom.com + Add or remove given member ports to range of VLANs given as input. + :param dut: + :param vlan_range: range of VLAN IDs in string format "first-VLAN last_VLAN". can be list of strings. + :param port: port or list of ports which needs to be added / deleted to vlan_range + :param config: flag which specifies to configure or unconfigure the cli. + add for config, del for unconfig. Default:add + :param skip_verify: True | False + :param skip_error: True | False + :return True: Member ports successfully added or deleted to vlan range. + :return False: Error in parameter passed. + """ + if config == "del": + st.log("Deleting member ports from range of vlans") + else: + st.log("Adding member ports to range of vlans") + + vlan_range_list = list(vlan_range) if isinstance(vlan_range, list) else [vlan_range] + port_list = list(port) if isinstance(port, list) else [port] + + entries = [] + for vlan_range in vlan_range_list: + for each_port in port_list: + if not st.is_community_build(): + command = "config vlan member range {} {} {}".format(config, vlan_range, each_port) + entries.append([command, each_port, vlan_range]) + else: + [range_min, range_max] = [int(vid) for vid in vlan_range.split()] + for vid in range(range_min, range_max+1): + command = "config vlan member {} {} {}".format(config, vid, each_port) + entries.append([command, each_port, vid]) + + if st.is_community_build() and config == "del": + skip_error=True + + ver_flag = True + for [command, each_port, vlan_range] in entries: + if not skip_verify and not st.is_community_build(): + # -w option displays warning, turned on so that existing vlans message can be checked + command += " -w" + output = st.config(dut, command, skip_error_check=skip_error) + if "is already a member of Vlan" in output: + st.error("{} is already a member of Vlan{}".format(each_port, vlan_range)) + ver_flag = False + if "doesn't exist" in output: + st.error(" Vlan{} doesn't exist".format(vlan_range)) + ver_flag = False + + return ver_flag + + +def config_vlan_members(dut, vlan_list, port_list, config="add", tagged=True, skip_verify=False, cli_type="click"): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Add or remove given member ports to VLANs given as input. + :param dut: + :param vlan_list: range of VLAN IDs is can be list of strings. + :param port_list: port or list of ports which needs to be added / deleted to vlan_range + :param config: flag which specifies to configure or unconfigure the cli. + add for config, del for unconfig. Default:add + :param skip_verify: True | False + :param tagged: + :param cli_type: + :return True: Member ports successfully added or deleted to vlan range. + :return False: Error in parameter passed. + """ + if config == "del": + st.log("Deleting member ports from range of vlans") + else: + st.log("Adding member ports to range of vlans") + + vlan_li = list(vlan_list) if isinstance(vlan_list, list) else [vlan_list] + port_li = list(port_list) if isinstance(port_list, list) else [port_list] + + ver_flag = True + for vlan in vlan_li: + if cli_type == "click": + for each_port in port_li: + command = "config vlan member {} {} {}".format(config, vlan, each_port) + if not tagged: + command += " -u" + output = st.config(dut, command) + if "is already a member of Vlan" in output: + st.error("{} is already a member of Vlan{}".format(each_port, vlan)) + ver_flag = False + if "doesn't exist" in output: + st.error(" Vlan{} doesn't exist".format(vlan)) + ver_flag = False + else: + no_form = True if config == "del" else False + add_vlan_member(dut, vlan, port_li, tagging_mode=tagged, skip_error=False, no_form=no_form, + cli_type=cli_type) + return ver_flag + +def create_multiple_vlans_and_members(dut, data): + """ + :param dut: + :param data: {'G7': {'normal_vlan': {'vlans': [1577, 1578, 1579], 'members':['Ethernet0', 'PortChannel3', 'PortChannel5', 'PortChannel4']}, 'peer_vlan': {'vlans': 1580, 'members': ['Ethernet12', 'PortChannel2']}}, 'G6':{'normal_vlan': {'vlans': [1577, 1578, 1579], 'members': ['Ethernet0', 'PortChannel5', 'Ethernet24']}}, 'G4': {'normal_vlan': {'vlans': [1577, 1578, 1579], 'members': ['Ethernet120', 'PortChannel2', 'PortChannel3', 'PortChannel4', 'Ethernet112']}, 'peer_vlan': {'vlans': 1581, 'members': ['PortChannel1']}}, 'G3': {'normal_vlan': {'vlans': [1577, 1578, 1579], 'members': ['Ethernet120', 'PortChannel3', 'PortChannel5', 'PortChannel4']}, 'peer_vlan': {'vlans': 1580, 'members': ['Ethernet0', 'PortChannel2']}}, 'G8': {'normal_vlan': {'vlans': [1577, 1578, 1579], 'members': ['Ethernet0', 'PortChannel2', 'PortChannel3', 'PortChannel4']}, 'peer_vlan': {'vlans':1581, 'members': ['PortChannel1']}}} + :return: True | False + """ + for key, value in data[dut].items(): + create_vlan(dut, value["vlans"]) + if not config_vlan_members(dut,value["vlans"],value["members"]): + st.log("ADDING MEMBERS {} to VLAN {} FAILED".format(value["members"], value["vlans"])) + st.report_fail("vlan_tagged_member_fail", value["members"], value["vlans"]) + return True diff --git a/spytest/apis/system/__init__.py b/spytest/apis/system/__init__.py new file mode 100644 index 00000000000..24b655ac89c --- /dev/null +++ b/spytest/apis/system/__init__.py @@ -0,0 +1 @@ +__all__ = ['basic', 'port'] \ No newline at end of file diff --git a/spytest/apis/system/basic.py b/spytest/apis/system/basic.py new file mode 100644 index 00000000000..35fdc5d92e0 --- /dev/null +++ b/spytest/apis/system/basic.py @@ -0,0 +1,1569 @@ +import json +import tempfile +import os +import re +import ast +import datetime +from spytest import st +from spytest.utils import filter_and_select +from spytest.utils import exec_all +from utilities.common import delete_file +import utilities.utils as utils_obj +import apis.system.connection as conn_obj + +def ensure_hwsku_config(dut): + #TODO: call sudo config-hwsku set if present in device params + pass + +def ensure_certificate(dut): + st.config(dut, "sudo /usr/bin/certgen admin") + +def get_system_status(dut, service=None, skip_error_check=False): + output = "???" + try: + output = st.show(dut, "show system status", skip_tmpl=True, + skip_error_check=skip_error_check) + if "Error: Got unexpected extra argument (status)" in output: + return None + + output = st.parse_show(dut, "show system status", output) + if not output: + return False + if output[0]["status"] == "ready": + return True + if service and output[0][service] == "Up": + return True + except Exception as exp: + msg = "Failed to read system online status output='{}' error='{}'" + st.warn(msg.format(output, exp)) + return False + + +def get_swver(dut): + """ + :param dut: + :return: + """ + version = show_version(dut)['version'] + return version + + +def get_sysuptime(dut): + """ + :param dut: + :type dut: + :return: + :rtype: + """ + up_time = show_version(dut)['uptime'] + return up_time + + +def get_hwsku(dut): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to ge the hwsku of the device. + :param dut: + :return: + """ + output = st.show(dut, "show platform summary") + if len(output) <= 0 or "hwsku" not in output[0]: + return None + hwsku = output[0]["hwsku"] + return hwsku + + +def get_platform_summary(dut, value=None): + """ + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + Function to ge the Platform summary of the device. + :param dut: + :param value: hwsku | platform | asic + :return: + """ + output = st.show(dut, "show platform summary") + if value: + if len(output) <= 0 or value not in output[0]: + return None + out = output[0][value] + return out + else: + if output: + return output[0] + + +def get_dut_date_time(dut): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to get the DUT date and time + :param dut: + :return: + """ + return utils_obj.remove_last_line_from_string(st.config(dut, "date")) + + +def get_dut_date_time_obj(dut): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to get the dur date and time object + :param dut: + :return: + """ + date_val = get_dut_date_time(dut) + try: + match = re.search('[A-Z]{2,}', date_val) + if match: + timezone = match.group(0) + return datetime.datetime.strptime(date_val, '%a %b %d %H:%M:%S {} %Y'.format(timezone)) + except Exception as e: + st.error(e) + return None + + +def get_mac_address(base_mac="00:00:00:00:00:00", start=1, end=100): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to get the mac addressses + :param base_mac: + :param start: + :param end: + :return: + """ + mac_address_list = list() + base_mac = base_mac.replace(":", '').replace(" ", '') + mac_int = int("0x"+base_mac, 16) + for i in range(mac_int+start, mac_int+end+1): + mac_address = "{0:0{1}x}".format(i, 12) + mac_formated = ":".join([mac_address[i:i+2] for i in range(0, len(mac_address), 2)]) + mac_address_list.append(mac_formated) + return mac_address_list + + +def get_ifconfig(dut, interface='eth0'): + """ + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + Function to get the ifconfig output + :param dut: + :param interface: + :return: + """ + command = '/sbin/ifconfig' + if interface: + command += " {}".format(interface) + return st.show(dut, command) + + +def get_ifconfig_inet(dut, interface): + """ + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + Function to get the ifconfig inet + :param dut: + :param interface: + :return: + """ + output = get_ifconfig(dut, interface) + if len(output) <= 0 or "inet" not in output[0]: + return None + ip_addresses = output[0]['inet'] + return ip_addresses + + +def get_ifconfig_inet6(dut, interface): + """ + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + Function to get the ifconfig inet6 + :param dut: + :param interface: + :return: + """ + output = get_ifconfig(dut, interface) + if len(output) <= 0 or "inet6" not in output[0]: + return None + ip_addresses = output[0]['inet6'] + return ip_addresses + + +def get_ifconfig_ether(dut, interface='eth0'): + """ + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + Function to get the ifconfig ethernet + :param dut: + :param interface: + :return: + """ + output = get_ifconfig(dut, interface) + if len(output) <= 0 or "mac" not in output[0]: + return None + mac_address = output[0]['mac'] + return mac_address + + +def ifconfig_operation(connection_obj, interface_name, operation, device="dut"): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to perform operations on ifconfig interface + :param connection_obj: + :param interface_name: + :param operation: + :param device: + :return: + """ + command = "if{} {}".format(operation, interface_name) + if device == "dut": + st.config(connection_obj, command) + else: + conn_obj.execute_command(connection_obj, command) + + +def get_hostname(dut): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to get the hostname + :param dut: + :return: + """ + cmd = 'hostname' + hostname = utils_obj.remove_last_line_from_string(st.show(dut, cmd, skip_tmpl=True)) + hostname = hostname.strip() + if hostname.startswith(cmd+'\n'): + hostname = hostname[len(cmd+'\n'):] + return hostname + + +def whoami(connection_obj): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to get the whoami + :param connection_obj: + :return: + """ + return utils_obj.remove_last_line_from_string(conn_obj.execute_command(connection_obj, "whoami")) + + +def service_operations(connection_obj, service, action="start", client="dut"): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to do the service operations + :param connection_obj: + :param service: + :param action: + :param client: + :return: + """ + st.log("####### Restarting {} service ########".format(service)) + command = "sudo service {} {}".format(service, action) + if client == "dut": + st.config(connection_obj, command) + else: + cnt = 1 + iteration = 4 + while True: + response = conn_obj.execute_command(connection_obj, command) + if response is None: + st.log("No response received, hence retrying..") + conn_obj.execute_command(connection_obj, command) + if response is not None: + st.log("Received response after executing command") + break + if cnt > iteration: + st.error("Prompt not found after {} attempts".format(iteration)) + break + cnt += 1 + st.wait(3) + + +def verify_service_status(dut, service, device="dut"): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to verify the service status + :param dut: DUT or server + :param service: + :param device : + :return: + """ + command = "systemctl is-active {}".format(service) + if device == "dut": + result = st.config(dut, command) + else: + result = conn_obj.execute_command(dut, command) + if utils_obj.remove_last_line_from_string(result) != "active": + if "active" not in result or result != "inactive": + return False + return True + + +def service_operations_by_systemctl(dut, service, operation): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to do the service operations using systemctl + :param dut: + :param service: + :param operation: + :return: + """ + command = "systemctl {} {}".format(operation, service) + st.config(dut, command) + + +def copy_file_to_local_path(dut, src_path, dst_path): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to copy the file to local path + :param dut: + :param src_path: + :param dst_path: + :return: + """ + command = "cp {} {}".format(src_path, dst_path) + st.config(dut, command) + + +def delete_line_from_file(connection_obj, line_number, file_path, device="dut"): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to delete the line from file + :param connection_obj: + :param line_number: + :param file_path: + :param device: + :return: + """ + command = "sed -i -e '{}d' {}".format(line_number, file_path) + if device == "dut": + st.config(connection_obj, command) + else: + conn_obj.execute_command(connection_obj, command) + + +def write_to_file(connection_obj, content, file_path, device="dut"): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to write to file + :param connection_obj: + :param content: + :param file_path: + :param device: + :return: + """ + command = "sudo echo {} >> {}".format(content, file_path) + st.log(command) + if device == "dut": + st.config(connection_obj, command) + else: + conn_obj.execute_command(connection_obj, command) + + +def create_and_write_to_file_sudo_mode(dut, content, file_path): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Functcion to creat and write to file + :param dut: + :param content: + :param file_path: + :return: + """ + command = 'sudo bash -c "echo {} > {}" '.format(content, file_path) + st.config(dut, command) + + +def write_to_file_sudo_mode(dut, content, file_path): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to write to file using sudo mode + :param dut: + :param content: + :param file_path: + :return: + """ + command = 'sudo bash -c "echo {} >> {}" '.format(content, file_path) + st.config(dut, command) + + +def verify_file_on_device(connection_obj, client_path, file_name="client.pem", device="dut"): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to verify the file on device + :param connection_obj: + :param client_path: + :param file_name: + :param device: + :return: + """ + path = "{}/{}".format(client_path, file_name) + command = '[ -f {} ] && echo 1 || echo 0'.format(path) + try: + if device == "dut": + files_list = st.config(connection_obj, command) + else: + files_list = conn_obj.execute_command(connection_obj, command) + if int(utils_obj.remove_last_line_from_string(files_list)) != 1: + return False + return True + except Exception as e: + st.log(e) + return False + + +def make_dir(connection_obj, folder_path, device="dut"): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to make directory + :param connection_obj: + :param folder_path: + :param device: + :return: + """ + command = "mkdir -p {}".format(folder_path) + if device == "dut": + st.config(connection_obj, command) + else: + conn_obj.execute_command(connection_obj, command) + + +def change_permissions(connection_obj, folder_path, permission=777, device="dut"): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to change permissions of the file + :param connection_obj: + :param folder_path: + :param permission: + :param device: + :return: + """ + command = "chmod {} {}".format(permission, folder_path) + if device == "dut": + st.config(connection_obj, command) + else: + conn_obj.execute_command(connection_obj, command) + + +def write_to_file_to_line(connection_obj, content, line, file_path, device="dut"): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to write content to a line in a file. + :param connection_obj: + :param content: + :param line: + :param file_path: + :param device: + :return: + """ + command = "sudo sed -i '{}i {}' {}".format(line, content, file_path) + st.log("COMMAND -- {}".format(command)) + if device == "dut": + st.config(connection_obj, command) + else: + conn_obj.execute_command(connection_obj, command) + + +def get_pwd(dut): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Get present working directory on the device + :param dut: + :return: + """ + return utils_obj.remove_last_line_from_string(st.show(dut, "pwd", skip_tmpl=True)) + + +def write_to_file_before_line(connection_obj, content, before_line, file_path, device="dut"): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to write content to a file before specific line + :param connection_obj: + :param content: + :param before_line: + :param file_path: + :param device: + :return: + """ + command = "sed -i '/^{}.*/i {}' {}".format(before_line, content, file_path) + if device == "dut": + st.config(connection_obj, command) + else: + conn_obj.execute_command(connection_obj, command) + + +def get_match_string_line_number(connection_obj, search_string, file_path, device="dut"): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to get the matching string line number + :param connection_obj: + :param search_string: + :param file_path: + :param device: + :return: + """ + command = "grep -hnr --color=never '{}' {}".format(search_string, file_path) + if device == "dut": + result = st.config(connection_obj, command) + else: + result = conn_obj.execute_command(connection_obj, command) + result = utils_obj.remove_last_line_from_string(result) + if result: + result = str(result) + match_string = re.search(r"(\d+):", result) + if match_string: + line_number = match_string.group(1) + return line_number + return -1 + + +def write_update_file(connection_obj, old_string, new_string, file_path): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to write the file to be updated + :param connection_obj: + :param old_string: + :param new_string: + :param file_path: + :return: + """ + # line_number = get_match_string_line_number(connection_obj, new_string, file_path, "server") + replace_line_in_file(connection_obj, old_string, new_string, file_path) + if not find_line_in_file(connection_obj, new_string, file_path): + new_string = "{}".format(new_string) + write_to_file_to_line(connection_obj, new_string, 51, file_path, device="server") + # before_line = "key dhcpupdate {" + # write_to_file_before_line(connection_obj, new_string, before_line, file_path, "server") + if not find_line_in_file(connection_obj, new_string, file_path): + return False + else: + return True + return True + + +def write_to_text_file(content): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to write content to a file + :param content: + :return: + """ + if content: + src_file = tempfile.mktemp() + src_fp = open(src_file, "w") + src_fp.write(content) + src_fp.close() + return src_file + + +def replace_line_in_file(ssh_conn_obj, old_string, new_string, file_path,device = 'server'): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to replace the content in a file in a specific line + :param ssh_conn_obj: + :param old_string: + :param new_string: + :param file_path: + :return: + """ + command = "sudo sed -i '/^{}/ c {}' {}".format(old_string, new_string, file_path) + if device == 'server': + conn_obj.execute_command(ssh_conn_obj, command) + else: + st.config(ssh_conn_obj, command, faster_cli=False) + + +def replace_line_in_file_with_line_number(dut,**kwargs): + """ + Author:Chaitanya Lohith Bollapragada + Usage: + This api take the file path, line number of the text in the file and the new_text to insert in. + for example to change the ssh port number in " /etc/ssh/sshd_config" + replace_line_in_file_with_line_number(vars.D1, line_number = 12, text = 'Port 233', file_path = '/etc/ssh/sshd_config') + User do not have to know about previous line to change it. + to get line number use api get_match_string_line_number(vars.D1, 'Port ', '/etc/ssh/sshd_config') + advanced usage: + replace_line_in_file_with_line_number(vars.D1, line_number = get_match_string_line_number(vars.D1, 'Port ', '/etc/ssh/sshd_config'), + text = 'Port 233', file_path = '/etc/ssh/sshd_config') + Assuming default device is server. + :param dut: + :param line_number: + :param text: + :param file_path: + :return: bool + """ + if not ('line_number' in kwargs and 'text' in kwargs and 'file_path' in kwargs): + return False + command = 'sed -i "{}s/.*/{}/" {}'.format(kwargs['line_number'], kwargs['text'], kwargs['file_path']) + if 'device' not in kwargs: + conn_obj.execute_command(dut, command) + else: + st.config(dut, command) + return True + + +def find_line_in_file(ssh_conn_obj, search_string, file_path, device='server'): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to file line in a file + :param ssh_conn_obj: + :param search_string: + :param file_path: + :return: + """ + command = "grep -w '{}' {}".format(search_string, file_path) + result = conn_obj.execute_command(ssh_conn_obj, command) if device == 'server' else st.config(ssh_conn_obj, command) + if utils_obj.remove_last_line_from_string(result).find(search_string) < 1: + return False + return True + + +def check_service_status(ssh_conn_obj, service_name, status="running", device='server'): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to check the service status + :param ssh_conn_obj: + :param service_name: + :param status: + :return: + """ + st.log("##### Checking {} status for {} service ######".format(status, service_name)) + command = "status {}".format(service_name) + result = conn_obj.execute_command(ssh_conn_obj, command) if device == 'server' else st.config(ssh_conn_obj, command) + result = utils_obj.remove_last_line_from_string(result) + if "command not found" not in result: + match = "start/running" if status == "running" else "stop/waiting" + if result.find("{}".format(match)) > 1: + return True + else: + command = "service --status-all | grep {}".format(service_name) + result = conn_obj.execute_command(ssh_conn_obj, command) + result = utils_obj.remove_last_line_from_string(result) + operator = "+" if status == "running" else "-" + return True if operator in result and service_name in result else False + return False + + +def write_to_json_file(json_content): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to write the json content to a file + :param json_content: + :return: + """ + json_dump = json.dumps(json_content) + parsed = json.loads(json_dump) + chef_cookbook_json = json.dumps(parsed, indent=4, sort_keys=True) + src_file = tempfile.mktemp() + src_fp = open(src_file, "w") + src_fp.write(chef_cookbook_json) + src_fp.close() + return src_file + + +def copy_file_from_client_to_server(ssh_con_obj, **kwargs): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to copy file from client to server + :param ssh_con_obj: + :param kwargs: + :return: + """ + try: + import netmiko + scp_conn = netmiko.SCPConn(ssh_con_obj) + scp_conn.scp_transfer_file(kwargs["src_path"], kwargs["dst_path"]) + scp_conn.close() + if "persist" not in kwargs: + os.remove(kwargs["src_path"]) + except Exception as e: + st.log(e) + st.report_fail("scp_file_transfer_failed", kwargs["src_path"], kwargs["dst_path"]) + + +def check_error_log(dut, file_path, error_string, lines=1, file_length=50, match=None): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to check the error log + :param dut: + :param file_path: + :param error_string: + :param lines: + :param file_length: + :return: + """ + command = 'sudo tail -{} {} | grep "{}" | tail -{}'.format(file_length, file_path, error_string, lines) + try: + response = utils_obj.remove_last_line_from_string(st.show(dut, command, skip_tmpl=True, skip_error_check=True)) + result = response.find(error_string) > 1 if not match else response + if result: + return True + else: + return False + except ValueError as e: + st.log(e) + return False + + +def poll_for_error_logs(dut, file_path, error_string, lines=1, file_length=50, iteration_cnt=10, delay=1, match=None): + """ + API to poll for error logs + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param file_path: + :param error_string: + :param lines: + :param file_length: + :param iteration_cnt: + :param delay: + :return: + """ + i=1 + while True: + if check_error_log(dut, file_path, error_string, lines, file_length, match=match): + st.log("Log found in {} iteration".format(i)) + return True + if i > iteration_cnt: + st.log("Max iteration count {} reached ".format(i)) + return False + i+=delay + st.wait(delay) + + +def show_version(dut): + """ + Get Show Version + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :return: + """ + command = 'show version' + output = st.show(dut, command) + if not output and st.is_dry_run(): return "" + exclude_keys = ['repository', 'tag', 'image_id', 'size'] + rv = {each_key: output[0][each_key] for each_key in output[0] if each_key not in exclude_keys} + return rv + + +def get_docker_info(dut): + """ + Get the docker infor from the show version output + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :return: + """ + command = 'show version' + output = st.show(dut, command) + include_keys = ['repository', 'tag', 'image_id', 'size'] + rv = [{each_key: each_line[each_key] for each_key in each_line if each_key in include_keys} for each_line in + output[1:]] + return rv + + +def get_docker_ps(dut): + """ + Get docker ps + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :return: + """ + command = 'docker ps --no-trunc' + output = st.show(dut, command) + return output + + +def verify_docker_ps(dut, image, **kwargs): + """ + To verify docker ps info + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :param image: + :param container_id: + :param command: + :param created: + :param status: + :param ports: + :param names: + :return: + """ + output = get_docker_ps(dut) + rv = filter_and_select(output, None, {'image': image}) + if not rv: + st.error("No match for {} = {} in table".format('image', image)) + return False + for each in kwargs.keys(): + if filter_and_select(rv, None, {each: kwargs[each]}): + st.error("No match for {} = {} in table".format(each, kwargs[each])) + return False + return True + + +def docker_operation(dut, docker_name, operation): + """ + To Perform Docker operations + Author: kesava-swamy.karedla@broadcom.com + :param dut: + :param docker_name: + :param operation: + :return: + """ + command = 'docker {} {}'.format(operation, docker_name) + return st.config(dut, command) + + +def get_memory_info(dut): + """ + Get Memory information form TOP command + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :return: + """ + command = "top -n 1 b | grep 'KiB Mem' " + output = st.show(dut, command) + include_keys = ['total', 'used', 'free', 'buff_cache'] + rv = {each_key: ast.literal_eval(output[0][each_key]) for each_key in output[0] if each_key in include_keys} + return rv + + +def get_top_info(dut, proc_name=None): + """ + Get Process details from the TOP command + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param proc_name: process name + :return: + """ + exclude_keys = ['total', 'used', 'free', 'buff_cache'] + if proc_name: + command = "top -n 1 b | grep {}".format(proc_name) + output = st.show(dut, command) + rv = [{each_key: each_line[each_key] for each_key in each_line if each_key not in exclude_keys} + for each_line in output] + rv = filter_and_select(rv, None, {'command': proc_name}) + else: + command = "top -n 1 b" + output = st.show(dut, command) + rv = [{each_key: each_line[each_key] for each_key in each_line if each_key not in exclude_keys} + for each_line in output[1:]] + return rv + + +def get_overall_cpu_util(dut, exclude_proc_name=None): + """ + Get Over all CPU Utlization - WIP + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param exclude_proc_name: exclude proc name + :return: + """ + + + +def get_platform_syseeprom(dut, tlv_name=None, key='value', decode=False): + """ + Get Platform Syseeprom + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param tlv_name: + :param key: value/len/code + :return: + """ + if decode: + command = "sudo decode-syseeprom" + else: + command = "show platform syseeprom" + output = st.show(dut, command) + if tlv_name: + output = filter_and_select(output, [key], {"tlv_name": tlv_name})[0][key] + return output + + +def get_platform_syseeprom_as_dict(dut, tlv_name=None, key='value', decode=False): + """ + Get Platform Syseeprom as dict + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param tlv_name: + :param key: + :param decode: + :return: + """ + rv = {} + output = get_platform_syseeprom(dut, tlv_name=tlv_name, key=key, decode=decode) + for each in output: + rv[each['tlv_name']] = each[key] + return rv + + +def copy_config_db_to_temp(dut, source_path, destination_path): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + This API is to backup config db json. + :param dut: + :param source_path: + :param destination_path: + :return: + """ + if not verify_file_on_device(dut, "/etc/sonic", "config_db.json", "dut"): + command = "config save -y" + st.config(dut, command) + copy_file_to_local_path(dut, source_path, destination_path) + + +def remove_file(dut, file_path): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + This API is to remove file from a specifc path + :param dut: + :param file_path: + :return: + """ + st.log("####### Removing file {} #######".format(file_path)) + command = "rm -f -- {}".format(file_path) + st.config(dut, command) + + +def get_from_redis_cli(dut, **kwargs): + """ + To get the output from redis-cli + Author : Chaitanya Lohith Bollapragada (chaitanyalohith.bollapragada@broadcom.com) + Expected input is dut and table_name/attribute + :param dut: + :param kwargs: + :return: + """ + pattern = '"(.*?)"' + cptrn = re.compile(pattern) + command = "redis-cli {}".format(kwargs["command"]) + output = st.show(dut, command, skip_tmpl=True) + reg_output = utils_obj.remove_last_line_from_string(output) + if "hgetall" not in command: + return [each for each in cptrn.findall(reg_output)[0]] + else: + if 'table_name' not in kwargs and 'attribute' not in kwargs: + st.error("Mandatory parameter table_name/attribute not found") + return False + processed_output = {} + re_find_output = cptrn.findall(reg_output) + for each in [i for i in range(len(re_find_output)) if i % 2 == 0]: + processed_output[re_find_output[each]] = re_find_output[each + 1] + if kwargs["attribute"] in processed_output.keys(): + return processed_output[kwargs["attribute"]] + else: + st.log("attribute not found") + return None + + +def verify_package(dut, packane_name): + """ + To verify package is installed or not + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param packane_name: + :return: + """ + command = "dpkg -s {} | grep Status".format(packane_name) + output = st.config(dut, command, skip_error_check=True) + if "package '{}' is not installed".format(packane_name) in output: + st.log("Package '{}' is not installed in DUT".format(packane_name)) + return False + return True + + +def deploy_package(dut, packane_name=None, mode='install', skip_verify_package=False): + """ + To verify package is installed or not + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param packane_name: + :param mode: install | update | remove | purge + :return: + """ + if mode == "install": + command = "apt-get install {} -y".format(packane_name) + if not skip_verify_package: + if verify_package(dut, packane_name): + st.log("Package is already present in DUT") + return True + st.config(dut, command, skip_error_check=True, faster_cli=False) + if not verify_package(dut, packane_name): + st.log("Package '{}' is failed to install in DUT".format(packane_name)) + return False + + elif mode in ['remove', 'purge']: + command = "apt-get {} {} -y".format(mode, packane_name) + st.config(dut, command, skip_error_check=True, faster_cli=False) + if verify_package(dut, packane_name): + st.log("Package '{}' is failed to {} in DUT".format(mode, packane_name)) + return False + + elif mode == "update": + command = "apt-get update" + output = st.config(dut, command, skip_error_check=True, faster_cli=False) + if "Done" not in output: + st.log("Package 'update' is failed in DUT".format(packane_name)) + return False + else: + st.log("invalid mode - {}".format(mode)) + return False + return True + + +def download_file_content(dut, file_path, device="dut"): + """ + This is the API to read the file content and return for further processing + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param file_path: + :param device: + :return: + """ + file_content = "" + if device == "dut": + command = "value=$(<{})".format(file_path) + st.show(dut, command, skip_tmpl=True) + command = "echo $value" + file_content = utils_obj.remove_last_line_from_string(st.show(dut, command, skip_tmpl=True)) + return file_content + + +def poll_for_system_status(dut, service=None, iteration=150, delay=2): + i = 1 + while True: + if get_system_status(dut, service): + st.log("System is ready in {} iteration".format(i)) + return True + if i > iteration: + st.log("Max iteration count {} reached ".format(i)) + return False + i += delay + st.wait(delay) + + +def get_interface_status(conn_obj, interface, device="dut"): + """ + API to get the linux interface status + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param conn_obj: + :param interface: + :param device: + :return: + """ + command = "cat /sys/class/net/{}/operstate".format(interface) + if device=="dut": + return utils_obj.remove_last_line_from_string(st.show(conn_obj, command, skip_tmpl=True)) + + +def check_interface_status(conn_obj, interface, state, device="dut"): + """ + API to check the interface state + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param conn_obj: + :param interface: + :param state: + :param device: + :return: + """ + interface_state = get_interface_status(conn_obj, interface, device=device) + if interface_state != state: + return False + return True + + +def get_ps_aux(connection_obj, search_string, device="dut"): + """ + API to get the ps aux output + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param conn_obj: + :param search_string: + :param device: + :return: + """ + command = "sudo ps aux" + if search_string: + command += " | grep {}".format(search_string) + if device == "dut": + return st.show(connection_obj, command) + else: + return conn_obj.execute_command(connection_obj, command) + + +def get_ifconfig_gateway(dut, interface='eth0'): + """ + API will return Gateway ip address + :param dut: + :param interface: + :return: + """ + + cmd = "sudo route -n" + output = st.show(dut, cmd) + gateway_list = [row['gateway'] for row in output if 'G' in row['flags'] and row['iface'] == interface] + return None if len(gateway_list) == 0 else gateway_list[0] + + +def get_frr_config(conn_obj, device="dut"): + """ + API to get frr config from frr.conf file + Author: Sooriya G (sooriya.gajendrababu@broadcom.com) + :param conn_obj: + :param device: + :return: + """ + command = " sudo cat /etc/sonic/frr/frr.conf" + if device=="dut": + return utils_obj.remove_last_line_from_string(st.show(conn_obj, command, skip_tmpl=True)) + + +def remove_user_log_in_frr(dut,log_file_name): + """ + API to get frr config from frr.conf file + Author: Gangadhara Sahu (gangadhara.sahu@broadcom.com) + :param dut: + :param log_file_name: + :return: + """ + st.config(dut,"docker exec -it bgp rm /var/log/frr/%s"%log_file_name) + + +def return_user_log_from_frr(dut,log_file_name): + """ + API to get frr config from frr.conf file + Author: Gangadhara Sahu (gangadhara.sahu@broadcom.com) + :param dut: + :param log_file_name: + :return: + """ + return st.config(dut,"docker exec -it bgp cat /var/log/frr/%s"%log_file_name) + + +def debug_bfdconfig_using_frrlog(dut,config="",log_file_name=""): + """ + API to get frr config from frr.conf file + Author: Gangadhara Sahu (gangadhara.sahu@broadcom.com) + :param dut: + :param config: + :param log_file_name: + :return: + """ + if config == "yes" or config=="": + st.config(dut,"debug bfd", type='vtysh') + st.config(dut,"log syslog warnings", type='vtysh') + st.config(dut,"log file /var/log/frr/%s"%log_file_name, type='vtysh') + elif config == "no": + st.config(dut,"no debug bfd", type='vtysh') + st.config(dut,"no log syslog warnings", type='vtysh') + st.config(dut,"no log file /var/log/frr/%s"%log_file_name, type='vtysh') + + +def set_hostname(dut, host_name): + """ + this function is used to set hostname in DUT + :param dut: Device name where the command to be executed + :type dut: string + :param host_name: hostname to be set + :type host_name: string + :return: None + + usage: set_hostname(dut1, "host1") + + created by: Julius + """ + cmd = "sudo hostname {}".format(host_name) + st.config(dut, cmd) + return + + +def get_attr_from_cfgdbjson(dut, attr): + """ + this function is used to get some attribute from DUT's config_db.json file + :param dut: Device name where the command to be executed + :type dut: string + :param attr: attribute details to be fetched + :type host_name: string + :return: attribute details + + usage: get_attr_from_cfgdbjson(dut1, "fdb_aging_time") + + created by: Julius + """ + cmd = "sudo cat /etc/sonic/config_db.json | grep {}".format(attr) + return utils_obj.remove_last_line_from_string(st.config(dut, cmd)) + + +def update_config_db_json(dut, attr, val1, val2): + """ + this function is used to replace val1 of attribute by val2 in DUT's config_db.json file + :param dut: Device name where the command to be executed + :type dut: string + :param attr: attribute name to be modified + :type attr: string + :param val1: to be replaced from val1 + :type val1: string + :param val2: to be changed to val2 + :type val2: string + :return: None + + usage: update_config_db_json(dut1, "fdb_aging_time", "300", "30") + + created by: Julius + """ + cmd = "sudo sed -i 's/\"{}\": \"{}\"/\"{}\": \"{}\"/g' /etc/sonic/config_db.json"\ + .format(attr,val1,attr,val2) + st.config(dut, cmd) + return + + +def delete_directory_contents(conn_obj, path, device="dut"): + """ + API to delete contents in the provided directory + :param conn_obj: + :param path: + :param device: + :return: + """ + command = "rm -rf {}/*".format(path.rstrip("/")) + if device == "dut": + st.config(conn_obj, command) + else: + conn_obj.execute_command(conn_obj, command) + return True + + +def get_file_number_with_regex(connection_obj, search_pattern, file_path, device="server"): + #COMMAND : sed -nE '/^\s*option\s+dhcp6.boot-file-url\s+"\S+";/=' /etc/dhcp/dhcpd6.conf + ''' + :param connection_obj: + :param search_pattern: + :param file_path: + :param device: + :return: + ''' + command = "sed -nE '/^{}/=' {}".format(search_pattern, file_path) + st.log("######{}##".format(command)) + if device == "server": + result = conn_obj.execute_command(connection_obj, command) + else: + result = st.config(connection_obj, command) + if utils_obj.remove_last_line_from_string(result): + line_number = re.findall(r'\d+',utils_obj.remove_last_line_from_string(result)) + if line_number: + return line_number[0] + return 0 + + +def delete_line_using_line_number(connection_obj, line_number, file_path, device="server"): + ''' + COMMAND: sed -i '20d' /etc/dhcp/dhcpd6.conf + :param connection_obj: + :param line_number: + :param file_path: + :param device: + :return: + ''' + st.log("Line number is {}".format(line_number)) + if line_number > 0: + command = "sed -i '{}d' {}".format(line_number, file_path) + st.log("COMMAND-- {}".format(command)) + if device == "server": + return conn_obj.execute_command(connection_obj, command) + else: + return st.config(connection_obj, command) + + +def get_dut_mac_address(dut): + """ + This is used to get the Duts and its mac addresses mapping + :param duts: List of DUTs + :return : Duts and its mac addresses mapping + + """ + duts_mac_addresses = {} + cmd = "show platform syseeprom" + eeprom_details = st.show(dut, cmd, skip_error_check=True) + if not eeprom_details: + iteration=3 + for i in range(1, iteration+1): + st.wait(2) + eeprom_details = st.show(dut, cmd, skip_error_check=True) + if eeprom_details: + break + if not eeprom_details and i >= iteration + 1: + st.log("EEPROM data not found for {}".format(dut)) + st.report_fail("eeprom_data_not_found", dut) + dut_mac_address = [details for details in eeprom_details if details.get('tlv_name') == "Base MAC Address"][ + 0].get("value").replace(':', "") + duts_mac_addresses[dut] = dut_mac_address + return duts_mac_addresses + + +def get_dut_mac_address_thread(dut_list, thread=True): + dut_li = list(dut_list) if isinstance(dut_list, list) else [dut_list] + result = dict() + params = list() + for dut in dut_li: + params.append([get_dut_mac_address, dut]) + if params: + [out, exceptions] = exec_all(thread, params) + st.log("#########OUTPUT###########") + st.log(out) + st.log(exceptions) + for value in exceptions: + if value is not None: + st.log("Exceptions Observed {}, hence returning None".format(value)) + return None + st.log("Framing required data as no exceptions observed ...") + for data in out: + st.log(data) + result[data.keys()[0]] = data.values()[0] + return result + else: + st.log("Empty Params Observed .... ") + return None + + +def get_number_of_lines_in_file(connection_obj, file_path, device="server"): + line_number=0 + if file_path: + command = "wc -l {}".format(file_path) + if device == "server": + output = conn_obj.execute_command(connection_obj, command) + else: + output = st.config(connection_obj, command) + result = utils_obj.remove_last_line_from_string(output) + if result: + match = re.match(r'^\d+', result) + if match: + st.log("####### LINE NUMBER- {}".format(match.group(0))) + return match.group(0) + return line_number + + +def is_vsonic_device(dut): + return st.is_vsonic(dut) + +def get_config_profiles(dut): + """ + Author: Nagappa Chincholi (nagappa.chincholi@broadcom.com) + Function to get configured profiles (L2/L3). + :param dut: + :return: + """ + if st.is_community_build(): + return "l3" + command = 'sudo config-profiles get factory' + output = st.show(dut, command, skip_tmpl=True) + if len(output) <= 0: + return None + return output[:2] + +def set_config_profiles(dut, profile, check_system_status=True): + cur_profile = get_config_profiles(dut) + if cur_profile == profile.lower(): + st.log('Device is in the desired profile') + else: + cmd = 'sudo config-setup factory' + cmd += '\n sudo config-profiles factory {}'.format(profile.lower()) + st.config(dut, cmd, max_time=300) + if check_system_status: + return bool(get_system_status(dut)) + return True + +def get_show_command_data(dut, command, type="txt"): + file_extension = "txt" if type != "json" else "json" + data = None + for i in range(1,3): + actual_cmd = "{} > /tmp/running_config.{}".format(command, file_extension) + st.config(dut, actual_cmd) + delete_file("/tmp/running_config.{}".format(file_extension)) + st.download_file_from_dut(dut, "/tmp/running_config.{}".format(file_extension), + "/tmp/running_config.{}".format(file_extension)) + import os + if not os.stat("/tmp/running_config.{}".format(file_extension)).st_size == 0: + break + try: + if type == "json": + with open('/tmp/running_config.json') as file: + data = eval(json.dumps(json.load(file), indent=4, sort_keys=True)) + else: + with open('/tmp/running_config.txt', 'r') as file: + data = file.read().replace('\n', '') + except Exception as e: + st.error("Exception occured: {}".format(e)) + st.debug(data) + return data + +def check_sonic_branding(build_name): + """ + Author1: Jagadish Chatrasi (jagadish.chatrasi@broadcom.com) + Author2: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + Function to verify the build version is in correct format or not + :param build_name: + :return: + """ + result = True + constants = st.get_datastore(None, "constants", "default") + st.log("The Given build version string is : {}".format(build_name)) + + regex_format = r"^([a-zA-Z]+-[a-zA-Z]+)-((\d+\.\d+\.\d+|\d+\.\d+)_*(\w*))_*(\d+_\d+_\d+)*-*(\S+)*$" + os_regex = r"^{}".format(constants['NOS_NAME']) + version_regex = r"(\d+\.\d+\.\d+|\d+\.\d+)" + + if not re.findall(os_regex, build_name): + st.error('Build OS NAME is not matching with the standard format - {}'.format(constants['NOS_NAME'])) + result = False + + if not re.findall(version_regex, build_name): + st.error('Build VERSION info is not matching with the standard format') + result = False + + if not any(ele in build_name for ele in constants['PRODUCT_PACKAGING_OPTIONS']): + st.error('Build PRODUCT info is not matching with any of the standard Package') + result = False + + if not re.findall(regex_format, build_name): + st.error('Build info is not as per the standard Format') + result = False + + if not result: + st.log("Output of OS NAME regex : {}, data : {}".format(os_regex, re.findall(os_regex, build_name))) + st.log("Output of VERSION regex : {}, data : {}".format(version_regex, re.findall(version_regex, build_name))) + st.log("Output of FORMAT regex : {}, data : {}".format(regex_format, re.findall(regex_format, build_name))) + st.log("CONSTANTS : {}".format(constants)) + + return result + + +def copy_file_to_docker(dut, file_name, docker_name): + """ + API to copy file to any docker + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param file_name: + :param docker_name: + :return: + """ + command = "docker cp {} {}:{}".format(file_name, docker_name, file_name) + output = st.config(dut, command) + if "Error response" in utils_obj.remove_last_line_from_string(output): + st.log(output) + return False + return True + +def get_user_group_details(connection_obj, device="server"): + """ + API to execute id command and parse the output + :param conn_obj: + :param type: + :return: + """ + command = "id" + if device == "server": + output = utils_obj.remove_last_line_from_string(conn_obj.execute_command(connection_obj, command)) + else: + output = utils_obj.remove_last_line_from_string(st.show(connection_obj, command, skip_tmpl=True, type="click")) + return output + +def verify_user_group_details(connection_obj, uid, group, device="server"): + """ + API to verify the user group details by executing id. + :param connection_obj: + :param search_str: + :param value: + :param device: + :return: + """ + output = get_user_group_details(connection_obj,device=device) + if not output: + st.log("Output not found {}".format(output)) + return False + if uid: + user_data = re.findall(r"uid=\d+\({}\)".format(uid), output) + if not user_data: + st.log("User data not found -- {}".format(uid)) + return False + if group: + group_data = re.findall(r"gid=\d+\({}\)".format(group), output) + if not group_data: + st.log("Group data not found -- {}".format(group)) + return False + return True + + +def delete_line_using_specific_string(connection_obj, specific_string, file_path, device="server"): + """ + Author: Santosh Votarikari(santosh.votarikari@broadcom.com) + API to remove line by using specific string + + COMMAND: sed -i "/DELETE THIS TEXT/d" /var/log/messages + :param connection_obj: + :param specific_string: + :param file_path: + :param device: + :return: + """ + st.log("Specific string is {}".format(specific_string)) + if specific_string: + command = "sed -i '/{}/d' {}".format(specific_string, file_path) + st.log("COMMAND-- {}".format(command)) + if device == "server": + return conn_obj.execute_command(connection_obj, command) + else: + return st.config(connection_obj, command) + + +def cmd_validator(dut, commands, cli_type='klish', error_list=[]): + """ + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param commands: + :param cli_type: + :param error_list: + :return: + """ + result = True + error_list.extend(['%Error']) + command_list = commands if isinstance(commands, list) else commands.split('\n') + st.log(command_list) + out = st.config(dut, command_list, type=cli_type, skip_error_check=True) + for each in error_list: + if each in out: + st.error("Error string '{}' found in command execution.".format(each)) + result = False + return result + + +def verify_docker_status(dut, status='Exited'): + """ + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param status: + :return: + """ + if status in st.config(dut, "docker ps --no-trunc -a"): + return False + else: + return True + + +def get_and_match_docker_count(dut, count=None): + """ + Get docker count + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param count: + :return: + """ + command = 'docker ps --no-trunc | wc -l' + if not count: + return utils_obj.get_word_count(dut, command) + else: + if int(count) == utils_obj.get_word_count(dut, command): + return True + return False + +def move_file_to_local_path(dut, src_path, dst_path, sudo=True): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Function to copy the file to local path + :param dut: + :param src_path: + :param dst_path: + :return: + """ + sucmd = "sudo" if sudo else "" + command = "{} mv {} {}".format(sucmd, src_path, dst_path) + st.config(dut, command) \ No newline at end of file diff --git a/spytest/apis/system/boot_up.py b/spytest/apis/system/boot_up.py new file mode 100644 index 00000000000..b4e7256892e --- /dev/null +++ b/spytest/apis/system/boot_up.py @@ -0,0 +1,108 @@ +from spytest.utils import filter_and_select +from spytest import st +import sys +import re + +def sonic_installer_cleanup(dut): + st.log(sys._getframe( ).f_code.co_name.replace('_',' ')) + command = "sudo sonic_installer cleanup -y" + output = st.config(dut, command) + retval = re.search(r".*No image\(s\) to remove.*", output) + if retval is None: + return True + else: + st.log("No image(s) to remove") + return False + return True + +def sonic_installer_remove(dut,image): + st.log(sys._getframe( ).f_code.co_name.replace('_',' ')) + command = "sudo sonic_installer remove {} -y".format(image) + output = st.config(dut, command) + retval = re.search(".*Image does not exist.*", output) + if retval is None: + return True + else: + st.log("Image does not exist") + return False + return True + +def sonic_installer_set_default(dut,image): + st.log(sys._getframe( ).f_code.co_name.replace('_',' ')) + command = "sudo sonic_installer set_default {}".format(image) + output = st.config(dut, command) + retval = re.search(".*Image does not exist.*", output) + if retval is None: + return True + else: + st.log("Image does not exist") + return False + return True + +def sonic_installer_set_next_boot(dut,image): + st.log(sys._getframe( ).f_code.co_name.replace('_',' ')) + command = "sudo sonic_installer set_next_boot {}".format(image) + output = st.config(dut, command) + retval = re.search(".*Image does not exist.*", output) + if retval is None: + return True + else: + st.log("Image does not exist") + return False + return True + +def sonic_installer_install(dut,image_path): + st.log(sys._getframe( ).f_code.co_name.replace('_',' ')) + command = "sudo sonic_installer install {} -y".format(image_path) + output = st.config(dut, command) + retval = re.search(".*No such file or directory.*|.*Image file '.*' does not exist or is not a regular file. Aborting.*", output) + if retval is None: + return True + else: + st.log("No such file or directory | Image file does not exist or is not a regular file. Aborting") + return False + return True + +def sonic_installer_binary_version(dut,image_path): + st.log(sys._getframe( ).f_code.co_name.replace('_',' ')) + command = "sudo sonic_installer binary_version {} -y".format(image_path) + output = st.config(dut, command) + retval = re.search(".*File does not appear to be a vaild SONiC image file.*", output) + if retval is None: + return True + else: + st.log("File does not appear to be a vaild SONiC image file") + return False + return True + +def sonic_installer_upgrade_docker(dut,container_name,image_path): + st.log(sys._getframe( ).f_code.co_name.replace('_',' ')) + command = "sudo sonic_installer upgrade_docker {} {} -y".format(container_name,image_path) + output = st.config(dut, command) + retval = re.search(".*No such file or directory.*|.*Image file '.*' does not exist or is not a regular file. Aborting.*", output) + if retval is None: + return True + else: + st.log("No such file or directory | Image file does not exist or is not a regular file. Aborting") + return False + return True + +def sonic_installer_list(dut): + st.log(sys._getframe( ).f_code.co_name.replace('_',' ')) + command = "sudo sonic_installer list" + output = st.show(dut, command) + entries = filter_and_select(output, ["current","next","available"]) + + retval = dict() + currentList = [] + nextList = [] + availableList = [] + for ent in entries: + if ent["current"]: currentList.append(ent["current"]) + if ent["next"]: nextList.append(ent["next"]) + if ent["available"]: availableList.append(ent["available"]) + retval["Current"] = currentList + retval["Next"] = nextList + retval["Available"] = availableList + return retval + diff --git a/spytest/apis/system/box_services.py b/spytest/apis/system/box_services.py new file mode 100644 index 00000000000..f5138997939 --- /dev/null +++ b/spytest/apis/system/box_services.py @@ -0,0 +1,707 @@ +from spytest.utils import filter_and_select +from spytest import st +from apis.system.basic import poll_for_system_status +import utilities.utils as utils +import utilities.common as cutils +import re + + +def get_system_uptime_in_seconds(dut): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :return: + """ + command = "show uptime" + output = st.show(dut, command) + if output[0]["years"]: + years = int(output[0]["years"]) * 3600 * 24 * 365.25 + else: + years = 0 + if output[0]["months"]: + months = int(output[0]["months"]) * 2629746 + else: + months = 0 + if output[0]["days"]: + days = int(output[0]["days"]) * 3600 * 24 + else: + days = 0 + if output[0]["hours"]: + hours = int(output[0]["hours"]) * 3600 + else: + hours = 0 + if output[0]["minutes"]: + minutes = int(output[0]["minutes"]) * 60 + else: + minutes = 0 + retval = years + months + days + hours + minutes + print(retval) + return retval + + +def show_interfaces_transceiver_presence(dut, intf=None): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param intf: + :return: + """ + command = "show interfaces transceiver presence" + output = st.show(dut, command) + if intf is not None: + entries = filter_and_select(output, ["port", "presence"], {"port": intf}) + else: + entries = filter_and_select(output, ["port", "presence"]) + return entries + + +def verify_interfaces_transceiver_presence(dut, intf, status): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param intf: + :param status: + :return: + """ + output = show_interfaces_transceiver_presence(dut, intf) + retval = filter_and_select(output, ["presence"], {"port": intf}) + if retval[0]["presence"].lower() == status.lower(): + return True + else: + return False + + +def get_platform_fan_status(dut, fan=None): + """ + To Get Platform Fan Status + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param fan: + :return: + """ + + command = "show platform fanstatus" + output = st.show(dut, command) + if fan: + return filter_and_select(output, None, {"fan": fan}) + else: + return output + + +def verify_platform_fan_params(dut, fan_list): + """ + To Verify platform Fan status parameters + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param fan_list: + :return: + """ + fans = fan_list if isinstance(fan_list, list) else [fan_list] + output = get_platform_fan_status(dut) + if not output: + st.log("Platform fan details not found") + return False + cnt = 0 + for data in output: + if data["fan"] not in fans: + st.error("Invalid FAN detected - {}".format(data["fan"])) + cnt += 1 + if data["status"] != "OK": + st.error("Invalid FAN status detected - {}".format(data["status"])) + cnt += 1 + if data["direction"] not in ["INTAKE", "EXHAUST"]: + st.error("Invalid FAN direction detected - {}".format(data["direction"])) + cnt += 1 + if data["speed"] in ['0', 'N/A']: + st.error("Invalid FAN speed detected - {}".format(data["speed"])) + cnt += 1 + if cnt > 0: + st.log("Fan parameters verification failed ..") + return False + return True + + +def verify_platform_fan_status(dut, fan, **kwargs): + """ + To Verify Platform Fan Status + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param fan: + :return: + """ + output = get_platform_fan_status(dut, fan) + result = True + for each in kwargs: + if not filter_and_select(output, None, {each: kwargs[each]}): + st.error("No match for {} = {} in table".format(each, kwargs[each])) + result = False + return result + + +def get_platform_psu_summary(dut, psu=None): + """ + To Get Platform PSU Status + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param psu: + :return: + """ + command = "show platform psusummary" + output = st.show(dut, command) + if psu: + return filter_and_select(output, None, {"psu": psu}) + else: + return output + + +def verify_platform_psu_params(dut): + """ + API to verify the Platform psu params + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :return: + """ + output = get_platform_psu_summary(dut) + if not output: + st.error("Output not found") + return False + psu_data = None + for data in output: + if data["psu_status"] == "OK": + psu_data = data + break + if psu_data and utils.check_empty_values_in_dict(psu_data): + st.log("PSU verification success") + return True + else: + st.log("Verification failed for psu data") + return False + + +def verify_platform_psu_summary(dut, psu=None, **kwargs): + """ + To Verify Platform PSU Status + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param :dut: + :param :fan: + :return: + """ + output = get_platform_psu_summary(dut, psu) + result = True + for each in kwargs: + if filter_and_select(output, None, {each: kwargs[each]}): + st.error("No match for {} = {} in table".format(each, kwargs[each])) + result = False + return result + + +def config_pddf_mode(dut, file_path="/usr/local/bin/pddf_util.py", module_name="switch-pddf", iteration=150, delay=2): + """ + API to enable / disable PDDF on the switch + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param file_path: + :param module_name: + :param iteration: + :param delay: + :return: + """ + command = "{} {}".format(file_path, module_name) + output = st.config(dut, command) + st.log("OUTPUT : {}".format(output)) + if module_name == "switch-pddf": + if 'REBOOT IS REQUIRED IMMEDIATELY' in output: + st.reboot(dut, skip_fallback=True) + if not poll_for_system_status(dut, iteration=iteration, delay=delay): + st.log("System status is not up ...") + return False + if not is_service_active(dut): + st.log("PDDF service is not active ...") + return False + else: + if is_service_active(dut): + st.log("PDDF service is still active ...") + return False + return True + + +def is_service_active(dut, service="pddf"): + """ + API to check whether service is active or not + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param service: + :return: + """ + command = "systemctl | grep -i {}".format(service) + output = st.config(dut, command) + if "active" not in output: + return False + return True + + +def get_psuutil_data(dut, mode="status"): + """ + API to get psuutil data based on type of the command + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param mode: + :return: + """ + if mode not in ["status", "numpsus", "version"]: + st.log("Unsupported command type") + return False + command = "sudo psuutil {}".format(mode) + skip_tmpl = False + if mode == "numpsus": + skip_tmpl = True + output = st.show(dut, command, skip_tmpl=skip_tmpl) + if mode == "numpsus": + return {"numpsus": utils.remove_last_line_from_string(output)} + else: + return output + + +def verify_psuutil_data(dut, *argv, **kwargs): + """ + API to get psuutil data based on type of the command + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param argv: + :param kwargs: + :return: + """ + result = True + for each_mode in argv: + output = get_psuutil_data(dut, each_mode) + if "numpsus" in each_mode and int(output['numpsus']) != len(kwargs['psu_list']): + st.error("Incorrect Number of PSUs detected.") + result = False + + if "status" in each_mode: + psu_li = cutils.dicts_list_values(output, "psu") + for psu in kwargs['psu_list']: + if psu not in psu_li: + st.error("PSU - {} is not present in DUT.") + result = False + + status_li = cutils.dicts_list_values(output, "status") + for status in status_li: + if status not in ['NOT OK', 'OK']: + st.error("Invalid PSU status in DUT.") + result = False + + if "OK" not in status_li: + st.error("None of the PSU status is - OK") + result = False + return result + + +def config_sfputil(dut, **kwargs): + """ + API to config sfputils + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param kwargs: + :return: + """ + if kwargs.get("mode"): + mode = kwargs.get("mode") + if mode in ["lpmode", "reset"]: + if not kwargs.get("interface"): + st.log("Interface should be provided for {}".format(mode)) + return False + if mode == "lpmode": + if not kwargs.get("action"): + st.log("Action should be provided for {}".format(mode)) + return False + if kwargs.get("action") not in ["on", "off"]: + st.log("Unsupported actions provided for {}".format(mode)) + return False + command = "sudo sfputil {} {} {}".format(mode, kwargs.get("action"), kwargs.get("interface")) + output = st.config(dut, command) + if "OK" in output: + return True + else: + return False + elif mode == "reset": + command = "sudo sfputil {} {}".format(mode, kwargs.get("interface")) + output = st.config(dut, command) + if "OK" in output: + return True + else: + return False + else: + st.log("Unsupported modes provided") + return False + else: + st.log("MODE NOT PROVIDED ..") + return False + + +def show_sfputil(dut, mode, interface=None): + """ + API to get the sfputil output + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param interface: + :param mode: eeprom, presence, lpmode + :return: + """ + if mode not in ["lpmode", "presence", "eeprom"]: + st.log("Unsupported mode provided") + return False + command = "sudo sfputil show {}".format(mode) + if interface: + command = "sudo sfputil show {} | grep -w {}".format(mode, interface) + output = st.show(dut, command) + return output + + +def verify_sfputil_show_interface_tranceiver(dut, mode, **kwargs): + """ + To Verify sfputil + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param mode: + :param kwargs: + :return: + """ + cmd = 'utils' + if 'cmd' in kwargs: + cmd = kwargs['cmd'] + kwargs.pop('cmd') + if cmd == 'utils': + output = show_sfputil(dut, mode, interface=kwargs.get('port')) + else: + output = show_interface_transceiver(dut, mode, interface=kwargs.get('port')) + result = True + for each in kwargs: + if not filter_and_select(output, None, {each: kwargs[each]}): + st.error("No match for {} = {} in table".format(each, kwargs[each])) + result = False + return result + + +def show_interface_transceiver(dut, mode, interface=None): + """ + API to get the interface transceiver eeprom details + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param mode: eeprom , presence + :param interface: + :return: + """ + if mode not in ["eeprom", "presence"]: + st.log("Unsupported modes provided ...") + return False + command = "show interface transceiver {}".format(mode) + if interface: + command = "show interface transceiver {} | grep -w {}".format(mode, interface) + return st.show(dut, command) + + +def show_pddf_psuutils(dut, mode): + """ + API to get PDDF PSUUTIL DATA based on type of mode + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param mode: + :return: + """ + if mode not in ["numpsus", "status", "mfrinfo", "seninfo", "version"]: + st.log("Unsupported modes provided ") + return False + skip_tmpl = False + if mode == "numpsus": + skip_tmpl = True + command = "sudo pddf_psuutil {}".format(mode) + output = st.show(dut, command, skip_tmpl=skip_tmpl) + if mode == "numpsus": + return {"numpsus": utils.remove_last_line_from_string(output)} + else: + return output + + +def verify_pddf_psuutils(dut, *argv, **kwargs): + """ + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param argv: + :return: + """ + result = True + for each_mode in argv: + output = show_pddf_psuutils(dut, each_mode) + if "numpsus" in each_mode and int(output['numpsus']) != len(kwargs['psu_list']): + st.error("Incorrect Number of PSUs detected.") + result = False + + if "status" in each_mode: + psu_li = cutils.dicts_list_values(output, "psu") + for psu in kwargs['psu_list']: + if psu not in psu_li: + st.error("PSU - {} is not present in DUT.") + result = False + + status_li = cutils.dicts_list_values(output, "status") + for status in status_li: + if status not in ['NOT OK', 'OK']: + st.error("Invalid PSU status in DUT.") + result = False + + if "OK" not in status_li: + st.error("None of the PSU status is - OK") + result = False + + if "mfrinfo" in each_mode: + status_li = cutils.dicts_list_values(output, "psu_status") + if "OK" not in status_li: + st.error("None of the PSU status is - OK") + result = False + + if 'seninfo' in each_mode: + status_li = cutils.dicts_list_values(output, "psu_status") + if "OK" not in status_li: + st.error("None of the PSU status is - OK") + result = False + for each in ['voltage', 'current', 'power']: + if '0.0' in cutils.dicts_list_values(output, "each"): + st.error("{} in 'seninfo' is 0.0".format(each)) + result = False + if '0' in cutils.dicts_list_values(output, "fan_speed"): + st.error("fan_speed in 'seninfo' is 0.0") + result = False + + return result + + +def show_pddf_fanutil(dut, mode): + """ + API to get PDDF FANUTIL DATA based on type of mode + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param mode: + :return: + """ + if mode not in ["direction", "getspeed", "numfans", "status", "version"]: + st.log("Unsupported modes provided ") + return False + skip_tmpl = False + if mode == "numfans": + skip_tmpl = True + command = "sudo pddf_fanutil {}".format(mode) + output = st.show(dut, command, skip_tmpl=skip_tmpl) + if mode == "numfans": + return {"numfans": utils.remove_last_line_from_string(output)} + else: + return output + + +def verify_pddf_fanutil(dut, mode, fan_list, version="2.0"): + """ + API to verify the fan util output for the given fan list + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param mode: + :param fan_list: + :param version: + :return: + """ + fans = fan_list if isinstance(fan_list, list) else [fan_list] + output = show_pddf_fanutil(dut, mode) + if not output: + st.error("PDDF FAN UTIL DATA NOT FOUND") + return False + for data in output: + count = 0 + for fan in fans: + if mode == "direction": + if fan == data["fan"] and data["direction"] not in ["INTAKE", "EXHAUST"]: + st.error("Invalid FAN direction detected - {}".format(data["direction"])) + count += 1 + elif mode == "getspeed": + if fan == data["fan"] and int(data["speed"]) == 0: + st.error("Invalid FAN speed detected - {}".format(data["speed"])) + count += 1 + elif mode == "numfans": + if str(len(fan_list)) != str(output[data]): + st.error("Incorrect FAN numbers detected - {}".format(output[data])) + count += 1 + elif mode == "status": + if fan == data["fan"] and data["status"] != "OK": + st.error("Invalid FAN status detected - {}".format(data["status"])) + count += 1 + elif mode == "version": + if str(data["version"]) != str(version): + st.error("Invalid FAN version detected - {}".format(data["version"])) + count += 1 + if count: + st.error("Mismatch in PDDF FAN UTIL data") + return False + return True + + +def config_pddf_fanutil(dut, speed): + """ + API to set PDDF FANUTIL speed + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param speed: + :return: + """ + command = "sudo pddf_fanutil setspeed {}".format(speed) + output = st.config(dut, command) + if "Successful" not in utils.remove_last_line_from_string(output): + st.log("Configuration of fan speed failed") + return False + return True + + +def run_debug_commands(dut, mode=None, module="pddf"): + """ + API to execute debug commands + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param mode: + :param module: + :return: + """ + if mode: + modes = cutils.make_list(mode) + else: + modes = ["lsmode", "systemctl", "pddf_fanutil", "pddf_psuutil"] + for each_mode in modes: + if each_mode in ["lsmode", "systemctl"]: + command = "{} | grep -i {}".format(each_mode, module) + if each_mode in ["pddf_fanutil", "pddf_psuutil"]: + command = "sudo {} debug dump_sysfs".format(each_mode) + output = st.config(dut, command) + return utils.remove_last_line_from_string(output) + + +def generate_tech_support(dut): + """ + To Generate tech support and return the error if occurs. + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + """ + command = "show techsupport > /dev/null" + return utils.remove_last_line_from_string(st.config(dut, command)) + + +def verify_show_environment(dut, verify_str_list): + """ + To get show environment. + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + """ + command = "show environment" + output = utils.remove_last_line_from_string(st.show(dut, command, skip_tmpl=True)) + result = True + for item in verify_str_list: + if not re.findall(item, output, re.IGNORECASE): + st.error("Item '{}' is NOT found".format(item)) + result = False + return result + + +def config_pddf_ledutil(dut, mode, led_type, state=None): + """ + API to set PDDF LEDUTIL + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param mode: + :param led_type: + :param state: + :return: + """ + command = "sudo pddf_ledutil getstatusled {}".format(led_type) + if mode.lower() == 'set': + command = "sudo pddf_ledutil setstatusled {} {}".format(led_type, state) + output = st.config(dut, command) + return utils.remove_last_line_from_string(output) + + +def verify_pddf_ledutil(dut, data): + """ + Verify PDDF LEDUTIL + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param data: + :return: + """ + result = True + for each_led in data: + st.banner("Validating - {} ".format(each_led)) + for each_test in data[each_led]: + st.log("Validating - {} - mode '{}'".format(each_led, each_test)) + set_out = config_pddf_ledutil(dut, 'set', each_led, data[each_led][each_test][0]) + if data[each_led][each_test][1] not in set_out: + st.error(">>>> {} != {}".format(set_out, data[each_led][each_test][1])) + st.error("Failed to SET - {} mode '{}'".format(each_led, each_test)) + result = False + get_out = config_pddf_ledutil(dut, 'get', each_led) + if get_out != data[each_led][each_test][2]: + st.error(">>>> {} != {}".format(get_out, data[each_led][each_test][2])) + st.error("Get Validation failed - {} mode '{}'".format(each_led, each_test)) + result = False + return result + + +def show_pddf_thermalutil(dut, mode): + """ + API to get PDDF thermal util DATA based on type of mode + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param mode: + :return: + """ + if mode not in ["gettemp", "numthermals", "version"]: + st.log("Unsupported modes provided ") + return False + skip_tmpl = False + if mode in ["numthermals", 'version']: + skip_tmpl = True + command = "sudo pddf_thermalutil {}".format(mode) + output = st.show(dut, command, skip_tmpl=skip_tmpl) + if mode == "numthermals": + return {"numthermals": utils.remove_last_line_from_string(output)} + elif mode == "version": + return {"version": utils.remove_last_line_from_string(output)} + else: + return output + + +def verify_pddf_thermalutil(dut, mode, thermal_list, version="2.0"): + """ + API to verify the thermal util output for the given thermal list + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param mode: + :param thermal_list: + :param version: + :return: + """ + thermal_li = thermal_list if isinstance(thermal_list, list) else [thermal_list] + output = show_pddf_thermalutil(dut, mode) + if not output: + st.error("PDDF THERMAL UTIL DATA NOT FOUND") + return False + count = 0 + if mode == "gettemp": + for data in output: + for each_thermal in thermal_li: + if each_thermal != data["temp_sensor"]: + st.error("Invalid Temp Sensor detected - {}".format(data["temp_sensor"])) + count += 1 + elif mode == "numthermals": + if str(len(thermal_list)) != str(output[data]): + st.error("Incorrect Thermal sensors numbers detected - {}".format(output[data])) + count += 1 + elif mode == "version": + if str(version) not in str(data["version"]): + st.error("Invalid Thermal version detected - {}".format(data["version"])) + count += 1 + if count: + st.error("Mismatch in PDDF Thermal UTIL data") + return False + return True diff --git a/spytest/apis/system/connection.py b/spytest/apis/system/connection.py new file mode 100644 index 00000000000..877ae174fb3 --- /dev/null +++ b/spytest/apis/system/connection.py @@ -0,0 +1,89 @@ +from spytest.access.connection import DeviceConnection, DeviceConnectionTimeout +from spytest import st + +def connect_to_device(ip, username, password, protocol='ssh', port=22,retry=0, alt_password=None, sudo=True): + + if st.is_dry_run(): + return None + + if protocol == "telnet": + type = 'sonic_terminal' + else: + type = 'sonic_ssh' + + device = { + 'access_model': type, + 'ip': ip, + 'port': port, + 'username': username, + 'password': password, + 'blocking_timeout': 30, + } + + connected = False + net_connect = None + count = 1 + while True: + try: + st.log("Trying %d.." % count) + net_connect = DeviceConnection(**device) + connected = True + break + except DeviceConnectionTimeout: + st.log("Timed-out..") + count += 1 + if count > retry: + break + except: + st.log("Except: Cannot connect..") + if alt_password: + st.log("Retrying with Alternate password..") + count = 1 + device['password'] = alt_password + try: + st.log("Trying %d.." % count) + net_connect = DeviceConnection(**device) + connected = True + break + except DeviceConnectionTimeout: + st.log("Timed-out..") + count += 1 + if count > retry: + break + except: + st.log("Except2: Cannot connect..") + break + break + + if connected: + st.log("Connected ...") + prompt = net_connect.find_prompt() + st.log("Detected prompt - {}".format(prompt)) + if sudo: + command = "sudo su \n{}".format(password) + net_connect.send_command(command, r"#|\?|$") + return net_connect + + st.log("Cannot connect..") + return None + + +def ssh_disconnect(ssh_obj): + if ssh_obj: + ssh_obj.disconnect() + + +def make_sudo(ssh_conn_obj, password): + command = "sudo su \n{}".format(password) + ssh_conn_obj.send_command(command, "#") + + +def execute_command(ssh_obj, command): + try: + prompt = ssh_obj.find_prompt() + result = ssh_obj.send_command(command, expect_string="{}|#".format(prompt)) + st.log(result) + return result + except Exception as e: + st.log("Exception : {}".format(e)) + return None diff --git a/spytest/apis/system/gnmi.py b/spytest/apis/system/gnmi.py new file mode 100644 index 00000000000..b36ac46fb9c --- /dev/null +++ b/spytest/apis/system/gnmi.py @@ -0,0 +1,294 @@ +# This file contains the list of API's for operations of GNMI CLI +# @author : Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + +from spytest import st +import tempfile +import utilities.utils as util_obj +import json +from apis.system.basic import service_operations_by_systemctl + +supported_gnmi_operations = ["set", "get", "cli"] + + +def get_docker_command(container="telemetry"): + """ + API to return docker container command for execution + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param container: + :return: + """ + command = "docker exec -it {} bash".format(container) + return command + + +def gnmi_get(dut, xpath, **kwargs): + """ + API to do GNMI get operations + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param xpath: + :param kwargs: + :return: + """ + gnmi_debug(dut) + credentails = st.get_credentials(dut) + ip_address = kwargs.get('ip_address', '127.0.0.1') + port = kwargs.get('port', '8080') + insecure = kwargs.get('insecure', '') + skip_tmpl = kwargs.get('skip_tmpl', False) + username = kwargs.get('username', credentails[0]) + password = kwargs.get('password', credentails[3]) + cert = kwargs.get('cert') + target_name = kwargs.get('target_name') + result = dict() + try: + docker_command = get_docker_command() + if not docker_command: + st.log("Docker command not found ..") + return False + gnmi_command = 'gnmi_get -xpath {} -target_addr {}:{}'.format(xpath, ip_address, port) + if username: + gnmi_command += " -username {}".format(username) + if password: + gnmi_command += " -password {}".format(password) + if cert: + gnmi_command += " -cert {}".format(cert) + if target_name: + gnmi_command += " -target_name {}".format(target_name) + if insecure != 'none': + gnmi_command += " -insecure {}".format(insecure) + command = '{} -c "{}"'.format(docker_command, gnmi_command) + output = st.show(dut, command, skip_tmpl=skip_tmpl) + st.log("OUTPUT : {}".format(output)) + if not output: + return result + if skip_tmpl: + if "data" in output[0]: + data = json.dumps(output[0]["data"]) + if not data: + return result + return json.loads(json.loads(json.loads(json.dumps(data[0]["data"])))) + return result + else: + response = output[0]["data"] + while True: + if not isinstance(response, dict): + response = json.loads(response) + else: + return response + except Exception as e: + st.log(e) + return result + + +def gnmi_set(dut, xpath, json_content, **kwargs): + """ + API to set GNMI configuration + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param xpath: + :param json_content: + :param kwargs: + :return: + """ + gnmi_debug(dut) + credentails = st.get_credentials(dut) + ip_address = kwargs.get('ip_address', '127.0.0.1') + port = kwargs.get('port', '8080') + insecure = kwargs.get('insecure', '') + username = kwargs.get('username', credentails[0]) + password = kwargs.get('password', credentails[3]) + cert = kwargs.get('cert') + target_name = kwargs.get('target_name') + pretty = kwargs.get('pretty') + logstostderr = kwargs.get('logstostderr') + mode = kwargs.get('mode', '-update') + + docker_command = get_docker_command() + if not docker_command: + st.log("Docker command not found ..") + return False + + if json_content: + temp_dir = tempfile.gettempdir() + current_datetime = util_obj.get_current_datetime() + file_name = "sonic_gnmi_{}.json".format(current_datetime) + tmp_path = "{}/{}".format(temp_dir, file_name) + docker_path = '/{}'.format(file_name) + cp_cmd = 'docker cp {} telemetry:{}'.format(tmp_path, docker_path) + rm_cmds = ['rm {}'.format(tmp_path), '{} -c "rm {}"'.format(docker_command, docker_path)] + file_operation = util_obj.write_to_json_file(json_content, tmp_path) + if not file_operation: + st.log("File operation failed.") + return False + st.upload_file_to_dut(dut, tmp_path, tmp_path) + st.config(dut, cp_cmd) + gnmi_command = 'gnmi_set {} {}:@{} -target_addr {}:{}'.format(mode, xpath, docker_path, ip_address, port) + if username: + gnmi_command += " -username {}".format(username) + if password: + gnmi_command += " -password {}".format(password) + if cert: + gnmi_command += " -cert {}".format(cert) + if target_name: + gnmi_command += " -target_name {}".format(target_name) + if pretty: + gnmi_command += " -pretty" + if logstostderr: + gnmi_command += " -alsologstostderr" + if insecure != 'none': + gnmi_command += " -insecure {}".format(insecure) + command = '{} -c "{}"'.format(docker_command, gnmi_command) + output = st.config(dut, command) + for rm_cmd in rm_cmds: + st.config(dut, rm_cmd) + error_strings = ["Error response", "rpc error", "Set failed", "Unknown desc", "failed"] + for err_code in error_strings: + if err_code in util_obj.remove_last_line_from_string(output): + st.log(output) + return False + return output + else: + return False + + +def gnmi_cli(dut, query_type="once", ip_address="127.0.0.1", port=8080, **kwargs): + """ + API to configure gnmi using cli + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param query_type: once, stream, poll + :param ip_address: + :param port: + :param kwargs: + :return: + """ + docker_command = get_docker_command() + if not docker_command: + st.log("Docker command not found ..") + return False + if query_type not in ["stream", "poll", "once"]: + st.log("Provided unsupported query type") + return False + mandatory_kwargs = ["query_type", "xpath"] + for arg in mandatory_kwargs: + if arg not in kwargs: + st.log("Please provide {} attribute".format(arg)) + return False + insecure = "" if "insecure" in kwargs and not kwargs["insecure"] else "-insecure" + logstostderr = "" if "logstostderr" in kwargs and not kwargs["logstostderr"] else "-logstostderr" + xpath_list = list(kwargs["xpath"]) if isinstance(kwargs["xpath"], list) else [kwargs["xpath"]] + version = kwargs["version"] if "version" in kwargs and kwargs["version"] else 0 + target = kwargs["target"] if "target" in kwargs and kwargs["target"] else "OC-YANG" + gnmi_cmd = "gnmi_cli {} {} -address {}:{} ".format(insecure, logstostderr, ip_address, port) + if query_type == "stream": + stream_type = kwargs["streaming_type"] if "streaming_type" in kwargs and kwargs["streaming_type"] else 1 + gnmi_cmd += " -query_type {} -streaming_type {} -q {} -v {} -target {}".\ + format("s", stream_type, ",".join(xpath_list), version, target) + elif query_type == "poll": + poll_interval = kwargs["poll_interval"] if "poll_interval" in kwargs and kwargs["poll_interval"] else 1 + gnmi_cmd += " -query_type {} -pi {} -q {} -v {} -target {}".\ + format("p", poll_interval, ",".join(xpath_list), version, target) + else: + gnmi_cmd += " -query_type {} -q {} -v {} -target {}".format("o", ",".join(xpath_list), version, target) + + if gnmi_cmd: + command = '{} -c "{}"'.format(docker_command, gnmi_cmd) + output = st.config(dut, command) + if "Error response" in util_obj.remove_last_line_from_string(output): + st.log(output) + return False + return True + + +def client_auth(dut, **kwargs): + """ + To enable disable gNMI client auth. + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param auth_type: + :return: + """ + st.log("Configuring gNMI authentication.") + docer_name = "TELEMETRY" + docker_name= "telemetry" + command = 'redis-cli -n 4 hmset "TELEMETRY|gnmi" client_auth' + if 'auth_type' in kwargs: + if kwargs.get('auth_type'): + command = 'redis-cli -n 4 hmset "TELEMETRY|gnmi" client_auth "{}"'.format(kwargs.get('auth_type')) + else: + command = 'redis-cli -n 4 hdel "TELEMETRY|gnmi" client_auth' + st.config(dut, command) + if 'server_key' in kwargs: + if kwargs.get('server_key'): + command = 'redis-cli -n 4 hmset "DEVICE_METADATA|x509" server_key "{}"'.format(kwargs.get('server_key')) + st.config(dut, command) + if 'server_crt' in kwargs: + if kwargs.get('server_crt'): + command = 'redis-cli -n 4 hmset "DEVICE_METADATA|x509" server_crt "{}"'.format(kwargs.get('server_crt')) + st.config(dut, command) + if 'ca_crt' in kwargs: + if kwargs.get('ca_crt'): + command = 'redis-cli -n 4 hmset "DEVICE_METADATA|x509" ca_crt "{}"'.format(kwargs.get('ca_crt')) + else: + command = 'redis-cli -n 4 hdel "DEVICE_METADATA|x509" ca_crt' + st.config(dut, command) + service_operations_by_systemctl(dut, docker_name, 'stop') + service_operations_by_systemctl(dut, docker_name, 'start') + command = 'sonic-cfggen -d -v "TELEMETRY"' + st.config(dut, command) + return True + +def gnmi_delete(dut, xpath, **kwargs): + """ + API to do GNMI get operations + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param xpath: + :param kwargs: + :return: + """ + gnmi_debug(dut) + st.log("Performing GNMI DELETE OPERATION ...") + ip_address = kwargs.get('ip_address', '127.0.0.1') + port = kwargs.get('port', '8080') + insecure = kwargs.get('insecure', '') + credentails = st.get_credentials(dut) + username = kwargs.get('username', credentails[0]) + password = kwargs.get('password', credentails[3]) + cert = kwargs.get('cert') + docker_command = get_docker_command() + try: + gnmi_command = 'gnmi_set --delete {} --target_addr {}:{}'.format(xpath, ip_address, port) + if username: + gnmi_command += " --username {}".format(username) + if password: + gnmi_command += " --password {}".format(password) + if cert: + gnmi_command += " --cert {}".format(cert) + gnmi_command += " --insecure {}".format(insecure) + command = '{} -c "{}"'.format(docker_command, gnmi_command) + output = st.config(dut, command) + st.log("OUTPUT : {}".format(output)) + if not output: + st.log("Observed empty OUTPUT") + return False + error_strings = ["Error response", "rpc error", "gnmi_set.go", "Set failed", "Unknown desc", "failed"] + for err_code in error_strings: + if err_code in util_obj.remove_last_line_from_string(output): + st.log(output) + return False + return output + except Exception as e: + st.error(e) + return False + +def gnmi_debug(dut): + """ + API to check the debug commands for GNMI + :param dut: + :return: + """ + command = 'sonic-cfggen -d -v "TELEMETRY"' + output = st.config(dut, command) + st.log("DEBUG OUPUT for GNMI STATUS --- {}".format(output)) \ No newline at end of file diff --git a/spytest/apis/system/interface.py b/spytest/apis/system/interface.py new file mode 100644 index 00000000000..2b19624161b --- /dev/null +++ b/spytest/apis/system/interface.py @@ -0,0 +1,1036 @@ +# This file contains the list of API's for operations on interface +# @author : Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + +from spytest import st +from utilities.common import filter_and_select, make_list, exec_all, dicts_list_values, convert_to_bits +from utilities.utils import get_interface_number_from_name +import apis.system.port as portapi +import re + + +def interface_status_show(dut, interfaces=None, cli_type="click"): + """ + Author: Chaitanya Vella (chaitanya.vella-kumar@broadcom.com) + Function to get the interface(s) status + :param dut: + :param interfaces: + :param cli_type: + :return: + """ + if cli_type == "click": + if interfaces: + return portapi.get_status(dut, ','.join(make_list(interfaces))) + return portapi.get_status(dut, interfaces) + elif cli_type == "klish": + command = "show interface status" + interface = make_list(interfaces) + if interface: + command += " | grep \"{}\"".format("|".join(interface)) + return st.show(dut, command, type=cli_type) + else: + st.log("Unsupported CLI TYPE {}".format(cli_type)) + return False + + +def interface_operation(dut, interfaces, operation="shutdown", skip_verify=True, cli_type=""): + """ + This is an internal common function for interface operations + Author: Chaitanya Vella (chaitanya.vella-kumar@broadcom.com) + :param dut: dut OBJ + :param interfaces: interfaces list + :param operation: shutdown or startup + :param skip_verify: to skip the verification + :param cli_type: (default: click) + :return: boolean + """ + + if not cli_type: + cli_type=st.get_ui_type(dut) + + if cli_type == "click": + interfaces_li = make_list(interfaces) + response = portapi.set_status(dut, interfaces_li, operation) + if "Error" in response: + st.log(response) + return False + + if not skip_verify: + concatd_interfaces = ",".join(interfaces_li) + interface_list = interface_status_show(dut, concatd_interfaces) + if operation == "shutdown": + if interface_list[0]["oper"] != "down" and interface_list[0]["admin"] != "down": + st.log("Error: Interface {} is not down.".format(concatd_interfaces)) + return False + elif operation == "startup": + if interface_list[0]["admin"] != "up": + st.log("Error: Interface {} is not up.".format(concatd_interfaces)) + return False + return True + elif cli_type == "klish": + interface = make_list(interfaces) + commands = list() + if interface: + for intf in interface: + intf_details = get_interface_number_from_name(intf) + if not intf_details: + st.error("Interface data not found for {} ".format(intf)) + else: + commands.append("interface {} {}".format(intf_details["type"], intf_details["number"])) + command = "shutdown" if operation == "shutdown" else "no shutdown" + commands.append(command) + commands.append("exit") + if commands: + st.config(dut, commands, type=cli_type) + return True + return False + else: + st.log("Unsupported CLI TYPE {}".format(cli_type)) + return False + + +def interface_operation_parallel(input, operation='startup', thread=True): + """ + Author : Chaitanya Lohith Bollapragada + This will perform the shutdown and noshutdown of given ports in given DUTs parallel. + :param input: dic keys = dut, values = list of interfaces + :param operation: shutdown | startup(default) + :param thread: + :return: + + Ex: interface_operation_parallel({vars:D1:[vars.D1D2P1,vars.D1D2P2], vars.D2:[vars.D2D1P1,vars.D2T1P1]},) + """ + [out, exceptions] = exec_all(thread, [[interface_operation, duts, input[duts], operation] + for duts in input.keys()]) + st.log(exceptions) + return False if False in out else True + + +def interface_shutdown(dut, interfaces, skip_verify=True, cli_type="click"): + """ + Function to shutdown interface + Author: Chaitanya Vella (chaitanya.vella-kumar@broadcom.com) + :param dut: + :param interfaces: + :param skip_verify: + :param cli_type: + :return: + """ + return interface_operation(dut, interfaces, "shutdown", skip_verify, cli_type=cli_type) + + +def interface_noshutdown(dut, interfaces, skip_verify=True, cli_type="click"): + """ + Function to no shut the interface + Author: Chaitanya Vella (chaitanya.vella-kumar@broadcom.com) + :param dut: + :param interfaces: + :param skip_verify: + :param cli_type: + :return: + """ + return interface_operation(dut, interfaces, "startup", skip_verify, cli_type=cli_type) + + +def interface_properties_set(dut, interfaces_list, property, value, skip_error=False, no_form=False, cli_type="click"): + """ + Function to set the interface properties. + Author: Chaitanya Vella (chaitanya.vella-kumar@broadcom.com) + :param dut: + :param interfaces_list: + :param property: + :param value: + :param skip_error: + :param no_form: + :param cli_type: + :return: + """ + interfaces_li = list(interfaces_list) if isinstance(interfaces_list, list) else [interfaces_list] + if cli_type == "click": + for each_interface in interfaces_li: + if property.lower() == "speed": + command = "config interface speed {} {}".format(each_interface, value) + if skip_error: + try: + st.config(dut, command) + except Exception as e: + st.log(e) + st.log("Error handled by API..") + return False + else: + st.config(dut, command) + elif property.lower() == "fec": + if value not in ["rs", "fc", "none"]: + st.log("Provided fec value not supported ...") + return False + command = "config interface fec {} {}".format(each_interface, value) + st.config(dut, command) + elif property.lower() == "mtu": + command = "config interface mtu {} {}".format(each_interface, value) + out = st.config(dut, command, skip_error_check=skip_error) + if re.search(r'Error: Interface MTU is invalid.*', out): + return False + else: + st.log("Invalid property '{}' used.".format(property)) + return False + return True + elif cli_type == "klish": + properties = {"mtu": "mtu", "description": "description", "ip_address": "ip address", + "ipv6_address": "ipv6 address", "speed": "speed", "autoneg": "autoneg"} + commands = list() + for interface in interfaces_li: + intf_details = get_interface_number_from_name(interface) + if not intf_details: + st.log("Interface data not found for {} ".format(interface)) + commands.append("interface {} {}".format(intf_details["type"], intf_details["number"])) + if not no_form: + if property.lower() == "autoneg": + command = "autoneg on" + else: + command = "{} {}".format(properties[property.lower()], value) + commands.append(command) + else: + if property.lower() == "autoneg": + command = "autoneg off" + elif property.lower() in ["ip_address", "ipv6_address"]: + command = "no {} {}".format(properties[property.lower()], value) + else: + command = "no {}".format(properties[property.lower()]) + commands.append(command) + commands.append("exit") + if commands: + st.config(dut, commands, type=cli_type) + return True + else: + st.log("Unsupported CLI TYPE {}".format(cli_type)) + return False + + +def _get_interfaces_by_status(dut, status): + """ + Internal function to get the interface status + Author: Chaitanya Vella (chaitanya.vella-kumar@broadcom.com) + :param dut: dut obj + :param status: status of the interface + :return: list of interface status + """ + output = interface_status_show(dut, None) + retval = [] + match = {"oper": status} if status else None + entries = filter_and_select(output, ["interface"], match) + for ent in entries: + retval.append(ent["interface"]) + return retval + + +def get_up_interfaces(dut): + """ + Author: Chaitanya Vella (chaitanya.vella-kumar@broadcom.com) + This is to get the list of up interfaces + :param dut: dut obj + :return: list of interfaces + """ + return _get_interfaces_by_status(dut, "up") + + +def get_down_interfaces(dut): + """ + Author: Chaitanya Vella (chaitanya.vella-kumar@broadcom.com) + :param dut: DUT object + :return: list of down interfaces + """ + return _get_interfaces_by_status(dut, "down") + + +def get_all_interfaces(dut, int_type=None, cli_type="click"): + """ + Author: Chaitanya Vella (chaitanya.vella-kumar@broadcom.com) + API to get all the interfaces nin DUT + :param dut: dut object + :param int_type: physical | port_channel + :param cli_type: + :return: interface list + """ + output = interface_status_show(dut, cli_type=cli_type) + out = dicts_list_values(output, 'interface') + if out: + if int_type == 'physical': + return [each for each in out if each.startswith("Ethernet")] + elif int_type == 'port_channel': + return [each for each in out if each.lower().startswith("portchannel")] + else: + return out + else: + return [] + + +def get_all_ports_speed_dict(dut): + """ + :param dut: + :return: dict of all ports of same speed + """ + all_speed_ports = dict() + output = interface_status_show(dut) + physical_port_list = [each['interface'] for each in output if each['interface'].startswith("Ethernet")] + for each in physical_port_list: + speed = filter_and_select(output, ['speed'], {'interface': each})[0]['speed'] + if speed not in all_speed_ports: + all_speed_ports[speed] = [each] + else: + all_speed_ports[speed].append(each) + return all_speed_ports + + +def verify_interface_status(dut, interface, property, value, cli_type="click"): + """ + Author: Chaitanya Vella (chaitanya.vella-kumar@broadcom.com) + This API to verify the interface status + :param dut: dut obj + :param interface: Interface Name + :param property: Interface property + :param value: Property Value + :param cli_type: + :return: Boolean + """ + interface_list = make_list(interface) + is_found = 1 + for port in interface_list: + interface_details = interface_status_show(dut, port, cli_type=cli_type) + match = {"interface": port, property: value} + entries = filter_and_select(interface_details, ["interface"], match) + if not bool(entries): + is_found = 0 + break + else: + is_found = 1 + if not is_found: + return False + return True + + +def clear_interface_counters(dut): + """ + Clear interface counters + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :return: + """ + if st.is_community_build(): + return st.config(dut, "sonic-clear counters") + else: + return st.show(dut, "show interfaces counters -c") + + +def show_interfaces_counters(dut, interface=None, property=None, cli_type="click"): + """ + show interface counter + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param interface: + :param property: + :param cli_type: + :return: + """ + if cli_type == "click": + command = 'show interfaces counters' + output = st.show(dut, command) + if interface: + if property: + output = filter_and_select(output, [property], {'iface': interface}) + else: + output = filter_and_select(output, None, {'iface': interface}) + return output + elif cli_type == "klish": + command = "show interface counters" + interface = make_list(interface) + if interface: + command += " | grep \"{}\"".format("|".join(interface)) + return st.show(dut, command, type=cli_type) + else: + st.log("Unsupported CLI TYPE {}".format(cli_type)) + return False + + +def show_interface_counters_all(dut): + """ + Show interface counter all. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :return: + """ + command = "show interfaces counters -a" + return st.show(dut, command) + + +def get_interface_counters(dut, port, *counter): + """ + This API is used to get the interface counters. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param port: + :param counter: + :return: + """ + output = show_specific_interface_counters(dut, port) + entries = filter_and_select(output, counter, {'iface': port}) + return entries + + +def poll_for_interfaces(dut, iteration_count=180, delay=1, cli_type="click"): + """ + This API is to poll the DUT to get the list of interfaces + Author: Chaitanya Vella (chaitanya.vella-kumar@broadcom.com) + :param dut: + :param iteration_count: + :param delay: + :param cli_type: + :return: + """ + i = 1 + while True: + intefaces_list = get_all_interfaces(dut, cli_type=cli_type) + if intefaces_list: + st.log("Interfaces list found ...") + return True + if i > iteration_count: + st.log("Max {} tries Exceeded. Exiting ..".format(i)) + return False + i += 1 + st.wait(delay) + + +def poll_for_interface_status(dut, interface, property, value, iteration=5, delay=1, cli_type="click"): + """ + API to poll for interface status + Author: Chaitanya Vella (chaitanya.vella-kumar@broadcom.com) + :param dut: + :param interface: + :param property: + :param value: + :param iteration: + :param delay: + :param cli_type: + :return: + """ + i = 1 + while True: + if verify_interface_status(dut, interface, property, value, cli_type=cli_type): + st.log("Observed interface status match at {} iteration".format(i)) + return True + if i > iteration: + st.log("Max iterations {} reached".format(i)) + return False + i += 1 + st.wait(delay) + + +def get_interface_property(dut, interfaces_list, property, cli_type="click"): + """ + + :param dut: + :param interfaces_list: API accepts interfaces list or single interface + :param property: single property need to provide + :param cli_type: + :return: Returns interfaces list properties in the interfaces order passed to api + """ + if not isinstance(interfaces_list, list): + interfaces_li = [interfaces_list] + output = interface_status_show(dut, interfaces_li, cli_type=cli_type) + return_list = [] + for each_interface in interfaces_li: + property_val = filter_and_select(output, [property], {'interface': each_interface}) + if not property_val: + break + return_list.append(property_val[0][property]) + return return_list + + +def config_static_ip_to_interface(dut, interface_name, ip_address, netmask, gateway): + """ + API to configure static ip address to an interface + Author: Chaitanya Vella (chaitanya.vella-kumar@broadcom.com) + :param dut: + :param interface_name: + :param ip_address: + :param netmask: + :param gateway: + :return: + """ + command = "ifconfig {} {} netmask {}".format(interface_name, ip_address, netmask) + st.config(dut, command) + command = 'ip route add default via {}'.format(gateway) + st.config(dut, command) + + +def delete_ip_on_interface_linux(dut, interface_name, ip_address): + """ + :param dut: + :param interface_name: + :param ip_address: + :return: + """ + command = "ip addr del {} dev {}".format(ip_address, interface_name) + st.config(dut, command) + + +def show_queue_counters(dut, interface_name, queue=None): + """ + Show Queue counters + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param interface_name: + :param queue: UC0-UC9 | MC10-MC19 (Default None) + :return: + """ + command = "show queue counters {}".format(interface_name) + output = st.show(dut, command) + if queue: + return filter_and_select(output, None, {'txq': queue}) + return output + + +def clear_queue_counters(dut, interfaces_list=[]): + """ + Clear Queue counters + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :return: + """ + interface_li = list(interfaces_list) if isinstance(interfaces_list, list) else [interfaces_list] + if not interface_li: + command = "show queue counters -c" + st.config(dut, command) + else: + for each_port in interface_li: + command = "show queue counters {} -c".format(each_port) + st.config(dut, command) + return True + + +def get_free_ports_speed_dict(dut, cli_type="click"): + """ + :param dut: + :param cli_type: + :return: dict of free ports of same speed + """ + free_speed_ports = dict() + free_ports = st.get_free_ports(dut) + output = interface_status_show(dut, cli_type=cli_type) + for each in free_ports: + speed = filter_and_select(output, ['speed'], {'interface': each})[0]['speed'] + if speed not in free_speed_ports: + free_speed_ports[speed] = [each] + else: + free_speed_ports[speed].append(each) + return free_speed_ports + + +def enable_dhcp_on_interface(dut, interface_name, type="v4", skip_error_check=False): + """ + :param dut: + :param interface_name: + :return: + """ + version = "" + if type == "v6": + version = "-6" + command = "dhclient {} {}".format(version, interface_name) + return st.config(dut, command, skip_error_check=skip_error_check) + + +def show_interface_counters_detailed(dut, interface, filter_key=None): + """ + show interfaces counters detailed . + Author : Rakesh Kumar Vooturi (rakesh-kumar.vooturi@broadcom.com) + :param dut: + :param interface: + :return: + """ + command = "show interfaces counters detailed {}".format(interface) + if st.is_community_build(): + output = st.show(dut, command, skip_error_check=True) + else: + output = st.show(dut, command) + if not filter_key: + return output + else: + if not output: + return False + return output[0][filter_key] + + +def clear_watermark_counters(dut, mode='all'): + """ + Clear Watermark counters + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param mode: + :return: + """ + if mode == 'multicast' or mode == 'all': + command = "sonic-clear queue watermark multicast" + st.config(dut, command) + if mode == 'unicast' or mode == 'all': + command = "sonic-clear queue watermark unicast" + st.config(dut, command) + if mode == 'shared' or mode == 'all': + command = "sonic-clear priority-group watermark shared" + st.config(dut, command) + if mode == 'headroom' or mode == 'all': + command = "sonic-clear priority-group watermark headroom" + st.config(dut, command) + return True + + +def show_watermark_counters(dut, mode='all'): + """ + Show Watermark counters + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param mode: + :return: + """ + result = '' + if mode == 'multicast' or mode == 'all': + command = "show queue watermark multicast" + result += st.show(dut, command, skip_tmpl=True) + if mode == 'unicast' or mode == 'all': + command = "show queue watermark unicast" + result += st.show(dut, command, skip_tmpl=True) + if mode == 'shared' or mode == 'all': + command = "show priority-group watermark shared" + result += st.show(dut, command, skip_tmpl=True) + if mode == 'headroom' or mode == 'all': + command = "show priority-group watermark headroom" + result += st.show(dut, command, skip_tmpl=True) + return result + + +def show_specific_interface_counters(dut, interface_name): + """ + API to fetch the specific interface counters + :param dut: + :param interface_name: + :return: + """ + command = "show interfaces counters -a -i {}".format(interface_name) + if st.is_community_build(): + command = "show interfaces counters -a | grep -w {}".format(interface_name) + output = st.show(dut, command) + st.log(output) + return output + + +def get_interface_counter_value(dut, ports, properties): + """ + This API is used to get the multiple interfaces counters value in dictionary of dictionaries. + Author : Ramprakash Reddy (ramprakash-reddy.kanala@broadcom.com) + :param dut: + :param ports: Interfaces names ["Ethernet0","Ethernet1"] + :param property: Interface properties ["rx_ok","tx_ok"] + :return: {"Ethernet0":{"rx_ok":"1234","tx_ok":"45"},"Ethenrnet1":{"rx_ok"="4325","tx_ok"="2424"}} + """ + if not isinstance(ports, list): + ports = [ports] + if not isinstance(properties, list): + properties = [properties] + counters_dict = dict() + output = show_interface_counters_all(dut) + for each_port in ports: + entries = filter_and_select(output, properties, {'iface': each_port})[0] + counters_dict[each_port] = entries + return convert_to_bits(counters_dict) + + +def verify_interface_counters(dut, params, cli_type="click"): + """ + :param dut: + :param params: {"module_type":"mirror","source":["Ethernet1","tx_ok"], "destination":["Ethernet2","rx_ok"], + "mirrored_port":["Ethernet3","rx_ok"]} + :param cli_type: + :return: + """ + if cli_type == "click": + st.log("Verifying interface counters on {}".format(dut)) + output = show_interface_counters_all(dut) + if not output: + st.log("Output not found") + return False + if params: + source_counters, destination_counters, mirror_counters = 0, 0, 0 + module_type = params.get("module_type", "mirror") + for data in output: + if params.get("source") and data["iface"] == params["source"][0]: + source_counters = data[params["source"][1]] + if params.get("destination") and data["iface"] == params["destination"][0]: + destination_counters = data[params["destination"][1]] + if module_type in ["mirror", "mirror_both"] and params.get("mirrored_port"): + if data["iface"] == params["mirrored_port"][0]: + mirror_counters = \ + data[params["mirrored_port"][1]] + try: + st.log('The source counter is {}'.format(source_counters)) + st.log('The destination counter is {}'.format(destination_counters)) + st.log("Mirror Counters:{}".format(mirror_counters)) + float(source_counters.split()[0].replace(",", "")) + float(destination_counters.split()[0].replace(",", "")) + except: + st.report_fail("counters_are_not_initilaized") + source_counters = int(source_counters.replace(",", "")) + destination_counters = int(destination_counters.replace(",", "")) + mirror_counters = int(mirror_counters.replace(",", "")) + if module_type == "mirror": + if not ((mirror_counters >= 0.98 * source_counters) and (destination_counters >= 0.98 * source_counters)): + st.log("Counters mismatch Source Counters:{},Destination Counters:{}Mirror" + " Counters:{}".format(source_counters, destination_counters, mirror_counters)) + st.log("Observed mismatch in counter validation") + st.log("Source Counters:{}".format(source_counters)) + st.log("Destination Counters:{}".format(destination_counters)) + st.log("Mirror Counters:{}".format(mirror_counters)) + return False + else: + return True + elif module_type == "mirror_both": + mirror_counters_both = int(source_counters) + int(destination_counters) + #mirror_counters_both = int(mirror_counters_both.replace(",", "")) + if not (int(mirror_counters) >= 0.99 * mirror_counters_both): + st.log("Observed mismatch in counter validation") + st.log("Source Counters:{}".format(source_counters)) + st.log("Destination Counters:{}".format(destination_counters)) + st.log("Mirror Counters:{}".format(mirror_counters)) + st.log("Mirror Counters both:{}".format(mirror_counters_both)) + return False + else: + return True + elif module_type == "bum": + source_counters = int(round(float(source_counters.split()[0]))) + destination_counters = int(round(float(destination_counters.split()[0]))) + if not destination_counters - source_counters <= 100: + st.log("Destination counter:{} and Source Counters:{}".format(destination_counters, + source_counters)) + return False + else: + return destination_counters + else: + st.log("Unsupported module type {}".format(module_type)) + return False + else: + st.log("Parameters not found - {} ...".format(params)) + return False + +def config_loopback_interfaces(dut, lpbk_if_data={}, config='yes'): + + if config == 'yes' or config == 'add': + config = 'add' + elif config == 'no' or config == 'del': + config = 'del' + else : + st.error("Invalid config type {}".format(config)) + return False + + command = [] + for if_name, if_data in lpbk_if_data.items(): + cmd_str = "sudo config loopback {} {} ".format(config, if_name) + command.append(cmd_str) + + if command != '': + try: + st.config(dut, command) + except Exception as e: + st.log(e) + return False + + return True + + +def config_portchannel_interfaces(dut, portchannel_data={}, config='yes'): + + if config == 'yes' or config == 'add': + config = 'add' + elif config == 'no' or config == 'del': + config = 'del' + else : + st.error("Invalid config type {}".format(config)) + return False + + command = [] + + if config == 'del' : + for if_name, if_data in portchannel_data.items(): + for link_member in if_data['members'] : + cmd_str = "sudo config portchannel member {} {} {} ".format(config, if_name, link_member) + command.append(cmd_str) + try: + st.config(dut, command) + except Exception as e: + st.log(e) + return False + + command = [] + for if_name, if_data in portchannel_data.items(): + cmd_str = "sudo config portchannel {} {} ".format(config, if_name) + command.append(cmd_str) + + try: + st.config(dut, command) + except Exception as e: + st.log(e) + return False + + command = [] + if config == 'add' : + for if_name, if_data in portchannel_data.items(): + for link_member in if_data['members'] : + cmd_str = "sudo config portchannel member {} {} {} ".format(config, if_name, link_member) + command.append(cmd_str) + try: + st.config(dut, command) + except Exception as e: + st.log(e) + return False + + return True + + +def config_vlan_interfaces(dut, vlan_data={}, config='yes', skip_error=False): + + if config == 'yes' or config == 'add': + config = 'add' + elif config == 'no' or config == 'del': + config = 'del' + else : + st.error("Invalid config type {}".format(config)) + return False + + command = [] + if config == 'del' : + for if_name, if_data in vlan_data.items(): + vlan_id = if_data['vlan_id'] + + range_cmd = False + if 'range' in if_data.keys(): + range_ids = if_data['range'] + if range_ids[0] < range_ids[1] : + range_min, range_max = range_ids[0], range_ids[1] + range_cmd = True + elif range_ids[0] > range_ids[1] : + range_min, range_max = range_ids[1], range_ids[0] + range_cmd = True + else : + vlan_id = range_ids[0] + + for link_member in if_data['members'] : + if not range_cmd : + cmd_str = "config vlan member {} {} {} ".format(config, vlan_id, link_member) + command.append(cmd_str) + elif not st.is_community_build(): + cmd_str = "config vlan member range {} {} {} {}".format(config, range_min, range_max, link_member) + command.append(cmd_str) + else: + skip_error = True + for vid in range(range_min, range_max+1): + cmd_str = "config vlan member {} {} {} ".format(config, vid, link_member) + command.append(cmd_str) + + try: + st.config(dut, command, skip_error_check=skip_error) + except Exception as e: + st.log(e) + return False + + command = [] + for if_name, if_data in vlan_data.items(): + vlan_id = if_data['vlan_id'] + + range_cmd = False + if 'range' in if_data.keys(): + range_ids = if_data['range'] + if range_ids[0] < range_ids[1] : + range_min, range_max = range_ids[0], range_ids[1] + range_cmd = True + elif range_ids[0] > range_ids[1] : + range_min, range_max = range_ids[1], range_ids[0] + range_cmd = True + else : + vlan_id = range_ids[0] + + if not range_cmd : + cmd_str = "sudo config vlan {} {} ".format(config, vlan_id) + command.append(cmd_str) + elif not st.is_community_build(): + cmd_str = "sudo config vlan range {} {} {}".format(config, range_min, range_max) + command.append(cmd_str) + else : + for vid in range(range_min, range_max+1): + cmd_str = "sudo config vlan {} {} ".format(config, vid) + command.append(cmd_str) + + try: + st.config(dut, command) + except Exception as e: + st.log(e) + return False + + command = [] + if config == 'add' : + for if_name, if_data in vlan_data.items(): + vlan_id = if_data['vlan_id'] + + range_cmd = False + if 'range' in if_data.keys(): + range_ids = if_data['range'] + if range_ids[0] < range_ids[1] : + range_min, range_max = range_ids[0], range_ids[1] + range_cmd = True + elif range_ids[0] > range_ids[1] : + range_min, range_max = range_ids[1], range_ids[0] + range_cmd = True + else : + vlan_id = range_ids[0] + + for link_member in if_data['members'] : + + if not range_cmd : + cmd_str = "config vlan member {} {} {} ".format(config, vlan_id, link_member) + command.append(cmd_str) + elif not st.is_community_build(): + cmd_str = "config vlan member range {} {} {} {}".format(config, range_min, range_max, link_member) + command.append(cmd_str) + else: + for vid in range(range_min, range_max+1): + cmd_str = "config vlan member {} {} {} ".format(config, vid, link_member) + command.append(cmd_str) + + try: + st.config(dut, command) + except Exception as e: + st.log(e) + return False + + return True + + +def config_interface_vrf_binds(dut, if_vrf_data={}, config='yes'): + + if config == 'yes' or config == 'add': + config = 'bind' + elif config == 'no' or config == 'del': + config = 'unbind' + else : + st.error("Invalid config type {}".format(config)) + return False + + command = [] + for if_name, if_data in if_vrf_data.items(): + vrf = if_data['vrf'] + cmd_str = "sudo config interface vrf {} {} {} ".format(config, if_name, vrf) + command.append(cmd_str) + + try: + st.config(dut, command) + except Exception as e: + st.log(e) + return False + + return True + + +def config_portgroup_property(dut, portgroup, value, property="speed", skip_error=False, cli_type="click"): + """ + Function to configure portgroup properties + Author: Ramprakash Reddy (ramprakash-reddy.kanala@broadcom.com) + :param dut: + :param portgroup: + :param value: + :param property: + :param skip_error: + :param cli_type: + :return: + """ + command = "config portgroup {} {} {}".format(property, portgroup, value) + st.config(dut, command, skip_error_check=skip_error, type=cli_type) + return True + +def show_portgroup(dut, interface=None, cli_type="click"): + """ + API to get the list of port groups available in DUT + Author: Chaitanya Vella (chaitanya.vella-kumar@broadcom.com) + :param dut: + :param interface: + :return: [{'ports': ['Ethernet0', 'Ethernet1', 'Ethernet2', 'Ethernet3', 'Ethernet4', + 'Ethernet5', 'Ethernet6', 'Ethernet7', 'Ethernet8', 'Ethernet9', 'Ethernet10', 'Ethernet11'], + 'valid_speeds': ['25000', '10000', '1000'], 'portgroup': '1'}, + {'ports': ['Ethernet12', 'Ethernet13', 'Ethernet14', 'Ethernet15', 'Ethernet16', 'Ethernet17', + 'Ethernet18', 'Ethernet19', 'Ethernet20', 'Ethernet21', 'Ethernet22', 'Ethernet23'], + 'valid_speeds': ['25000', '10000', '1000'], 'portgroup': '2'}, {'ports': ['Ethernet24', + 'Ethernet25', 'Ethernet26', 'Ethernet27', 'Ethernet28', 'Ethernet29', 'Ethernet30', 'Ethernet31', + 'Ethernet32', 'Ethernet33', 'Ethernet34', 'Ethernet35'], 'valid_speeds': ['25000', '10000', '1000'], + 'portgroup': '3'}, {'ports': ['Ethernet36', 'Ethernet37', 'Ethernet38', 'Ethernet39', 'Ethernet40', + 'Ethernet41', 'Ethernet42', 'Ethernet43', 'Ethernet44', 'Ethernet45', 'Ethernet46', 'Ethernet47'], + 'valid_speeds': ['25000', '10000', '1000'], 'portgroup': '4'}] + """ + response=list() + command = "show portgroup" + output = st.show(dut, command, type=cli_type) + if output: + for data in output: + port_range = data["ports"].replace("Ethernet", "").split("-") + res = dict() + res["ports"] = list() + for i in range(int(port_range[0]), int(port_range[1]) + 1): + if not interface: + res["ports"].append("Ethernet{}".format(i)) + else: + if interface == "Ethernet{}".format(i): + res["ports"].append("Ethernet{}".format(i)) + break + if res["ports"]: + res["portgroup"] = data["portgroup"] + res["valid_speeds"] = data["valid_speeds"].split(",") + response.append(res) + if interface and res["ports"]: + break + return response + +def verify_portgroup(dut, **kwargs): + """ + API to verify portgroup + Author: Chaitanya Vella (chaitanya.vella-kumar@broadcom.com) + :param dut: + :param kwargs: {"cli_type":"click","interface":"Ethernet5","portgroup":"1","speed":"1000"} + :return: + """ + cli_type = kwargs.get("cli_type","click") + interface = kwargs.get("interface", None) + portgroup = kwargs.get("portgroup", None) + speed = kwargs.get("speed", None) + result = 0 + output = show_portgroup(dut, interface=interface,cli_type=cli_type) + if not output: + st.log("Empty output observed - {}".format(output)) + return False + for data in output: + if portgroup and str(data["portgroup"]) != str(portgroup): + result = 1 + else: + result = 0 + if speed and str(speed) not in data["speed"]: + result = 1 + else: + result = 0 + if result: + return False + return True + +def is_port_group_supported(dut, cli_type="click"): + """ + API to check whether port group is supported or not + Author: Chaitanya Vella (chaitanya.vella-kumar@broadcom.com) + :param dut: + :return: False -- Unsupported + True -- Supported + """ + output = show_portgroup(dut, cli_type=cli_type) + if not output: + return False + else: + return True diff --git a/spytest/apis/system/lldp.py b/spytest/apis/system/lldp.py new file mode 100644 index 00000000000..85a9d6e7010 --- /dev/null +++ b/spytest/apis/system/lldp.py @@ -0,0 +1,131 @@ +# This file contains the list of API's which performs LLDP operations. +# Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) +from spytest import st +from spytest.utils import filter_and_select +import json + + +def get_lldp_table(dut, interface=None): + """ + Get LLDP table Info + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param interface: localport + :return: + """ + command = "show lldp table" + output = st.show(dut, command) + if interface: + return filter_and_select(output, None, {"localport": interface}) + return output + + +def get_lldp_neighbors(dut, interface=None): + """ + Get LLDP Neighbours Info + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param interface: localport + :return: + """ + command = "show lldp neighbors" + if interface: + command = "show lldp neighbors {}".format(interface) + return st.show(dut, command) + +def lldp_config(dut, **kwargs): + """ + Set LLDP non default config parameters + Author: Prasad Darnasi (prasad.darnasi@broadcom.com) + :param dut: + :param txinterval:LLDP update packet interval + :param txhold:LLDP hold time + :param interface:remote interface + :param status:LLDP enable|disable + :param hostname:remote system name + :param capability:LLDP optional capabilities + :return: + """ + if 'txinterval' in kwargs: + command = "configure lldp {} {}".format('tx-interval',kwargs['txinterval']) + st.config(dut, command, type='lldp') + if 'txhold' in kwargs: + command = "configure lldp {} {}".format('tx-hold',kwargs['txhold']) + st.config(dut, command, type='lldp') + if 'interface' in kwargs and 'status' in kwargs: + command = "configure ports {} lldp status {}".format(kwargs['interface'], kwargs['status']) + st.config(dut, command, type='lldp') + if 'hostname' in kwargs: + command = "configure system hostname {}".format(kwargs['hostname']) + st.config(dut, command, type='lldp') + if 'capability' in kwargs and 'config' in kwargs: + cap = kwargs['capability'] + cap_li = list(cap) if isinstance(cap, list) else [cap] + for each_cap in cap_li: + if kwargs['config'] == 'yes': + command = "config lldp {}".format(each_cap) + else: + command = "unconfigure lldp {}".format(each_cap) + st.config(dut, command, type='lldp') + + +def set_lldp_local_parameters(dut, name, **kwargs): + """ + Set LLDP Local parameters + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param name: + :param mgmt_addr: + :param hwsku: + :param lo_addr: + :param local_port: + :param local_port: + :param type: + :param port: + :return: + """ + st.log("Adding local lldp data") + temp_local_data = {} + lldp_local_final = {} + if not kwargs: + st.error("SET LLDP Local parameters failed because of invalid data.") + return False + if 'mgmt_addr' in kwargs: + temp_local_data['mgmt_addr'] = kwargs['mgmt_addr'] + if 'hwsku' in kwargs: + temp_local_data['hwsku'] = kwargs['hwsku'] + if 'lo_addr' in kwargs: + temp_local_data['lo_addr'] = kwargs['lo_addr'] + if 'local_port' in kwargs: + temp_local_data['local_port'] = kwargs['local_port'] + if 'type' in kwargs: + temp_local_data['type'] = kwargs['type'] + if 'port' in kwargs: + temp_local_data['port'] = kwargs['port'] + + lldp_local_final['DEVICE_NEIGHBOR'] = {name: temp_local_data} + lldp_local_final_json = json.dumps(lldp_local_final) + st.apply_json(dut, lldp_local_final_json) + return True + + +def poll_lldp_neighbors(dut, iteration_count=180, delay=1, interface=None): + """ + Poll for LLDP Neighbours Info + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param iteration_count: + :param delay: + :param interface: + :return: + """ + i = 1 + while True: + rv = get_lldp_neighbors(dut, interface) + if rv: + return rv + if i > iteration_count: + st.log(" Max {} tries Exceeded for lldp neighbors polling .Exiting ...".format(i)) + return False + i += 1 + st.wait(delay) diff --git a/spytest/apis/system/logging.py b/spytest/apis/system/logging.py new file mode 100644 index 00000000000..8117a0243a9 --- /dev/null +++ b/spytest/apis/system/logging.py @@ -0,0 +1,229 @@ +# This file contains the list of API's which performs logging / Syslog operations. +# Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + +from spytest import st +import apis.system.switch_configuration as sc_obj +import re +import json +import apis.system.connection as conf_obj +import utilities.utils as utils +from utilities.common import exec_foreach + + +log_files = [r'/var/log/syslog', r'/var/log/syslog.1'] + + +def show_logging(dut, severity=None, filter_list=None, lines=None): + """ + To get logs from DUT. + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :param severity: + :param filter_list: + :param lines: + :return: + """ + + if filter_list is None: + filter_list = [] + filter_list = list(filter_list) if isinstance(filter_list, list) else [filter_list] + command = "show logging" + if lines: + command += " -l {}".format(lines) + if severity: + command += " | grep '{}'".format(severity) + for each_filter in filter_list: + command += " | grep -i '{}'".format(each_filter) + output = st.show(dut, command, skip_tmpl=True, skip_error_check=True, faster_cli=False, max_time=1200) + out_list = output.strip().split('\n')[:-1] + for each in range(out_list.count("'")): + out_list.remove("'") + return out_list + + +def get_logging_count(dut, severity=None, filter_list=None): + """ + To get log count + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :param severity: + :param filter_list: + :return: + """ + + if not severity and not filter_list: + command = "sudo wc -l {} | grep total".format(' '.join(log_files)) + output = st.config(dut, command) + output2 = re.findall(r'\d+', output) + return int(output2[0]) if output2 else 0 + else: + return len(show_logging(dut, severity, filter_list, lines=None)) + + +def set_logging_severity(dut, **kwargs): + """ + Set logging severity + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :param severity: + :param comp: + :return: + """ + if 'severity' not in kwargs: + st.log("API: Mandatory parameter 'severity' is not provied.") + return False + command = "swssloglevel -l {} -a".format(kwargs['severity'].upper()) + if 'comp' in kwargs: + command = '' + comp_li = list( kwargs['comp']) if isinstance(kwargs['comp'], list) else [kwargs['comp']] + for each_comp in comp_li: + command += "swssloglevel -l {} -c {}\n".format(kwargs['severity'].upper(),each_comp) + st.config(dut, command) + return True + + +def clear_logging(dut, thread=True): + """ + Clear all logging + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: list + :param thread: true + :return: + """ + def _clear_logging(dut): + for each_log in log_files: + command = "sudo truncate -s 0 {}".format(each_log) + st.config(dut, command) + return True + + dut_li = utils.make_list(dut) + [out, exceptions] = exec_foreach(thread, dut_li, _clear_logging) + st.log(exceptions) + return False if False in out else True + + +def write_logging(dut, message): + """ + Write logging + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :param message: + :return: + """ + command = "logger {}".format(message) + st.config(dut, command) + return True + + +def check_unwanted_logs_in_logging(dut, user_filter=None): + """ + Check unwanted log based on uers filter list + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :param user_filter: + :return: + """ + result = True + if user_filter is None: + user_filter = [] + static_filter = ['error', 'i2c', 'fan', 'power'] + over_all_filter = static_filter + user_filter + for each_string in over_all_filter: + temp_count = get_logging_count(dut, filter_list=each_string) + st.log("{} - logs found on the error string '{}'".format(temp_count, each_string)) + if temp_count: + result = False + return result + + +def config_syslog_server(dut, ipaddress_list): + """ + Configure syslog servers. + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :param ipaddress_list: + :return: + """ + + ipaddress_li = list(ipaddress_list) if isinstance(ipaddress_list, list) else [ipaddress_list] + st.log("Adding syslog server(s)") + temp_local_data = {} + syslog_local_final = {} + for each_address in ipaddress_li: + temp_local_data[each_address] = {} + syslog_local_final['SYSLOG_SERVER'] = temp_local_data + syslog_local_final_json = json.dumps(syslog_local_final) + st.apply_json(dut, syslog_local_final_json) + return True + + +def get_syslog_server(dut): + """ + Get syslog servers. + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :return: + """ + + output = sc_obj.get_running_config(dut, 'SYSLOG_SERVER') + return output + + +def clear_syslog_from_remote_server(dut): + """ + Clear the logs from the syslog server + Author: Chaitanya Lohith Bollapragada (chaitanyalohith.bollapragada@broadcom.com) + + :param dut: + :return: + """ + syslog_ip = utils.ensure_service_params(dut, "syslog", "ip") + syslog_port = utils.ensure_service_params(dut, "syslog", "port") + syslog_username = utils.ensure_service_params(dut, "syslog", "username") + syslog_password = utils.ensure_service_params(dut, "syslog", "password") + syslog_path = utils.ensure_service_params(dut, "syslog", "path") + command = "sudo truncate -s 0 {}".format(syslog_path) + syslog_con_obj = conf_obj.connect_to_device(syslog_ip, syslog_username, syslog_password, port=syslog_port) + conf_obj.execute_command(syslog_con_obj, command) + return True + + +def get_syslog_from_remote_server(dut, severity=None, filter_list=None, lines=None): + """ + Get the logs from the syslog server + Author: Chaitanya Lohith Bollapragada (chaitanyalohith.bollapragada@broadcom.com) + + :param dut: + :param severity: + :param filter_list: + :param lines: + :return: + """ + + syslog_ip = utils.ensure_service_params(dut, "syslog", "ip") + syslog_port = utils.ensure_service_params(dut, "syslog", "port") + syslog_username = utils.ensure_service_params(dut, "syslog", "username") + syslog_password = utils.ensure_service_params(dut, "syslog", "password") + syslog_path = utils.ensure_service_params(dut, "syslog", "path") + + if filter_list is None: + filter_list = [] + filter_list = list(filter_list) if isinstance(filter_list, list) else [filter_list] + command = "cat {}".format(syslog_path) + if severity: + command += " | grep '{}'".format(severity) + for each_filter in filter_list: + command += " | grep '{}'".format(each_filter) + if lines: + command += "| tail -n {} ".format(lines) + syslog_con_obj = conf_obj.connect_to_device(syslog_ip, syslog_username, syslog_password, port=syslog_port) + syslog_file_contents = conf_obj.execute_command(syslog_con_obj, command) + return syslog_file_contents diff --git a/spytest/apis/system/ntp.py b/spytest/apis/system/ntp.py new file mode 100644 index 00000000000..acf1b9df7e8 --- /dev/null +++ b/spytest/apis/system/ntp.py @@ -0,0 +1,460 @@ +from spytest.utils import filter_and_select +from spytest import st +import json +from utilities.utils import ensure_service_params + + +def add_ntp_servers(dut, iplist=[]): + """ + + :param dut: + :param iplist: + :return: + """ + st.log("add ntp servers") + final_data = {} + temp_data = {} + if iplist: + for ip in iplist: + temp_data[ip] = {} + else: + st.log("please provide atleast 1 server to configure") + return False + final_data['NTP_SERVER'] = temp_data + final_data = json.dumps(final_data) + st.apply_json(dut, final_data) + st.log("Regenerate the ntp-config") + command = "systemctl restart ntp-config" + st.config(dut, command) + return True + + +def delete_ntp_servers(dut, iplist=[]): + """ + + :param dut: + :param iplist: + :return: + """ + return True + + +def enable_ntp(dut): + """ + + :param dut: + :return: + """ + st.log("enable ntp") + command = "sudo timedatectl set-ntp true" + st.config(dut, command) + return True + + +def disable_ntp(dut): + """ + + :param dut: + :return: + """ + st.log("disable ntp") + command = "sudo timedatectl set-ntp false" + st.config(dut, command) + return True + + +def enable_local_rtc(dut): + st.log("enable set-local-rtc") + command = "sudo timedatectl set-local-rtc true" + st.config(dut, command) + return True + + +def disable_local_rtc(dut): + """ + + :param dut: + :return: + """ + st.log("disable set-local-rtc") + command = "sudo timedatectl set-local-rtc false" + st.config(dut, command) + return True + + +def config_timezone(dut, zone): + """ + + :param dut: + :param zone: + :return: + """ + st.log("config timezone") + if zone: + command = "sudo timedatectl set-timezone {}".format(zone) + st.config(dut, command) + return True + else: + st.log("please provide zone name") + return False + + +def show_ntp_server(dut): + """ + + :param dut: + :return: + """ + st.log("show ntp servers") + command = "show ntp" + output = st.show(dut, command) + return output + +def verify_ntp_server_details(dut, server_ip=None, **kwargs): + output = show_ntp_server(dut) + flag = 1 + if server_ip is None: + if "No association ID's returned" in output: + return True + else: + return False + else: + server_ips = [server_ip] if type(server_ip) is str else list([str(e) for e in server_ip]) + data = kwargs + for ent in output: + remote_ip = ent["remote"].strip("+*#o-x").strip() + if remote_ip in server_ips: + if 'remote' in data and remote_ip not in data['remote']: + st.log("Remote Server IP is not matching") + flag = 0 + if 'refid' in data and ent["refid"] != data["refid"]: + st.log("Ref ID is not matching") + flag = 0 + if 'st' in data and ent["st"] != data["st"]: + st.log("Stratum value is not matching") + flag = 0 + if 't' in data and ent["t"] != data["t"]: + st.log("Type is not matching") + flag = 0 + if 'when' in data and ent["when"] != data["when"]: + st.log("Polling value is not matching") + flag = 0 + if 'poll' in data and ent["poll"] != data["poll"]: + st.log("Polling in seconds is not matching") + flag = 0 + if 'reach' in data and ent["reach"] != data["reach"]: + st.log("Reach is not matching") + flag = 0 + if 'delay' in data and ent["delay"] != data["delay"]: + st.log("Delay is not matching") + flag = 0 + if 'offset' in data and ent["offset"] != data["offset"]: + st.log("Offset value is not matching") + flag = 0 + if 'jitter' in data and ent["jitter"] != data["jitter"]: + st.log("Jitter value is not matching") + flag = 0 + else: + st.log("Server IP is not matching") + flag = 0 + if flag: + st.log("Server IP's matched.") + return True + else: + st.log("Server IP's not matched.") + return False + +def show_ntp_status(dut,mvrf=False): + """ + + :param dut: + :return: + """ + st.log("show ntp status") + if mvrf: + command = "sudo cgexec -g l3mdev:mgmt ntpstat" + else: + command = "ntpstat" + output = st.show(dut, command) + retval = [] + entries = filter_and_select(output, ["server", "stratum", "time", "poll"]) + for ent in entries: + retval.append(ent["server"].strip("()")) + retval.append(ent["stratum"]) + retval.append(ent["time"]) + retval.append(ent["poll"]) + return retval + + +def config_date(dut, date): + """ + + :param dut: + :param date: + :return: + """ + st.log("config date") + command = "date --set='{}'".format(date) + st.config(dut, command) + return True + +def set_date_ntp(dut): + """ + + :param dut: + :param date: + :return: + """ + st.log("set date using ntpd") + command = "sudo /usr/sbin/ntpd -q -g -x &" + st.config(dut, command) + return True + + +def show_timedatectl_status(dut): + """ + + :param dut: + :return: + """ + st.log("timedatectl status") + command = "timedatectl status" + output = st.show(dut, command) + return output + + +def show_clock(dut): + """ + + :param dut: + :return: + """ + st.log("show clock") + command = "show clock" + output = st.show(dut, command) + return output[0] + + +def verify_clock(dut, time): + """ + + :param dut: + :param time: + :return: + """ + st.log("verifying show clock") + retval = show_clock(dut) + if retval == time: + return True + else: + return False + + +def verify_timedatectl(dut, **kwargs): + """ + + :param dut: + :param kwargs: + :return: + """ + st.log("verifying timedatectl") + retval = show_timedatectl_status(dut) + flag = 1 + data = kwargs + if not data: + st.error("Please provide details to be verified.") + return False + else: + if 'rtctime' in data: + if retval[0]['rtctime'] != data['rtctime']: + flag = 0 + if 'universaltime' in data: + if retval[0]['universaltime'] != data['universaltime']: + flag = 0 + if 'networktimeon' in data: + if retval[0]['networktimeon'] != data['networktimeon']: + flag = 0 + if 'ntpsynchronized' in data: + if retval[0]['ntpsynchronized'] != data['ntpsynchronized']: + flag = 0 + if 'timezone' in data: + if retval[0]['timezone'] != data['timezone']: + flag = 0 + if 'localtime' in data: + if retval[0]['localtime'] != data['localtime']: + flag = 0 + if flag: + return True + else: + return False + + +def verify_ntp_status(dut, iteration=1, delay=1, mvrf=False, **kwargs): + """ + Verify NTP status with polling. + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param server: single or list of servers. + :param stratum: + :param time: + :param poll: + :param iteration: 1 sec (default) + :param delay: 1 sec (default) + :return: + """ + st.log("verifying ntp status") + i = 0 + if not kwargs: + st.error("Please provide details to be verified.") + return False + else: + while True: + flag = 0 + retval = show_ntp_status(dut,mvrf) + if not retval: + st.log("No o/p from ntpstat command") + if i > iteration: + st.log("NTP status failed.") + st.log("Max iterations {} reached".format(i)) + return False + i += 1 + st.wait(delay) + continue + if 'server' in kwargs: + server_li = list(kwargs['server']) if isinstance(kwargs['server'], list) else [kwargs['server']] + if retval[0] in server_li: + st.log("Detected NTP server - {}".format(retval[0])) + flag += 1 + if 'stratum' in kwargs: + if retval[1] == kwargs['stratum']: + flag += 1 + if 'time' in kwargs: + if retval[2] == kwargs['time']: + flag += 1 + if 'poll' in kwargs: + if retval[3] == kwargs['poll']: + flag += 1 + if flag == len(kwargs): + return True + if i > iteration: + st.log("NTP status failed.") + st.log("Max iterations {} reached".format(i)) + return False + i += 1 + st.wait(delay) + + +def verify_ntp_server(dut, serverip, **kwargs): + """ + + :param dut: + :param serverip: + :param kwargs: + :return: + """ + st.log("verifying ntp server") + flag = 1 + data = kwargs + if not data or not serverip: + st.error("Please provide details to be verified.") + return False + else: + retval = show_ntp_server(dut) + if not retval: + return False + else: + if 'remote' in data: + if retval[0] != data['remote']: + flag = 0 + if 'refid' in data: + if retval[1] != data['refid']: + flag = 0 + if 'st' in data: + if retval[2] != data['st']: + flag = 0 + if 't' in data: + if retval[3] != data['t']: + flag = 0 + if 'when' in data: + if retval[4] != data['when']: + flag = 0 + if 'poll' in data: + if retval[5] != data['poll']: + flag = 0 + if 'reach' in data: + if retval[6] != data['reach']: + flag = 0 + if 'delay' in data: + if retval[7] != data['delay']: + flag = 0 + if 'offset' in data: + if retval[8] != data['offset']: + flag = 0 + if 'jitter' in data: + if retval[9] != data['jitter']: + flag = 0 + if flag: + return True + else: + return False + + +def verify_ntp_service_status(dut, status, iteration=1, delay=1): + """ + Verify NTP service status with polling + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :param status: + :param iteration: 1 sec (default) + :param delay: 1 sec (default) + :return: + """ + command = "service ntp status | grep Active" + i = 1 + while True: + output = st.config(dut, command) + if status in output: + st.log("NTP service status is '{}' iteration".format(i)) + return True + if i > iteration: + st.log("NTP service status is not '{}'") + st.log("Max iterations {} reached".format(i)) + return False + i += 1 + st.wait(delay) + +def verify_ntp_server_exists(dut, server_ip=None, **kwargs): + output = show_ntp_server(dut) + flag = 1 + if server_ip is None: + if "No association ID's returned" in output: + return True + else: + return False + else: + server_ips = [server_ip] if type(server_ip) is str else list([str(e) for e in server_ip]) + data = kwargs + for ent in output: + remote_ip = ent["remote"].strip("+*#o-x").strip() + if remote_ip in server_ips: + if 'remote' in data and remote_ip not in data['remote']: + st.log("Remote Server IP is not matching") + return False + else: + return True + + +def ensure_ntp_config(dut,iplist=[]): + if not iplist: + iplist = ensure_service_params(dut, "ntp", "default") + if not iplist: + st.log("NTP server IPs missing") + return False + commands = [] + for ip in iplist: + if not verify_ntp_server_exists(dut, ip, remote=ip): + commands.append("config ntp add {}".format(ip)) + st.config(dut, commands) + return True + diff --git a/spytest/apis/system/port.py b/spytest/apis/system/port.py new file mode 100644 index 00000000000..cd797a0f2cf --- /dev/null +++ b/spytest/apis/system/port.py @@ -0,0 +1,187 @@ +from spytest.utils import filter_and_select + +from spytest import st + +def set_status(dut, portlist, status): + """ + + :param dut: + :type dut: + :param portlist: + :type portlist: + :param status: "shutdown" or "startup" + :type status: string + :return: + :rtype: + """ + + if '-' in portlist: + st.config(dut, "config interface {} {}".format(status, portlist)) + return + + if not st.is_community_build(): + try: + port = ",".join(portlist) + return st.config(dut, "config interface {} {}".format(status, port)) + except Exception as exp: + st.warn("Failed to execute {} command - try alternative".format(status)) + + for port in portlist: + try: + st.config(dut, "config interface {} {}".format(status, port)) + except ValueError as ex: + st.warn("Failed to execute {} command - try alternative".format(status)) + st.config(dut, "config interface {} {}".format(port, status)) + return "" + +def shutdown(dut, portlist): + """ + + :param dut: + :type dut: + :param portlist: + :type portlist: + :return: + :rtype: + """ + set_status(dut, portlist, "shutdown") + +def noshutdown(dut, portlist): + """ + + :param dut: + :type dut: + :param portlist: + :type portlist: + :return: + :rtype: + """ + set_status(dut, portlist, "startup") + +def get_status(dut, port=None): + """ + + :param dut: + :type dut: + :param port: + :type port: + :return: + :rtype: + """ + + if not port: + return st.show(dut, "show interfaces status") + + # no range support in community build + if st.is_community_build(): + if "," in port or "-" in port: + return st.show(dut, "show interfaces status") + + # port could be range switch to all when failed + try: + return st.show(dut, "show interfaces status {}".format(port)) + except ValueError as ex: + st.warn("Failed to use interface command - try global") + + return st.show(dut, "show interfaces status") + +def get_interfaces_by_status(dut, status): + """ + + :param dut: + :type dut: + :param status: + :type status: + :return: + :rtype: + """ + output = get_status(dut, None) + retval = [] + match = {"oper": status} if status else None + entries = filter_and_select(output, ["interface"], match) + for ent in entries: + retval.append(ent["interface"]) + return retval + + +def get_interfaces_up(dut): + """ + + :param dut: + :type dut: + :return: + :rtype: + """ + return get_interfaces_by_status(dut, "up") + + +def get_interfaces_down(dut): + """ + + :param dut: + :type dut: + :return: + :rtype: + """ + return get_interfaces_by_status(dut, "down") + + +def get_interfaces_all(dut): + """ + + :param dut: + :type dut: + :return: + :rtype: + """ + return get_interfaces_by_status(dut, None) + + +def get_interface_status(dut, port): + """ + + :param dut: + :type dut: + :param port: + :type port: + :return: + :rtype: + """ + output = get_status(dut, port) + match = {"interface": port} + entries = filter_and_select(output, ["oper"], match) + for ent in entries: + return ent["oper"] + return None + + +def verify_oper_state(dut, port, state): + """ + + :param dut: + :type dut: + :param port: + :type port: + :param state: + :type state: + :return: + :rtype: + """ + if get_interface_status(dut, port) != state: + return False + return True + +def get_interface_counters_all(dut): + return st.show(dut, "show interfaces counters -a") + +def clear_interface_counters(dut): + if st.is_community_build(): + return st.config(dut, "sonic-clear counters") + else: + return st.show(dut, "show interfaces counters -c") + +def get_interface_counters(dut, port, *counter): + output = get_interface_counters_all(dut) + entries = filter_and_select(output, counter, {'iface': port}) + return entries + diff --git a/spytest/apis/system/reboot.py b/spytest/apis/system/reboot.py new file mode 100644 index 00000000000..8bc1242ad9b --- /dev/null +++ b/spytest/apis/system/reboot.py @@ -0,0 +1,165 @@ +# This file contains the list of API's which performs config and reboot operation. +# Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) +from spytest import st, utils + +def config_save(dut,shell='sonic', skip_error_check=True): + """ + To perform config save. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: single or list of duts + :return: + """ + dut_li = list(dut) if isinstance(dut, list) else [dut] + st.log("Performing config save") + if shell == 'sonic': + command = 'config save -y' + [retvals, exceps] = utils.exec_foreach(True, dut_li, st.config, command) + elif shell == "vtysh": + command = 'do copy running-config startup-config' + [retvals, exceps] = utils.exec_foreach(True, dut_li, st.config, command, type=shell, skip_error_check=skip_error_check) + else: + command = "do write memory" + [retvals, exceps] = utils.exec_foreach(True, dut_li, st.config, command, type=shell, skip_error_check=skip_error_check) + st.debug([retvals, exceps]) + return True + + +def config_reload(dut): + """ + To perform config reload. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: single or list of duts + :return: + """ + st.log("Performing config reload") + dut_li = list(dut) if isinstance(dut, list) else [dut] + [retvals, exceps] = utils.exec_foreach(True, dut_li, st.config_db_reload) + st.debug([retvals, exceps]) + return True + + +def config_save_reload(dut): + """ + To perform config save and reload. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: single or list of duts + :return: + """ + st.log("Performing config save and reload") + dut_li = list(dut) if isinstance(dut, list) else [dut] + [retvals, exceps] = utils.exec_foreach(True, dut_li, st.config_db_reload, True) + st.debug([retvals, exceps]) + return True + + +def get_reboot_cause(dut): + """ + To get reboot cause. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :return: + """ + command = "show reboot-cause" + return st.show(dut, command) + + +def config_warm_restart(dut, **kwargs): + """ + Config Warm Restart operation state and parameters. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :param oper: enable | disable + :param tasks: single task name | list of task name + :param bgp_timer: + :param neighsyncd_timer: + :param teamsyncd_timer: + :return: + + config_warm_restart,oper="enable") + config_warm_restart,oper="enable",tasks="bgp") + config_warm_restart,oper="enable", bgp_timer=120) + config_warm_restart,oper="enable",tasks="bgp",bgp_timer=120,neighsyncd_timer=60,teamsyncd_timer=60) + config_warm_restart,oper="enable",tasks=["bgp","system","teamd"], bgp_timer=120,neighsyncd_timer=60,teamsyncd_timer=60) + + """ + if "oper" in kwargs and 'tasks' not in kwargs: + command = "config warm_restart {}".format(kwargs['oper']) + st.config(dut, command) + if "oper" in kwargs and "tasks" in kwargs: + task_list = list(kwargs['tasks']) if isinstance(kwargs['tasks'], list) else [kwargs['tasks']] + for each_task in task_list: + command = "config warm_restart {} {}".format(kwargs['oper'], each_task) + st.config(dut, command) + if "bgp_timer" in kwargs: + command = "config warm_restart bgp_timer {}".format(kwargs['bgp_timer']) + st.config(dut, command) + if "neighsyncd_timer" in kwargs: + command = "config warm_restart neighsyncd_timer {}".format(kwargs['neighsyncd_timer']) + st.config(dut, command) + if "teamsyncd_timer" in kwargs: + command = "config warm_restart teamsyncd_timer {}".format(kwargs['teamsyncd_timer']) + st.config(dut, command) + return True + + +def verify_warm_restart(dut, **kwargs): + """ + To verify warm restart state and config + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :param mode: config | state + :param name: + :param restore_count: + :param state: + :param enable: + :param timer_name: + :param timer_duration: + :return: + """ + + if 'mode' not in kwargs: + st.error("mode is not passed as argument to API.") + return False + if kwargs['mode'] in ['config', 'state']: + command = "show warm_restart {}".format(kwargs['mode']) + else: + st.error("Invalid mode provided, supported - mode or config") + return False + del kwargs['mode'] + output = st.show(dut, command, type="click") + st.debug(output) + + entries = utils.filter_and_select(output, None, kwargs) + if not entries: + return False + + return True + + +def poll_for_warm_restart_status(dut, pname, state, iteration=20, delay=2): + """ + To verify warm restart state poll. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param pname: + :param state: + :param iteration: + :param delay: + :return: + """ + itercount = 1 + while True: + if verify_warm_restart(dut, mode='state', name=pname, state=state): + return True + if itercount > iteration: + st.error("For warm restart {}:{} status verification max iteration count {} reached".format(pname, state, + itercount)) + return False + itercount += delay + st.wait(delay) diff --git a/spytest/apis/system/rest.py b/spytest/apis/system/rest.py new file mode 100644 index 00000000000..422a23a5ac7 --- /dev/null +++ b/spytest/apis/system/rest.py @@ -0,0 +1,228 @@ +# This file contains the list of API's which performs REST operations. +# Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + +import json +import requests +import warnings +from spytest import st +from apis.system.basic import service_operations_by_systemctl + +# disable warnings from SSL/TLS certificates +requests.packages.urllib3.disable_warnings() + +global request_id +request_id = 1 + + +def send_rest_request(dut, feature, method, parms_data, timeout=30, port=8361): + """ + Construct URL for rest request and send to device + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + Ex:- http://10.130.84.46:80/broadview/bst/clear-bst-thresholds + + :param dut: + :param feature: + :param method: + :param parms_data: + :param timeout: (Default 30sec) + :param port: (Default 8361) + :return: + """ + global request_id + device_ip = st.get_mgmt_ip(dut) + url = 'http://{}:{}/broadview/{}/{}'.format(device_ip, port, feature, method) + st.log("URL: {}".format(url)) + + json_data = '{"jsonrpc": "2.0", "method": "' + method + '", "asic-id": "0","params": ' + \ + json.dumps(parms_data) + ',"id": ' + str(request_id) + '}' + request_id += 1 + st.log("JSON Data: {}".format(json_data)) + + try: + response = requests.post(url, data=json_data, timeout=timeout) + except requests.ConnectionError: + st.error("A Connection error occurred.") + return False + except requests.Timeout: + st.error("The request timed out.") + return False + + st.log("Response code : {}".format(response.status_code)) + if response.status_code != 200: + st.log("Error: Response : {}".format(response)) + st.log("Error: Response.text : {}".format(response.text)) + return False + return response + + +def client_auth(dut, **kwargs): + """ + To enable disable REST client auth. + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param kwargs: + :return: + """ + st.log("Configuring REST authentication.") + docer_name = "mgmt-framework" + show_command = [] + if 'auth_type' in kwargs: + show_command.append('sonic-cfggen -d -v "REST_SERVER"') + if kwargs.get('auth_type'): + command = 'redis-cli -n 4 hmset "REST_SERVER|default" client_auth "{}"'.format(kwargs.get('auth_type')) + else: + command = 'redis-cli -n 4 hdel "REST_SERVER|default" client_auth' + st.config(dut, command) + if 'ca_crt' in kwargs: + show_command.append('redis-cli -n 4 hgetall "DEVICE_METADATA|x509"') + if kwargs.get('ca_crt'): + command = 'redis-cli -n 4 hmset "DEVICE_METADATA|x509" ca_crt {}'.format(kwargs.get('ca_crt')) + else: + command = 'redis-cli -n 4 hdel "DEVICE_METADATA|x509" ca_crt' + st.config(dut, command) + service_operations_by_systemctl(dut, docer_name, 'stop') + service_operations_by_systemctl(dut, docer_name, 'start') + st.config(dut, show_command) + return True + + +def rest_call(dut, **kwargs): + """ + Rest call to perform GET, POST, PUT operation with auth and JWT token. + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param kwargs: + :return: + + Usage: + + # User based calls + rest_call(dut, headers=headers1, username='test', password='pass', + url='restconf/data/sonic-port:sonic-port/PORT/PORT_LIST=Ethernet0/admin_status', call_type='get') + rest_call(dut, headers=headers1, username='test', password='pass', + url='restconf/data/sonic-port:sonic-port/PORT/PORT_LIST=Ethernet0/admin_status', call_type='put', + data={"sonic-port:admin_status":"down"}) + rest_call(dut, headers=headers1, username='test', password='pass', + url='restconf/data/sonic-port:sonic-port/PORT/PORT_LIST=Ethernet0/admin_status', call_type='get') + + # Token based calls + rest_call(dut, headers=headers2, + url='restconf/data/sonic-port:sonic-port/PORT/PORT_LIST=Ethernet0/admin_status', call_type='get') + rest_call(dut, headers=headers2, + url='restconf/data/sonic-port:sonic-port/PORT/PORT_LIST=Ethernet0/admin_status', call_type='put', + data={"sonic-port:admin_status":"up"}) + rest_call(dut, headers=headers2, + url='restconf/data/sonic-port:sonic-port/PORT/PORT_LIST=Ethernet0/admin_status', call_type='get') + """ + device_ip = st.get_mgmt_ip(dut) + call_type = kwargs.get('call_type', 'get') + port = kwargs.get('port', '443') + url = kwargs.get('url') + headers = kwargs.get('headers') + data = kwargs.get('data') + username = kwargs.get('username') + password = kwargs.get('password') + timeout = kwargs.get('timeout', 30) + cert = kwargs.get('cert') + verify = kwargs.get('verify', False) + + if port: + final_url = "https://{}:{}/{}".format(device_ip, port, url) + else: + final_url = "https://{}/{}".format(device_ip, url) + st.log("{} - URL : {}".format(call_type.upper(), final_url)) + + call_data = {'verify': verify, 'timeout': timeout} + if data: + if isinstance(data, dict): + call_data['data'] = json.dumps(data) + else: + call_data['data'] = data + if username and password: + call_data['auth'] = (username, password) + if headers: + call_data['headers'] = headers + if cert: + call_data['cert'] = cert + + st.log("Call Data : {}".format(call_data)) + + # response type + warnings.filterwarnings('ignore', message='Unverified HTTPS request') + try: + if call_type == 'put': + response = requests.put(final_url, **call_data) + elif call_type == 'post': + response = requests.post(final_url, **call_data) + elif call_type == 'patch': + response = requests.patch(final_url, **call_data) + elif call_type == 'delete': + response = requests.delete(final_url, **call_data) + else: + response = requests.get(final_url, **call_data) + except requests.ConnectionError: + st.error("A Connection error occurred.") + return False + except requests.Timeout: + st.error("The request timed out.") + return False + except Exception as e: + st.error(e) + return False + st.log("Response Code: {}, Text: {}".format(response.status_code, response.text)) + return {"status": response.status_code, "output": response.text} + + +def get_jwt_token(dut, **kwargs): + """ + To get JWT token using REST call. + Author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param kwargs: + :return: + + Usage: + get_jwt_token(dut, username='test', password='pass') + """ + username = kwargs.get('username') + password = kwargs.get('password') + url = kwargs.get('url', 'authenticate') + headers = kwargs.get('headers', {'Accept': 'application/json'}) + data = {'username': username, 'password': password} + out = rest_call(dut, data=data, headers=headers, url=url, call_type='post') + if not out: + return None + if out.get('status') not in [200]: + return None + token_dic = eval(out['output']) + return token_dic.get('access_token', None) + + +def rest_status(status): + """ + To give the response as per status code + Author: Ramprakash Reddy (ramprakash-reddy.kanala@broadcom.com) + :param status: + :return: + """ + if status in [200, 201, 204]: + st.log("Rest operation successful") + return True + else: + if status == 400: + st.log("Bad Request") + elif status == 401: + st.log("Unauthorized") + elif status == 403: + st.log("Forbidden") + elif status == 404: + st.log("Page not found") + elif status == 405: + st.log("Method not allowed") + elif status == 409: + st.log("Conflict") + elif status == 415: + st.log("Unsupported Media Type") + else: + st.log("Internal Server Error") + return False diff --git a/spytest/apis/system/sflow.py b/spytest/apis/system/sflow.py new file mode 100644 index 00000000000..3f877276168 --- /dev/null +++ b/spytest/apis/system/sflow.py @@ -0,0 +1,462 @@ +# This file contains the list of API's which performs SFLOW operations. +# @author : Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + +from spytest import st +import utilities.utils as utils_obj +import utilities.common as common_utils +from spytest.utils import filter_and_select +from apis.routing.ip import get_interface_ip_address + +YANG_MODULE = "sonic-sflow:sonic-sflow" +REST_URI = "/restconf/data/{}".format(YANG_MODULE) +DEFAULT_COLLECTOR_PORT = 6343 +def add_del_collector(dut, collector_name, ip_address=None, port_number=None, action="add", cli_type="klish",skip_error_check=False): + """ + API to add/del SFLOW collector + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param collector_name: + :param ip_address: IPV4 / IPV6 address, this is optional for del operations + :param port_number: None, this is optional for del operations + :param action: add / del + :return: True / False + """ + command = None + if action == "add": + if ip_address: + if cli_type == "click": + command = "config sflow collector add {} {} --port {}".format(collector_name, ip_address, port_number) if port_number else \ + "config sflow collector add {} {}".format(collector_name, ip_address) + elif cli_type == "klish": + command = "sflow collector {} {} {}".format(collector_name, ip_address, port_number) if port_number else \ + "sflow collector {} {}".format(collector_name, ip_address) + elif cli_type == "rest": + data = dict() + data["sonic-sflow:SFLOW_COLLECTOR"] = dict() + data["sonic-sflow:SFLOW_COLLECTOR"]["sonic-sflow:SFLOW_COLLECTOR_LIST"] = list() + collector_data = dict() + collector_data["collector_name"] = collector_name + collector_data["collector_ip"] = ip_address + collector_data["collector_port"] = int(port_number) if port_number else DEFAULT_COLLECTOR_PORT + data["sonic-sflow:SFLOW_COLLECTOR"]["sonic-sflow:SFLOW_COLLECTOR_LIST"].append(collector_data) + json_data = data + url = "{}/SFLOW_COLLECTOR".format(REST_URI) + output = st.rest_modify(dut,url,json_data) + st.log("ADD / DEL COLLECTOR AT INTF level -- {}".format(output)) + if output and output["status"] != 204: + return False + return True + else: + st.log("IP ADDRESS not provided for add operation ..") + return False + elif action == "del": + if cli_type == "click": + command = "config sflow collector del {}".format(collector_name) + elif cli_type == "klish": + command = "no sflow collector {}".format(collector_name) + elif cli_type == "rest": + url = "{}/SFLOW_COLLECTOR".format(REST_URI) + output = st.rest_delete(dut, url, SFLOW_COLLECTOR_LIST=collector_name) + st.log("ADD / DEL COLLECTOR AT INTF level -- {}".format(output)) + if output and output["status"] != 204: + return False + return True + if cli_type != "rest" and command and utils_obj.ensure_cli_type(cli_type, ["click","klish"]): + output = st.config(dut, command, type=cli_type, skip_error_check=skip_error_check) + return output + return True + + +def add_del_agent_id(dut, interface_name=None, action="add", cli_type="klish", sflow_list="global", skip_error_check=False): + """ + API to add/del SFLOW AGENT ID + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param interface_name: + :param action: add / del + :return: True / False + """ + if action not in ["add", "del"]: + st.log("Unsupported action {}..".format(action)) + return False + if cli_type == "click": + if action != "add": + command = "config sflow agent-id {}".format(action) + else: + if not interface_name: + st.log("Interface name -- {} not provided ".format(interface_name)) + return False + command = "config sflow agent-id {} {}".format(action, interface_name) + elif cli_type == "klish": + if action != "add": + command = "no sflow agent-id" + else: + command = "sflow agent-id {}".format(interface_name) + elif cli_type == "rest": + url = "{}/SFLOW/SFLOW_LIST={}/agent_id".format(REST_URI, sflow_list) + if action == "add": + data = {"sonic-sflow:agent_id":interface_name} + output = st.rest_modify(dut, url, data) + st.log("REST del agent_id OUTPUT -- {}".format(output)) + if output and output["status"] != 204: + return False + else: + output = st.rest_delete(dut, url) + st.log("REST del agent_id OUTPUT -- {}".format(output)) + if output and output["status"] != 204: + return False + return True + if utils_obj.ensure_cli_type(cli_type, ["click","klish"]) and command: + output = st.config(dut, command, type=cli_type, skip_error_check=skip_error_check) + return output + return True + +def enable_disable_config(dut, interface=False, interface_name=None, action="enable", cli_type="klish", sflow_key="global"): + """ + API to enable / disable SFLOW Globally / on interface level + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param interface: + :param interface_name: + :param action: + :return: + """ + if action not in ["enable", "disable"]: + st.log("Unsupported action {} ".format(action)) + return False + if interface and interface_name: + commands = list() + if cli_type == "click": + command = "config sflow interface {} {}".format(action, interface_name) + commands.append(command) + elif cli_type=="klish": + interface_details = utils_obj.get_interface_number_from_name(interface_name) + if not interface_details: + st.log("Interface details not found {}".format(interface_details)) + return False + commands.append("interface {} {}".format(interface_details.get("type"), interface_details.get("number"))) + if action == "enable": + command = "sflow {}".format(action) + else: + command = "no sflow enable" + commands.append(command) + elif cli_type=="rest": + session_list = dict() + session_list["sonic-sflow:SFLOW_SESSION_LIST"] = list() + session_data = dict() + session_data["ifname"] = interface_name + session_data["admin_state"] = "up" if action == "enable" else "down" + session_list["sonic-sflow:SFLOW_SESSION_LIST"].append(session_data) + url = "{}/SFLOW_SESSION".format(REST_URI) + output = st.rest_modify(dut, url, session_list,SFLOW_SESSION_LIST=interface_name) + st.log("ENABLE / DISABLE SFLOW AT INTF level -- {}".format(output)) + if output and output["status"] != 204: + return False + return True + if commands: + st.config(dut, commands, type=cli_type) + else: + if cli_type == "click": + command = "config sflow {}".format(action) + elif cli_type == "klish": + if action != "enable": + command = "no sflow enable" + else: + command = "sflow enable" + elif cli_type == "rest": + data={"sonic-sflow:admin_state":"up" if action == "enable" else "down"} + url = "{}/SFLOW/SFLOW_LIST={}/admin_state".format(REST_URI, sflow_key) + output = st.rest_modify(dut, url, data) + st.log("ENABLE / DISABLE SFLOW AT GLOBAL level -- {}".format(output)) + if output and output["status"] != 204: + return False + return True + if command: + st.config(dut, command, type=cli_type) + return True + +def config_attributes(dut, **kwargs): + """ + Common API to configure sflow sample rate on interface, polling interval and sample rate per speed. + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param kwargs: + :return: True + NOTE: + 1) To configure interface sample rate, + config_sflow_attributes(dut, sample_rate=100, interface_name="Ethernet10") + 2) To configure polling interval + config_sflow_attributes(dut, polling_interval=20) + 3) To configure sample rate per speed + config_sflow_attributes(dut, speed=10G, sample_rate=10000) + """ + cli_type = kwargs["cli_type"] if kwargs.has_key("cli_type") else "klish" + sflow_key = kwargs.get("sflow_key", "global") + command = "" + commands = list() + if "sample_rate" in kwargs and "interface_name" in kwargs: + if cli_type == "click": + command += "config sflow interface sample-rate {} {};".format(kwargs["interface_name"], kwargs["sample_rate"]) + commands.append(command) + elif cli_type == "klish": + interface_details = utils_obj.get_interface_number_from_name(kwargs["interface_name"]) + if not interface_details: + st.log("Interface details not found {}".format(interface_details)) + return False + commands.append("interface {} {}".format(interface_details.get("type"), interface_details.get("number"))) + if kwargs.has_key("no_form"): + command = "no sflow sampling-rate" + else: + command = "sflow sampling-rate {}".format(kwargs["sample_rate"]) + commands.append(command) + commands.append("exit") + elif cli_type == "rest": + data = {"sonic-sflow:sample_rate":int(kwargs["sample_rate"])} + url = "{}/SFLOW_SESSION/SFLOW_SESSION_LIST={}/sample_rate".format(REST_URI, kwargs["interface_name"]) + output = st.rest_modify(dut, url, data) + st.log("REST config_attributes SAMPLE RATE OUTPUT -- {}".format(output)) + if output and output["status"] != 204: + return False + return True + st.config(dut, commands, type=cli_type) + if "polling_interval" in kwargs: + if cli_type == "click": + command += "config sflow polling-interval {};".format(kwargs["polling_interval"]) + commands.append(command) + elif cli_type == "klish": + if kwargs.has_key("no_form"): + command = "no sflow polling-interval" + else: + command = "sflow polling-interval {}".format(kwargs["polling_interval"]) + commands.append(command) + elif cli_type == "rest": + data = {"sonic-sflow:polling_interval":int(kwargs["polling_interval"])} + url = "{}/SFLOW/SFLOW_LIST={}/polling_interval".format(REST_URI, sflow_key) + output = st.rest_modify(dut, url, data) + st.log("REST config_attributes POLLING RATE OUTPUT -- {}".format(output)) + if output and output["status"] != 204: + return False + return True + st.config(dut, commands, type=cli_type) + return True + +def show(dut, cli_type="klish"): + """ + API to show sflow configuration + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :return: {u'agent_ip': '10.0.0.10', 'collectors': [{'port': '6343', 'collector_ip': '10.100.12.13'}, + {'port': '6344', 'collector_ip': '10.144.1.2'}], u'collectors_cnt': '2', + u'state': 'enabled', u'agent_id': 'loopback0', u'polling_interval': '20'} + """ + result = dict() + if cli_type == "klish": + command = "show sflow" + output = st.show(dut, command, type=cli_type) + if output: + result["collectors"] = list() + for data in output: + for key, value in data.items(): + if value != "": + if key not in ["collector_ip", "collector_port", "collector_name"]: + result[key] = value + else: + result["collectors"].append( + {"collector_name": data["collector_name"], + "collector_ip": data["collector_ip"], "port": data["collector_port"]}) + if result: + result["collectors"] = utils_obj.remove_duplicate_dicts_from_list(result["collectors"]) + else: + return False + elif cli_type == "rest": + output = st.rest_read(dut, REST_URI) + if output and output.get("status") == 200 and output.get("output"): + if YANG_MODULE in output["output"]: + data = output["output"][YANG_MODULE] + if "SFLOW" in data: + for key, value in data["SFLOW"].items(): + if isinstance(value, list): + for attributes in value: + result.update({"state": attributes.get("admin_state")}) + result.update({"agent_id": attributes.get("agent_id")}) + result.update({"polling_interval": attributes.get("polling_interval")}) + result.update({"sflow_key": attributes.get("sflow_key")}) + if attributes.get("agent_id"): + ip_address = get_interface_ip_address(dut, attributes.get("agent_id")) + if ip_address: + ip, subnet = ip_address[0]['ipaddr'].split('/') + result.update({"agent_ip": ip}) + if "SFLOW_COLLECTOR" in data: + result.update({"collectors_cnt": len(data["SFLOW_COLLECTOR"]["SFLOW_COLLECTOR_LIST"])}) + result.update({"collectors":list()}) + for value in data["SFLOW_COLLECTOR"]["SFLOW_COLLECTOR_LIST"]: + collector_data = dict() + collector_data.update({"port":value.get("collector_port", DEFAULT_COLLECTOR_PORT)}) + collector_data.update({"collector_ip":value.get("collector_ip")}) + collector_data.update({"collector_name":value.get("collector_name")}) + st.log("COLLECTORS {}".format(collector_data)) + result["collectors"].append(collector_data) + else: + st.log("{} not observed in ouput".format(YANG_MODULE)) + else: + st.log("REST show GET CALL --- {}".format(output)) + return result + +def show_interface(dut, interface_name = None, cli_type="klish"): + """ + API to show sflow interface configuration + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :return: + """ + output = list() + if cli_type == "klish": + command = "show sflow interface" + if interface_name: + command = "{} | grep {}".format(command, interface_name) + return st.show(dut, command, type=cli_type) + elif cli_type == "rest": + if not interface_name: + url = REST_URI + else: + url = "{}/SFLOW_SESSION/SFLOW_SESSION_TABLE".format(REST_URI) + result = st.rest_read(dut, url, SFLOW_SESSION_LIST=interface_name) + if result and result.get("status") == 200 and result.get("output"): + if YANG_MODULE in output["output"]: + data = result["output"][YANG_MODULE] + if data.get("SFLOW_SESSION_TABLE").get("SFLOW_SESSION_LIST"): + for intf_list in data.get("SFLOW_SESSION_TABLE").get("SFLOW_SESSION_LIST"): + response = dict() + response["sampling_rate"] = intf_list.get("sample_rate") + response["admin_status"] = intf_list.get("admin_state") + response["interface"] = intf_list.get("ifname") + if response: + output.append(response) + else: + st.log("{} not observed in ouput".format(YANG_MODULE)) + else: + st.log("REST show INTERFACE GET CALL --- {}".format(output)) + return output + else: + st.log("UNSUPPORTED CLI TYPE {}".format(cli_type)) + return False + +def verify_config(dut, **kwargs): + """ + API to verify sflow configuration + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param kwargs: {"data" : [{'collector_ip': '1.1.1.1', 'polling_interval': '0', + 'collectors_cnt': '2', 'state': 'enable', 'agent_id': 'Ethernet6', 'port': '6343'}, + {'port': '6343', 'collector_ip': '192.168.4.4'}], "cli_type": "click"} + :return: True / False + """ + st.log("KWARGS -- {}".format(kwargs)) + cli_type = kwargs["cli_type"] if kwargs.has_key("cli_type") else "klish" + output = show(dut, cli_type) + st.log("OUTPUT === {}".format(output)) + supported_params = ["state", "polling_interval", "collector_name", "collectors_cnt", "collector_ip", "port", + "agent_id"] + if output: + if not kwargs.get("data"): + st.log("DATA NOT PROVIDED ...") + return False + verify_data = kwargs.get("data") if isinstance(kwargs.get("data"), list) else [kwargs.get("data")] + for data in verify_data: + for key, value in data.items(): + if key not in supported_params: + st.log("Unsupported params {}".format(key)) + return False + if key not in ["collector_name", "collector_ip", "port"]: + if str(data[key]) != str(output[key]): + st.log("Verification failed for {} with {}, hence checking other values ...".format(data[key], output[key])) + return False + else: + is_found = 0 + for collector_data in output["collectors"]: + if str(data[key]) != str(collector_data[key]): + is_found = 1 + st.log("Verification failed for {} with {}".format(data[key], collector_data[key])) + else: + is_found = 0 + break + if is_found >= 1: + st.log("Verification failed ...") + return False + st.log("Verification successful ...") + return True + else: + st.log("Show output not found ...") + return False + +def verify_interface(dut, **kwargs): + """ + API to verify sflow interface configuration + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param interface_name: + :param kwargs: sampling_rate, admin_status + :return: True / False + """ + if not kwargs.get("interface_name"): + st.log("Interface name not provided") + return False + interface_name = kwargs.get("interface_name") + cli_type = kwargs.get("cli_type", "klish") + output = show_interface(dut, interface_name=interface_name, cli_type=cli_type) + if output: + for data in output: + if data["interface"] == interface_name: + st.log("Parsing data for interface {}".format(interface_name)) + if "sampling_rate" in kwargs: + if str(data["sampling_rate"]) != str(kwargs["sampling_rate"]): + st.log("Sampling rate verification failed ..") + return False + if "admin_status" in kwargs: + if data["admin_status"] != kwargs["admin_status"]: + st.log("Admin status verification failed ..") + return False + st.log("Verification successful ...") + return True + else: + st.log("Show output not found ...") + return False + +def psample_stats(dut, attr_data): + """ + API to get psampe stats + :param dut: + :param attr_list: ["drop_sampling", "psample", "psample_cb"] + :return: + """ + result = dict() + attr_list = common_utils.make_list(attr_data) + output = st.show(dut, "sudo cat /proc/bcm/knet-cb/psample/stats") + if not output: + st.log("Output not found") + return result + for attr in attr_list: + if attr in output[0]: + result[attr] = output[0][attr] + return result + + +def verify_psample_stats(dut, params): + """ + API to verify psample stats + :param dut: + :param params: {u'drop_sampling': '0', u'psample': '140', u'psample_cb': '148', + u'drop_psample_not_ready': '0', + u'drop_no_skb': '0', u'drop_no_psample': '0', u'invalid_src_port': '0', u'psample_module': '148'} + u'pass_through': '8', u'drop_metadeta': '0', u'invalid_dst_port': '0', u'dcb_type': '36', + :return: + """ + output = psample_stats(dut) + if not output: + st.log("Observed empty output") + return False + entries = filter_and_select(output, None, params) + if not entries: + st.log("PSAMPLE STATS VERIFICATION FAILED") + return False + return True \ No newline at end of file diff --git a/spytest/apis/system/snmp.py b/spytest/apis/system/snmp.py new file mode 100644 index 00000000000..3d04fda8082 --- /dev/null +++ b/spytest/apis/system/snmp.py @@ -0,0 +1,1145 @@ +# This file contains the list of API's which performs SNMP operation. +# Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + +import subprocess +import re +from spytest import st +from spytest.utils import filter_and_select +from apis.system.basic import replace_line_in_file, service_operations +import utilities.utils as utils_obj +from apis.system.connection import execute_command + +snmp_config_file_path = r'/etc/sonic/snmp.yml' + + +def set_snmp_config(dut, **kwargs): + """ + To set SNMP config, community and location + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param :dut: + :param :snmp_rocommunity: string + :param :snmp_rocommunity6: string + :param :snmp_location: string + :param :v1_trap_dest: string + :param :v2_trap_dest: string + :param :v3_trap_dest: string + :return: + """ + for each in kwargs: + replace_line_in_file(dut, '{}:'.format(each), "{}: {}".format(each, kwargs[each].strip()), + snmp_config_file_path, device='dut') + get_snmp_config(dut) + st.log("Restarting the 'snmp' service , post configuring snmp community.") + service_operations(dut, 'snmp', 'restart') + return True + + +def get_snmp_config(dut): + """ + To Get SNMP config, community and location + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :return: + """ + return st.show(dut, 'cat {}'.format(snmp_config_file_path)) + + +def restore_snmp_config(dut): + """ + To restore the snmp config to default. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param dut: + :return: + """ + defaults = {'snmp_rocommunity': 'NotConfigured', + 'snmp_rocommunity6': 'NotConfigured', + 'snmp_location': 'NotConfigured', + 'snmp_syscontact': 'NotConfigured', + 'v1_trap_dest': 'NotConfigured', + 'v2_trap_dest': 'NotConfigured', + 'v3_trap_dest': 'NotConfigured'} + return set_snmp_config(dut, **defaults) + + +def verify_snmp_config(dut, **kwargs): + """ + To restore the snmp config to default. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param :dut: + :param :snmp_rocommunity: string + :param :snmp_rocommunity6: string + :param :snmp_location: string + :param :v1_trap_dest: string + :param :v2_trap_dest: string + :param :v3_trap_dest: string + :return: + """ + output = get_snmp_config(dut) + for each in kwargs: + if not filter_and_select(output, None, {each: kwargs[each]}): + st.log("{} and {} is not match ".format(each, kwargs[each])) + return False + return True + + +def get_snmp_operation(**kwargs): + """ + To perform SNMP GET operation + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param :ipaddress: + :param :oid: + :param :community_name: + :param :timeout: + :param :retry: + :param :snmp_port: + :param :get_next: True | False (default : False) + version : -v, community_name : -c, usr_name: -u, auth_type: -a, auth_pwd: -A, + privacy_type: -x, privacy_pwd: -X, security_lvl: -l + :return: + """ + report = kwargs.get('report', True) + community_name = kwargs.get("community_name") + ip_address = kwargs.get("ipaddress") + oid = kwargs.get("oid") + snmp_port = kwargs.get("snmp_port") + timeout = kwargs.get("timeout") + retry = kwargs.get("retry") + version = kwargs.get("version", "2") + user_name = kwargs.get("usr_name") + auth_type = kwargs.get("auth_type") + auth_pwd = kwargs.get("auth_pwd") + privacy_type = kwargs.get("privacy_type") + privacy_pwd = kwargs.get("privacy_pwd") + security_lvl = kwargs.get("security_lvl") + security_lvl_type = ["noAuthNoPriv", "authNoPriv", "authPriv"] + connection_obj = kwargs.get("connection_obj") + filter = kwargs.get("filter", "-Oqv") + command = 'snmpget' + if kwargs.get('get_next'): + command = 'snmpgetnext' + if version not in ["1", "2", "3"]: + st.log("Unsupported version provided") + return False + if not ip_address or not oid: + st.log("Mandatory parameters like ipaddress or/and oid not passed") + return False + if version in ["1", "2"]: + if not community_name: + st.log("Mandatory parameter community_name not passed") + return False + act_version = "1" if version == "1" else "2c" + snmp_command = "{} {} -v {} -c {} {} {}".format(command, filter, act_version, community_name, + ip_address, oid) + if snmp_port: + snmp_command = "{} {} -v {} -c {} {}:{} {}".format(command, filter, act_version, community_name, ip_address, + snmp_port, oid) + if timeout: + snmp_command += " -t {}".format(timeout) + if retry: + snmp_command += " -r {}".format(retry) + else: + if not user_name: + st.log("Please provide Username") + return False + if auth_type: + if not auth_pwd: + st.log("Please provide AUTHENTICATION PWD") + return False + if privacy_type: + if not auth_type: + st.log("Please provide AUTHENTICATION TYPE") + return False + if not privacy_pwd: + st.log("Please provide PRIVACY PWD") + return False + if not security_lvl: + st.log("Security level not provided ") + return False + if security_lvl not in security_lvl_type: + st.log("Unsupported security level provided") + return False + if security_lvl == "authNoPriv": + if not auth_type: + st.log("Authentication type not provided with security lvl {}".format(security_lvl)) + return False + if security_lvl == "authPriv": + if not auth_type: + st.log("Authentication type not provided with security lvl {}".format(security_lvl)) + return False + if not privacy_type: + st.log("Privacy type not provided with security lvl {}".format(security_lvl)) + return False + snmp_command = "{} {} -v {} -n \"\" -u {} -l {}".format(command, filter, version, user_name, security_lvl) + if auth_type: + snmp_command += " -a {} -A {}".format(auth_type, auth_pwd) + if privacy_type: + snmp_command += " -x {} -X {}".format(privacy_type, privacy_pwd) + if not snmp_port: + snmp_command += " {} {}".format(ip_address, oid) + else: + snmp_command += " {}:{} {}".format(ip_address, snmp_port, oid) + if timeout: + snmp_command += " -t {}".format(timeout) + if retry: + snmp_command += " -r {}".format(retry) + st.log("Command: {}".format(snmp_command)) + if version in ["1", "2"]: + pprocess = subprocess.Popen(snmp_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + universal_newlines=True) + (stdout, stderr) = pprocess.communicate() + st.log("SNMP stdout: {}".format(stdout)) + if pprocess.poll() is not None: + if pprocess.returncode == 0 \ + and "No Such Object available on this agent at this OID" not in stdout \ + and "No Such Instance currently exists at this OID" not in stdout \ + and "No more" not in stdout: + result = stdout.rstrip('\n').split("\n") + result1 = [each.replace('"', '') for each in result] + return result1 + elif "Timeout" in stderr: + st.error("SNMP Timeout occurs") + if report: + st.report_fail('snmp_operation_fail', 'GET', 'Timeout') + return False + else: + st.log("SNMP Error: return code = {}".format(pprocess.returncode)) + st.log("SNMP stdout: {}".format(stdout)) + st.error("SNMP stderr: {}".format(stderr)) + if report: + st.report_fail('snmp_operation_fail', 'GET', 'Error') + return False + if "No Such Instance currently exists at this OID" in stdout: + result = stderr.strip("\n") + st.error(result) + if report: + st.report_fail('snmp_operation_fail', 'GET', 'No Instance Found') + return False + else: + output = execute_command(connection_obj, snmp_command) + st.log("OUTPUT: {}".format(output)) + error_codes = ["No Such Object available on this agent at this OID", + "No Such Instance currently exists at this OID", "No more"] + output = utils_obj.remove_last_line_from_string(output) + if output: + for err_code in error_codes: + if err_code not in output: + result = output.rstrip('\n').split("\n") + result1 = [each.replace('"', '') for each in result] + return result1 + else: + st.log("SNMP OUTPUT: {}".format(output)) + return False + if "No Such Instance currently exists at this OID" in output: + st.error(output) + return False + + +def walk_snmp_operation(**kwargs): + """ + To perform SNMP WALK operation + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + + :param :ipaddress: + :param :oid: + :param :community_name: + :param :timeout: + :param :retry: + :param :snmp_port: + :param :filter: + :return: + """ + report = kwargs.get('report', True) + community_name = kwargs.get("community_name") + ip_address = kwargs.get("ipaddress") + oid = kwargs.get("oid", "1") + snmp_port = kwargs.get("snmp_port") + timeout = kwargs.get("timeout") + retry = kwargs.get("retry") + version = kwargs.get("version", "2") + user_name = kwargs.get("usr_name") + auth_type = kwargs.get("auth_type") + auth_pwd = kwargs.get("auth_pwd") + privacy_type = kwargs.get("privacy_type") + privacy_pwd = kwargs.get("privacy_pwd") + security_lvl = kwargs.get("security_lvl") + filter = kwargs.get("filter", '') + security_lvl_type = ["noAuthNoPriv", "authNoPriv", "authPriv"] + connection_obj = kwargs.get("connection_obj") + command = 'snmpwalk' + if version not in ["1", "2", "3"]: + st.log("Unsupported version provided") + return False + if not ip_address or not oid: + st.log("Mandatory parameters like ipaddress or/and oid not passed") + return False + if version in ["1", "2"]: + if not community_name: + st.log("Mandatory parameter community_name not passed") + return False + if version == '1': + act_version = "1" + else: + if 'bulk_walk' in kwargs: + command = "snmpbulkwalk" + act_version = "2c" + snmp_command = "{} {} -v {} -c {} {} {}".format(command, filter, act_version, community_name, + ip_address, oid) + if snmp_port: + snmp_command = "{} {} -v {} -c {} {}:{} {}".format(command, filter, act_version, community_name, ip_address, + snmp_port, oid) + else: + if not user_name: + st.log("Please provide Username") + return False + if auth_type: + if not auth_pwd: + st.log("Please provide AUTHENTICATION PWD") + return False + if privacy_type: + if not auth_type: + st.log("Please provide AUTHENTICATION TYPE") + return False + if not privacy_pwd: + st.log("Please provide PRIVACY PWD") + return False + if not security_lvl: + st.log("Security level not provided ") + return False + if security_lvl not in security_lvl_type: + st.log("Unsupported security level provided") + return False + if security_lvl == "authNoPriv": + if not auth_type: + st.log("Authentication type not provided with security lvl {}".format(security_lvl)) + return False + if security_lvl == "authPriv": + if not auth_type: + st.log("Authentication type not provided with security lvl {}".format(security_lvl)) + return False + if not privacy_type: + st.log("Privacy type not provided with security lvl {}".format(security_lvl)) + return False + snmp_command = "{} {} -v {} -n \"\" -u {} -l {}".format(command, filter, version, user_name, security_lvl) + if auth_type: + snmp_command += " -a {} -A {}".format(auth_type, auth_pwd) + if privacy_type: + snmp_command += " -x {} -X {}".format(privacy_type, privacy_pwd) + if not snmp_port: + snmp_command += " {} {}".format(ip_address, oid) + else: + snmp_command += " {}:{} {}".format(ip_address, snmp_port, oid) + if timeout: + snmp_command += " -t {}".format(timeout) + if retry: + snmp_command += " -r {}".format(retry) + st.log("Command: {}".format(snmp_command)) + if version in ["1", "2"]: + pprocess = subprocess.Popen(snmp_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + universal_newlines=True) + result = [] + while True: + output = pprocess.stdout.readline() + try: + output = output.decode(errors='ignore') + except: + pass # already decoded in python3 + if pprocess.poll() is not None and output.strip() == '': + snmp_error_stderr = pprocess.communicate()[1].decode() + if snmp_error_stderr != r'': + if "Timeout" in snmp_error_stderr: + st.error("SNMP Timeout occurs") + if report: + st.report_fail('snmp_operation_fail', 'WALK', 'Timeout') + return False + st.error("SNMP ERROR: {}".format(snmp_error_stderr)) + if report: + st.report_fail('snmp_operation_fail', 'WALK', 'Error') + return False + break + + elif output: + st.log(output.strip()) + if "No Such Object available on this agent at this OID" in output or \ + "No Such Instance currently exists at this OID" in output: + if report: + st.report_fail('snmp_operation_fail', 'WALK', 'No Instance Found') + return False + result.append(output.strip("\n")) + + if str(kwargs['oid']) == '1': + if 'No more variables left in this MIB View' not in output: + if report: + st.report_fail('snmp_operation_fail', 'WALK', 'In-Complete Walk termination') + return False + return result + + else: + output = execute_command(connection_obj, snmp_command) + st.log("OUTPUT: {}".format(output)) + error_codes = ["No Such Object available on this agent at this OID", + "No Such Instance currently exists at this OID", "No more"] + output = utils_obj.remove_last_line_from_string(output) + if output: + for err_code in error_codes: + if err_code not in output: + result = output.rstrip('\n').split("\n") + result1 = [each.replace('"', '') for each in result] + return result1 + else: + st.log("SNMP OUTPUT: {}".format(output)) + return False + if "No Such Instance currently exists at this OID" in output: + st.error(output) + return False + + +def poll_for_snmp(dut, iteration_count=30, delay=1, **kwargs): + """ + This API is to poll the DUT to get the snmp operation output + Author: Chaitanya Vella (chaitanya.vella-kumar@broadcom.com) + :param dut: + :param iteration_count: + :param delay: + :param kwargs: + :return: + """ + i = 1 + while True: + snmp_operation = get_snmp_operation(report=False, **kwargs) + if snmp_operation: + st.log("snmp o/p is found ...") + return True + if i > iteration_count: + st.log("Max {} tries Exceeded. Exiting..".format(i)) + return False + i += 1 + st.wait(delay) + + +def poll_for_snmp_walk(dut, iteration_count=30, delay=1, **kwargs): + """ + This API is to poll the DUT to get the snmp operation output + Author: Chaitanya Vella (chaitanya.vella-kumar@broadcom.com) + :param dut: + :param iteration_count: + :param delay: + :param kwargs: + :return: + """ + i = 1 + while True: + snmp_operation = walk_snmp_operation(report=False, **kwargs) + if snmp_operation: + st.log("snmp o/p is found ...") + return True + if i > iteration_count: + st.log("Max {} tries Exceeded. Exiting..".format(i)) + return False + i += 1 + st.wait(delay) + + +def get_oids_from_walk_output(data): + """ + To get OID from SNMP WALK operation output. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param data: + :return: + """ + result = [] + for each in utils_obj.make_list(data): + if "=" in each: + result.append(re.findall(r"(\S+)\s+=", each)[0]) + return result + + +def config_snmp_agent(dut, **kwargs): + """ + API to create SNMP agent address. + Author : Kiran Vedula (kvedula@broadcom.com) + :param :dut: + :param :cli_type: default - click + :param :no_form: default - False + :return: + + Usage: + config_snmp_agent(vars.D1, ip_addr= ip_addr[0], port =161, vrf= 'mgmt') + config_snmp_agent(vars.D1, ip_addr= ip_addr[0], port =161, vrf= 'mgmt', no_form=True) + + """ + commands = [] + if 'ip_addr' not in kwargs: + st.log("Mandatory parameter ipaddress not passed") + return False + cli_type = kwargs.get("cli_type", "click") + ip_addr = kwargs.get('ip_addr') + port = kwargs.get("port", 161) + vrf = kwargs.get("vrf", None) + no_form = kwargs.get("no_form", False) + if cli_type == 'klish': + st.log("UNSUPPORTED CLI TYPE {}".format(cli_type)) + elif cli_type == "click": + st.log('Config SNMP Agent address') + action = "add" if not no_form else "del" + my_cmd = 'config snmpagentaddress {} {} -p {} -v {}'.format(action, ip_addr, port, vrf) + commands.append(my_cmd) + if commands: + st.config(dut, commands, type=cli_type) + return True + + +def config_snmp_trap(dut, **kwargs): + """ + API to create SNMP agent address. + Author : Kiran Vedula (kvedula@broadcom.com) + :param :dut: + :param :cli_type: default - click + :param :no_form: default - False + :return: + + Usage: + config_snmp_agent(vars.D1, version =2, ip_addr= ip_addr[0], port =162, vrf= 'mgmt', community= 'snmp_ro') + config_snmp_agent(vars.D1, ip_addr= ip_addr[0], port =162, vrf= 'mgmt', no_form='True') + + """ + commands = [] + cli_type = kwargs.get("cli_type", "click") + ip_addr = kwargs.get('ip_addr') + port = kwargs.get("port", 162) + vrf = kwargs.get("vrf", None) + community = kwargs.get("community", "public") + no_form = kwargs.get("no_form", False) + if cli_type == 'klish': + st.log("UNSUPPORTED CLI TYPE {}".format(cli_type)) + return True + elif cli_type == "click": + st.log('Config SNMP Trap receiver') + if not no_form: + if 'ip_addr' not in kwargs: + st.log("Mandatory parameter ipaddress not passed") + return False + version = kwargs.get("version", 2) + my_cmd = 'config snmptrap modify {} {} -p {} -v {} -c {}'.format(version, ip_addr, port, vrf, community) + else: + if 'version' not in kwargs: + st.log("Mandatory parameter version not passed") + return False + my_cmd = 'config snmptrap del {}'.format(kwargs.get("version")) + commands.append(my_cmd) + if commands: + st.config(dut, commands, type=cli_type) + return True + + +def config(dut, params): + """ + API to create/Delete SNMP commands for communities,users,traps,system and location. + Author : Santosh Votarikari(santosh.votarikari@broadcom.com) + :param :dut: + :param :params: {"cli_type":"klish","globals":{"contact":["Value","True/False"], "location":["Value",True/False], + "sysname":["Value","True/False"],"engine":["Value","True/False"]}, + "traps":"enable/disable","community":{"name":"Value","group_name":"Value", "no_form":"True/False"}, + "groups":{"name":"Value","version":{"type":"v1|v2c|v3|any","options":"auth|noatuh|priv"}, + "operations":{"read_view":"value","write_view":"value","notify_view":"value"}},"host":{"address":"IP_ADDR", + "community":{"name":"Value", "traps":"v2c | v1","informs":"Value","timeout":"Value","retries":"Value", + "port":, "interface":"Value"},"user":{"name":"Value", "traps":" auth | noauth | priv", + "informs":"auth | noauth | priv","timeout":"Value","retries":"Value", "port":, "interface":"Value"}}, + "user":{"name":"Value","group":"Value","encrypted":"enable/disable","auth":"Value", + "auth_pwd":"Value","privacy":"Value","priv_pwd":"Value"}, + "view":{"name":"Value","oid":"Value","option":"included/excluded"}} + "agent_address":{"agent-addr":"Value","udp-port":"Value","interface_name":"Value"} + :return: True + + Usage: + snmp-server [ contact ] [ location ] [ sysname ] [ engine ] + snmp-server agentaddress [port ] [interface ] + snmp-server enable trap + snmp-server community [groupname ] + snmp-server group { any | v1 | v2c | v3 { auth | noauth | priv }} [ read ] + [ write ] [ notify ] + snmp-server host community {[ traps { v2c | v1 }] | informs [timeout seconds] + [retries retries] [port udpPort] [interface ifaceName]} + snmp-server host user {[traps {auth | noauth | priv}] | [informs {auth | noauth | priv} + [timeout seconds] [retries retries]] [port udpPort] [interface ifaceName]} + snmp-server user [ group ] [ encrypted ] [ auth { md5 | sha | noauth } + [ auth-password ] [ priv { DES | AES-128 } [ priv-password ] ] + snmp-server view {included | excluded} + + + """ + command = '' + commands = list() + mandatory_params = {"globals": ["contact", "location", "sysname", "engine"], "traps": ["enable", "disable"], + "community": ["name"], "groups": ["name", "version"], "host_name": ["address", "community"], + "host_user": ["address", "user"], "user": ["name", "group"], "view": ["name", "oid", "option"], + "agent_address": ["agent-addr"]} + + cli_type = params.get('cli_type', 'klish') + if cli_type == "click": + st.log("This CLI TYPE is not supported for SNMP Commands {}". format(cli_type)) + return False + # To verify mandatory values for given commands + for key, value in params.items(): + if key == "globals": + for field, data in value.items(): + if field not in mandatory_params["globals"]: + st.log("Mandatory params is not provided for Globals") + return False + if key == "traps": + if value not in mandatory_params["traps"]: + st.log("Unsupported value for traps-- {}".format(value)) + return False + if key == "community": + if value.get("no_form") in [False, True]: + if mandatory_params["community"][0] not in value.keys(): + st.log("Mandatory params is not provided for community-- {}". + format(mandatory_params["community"][0])) + return False + else: + st.log("Unsupported no_form for key parameters") + return False + if key == "groups": + if value.get("no_form") in [False, True]: + for field in mandatory_params["groups"]: + if field not in value.keys(): + st.log("Mandatory params is not provided for group-- {}".format(field)) + return False + else: + st.log("Unsupported no_form for groups parameters") + return False + if key == "host": + result = 0 + if value.get("no_form") in [False, True]: + if not value.get("no_form"): + if value.get("community"): + for field in mandatory_params["host_name"]: + if field not in value.keys(): + st.log("Mandatory params is not provided for host community-- {}".format(field)) + result = result + 1 + if result > 0: + return False + + elif value.get("user"): + for field in mandatory_params["host_user"]: + if field not in value.keys(): + st.log("Mandatory params is not provided for host user-- {}".format(field)) + result = result + 1 + if result > 0: + return False + else: + if mandatory_params["host_name"][0] not in value.keys(): + st.log("Mandatory params is not provided for no form of host -- {}". + format(mandatory_params["host_name"][0])) + return False + else: + st.log("Unsupported no_form for host parameters") + return False + if key == "user": + result = 0 + if value.get("no_form") in [False, True]: + if not value.get("no_form"): + for key_1 in mandatory_params["user"]: + if key_1 not in value.keys(): + st.log("Mandatory params is not provided for user-- {}".format(key_1)) + result = result + 1 + if result > 0: + return False + else: + if mandatory_params["user"][0] not in value.keys(): + st.log("Mandatory params is not provided for no form of user -- {}". + format(mandatory_params["user"][0])) + return False + else: + st.log("Unsupported no_form for user parameters") + return False + if key == "view": + result = 0 + if value.get("no_form") in [False, True]: + if not value.get("no_form"): + for key_1 in mandatory_params["view"]: + if key_1 not in value.keys(): + st.log("Mandatory params is not provided for view-- {}".format(key_1)) + result = result + 1 + if result > 0: + return False + else: + if mandatory_params["view"][0] not in value.keys() and mandatory_params["view"][1] not in \ + value.keys(): + st.log("Mandatory params is not provided for no form of view -- {} {}". + format(mandatory_params["view"][0], mandatory_params["view"][1])) + return False + else: + st.log("Unsupported no_form for view parameters") + return False + if key == "agent_address": + if value.get("no_form") in [False, True]: + for field in mandatory_params["agent_address"]: + if field not in value.keys(): + st.log("Mandatory params is not provided for agent_address-- {}".format(field)) + return False + else: + st.log("Unsupported no_form for agent_address parameters") + return False + + + # To verify empty values in given lists + for empty_parameter in [key for key, value in params.items() if value == ""]: + st.log("Value is not defined for {} value ".format(empty_parameter)) + return False + + # To un-configure/configure snmp communities,traps, hosts and users + for key, value in params.items(): + if key == "globals": + for field, data in value.items(): + if field not in mandatory_params["globals"]: + st.log("Unsupported params provided -- {}".format(field)) + return False + if data: + if len(data) > 1 and data[1]: + command = "snmp-server {} {}".format(field, data[0]) + else: + command = "no snmp-server {}".format(field) + commands.append(command) + if key == "traps": + if params[key] not in ["enable", "disable"]: + st.log("Unsupported operation for traps provided {}".format(params[key])) + return False + command = "snmp-server enable trap" if params[key] == "enable" else "no snmp-server enable trap" + commands.append(command) + if key == "community": + command = "snmp-server community" + if not value.get("no_form"): + command += " {}".format(value.get("name")) + if value.get("group_name"): + command += " groupname {}".format(value.get("group_name")) + else: + command = "no {} {}".format(command, value.get("name")) + if command: + commands.append(command) + if key == "view": + command = "snmp-server view " + if not value.get("no_form"): + command += " {}".format(value.get("name")) + if value.get("oid"): + command += " {}".format(value.get("oid")) + if value.get("option") not in ["included", "excluded"]: + st.log("Unsupported params for oid provided -- {}".format(value.get("option"))) + return False + else: + command += " {}".format(value.get("option")) + else: + command = "no {} {} {}".format(command, value.get("name"), value.get("oid")) + if command: + commands.append(command) + + if key == "groups": + command = "snmp-server group" + if not value.get("no_form"): + command += " {}".format(value.get("name")) + if value.get("version"): + version_type = value.get("version").get("type") + version_options = value.get("version").get("options") + if version_type not in ["v1", "v2c", "v3", "any"]: + st.log("Unsupported params for snmp version type provided -- {}". + format(version_type)) + return False + else: + if version_type in ["v1", "v2c", "any"]: + command += " {}".format(version_type) + elif version_type == 'v3': + if version_options not in ["auth", "noauth", "priv"]: + st.log("Unsupported params for group auth provided -- {}".format(version_options)) + return False + else: + command += " {} {}".format(version_type, version_options) + if value.get("operations"): + view_options = value.get("operations") + if view_options.get("read_view"): + command += " read {}".format(view_options.get("read_view")) + if view_options.get("write_view"): + command += " write {}".format(view_options.get("write_view")) + if view_options.get("notify_view"): + command += " notify {}".format(view_options.get("notify_view")) + else: + if value.get("version").get("type") in ["v1", "v2c", "any"]: + command = "no {} {} {}".format(command, value.get("name"), value.get("version").get("type")) + else: + command = "no {} {} {} {}".format(command, value.get("name"), value.get("version").get("type"), + value.get("version").get("options")) + if command: + commands.append(command) + + if key == "host": + command = "snmp-server host" + if not value.get("no_form"): + command += " {}".format(value.get("address")) + if value.get("community"): + host_community = value.get("community") + command += " community {}".format(host_community.get("name")) + if host_community.get("traps"): + if host_community.get("traps") not in ["v2c", "v1"]: + st.log("Unsupported params for trap community provided -- {}". + format(host_community.get("traps"))) + return False + else: + command += " traps {}".format(host_community.get("traps")) + elif host_community.get("informs") == "True": + command += " informs " + if host_community.get("timeout"): + command += " timeout {}".format(host_community.get("timeout")) + if host_community.get("retries"): + command += " retries {}".format(host_community.get("retries")) + if host_community.get("port"): + command += " port {}".format(host_community.get("port")) + if host_community.get("interface"): + command += " interface {}".format(host_community.get("interface")) + elif value.get("user"): + host_user = value.get("user") + command += " user {}".format(host_user.get("name")) + if host_user.get("traps"): + if host_user.get("traps") not in ["auth", "noauth", "priv"]: + st.log("Unsupported params for trap user provided -- {}".format(host_user.get("traps"))) + return False + else: + command += " traps {}".format(host_user.get("traps")) + elif host_user.get("informs"): + if host_user.get("informs") not in ["auth", "noauth", "priv"]: + st.log("Unsupported params for inform user provided -- {}".format(host_user.get("informs"))) + return False + else: + command += " informs {}".format(host_user.get("informs")) + if host_user.get("timeout"): + command += " timeout {}".format(host_user.get("timeout")) + if host_user.get("retries"): + command += " retries {}".format(host_user.get("retries")) + if host_user.get("port"): + command += " port {}".format(host_user.get("port")) + if host_user.get("interface"): + command += " interface {}".format(host_user.get("interface")) + else: + command = "no {} {} ".format(command, value.get("address")) + if command: + commands.append(command) + + if key == "user": + command = "snmp-server user" + if not value.get("no_form"): + command += " {}".format(value.get("name")) + if value.get("group"): + command += " group {}".format(value.get("group")) + if value.get("encrypted"): + if value.get("encrypted") not in ["enable", "disable"]: + st.log("Unsupported params for encrypted user provided--{}".format(value.get("encrypted"))) + return False + elif value.get("encrypted") == "enable": + command += " encrypted" + else: + pass + if value.get("auth"): + command += " auth" + user_auth = value.get("auth") + if user_auth in ["md5", "sha"]: + command += " {}".format(user_auth) + if not value.get("auth_pwd"): + st.log("Authentication password is not provided {}".format(value.get("auth_pwd"))) + return False + else: + command += " auth-password {}".format(value.get("auth_pwd")) + elif user_auth == "noauth": + command += " {}".format(user_auth) + else: + st.log("Authentication protocol is not provided {}".format(user_auth)) + return False + if value.get("priv"): + command += " priv" + user_priv = value.get("priv") + if user_priv not in ["DES", "AES-128"]: + st.log("Privacy protocol is not provided {}".format(user_priv)) + return False + else: + command += " {}".format(user_priv) + if not value.get("priv_pwd"): + st.log("Privacy password is not provided {}".format(value.get("priv_pwd"))) + return False + else: + command += " priv-password {}".format(value.get("priv_pwd")) + else: + command = "no {} {} ".format(command, value.get("name")) + if command: + commands.append(command) + if key == "agent_address": + command = "snmp-server agentaddress" + if not value.get("no_form"): + if value.get("agent-addr"): + command += " {}".format(value.get("agent-addr")) + if value.get("udp-port"): + command += " port {}".format(value.get("udp-port")) + if value.get("interface_name"): + command += " interface {}".format(value.get("interface_name")) + else: + command = "no snmp-server agentaddress {}".format(value.get("agent-addr")) + if value.get("udp-port"): + command += " port {}".format(value.get("udp-port")) + if value.get("interface_name"): + command += " interface {}".format(value.get("interface_name")) + if command: + commands.append(command) + + if commands: + st.config(dut, commands, type=cli_type, faster_cli=False) + return True + return False + + +def show(dut, **kwargs): + """ + API to show snmp-server (community|group|user|view|host) commands + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param kwargs: + :return: + COMMAND : show snmp-server OUTPUT: {'system_name': 'sonic', 'traps': 'enable', 'location': 'hyd', + 'engine_id': '8000013703d8c497726914', 'contact: 'admin'} + COMMAND : show snmp-server community OUTPUT: [{'group': 'None', 'community': 'sonic'}, + {'group': 'None', 'community': 'sonic1'}, {'group': 'None', 'community': 'sonic2'}] + COMMAND : show snmp-server group OUTPUT: [{'grp_name': 'group2', 'grp_model': 'v3', 'grp_security': 'auth-no-priv', + 'grp_read_view': 'view1', 'grp_write_view': 'view1', 'grp_notify_view': 'view1'}, {'grp_name': 'group2', + 'grp_model': 'v3', 'grp_security': 'auth-priv', 'grp_read_view': 'view1', 'grp_write_view': 'view1', + 'grp_notify_view': 'view1'}] + COMMAND : show snmp-server view OUTPUT: [{'view_name': 'view1', 'view_oid': 'iso', 'view_type': 'included'}, + {'view_name': 'view2', 'view_oid': 'iso', 'view_type': 'included'}] + COMMAND : show snmp-server user OUTPUT: [{'user_name': 'user1', 'usr_grp_name': 'group1', + 'usr_authentication': 'md5', 'usr_privacy': 'AES-128'}, {'user_name': 'user1', 'usr_grp_name': 'group2', + 'usr_authentication': 'md5', 'usr_privacy': 'AES-128'}] + COMMAND : show snmp-server host OUTPUT: {'target_address': '10.52.143.249', 'target_type': 'trapNotify', + 'target_community': 'sonic', 'target_version': 'v2c', 'target_timeout': '1500', 'target_retries': '3'} + """ + response = list() + cli_type=kwargs.get("cli_type", "klish") + snmp_type= kwargs.get("snmp_type", "server") + command = "show snmp-server" + if snmp_type and snmp_type != "server": + command += " {}".format(snmp_type) + skip_tmpl = kwargs.get("skip_tmpl", False) + output = st.show(dut, command, type=cli_type, skip_tmpl=skip_tmpl) + if output: + if snmp_type in ["server", "community", "group", "view", "user", "host"]: + sub_attr = list() + if snmp_type == "community": + attributes = ["community", "group"] + elif snmp_type == "group": + attributes = ["grp_name", "grp_model", "grp_security", "grp_read_view", "grp_write_view", + "grp_notify_view"] + elif snmp_type == "view": + attributes = ["view_name", "view_oid", "view_type"] + elif snmp_type == "user": + attributes = ["user_name", "usr_grp_name", "usr_authentication", "usr_privacy"] + elif snmp_type == "host": + attributes = ["target_address", "target_type", "target_community_user", "target_version_security", "target_timeout", + "target_retries"] + else: + attributes = ["system_name", "traps", "location", "engine_id", "contact"] + sub_attr = ["agent_ip_address", "agent_udp_port", "agent_interface"] + if snmp_type != "server": + for data in output: + result = dict() + for attr in attributes: + result.update({attr: data[attr]}) + if result: + response.append(result) + else: + result = dict() + for key,value in output[0].items(): + if key in attributes: + result.update({key: value}) + result.update({"agents":[]}) + for agent_data in output: + agents = dict() + for attr in sub_attr: + agents.update({attr: agent_data[attr]}) + result["agents"].append(agents) + if result: + response.append(result) + return response + + + +def verify(dut, **kwargs): + """ + API to verify snmp configurtion + :param dut: + :param kwargs: cli_type="klish","snmp_type":"community","data":[{'group': 'None', 'community': 'sonic'}, + {'group': 'None', 'community': 'sonic1'}, {'group': 'None', 'community': 'sonic2'}] + :return: True/False + """ + cli_type = kwargs.get("cli_type", "klish") + snmp_type = kwargs.get("snmp_type", "") + filter_data = kwargs.get("data") + output = show(dut, cli_type=cli_type, snmp_type=snmp_type) + if not output: + st.log("SHOW OUTPUT NOT FOUND --- {}".format(output)) + return False + if snmp_type == "server" and isinstance(filter_data, list): + st.log("Observed invalid filter data for SNMP type Server") + return False + if filter_data: + if isinstance(filter_data, list): + for data in filter_data: + entries = filter_and_select(output, data.keys(), data) + if not entries: + return False + else: + agent_details = list() + if snmp_type == "server" and "agents" in filter_data: + agent_details = filter_data.pop("agents") + entries = filter_and_select(output, filter_data.keys(), filter_data) + if not entries: + return False + if agent_details and "agents" in output[0]: + for agent_data in agent_details: + entries = filter_and_select(output[0]["agents"], agent_data.keys(), agent_data) + if not entries: + return False + return True + else: + st.log("DATA TO BE VERIFIED IS NOT PROVIDED") + return False + + +def poll_for_snmp_walk_output(dut, iteration_count=5, delay=1, expected_output="", **kwargs): + """ + This API is to poll the DUT to get the valid output for walk operation + Author: Santosh Votarikari(santosh.votarikari@broadcom.com) + :param dut: + :param iteration_count: + :param expected_output: + :param delay: + :param kwargs: + :return: snmp walk output + """ + i = 1 + while True: + snmp_walk_out_put = walk_snmp_operation(**kwargs) + for match in snmp_walk_out_put: + if expected_output in match: + st.log("Found expected output\n") + return snmp_walk_out_put + if i > iteration_count: + st.log("Max {} tries Exceeded. Exiting..".format(i)) + return snmp_walk_out_put + i += 1 + st.wait(delay) + + +def poll_for_snmp_get_output(dut, iteration_count=5, delay=1, expected_output="", **kwargs): + """ + This API is to poll the DUT to get the valid output for get operation + Author: Santosh Votarikari(santosh.votarikari@broadcom.com) + :param dut: + :param iteration_count: + :param expected_output: + :param delay: + :param kwargs: + :return: snmp get output + """ + i = 1 + while True: + snmp_get_out_put = get_snmp_operation(**kwargs) + for match in snmp_get_out_put: + if expected_output in match: + st.log("Found expected output\n") + return snmp_get_out_put + if i > iteration_count: + st.log("Max {} tries Exceeded. Exiting..".format(i)) + return snmp_get_out_put + i += 1 + st.wait(delay) + + +def get_auth_priv_keys(**kwargs): + """ + This API is to get the encrypted password for SHA,MD5,AES and DES protocols + Author: Santosh Votarikari(santosh.votarikari@broadcom.com) + :param kwargs: + :param engine_id: + :param auth_type: + :param priv_type: + :param auth_password: + :param priv_password: + :return: authkey and privkey + """ + command = "snmpkey " + auth_protocol = ["md5", "sha"] + priv_protocol = ["des", "aes-128", "aes"] + engine_id = kwargs.get('engine_id') + auth_type = kwargs.get('auth_type') + priv_type = kwargs.get('priv_type') + auth_password = kwargs.get('auth_password') + priv_password = kwargs.get('priv_password') + + if not engine_id or not auth_type or not auth_password: + st.log("Mandatory parameters like engine-id,auth_type,auth_password are not passed") + return False + if auth_type in auth_protocol: + command += "{}".format(auth_type) + else: + st.log("Unsupported authentication protocol is provided:{}".format(auth_type)) + return False + if auth_password: + command += " {}".format(auth_password) + if engine_id: + command += " 0x{}".format(engine_id) + if priv_type in priv_protocol: + command += " {}".format(priv_type) + else: + st.log("Unsupported privacy protocol is provided:{}".format(auth_type)) + return False + if priv_password: + command += " {}".format(priv_password) + st.log("Command is:{}".format(command)) + pprocess1 = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + universal_newlines=True) + (stdout, stderr) = pprocess1.communicate() + st.log("Auth Priv Keys output: {}".format(stdout)) + if pprocess1.returncode == 0: + result = stdout.rstrip('\n').split("\n") + result1 = [each.replace('"', '') for each in result] + return result1 + +def verify_snmp_details_using_docker(dut, **kwargs): + """ + API to verify the configured SNMP details using docker commands + :param dut: + :param kwargs: {'sysname': 'sonic', 'syscontact': '', 'rocommunityv6': 'sonic', 'syslocation': 'Hyderabad', 'rocommunity': 'sonic'} + {'sysname': 'sonic', 'syscontact': '', 'rocommunityv6': ['sonic','buzznik'], 'syslocation': 'Hyderabad', 'rocommunity': ['sonic','buzznik']} + :return: + """ + command = "sudo docker exec -ti snmp cat /etc/snmp/snmpd.conf" + search_cmd = [] + for key, value in kwargs.items(): + if value: + if isinstance(value, list): + for search_str in value: + search_cmd.append("{} | grep {}".format(command, search_str)) + else: + search_cmd.append("{} | grep {}".format(command, value)) + for cmd in search_cmd: + output = st.show(dut, cmd) + if not output: + st.log("No output found with the command {}".format(cmd)) + return False + for key,value in kwargs.items(): + if value: + if isinstance(value, list): + if output[0][key] not in value: + st.log("Expected value not found for {} -- {} in output".format(key, value)) + return False + else: + if value != output[0][key]: + st.log("Expected value not found for {} -- {} in output".format(key, value)) + return False + return True diff --git a/spytest/apis/system/ssh.py b/spytest/apis/system/ssh.py new file mode 100644 index 00000000000..245a5da14ba --- /dev/null +++ b/spytest/apis/system/ssh.py @@ -0,0 +1,140 @@ +from spytest import st +from apis.system.basic import deploy_package +from utilities.common import make_list, filter_and_select + + +def enable_ssh(dut): + st.log(" # enable ssh") + command = "/etc/init.d/ssh start" + st.config(dut, command) + return True + + +def disable_ssh(dut): + st.log(" # disable ssh") + command = "/etc/init.d/ssh stop" + st.config(dut, command) + return True + + +def enable_sshv6(dut): + st.log(" # Enable SSH on a device to listen on IPv6") + command = "sed -i 's/#ListenAddress ::/ListenAddress ::/g' /etc/ssh/sshd_config" + st.config(dut, command) + command = "/etc/init.d/ssh restart" + st.config(dut, command) + return True + + +def disable_sshv6(dut): + st.log(" # Disable SSH on a device to listen on IPv6") + command = "sed -i 's/ListenAddress ::/#ListenAddress ::/g' /etc/ssh/sshd_config" + st.config(dut, command) + command = "/etc/init.d/ssh restart" + st.config(dut, command) + return True + + +def ssh_keygen(dut, mode='create', path=r'/home/admin/.ssh/'): + """ + To generate the SSH keys to DUT + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param mode: create | destroy + :param path: + :return: + """ + st.banner(" Generate SSH keys to DUT - Mode={}".format(mode)) + if mode == 'create': + cmd_list = ['mkdir {}', 'touch {}authorized_keys', 'chmod 0700 {}', 'chmod 0600 {}authorized_keys', + "echo -e 'y\n' | ssh-keygen -o -b 4096 -t rsa -f ~/.ssh/id_rsa -N ''", 'ls -lrt {}'] + else: + cmd_list = ['sudo rm -rf {}', 'ls -lrt {}'] + for each_cmd in cmd_list: + st.show(dut, each_cmd.format(path), skip_tmpl=True) + + +def ssh_copyid(dut, ip, **kwargs): + """ + To copy SSH ID from DUT to server. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param ip: + :param kwargs: + :return: + """ + result = True + st.log(" # Copy SSH ID from DUT to server") + if not (kwargs.get('username') and kwargs.get('password')): + st.error("Mandatory arguments are missing.") + return False + if 'sshpass' not in st.config(dut, 'which sshpass', skip_error_check=True): + deploy_package(dut, mode='update') + result = deploy_package(dut, packane_name='sshpass', mode='install') + st.show(dut, 'sshpass -p "{}" ssh-copy-id -o StrictHostKeyChecking=no {}@{} -f'.format( + kwargs['password'], kwargs['username'], ip), skip_tmpl=True) + return result + + +def default_user_password_finder(dut, username, password_list): + """ + To Find default user password. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param username: + :param password_list: + :return: + """ + st.log(" # Finding default user password.") + for each_pass in make_list(password_list): + st.log('Trying SSH connection to device with username={},password={}'.format(username, each_pass)) + if st.exec_ssh(dut, username, each_pass, ['show system status']): + st.log("Detected password = {}".format(each_pass)) + return each_pass + + +def enable_ssh_in_user_vrf(dut, **kwargs): + """ + To enable SSH-in over user defined VRF + :param dut: + :param kwargs: + :return: + Usage:enable_ssh_in_user_vrf(vars.D1, config='add',vrf_name='mgmt') + """ + cli_type = kwargs.get("cli_type", "click") + st.log(" Configure SSH-in for user VRF") + if not kwargs.get('vrf_name'): + st.error("Mandatory arguments are missing.") + return False + if kwargs.get('config') not in ['add', 'del']: + st.error("Incorrect config type") + return False + command = "config ssh-server vrf {} {}".format(kwargs.get('config'), kwargs.get('vrf_name')) + st.config(dut, command, cli_type=cli_type, skip_error_check=True) + return True + + +def get_ssh_server_vrf(dut, vrf_name=None, cli_type='click'): + """ + To Get SSH server VRF + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param vrf_name: + :param cli_type: + :return: + + Usage: + get_ssh_server_vrf(vars.D1) + get_ssh_server_vrf(vars.D1, vrf_name='VRF_1') + + """ + command = "show ssh-server vrfs" + output = st.show(dut, command, type=cli_type) + if not vrf_name in output: + return output + else: + out = filter_and_select(output, None, {'vrf_name': vrf_name}) + if not out: + return False + else: + return out[0]['status'] diff --git a/spytest/apis/system/storm_control.py b/spytest/apis/system/storm_control.py new file mode 100644 index 00000000000..b941062918b --- /dev/null +++ b/spytest/apis/system/storm_control.py @@ -0,0 +1,92 @@ +# This file contains the list of API's for storm control feature +# @author : Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + +from spytest import st +import utilities.common as utils_obj + +def config(dut, **kwargs): + """ + API to configure storm control on DUT + Author : Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param kwargs: bits_per_sec, type, interface_name, cli_type, no_form, action + :return: + """ + mandatory_args = ["bits_per_sec", "type", "interface_name"] + cli_type= kwargs["cli_type"] if "cli_type" in kwargs else "click" + no_form = kwargs["no_form"] if "no_form" in kwargs else "" + action = kwargs["action"] if "action" in kwargs else "add" + for arg in mandatory_args: + if arg not in kwargs: + st.log("Expecting {} value".format(arg)) + return False + if cli_type != "click": + if not no_form: + command = "storm-control {} {}".format(kwargs["type"], kwargs["bits_per_sec"]) + else: + command = "no storm-control {}".format(kwargs["type"]) + st.cli_config(dut, command, "mgmt-intf-config", interface=kwargs["interface_name"]) + else: + if action == "add": + command = "config interface storm-control {} {} {} {}".format(kwargs["type"], action, kwargs["interface_name"], kwargs["bits_per_sec"]) + else: + command = "config interface storm-control {} {} {}".format(kwargs["type"], action, kwargs["interface_name"]) + st.config(dut, command, skip_error_check=kwargs.get("skip_error_check", False)) + return True + +def show(dut, interface_name=None, cli_type="click"): + """ + API to show storm control configuration + Author : Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param interface_name: + :param type: + :param bits_per_sec: + :return: + """ + if not interface_name: + command = "show storm-control" if cli_type != "click" else "show storm-control all" + else: + command = "show storm-control interface {}".format(interface_name) + if cli_type != "click": + return st.cli_show(dut, command, "mgmt-user") + else: + return st.show(dut, command) + + +def verify_config(dut, interface_name=None, type=None, rate=None, cli_type="click"): + """ + API to verify storm control configuration + Author : Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param interface_name: + :param type: + :param bits_per_sec: + :return: + """ + output = show(dut, interface_name, cli_type=cli_type) + if not output: + st.log("Storm control data not found") + return False + match = dict() + if interface_name: + match["interface"] = interface_name + if type: + match["type"] = type + if rate: + match["rate"] = rate + entries = utils_obj.filter_and_select(output, None, match) + if match: + st.log("MATCH : {}".format(match)) + st.log("Entries: {}".format(entries)) + if not entries: + st.log("Entries not found ...") + return False + return True + else: + st.log("Type and rate not provided") + return False + + + + diff --git a/spytest/apis/system/switch_configuration.py b/spytest/apis/system/switch_configuration.py new file mode 100644 index 00000000000..6e209f4e6fc --- /dev/null +++ b/spytest/apis/system/switch_configuration.py @@ -0,0 +1,184 @@ +from spytest import st +import json +import pprint +import utilities.utils as utils_obj +import apis.system.basic as basic_obj + + +def verify_running_config(dut, table, object, attribute=None, value=None, max_retry=3): + """ + Verify running config value based on table, object, attribute + Author: Rakesh Kumar Vooturi (rakesh-kumar.vooturi@broadcom.com) + :param dut: + :param table: + :param object: + :param attribute: + :param value: + :return: + """ + command = "show runningconfiguration all" + data = basic_obj.get_show_command_data(dut, command, type="json") + if data is None: + st.log("Content not found ..") + return False + + st.log("verifying for Table: {}, Object: {}, Attribute: {} and Value: {} " + "in show runningconfiguration all o/p".format(table, object, attribute, value)) + + if table in data: + if object in data[table]: + if attribute is not None and value is not None: + if attribute in data[table][object]: + if data[table][object][attribute] == value: + st.log("Found the data in show running config all - {}".format(data[table][object])) + return True + else: + st.log("Did not find the data in show running config all - {}".format(data[table][object])) + return False + else: + st.log("Did not find the Table: {}, Object: {}, Attribute: {}" + " in show running config all".format(table, object, attribute)) + return False + else: + st.log("Found the data in show running config all - {}".format(data[table][object])) + return True + else: + st.log("Did not find the Table: {}, Object: {} in show running config all".format(table, object)) + return False + else: + st.log("Did not find the Table: {} in show running config all".format(table)) + return False + + +def get_running_config(dut, table=None, object=None, attribute=None, max_retry=3): + """ + Get running config value based on table, object, attribute + Author: Rakesh Kumar Vooturi (rakesh-kumar.vooturi@broadcom.com) + :param dut: + :param table: + :param object: + :param attribute: + :return: + """ + command = "sudo show runningconfiguration all" + i = 1 + while True: + try: + output = st.show(dut, command, skip_tmpl=True) + reg_output = utils_obj.remove_last_line_from_string(output) + data = eval(json.dumps(json.loads(reg_output))) + break + except Exception as e: + st.error("Exception occured in try-{} - {}".format(i,e)) + if i == max_retry: + st.error("MAX retry {} reached..".format(i)) + return None + i += 1 + try: + if table is None and object is None and attribute is None: + return data + elif table is not None and object is None and attribute is None: + return data[table] + elif table is not None and object is not None and attribute is None: + return data[table][object] + elif table is not None and object is not None and attribute is not None: + return data[table][object][attribute] + except Exception as e: + st.log(e) + return None + + +def verify_config_db(dut, table, object, attribute=None, value=None, max_retry=3): + """ + Verify Config DB json value based on table, object, attribute + Author: Rakesh Kumar Vooturi (rakesh-kumar.vooturi@broadcom.com) + :param dut: + :param table: + :param object: + :param attribute: + :param value: + :return: + """ + command = "cat /etc/sonic/config_db.json" + # Adding while loop and try/except to catch the truncated data issue. + i = 1 + while True: + try: + output = st.show(dut, command, skip_tmpl=True) + reg_output = utils_obj.remove_last_line_from_string(output) + data = eval(json.dumps(json.loads(reg_output))) + break + except Exception as e: + st.error("Exception occured in try-{} - {}".format(i,e)) + if i == max_retry: + st.error("MAX retry {} reached..".format(i)) + return False + i += 1 + + st.log("verifying for Table: {}, Object: {}, Attribute: {} and Value: {} in " + "config DB".format(table, object, attribute, value)) + + if table in data: + if object in data[table]: + if attribute is not None and value is not None: + if attribute in data[table][object]: + if data[table][object][attribute] == value: + st.log("Found the data in config DB - {}".format(data[table][object])) + return True + else: + st.log("Did not find the data in config DB - {}".format(data[table][object])) + return False + else: + st.log("Did not find the Table: {}, Object: {}, Attribute: {} " + "in config DB".format(table, object, attribute)) + return False + else: + st.log("Found the data in config DB - {}".format(data[table][object])) + return True + else: + st.log("Did not find the Table: {}, Object: {} in config DB".format(table, object)) + return False + else: + st.log("Did not find the Table: {} in config DB".format(table)) + return False + + +def get_config_db(dut, table=None, object=None, attribute=None): + """ + Get Config DB json value based on table, object, attribute + Author: Rakesh Kumar Vooturi (rakesh-kumar.vooturi@broadcom.com) + :param dut: + :param table: + :param object: + :param attribute: + :return: + """ + command = "cat /etc/sonic/config_db.json" + output = st.show(dut, command, skip_tmpl=True) + reg_output = utils_obj.remove_last_line_from_string(output) + try: + data = eval(json.dumps(json.loads(reg_output))) + if table is None and object is None and attribute is None: + return data + elif table is not None and object is None and attribute is None: + return data[table] + elif table is not None and object is not None and attribute is None: + return data[table][object] + elif table is not None and object is not None and attribute is not None: + return data[table][object][attribute] + except Exception as e: + st.log(e) + return None + + +def write_config_db(dut, data): + """ + To Write config to config DB + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param data: dictionary/ JSON format + :return: + """ + st.log("JSON Data Provided:") + st.log(pprint.pformat(data, width=2)) + return st.apply_json(dut, json.dumps(data)) diff --git a/spytest/bin/.pylintrc b/spytest/bin/.pylintrc new file mode 100644 index 00000000000..e69de29bb2d diff --git a/spytest/bin/clean.sh b/spytest/bin/clean.sh new file mode 100644 index 00000000000..34999ea4f44 --- /dev/null +++ b/spytest/bin/clean.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +cd $(dirname $0)/.. + +find . -name __pycache__ | xargs rm -rf +find . -name .pytest_cache | xargs rm -rf +find . -name "*.pyc" | xargs rm -f + diff --git a/spytest/bin/env b/spytest/bin/env new file mode 100644 index 00000000000..ef17d261633 --- /dev/null +++ b/spytest/bin/env @@ -0,0 +1,44 @@ +#!/bin/sh + +if [ -z "$SCID" ]; then + export SCID=/opt/projects/scid + if [ ! -d $SCID ]; then + export SCID=/projects/scid + else + echo "================== USING LOCAL SPYTEST TOOLS =================" + fi +fi + +if [ "$SCID_PYTHON_BIN" != "" ]; then + echo "USING Python From $SCID_PYTHON_BIN" +elif [ "$SPYTEST_PYTHON_VERSION" = "3.6.6" ]; then + export SCID_PYTHON_BIN=$SCID/tools/ActivPython/3.6.6/bin +elif [ "$SPYTEST_PYTHON_VERSION" = "3.7.1" ]; then + export SCID_PYTHON_BIN=$SCID/tools/ActivPython/3.7.1/bin +else + export SCID_PYTHON_BIN=$SCID/tools/ActivPython/venv/3.8.0/bin + export SCID_PYTHON_BIN=$SCID/tools/ActivPython/3.7.1/bin + export SCID_PYTHON_BIN=$SCID/tools/ActivPython/3.6.6/bin + export SCID_PYTHON_BIN=$SCID/tools/ActivPython/current/bin +fi + +export SCID_TGEN_PATH=$SCID/tgen +if [ -z "$SPYTEST_PYTHON" ]; then + export SPYTEST_PYTHON=$SCID_PYTHON_BIN/python +fi +export PATH=$SCID/tools/bin:$PATH + +if [ ! -f $SPYTEST_PYTHON ]; then + # use native python + SPYTEST_PYTHON=$(which python) +fi + +if [ -z "$SCID_TCL85_BIN" ]; then + export SCID_TCL85_BIN=$SCID/tools/ActivTcl/current/bin +fi +if [ ! -d $SCID_TCL85_BIN ]; then + # use native tcl 8.5 + SCID_TCL85_BIN=$(dirname $(which tclsh8.5)) +fi +export SCID_TCL84_BIN=$SCID/tools/tcl/8.4.20/bin + diff --git a/spytest/bin/generate_api_docs.sh b/spytest/bin/generate_api_docs.sh new file mode 100644 index 00000000000..cb03474ebec --- /dev/null +++ b/spytest/bin/generate_api_docs.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +dir=$(dirname $0) +dir=$(cd $dir;pwd -P) +ddir=$(cd $dir/..;pwd -P) + +pushd $ddir/docs/source +# create rst files +$dir/python -m sphinx.apidoc -f -o . ../.. +# create html documents +$dir/python -m sphinx $ddir/docs/source $ddir/docs/build +# open index in default browser +xdg-open $ddir/docs/build/index.html + diff --git a/spytest/bin/lint.sh b/spytest/bin/lint.sh new file mode 100644 index 00000000000..b08090e51a5 --- /dev/null +++ b/spytest/bin/lint.sh @@ -0,0 +1,112 @@ +#!/bin/sh + +dir=$(dirname $0) +dir=$(cd $dir;pwd -P) +ddir=$(cd $dir/..;pwd -P) + +IGNORE1="" +IGNORE2="" + +IGNORE1="$IGNORE1 --disable=W0311" #bad-indentation + +IGNORE1="$IGNORE1 --disable=C0103" #invalid-name +IGNORE1="$IGNORE1 --disable=C0111" #missing-docstring +IGNORE1="$IGNORE1 --disable=C0305" #trailing-newlines +IGNORE1="$IGNORE1 --disable=C0325" #superfluous-parens +IGNORE1="$IGNORE1 --disable=C0326" #bad-whitespace +IGNORE1="$IGNORE1 --disable=C0410" #multiple-imports +IGNORE1="$IGNORE1 --disable=C0413" #wrong-import-position + +IGNORE2="$IGNORE2 --disable=W0102" #dangerous-default-value +IGNORE2="$IGNORE2 --disable=W0105" #pointless-string-statement +IGNORE2="$IGNORE2 --disable=W0107" #unnecessary-pass +IGNORE2="$IGNORE2 --disable=W0201" #attribute-defined-outside-init +IGNORE2="$IGNORE2 --disable=W0212" #protected-access +IGNORE2="$IGNORE2 --disable=W0232" #no-init +IGNORE2="$IGNORE2 --disable=W0301" #unnecessary-semicolon +IGNORE2="$IGNORE2 --disable=W0312" #mixed-indentation +IGNORE2="$IGNORE2 --disable=W0401" #wildcard-import +IGNORE2="$IGNORE2 --disable=W0404" #reimported +IGNORE2="$IGNORE2 --disable=W0511" #fixme +IGNORE2="$IGNORE2 --disable=W0601" #global-variable-undefined +IGNORE2="$IGNORE2 --disable=W0603" #global-statement +IGNORE2="$IGNORE2 --disable=W0604" #global-at-module-level +#IGNORE2="$IGNORE2 --disable=W0611" #unused-import +IGNORE2="$IGNORE2 --disable=W0612" #unused-variable +IGNORE2="$IGNORE2 --disable=W0613" #unused-argument +IGNORE2="$IGNORE2 --disable=W0614" #unused-wildcard-import +IGNORE2="$IGNORE2 --disable=W0621" #redefined-outer-name +IGNORE2="$IGNORE2 --disable=W0622" #redefined-builtin +IGNORE2="$IGNORE2 --disable=W0702" #bare-except +IGNORE2="$IGNORE2 --disable=W0703" #broad-except + +IGNORE2="$IGNORE2 --disable=C0112" #empty-docstring +IGNORE2="$IGNORE2 --disable=C0113" #unneeded-not +IGNORE2="$IGNORE2 --disable=C0121" #singleton-comparison +IGNORE2="$IGNORE2 --disable=C0123" #unidiomatic-typecheck +IGNORE2="$IGNORE2 --disable=C0200" #consider-using-enumerate +IGNORE2="$IGNORE2 --disable=C0201" #consider-iterating-dictionary +IGNORE2="$IGNORE2 --disable=C0301" #line-too-long +IGNORE2="$IGNORE2 --disable=C0302" #too-many-lines +IGNORE2="$IGNORE2 --disable=C0303" #trailing-whitespace +IGNORE2="$IGNORE2 --disable=C0304" #missing-final-newline +IGNORE2="$IGNORE2 --disable=C0321" #multiple-statements +IGNORE2="$IGNORE2 --disable=C0330" #bad-continuation +IGNORE2="$IGNORE2 --disable=C0411" #wrong-import-order +IGNORE2="$IGNORE2 --disable=C0412" #ungrouped-imports +IGNORE2="$IGNORE2 --disable=C1001" #old-style-class +IGNORE2="$IGNORE2 --disable=C1801" #len-as-condition + +IGNORE2="$IGNORE2 --disable=E0632" #unbalanced-tuple-unpacking +IGNORE2="$IGNORE2 --disable=E1305" #too-many-format-args + +IGNORE2="$IGNORE2 --disable=R0101" #too-many-nested-blocks +IGNORE2="$IGNORE2 --disable=R0102" #simplifiable-if-statement +IGNORE2="$IGNORE2 --disable=R0201" #no-self-use +IGNORE2="$IGNORE2 --disable=R0205" #useless-object-inheritance +IGNORE2="$IGNORE2 --disable=R0902" #too-many-instance-attributes +IGNORE2="$IGNORE2 --disable=R0903" #too-few-public-methods +IGNORE2="$IGNORE2 --disable=R0904" #too-many-public-methods +IGNORE2="$IGNORE2 --disable=R0911" #too-many-return-statements +IGNORE2="$IGNORE2 --disable=R0912" #too-many-branches +IGNORE2="$IGNORE2 --disable=R0913" #too-many-arguments +IGNORE2="$IGNORE2 --disable=R0914" #too-many-locals +IGNORE2="$IGNORE2 --disable=R0915" #too-many-statements +IGNORE2="$IGNORE2 --disable=R0916" #too-many-boolean-expressions +IGNORE2="$IGNORE2 --disable=R1705" #no-else-return +IGNORE2="$IGNORE2 --disable=R1710" #inconsistent-return-statements +IGNORE2="$IGNORE2 --disable=R1710" #useless-return + +IGNORE="$IGNORE1" +IGNORE="$IGNORE1 $IGNORE2" + +LINT="$dir/python3 -m pylint --rcfile=$dir/.pylintrc $IGNORE" +LINT="$dir/python -m pylint --rcfile=$dir/.pylintrc $IGNORE" +LINT2="$dir/python -m pyflakes" +LINT3="$dir/python -m flake8 --select F --ignore=F401,F841" + +if [ $# -eq 0 ]; then + files1=$(find $ddir/spytest/ -name "*.py" | grep -v __init__.py | grep -v $ddir/spytest/ddm | grep -v $ddir/spytest/tg) + files2=$(find $ddir/scheduler/ -name "*.py" | grep -v __init__.py) + files3=$(find $ddir/apis/ -name "*.py" | grep -v __init__.py) + files4=$(find $ddir/utilities/ -name "*.py" | grep -v __init__.py | grep -v ipaddress.py) + files5=$(find $ddir/tests/ -name "*.py" | grep -v __init__.py | grep -v $ddir/tests/ddm) + files="$files1 $files2 $files3 $files4 $files5" +else + files="" + for arg in $*; do + [ -f $arg ] && files="$files $arg" + [ -d $arg ] && files="$files $(find $arg -name '*.py' | grep -v __init__.py)" + done +fi + +rm -f errors.log +for f in $files;do + #echo ================== FLAKES8 $f | tee -a errors.log + #$LINT3 $f 2>&1 | tee -a errors.log + #echo ================== PYFLAKES $f | tee -a errors.log + #$LINT2 $f 2>&1 | tee -a errors.log + echo ================== PYLINT $f | tee -a errors.log + $LINT $f 2>&1 | grep -v "Using config file " | tee -a errors.log +done + diff --git a/spytest/bin/python b/spytest/bin/python new file mode 100644 index 00000000000..654ba8fb25c --- /dev/null +++ b/spytest/bin/python @@ -0,0 +1,17 @@ +#!/bin/sh + +dir=$(dirname $0) +dir=$(cd $dir;pwd -P) +ddir=$(cd $dir/..;pwd -P) + +# sourde environment +. $dir/env + +if [ "$SPYTEST_PYTHON_VERSION" != "3.6.6" -a "$SPYTEST_PYTHON_VERSION" != "3.7.1" ]; then + PYTHONPATH=$($SPYTEST_PYTHON -c 'import site; print(site.getsitepackages()[0])') +fi +export PYTHONPATH=$PYTHONPATH:$ddir:$ddir/spytest:. +export PATH=$dir:$PATH + +exec $SPYTEST_PYTHON "$@" + diff --git a/spytest/bin/requirements0.txt b/spytest/bin/requirements0.txt new file mode 100644 index 00000000000..6cc90a5b33b --- /dev/null +++ b/spytest/bin/requirements0.txt @@ -0,0 +1,25 @@ +pyfiglet +pylint==1.8.1 +textfsm +netmiko==2.4.2 +pytest>=4.4.1,<=4.6.5 +pytest-repeat +pytest-timeout +pytest-xdist==1.28.0 +gitpython +ansible +jinja2 +future>=0.16.0 +psutil +prettytable +tabulate +pycryptodome +cryptography >= 2.5 +natsort +redis +requests +jsonpatch +rpyc +Pyro4 +scapy==2.4.3rc1 +netaddr diff --git a/spytest/bin/requirements1.txt b/spytest/bin/requirements1.txt new file mode 100644 index 00000000000..95028ee64ba --- /dev/null +++ b/spytest/bin/requirements1.txt @@ -0,0 +1,13 @@ +pyflakes +flake8 +autoflake +autopep8 +scapy==2.4.3rc1 +sphinx +python-jenkins +jira +pysnmp +pyang +mmh3 +Pyro4 +protobuf diff --git a/spytest/bin/spytest b/spytest/bin/spytest new file mode 100644 index 00000000000..eed5cd9457b --- /dev/null +++ b/spytest/bin/spytest @@ -0,0 +1,32 @@ +#!/bin/sh + +''':' +export LD_LIBRARY_PATH=/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH +exec $(dirname $0)/python "$0" "$@" +''' + +import os, re, sys, warnings + +warnings.filterwarnings(action='ignore', module='.*paramiko.*') + +root = os.path.join(os.path.dirname(__file__), '..') +root = os.path.abspath(root) +sys.path.append(os.path.join(root)) +sys.path.append(os.path.join(root, "apis")) +os.environ["SPYTEST_USER_ROOT"] = os.path.abspath(".") + +if __name__ == '__main__': + arg_list = [] + for arg in sys.argv[1:]: + if " " in arg: + arg_list.append("'{}'".format(arg)) + else: + arg_list.append(arg) + os.environ["SPYTEST_CMDLINE_ARGS"] = " ".join(arg_list) + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.argv.insert(1, "-c") + sys.argv.insert(2, os.path.join(root, "spytest", "pytest.ini")) + sys.argv.extend(["-p", "splugin"]) + os.chdir(os.path.join(root, "tests")) + from spytest.main import main + sys.exit(main()) diff --git a/spytest/bin/sshpass b/spytest/bin/sshpass new file mode 100644 index 00000000000..73cf8879a3b Binary files /dev/null and b/spytest/bin/sshpass differ diff --git a/spytest/bin/tgen_folders.txt b/spytest/bin/tgen_folders.txt new file mode 100644 index 00000000000..45823c63deb --- /dev/null +++ b/spytest/bin/tgen_folders.txt @@ -0,0 +1,1211 @@ +/projects/scid/tgen/ixia/8.42/ +/projects/scid/tgen/ixia/8.42/lib +/projects/scid/tgen/ixia/8.42/lib/hltapi +/projects/scid/tgen/ixia/8.42/lib/hltapi/library +/projects/scid/tgen/ixia/8.42/lib/hltapi/library/dependencies +/projects/scid/tgen/ixia/8.42/lib/hltapi/library/common +/projects/scid/tgen/ixia/8.42/lib/hltapi/library/common/ixia_hl_lib-8.42 +/projects/scid/tgen/ixia/8.42/lib/hltapi/library/common/ixiangpf +/projects/scid/tgen/ixia/8.42/lib/hltapi/library/common/ixiangpf/tcl +/projects/scid/tgen/ixia/8.42/lib/hltapi/library/common/ixiangpf/documentation +/projects/scid/tgen/ixia/8.42/lib/hltapi/library/common/ixiangpf/perl +/projects/scid/tgen/ixia/8.42/lib/hltapi/library/common/ixiangpf/python +/projects/scid/tgen/ixia/8.42/lib/hltapi/library/common/ixiangpf/python/ixiangpf_commands +/projects/scid/tgen/ixia/8.42/lib/hltapi/library/common/ixiangpf/python/ixiangpf_commands/__pycache__ +/projects/scid/tgen/ixia/8.42/lib/hltapi/library/common/ixiangpf/python/__pycache__ +/projects/scid/tgen/ixia/8.42/lib/hltapi/library/common/robot_plugin +/projects/scid/tgen/ixia/8.42/lib/hltapi/library/utracker +/projects/scid/tgen/ixia/8.42/lib/hltapi/bin +/projects/scid/tgen/ixia/8.42/lib/IxTclNetwork +/projects/scid/tgen/ixia/8.42/lib/IxTclNetwork/Generic +/projects/scid/tgen/ixia/8.42/lib/IxPublisher +/projects/scid/tgen/ixia/8.42/lib/IxTclNetworkConnector +/projects/scid/tgen/ixia/8.42/lib/Scriptgen +/projects/scid/tgen/ixia/8.42/lib/ssh +/projects/scid/tgen/ixia/8.42/lib/PerlApi +/projects/scid/tgen/ixia/8.42/lib/ixtc +/projects/scid/tgen/ixia/8.42/lib/sftp +/projects/scid/tgen/ixia/8.42/lib/PythonApi +/projects/scid/tgen/ixia/8.42/lib/PythonApi/__pycache__ +/projects/scid/tgen/ixia/8.42/lib/RubyApi +/projects/scid/tgen/ixia/8.42/lib/IxTclProtocol +/projects/scid/tgen/ixia/8.42/lib/IxTclProtocol/Generic +/projects/scid/tgen/ixia/8.42/lib/ixTclServices +/projects/scid/tgen/ixia/8.42/lib/ixTclServices/Generic +/projects/scid/tgen/ixia/8.42/lib/ixTcl1.0 +/projects/scid/tgen/ixia/8.42/lib/ixTcl1.0/Dap +/projects/scid/tgen/ixia/8.42/lib/ixTcl1.0/Generic +/projects/scid/tgen/ixia/8.42/lib/scriptgen-protocols +/projects/scid/tgen/ixia/8.42/lib/scriptgen-protocols/Protocols +/projects/scid/tgen/stc/4.91/ +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/HltAPI +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/HltAPI/SourceCode +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/HltAPI/SourceCode/hltapiPythonWrapper +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/HltAPI/SourceCode/hltapiForPython +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/HltAPI/SourceCode/hltapiForPython/__pycache__ +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/HltAPI/SourceCode/sqllibraries +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/HltAPI/SourceCode/sqllibraries/SunOS +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/HltAPI/SourceCode/sqllibraries/Linux +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/HltAPI/SourceCode/sqllibraries/Windows +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/HltAPI/SourceCode/hltapiForPerl +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/HltAPI/SourceCode/model +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/HltAPI/SourceCode/hltapiPerlWrapper +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/HltAPI/SourceCode/hltapiWrapper +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/HltAPI/SourceCode/tools +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/HltAPI/SourceCode/tools/iTest +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/HltAPI/SampleScripts +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/HltAPI/SampleScripts/hltapi +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/HltAPI/SampleScripts/hltapiForPython +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/HltAPI/SampleScripts/hltapiForPerl +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/HltAPI/SampleScripts/hltapiForRobot +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/stcsessions +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Tcl +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Tcl/lib +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Tcl/lib/tcl8.5 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Tcl/lib/tcl8.5/opt0.4 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Tcl/lib/tcl8.5/encoding +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Tcl/lib/tcl8.5/msgs +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Tcl/lib/tcl8.5/http1.0 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Tcl/lib/tcl8 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Tcl/lib/tcl8/8.4 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Tcl/lib/tcl8/8.4/platform +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Tcl/lib/tcl8/8.5 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Tcl/bin +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/cloudstress +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/testintel +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/results +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/results/res +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/results/res/dhcpv4 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/results/res/isis +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/results/res/user +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/results/res/ieee80211 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/results/res/igmpmld +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/results/res/bgp +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/results/res/view +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/results/res/core +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/results/res/dhcpv6 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/results/res/l2l3 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/results/res/ieee1588v2 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/results/event +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/results/event/core +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/results/dim +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/results/dim/dhcpv4 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/results/dim/isis +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/results/dim/user +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/results/dim/ieee80211 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/results/dim/igmpmld +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/results/dim/bgp +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/results/dim/core +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/results/dim/dhcpv6 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/results/dim/l2l3 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/results/dim/ieee1588v2 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/results/schema +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/methodology +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/methodology/utils +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/methodology/traffic +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/methodology/results +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/methodology/schema +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/methodology/manager +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/methodology/manager/utils +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/methodology/sampling +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/methodology/routing +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/methodology/routing/utils +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/methodology/portsetup +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/methodology/api +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/methodology/api/utils +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/xtapi +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/proto +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/proto/multihoming +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/vcm +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/switching +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/switching/ose +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/switching/vxlan +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/switching/openflow +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/LagCapture +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/core +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/core/utils +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/routing +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/routing/tsn +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/routing/tsn/tsnWizardUnitTestConfig +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/routing/tsn/Core +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/routing/tsn/Core/Utils +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/routing/utils +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/routing/srp +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/routing/srp/avbUnitTestConfig +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/routing/srp/Core +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/routing/srp/Core/Utils +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/routing/evpnmpls +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/routing/evpn +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/cars +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/telemetry +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/datacenter +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/datacenter/vxlanUnitTestConfig +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/datacenter/oseUnitTestConfig +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/datacenter/Core +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKCommands/spirent/datacenter/Core/Utils +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Utilities +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Utilities/Results +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Utilities/Results/lib +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Utilities/Results/lib/components +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Utilities/Results/lib/components/analytic +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Utilities/Results/lib/js +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Utilities/Results/lib/js/themes +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Utilities/Results/lib/js/adapters +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Utilities/Results/lib/js/modules +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Utilities/Results/lib/style +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Utilities/Results/lib/fonts +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Utilities/Results/images +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/TLVs +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/TLVs/VIC +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/TLVs/BFD-CV +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/TLVs/ANCP +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/TLVs/MPLS-TPOAM +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/TLVs/CSMP +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/TLVs/LLDP +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/TLVs/EOAM +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/TLVs/OPENFLOW +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/TLVs/LSPPING +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/Filters +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/Traffic +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/Traffic/Capture +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/Traffic/Capture/WlanFilters +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/Traffic/L2L3 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/Traffic/L2L3/FrameLengthDistribution +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/Traffic/L2L3/CountersResultModeMap +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/Mii +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/Qos +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/Qos/Fwd +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Filters +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Filters/PortConfigTableView +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/TSN +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/TSN/SRP Results +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/TSN/IEEE 8021AS Results +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Switching Protocols +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Switching Protocols/EVB +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Switching Protocols/VIC +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Switching Protocols/OTV +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Switching Protocols/OSE +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Switching Protocols/CDCP +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Switching Protocols/VXLAN +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Switching Protocols/FCoE VN_Port Results +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Switching Protocols/Openflow +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Switching Protocols/TRILL +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Switching Protocols/OVSDB +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Switching Protocols/VDP +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Switching Protocols/DCBX +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Carrier Ethernet +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Carrier Ethernet/TWAMP Results +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Carrier Ethernet/EOAM Results +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Carrier Ethernet/IEEE 1588v2 Results +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Carrier Ethernet/SyncE Results +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/External Device +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Stream Results +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Frame Response +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Port Protocols +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Port Traffic +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Event Manager +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Event Manager/Control-Plane Convergence +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Wireless LAN +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Application Layer Protocols +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Video Quality +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Access Protocols +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Access Protocols/IGMP-MLD Group Results +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Routing Protocols +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Routing Protocols/BFD Session Results +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Routing Protocols/Echo Request - Trace Route Results +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Routing Protocols/PCEP Results +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Routing Protocols/EVPN Stream Results +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Routing Protocols/LDP-RSVP LSP Results +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Routing Protocols/MSDP Results +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Routing Protocols/MPLS-TP Y1731 OAM Results +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/Routing Protocols/MPLS-TP Results +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Result Views/IPTV +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/EOT Result Views +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/EOT Result Views/Stream Results +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/EOT Result Views/Port Traffic +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/EOT Result Views/Routing Protocols +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/EOT Result Views/Routing Protocols/LDP-RSVP LSP Results +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/System/Perspectives +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/Mdio +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/EnhancedResults +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/EnhancedResults/Profiles +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/EnhancedResults/Views +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/EnhancedResults/Views/DerivedViews +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/EnhancedResults/Reports +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/ResultProvider +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/ExternalDevice +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/ExternalDevice/Spirent +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/ExternalDevice/Spirent/test_cases +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/ExternalDevice/Spirent/test_cases/Cisco7600 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/ExternalDevice/Spirent/test_cases/JuniperMx480 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/ExternalDevice/Spirent/test_cases/DemoDUT +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/ExternalDevice/Spirent/test_cases/Cisco6500 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/ExternalDevice/Spirent/session_profiles +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/ExternalDevice/EDCScript +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/ExternalDevice/EDCScript/iTest +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Templates/Interfaces +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/orion-res +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/orion-res/etc +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/orion-res/content +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/orion-res/content/report +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/orion-res/content/report/html-template +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/orion-res/content/report/html-template/styles +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/orion-res/content/report/html-template/styles/fonts +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/orion-res/content/report/html-template/images +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/orion-res/content/report/scripts +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/orion-res/content/report/scripts/lib +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/orion-res/content/report/scripts/lib/highcharts +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/orion-res/content/static +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/orion-res/content/static/js +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/orion-res/content/static/media +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/orion-res/content/static/css +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/orion-res/bin +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Results +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Results/Influx +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Results/Grafana +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Results/InitRealTime +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Templates +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Scripts +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/ISIS_CONVERGENCE +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/RTR_ROUTE_SCALE +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/Y1564CONFIG +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/RTRSCALEOSPFV2 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/RFC2889FP +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/CLOUDSTRESS_NETWORKBENCHMARK_FIXEDLOAD +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/BGPRR_RS6 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/VSW_IPPLATENCY +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/CLOUDSTRESS_CPUBENCHMARK_FIXEDLOAD +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/CLOUDSTRESS_CPUBENCHMARK_FIXEDCOUNT +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/RFC2544BACKTOBACK +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/VSW_TPUT_LATENCY_EXP +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/VSW_FLOWSCALE_EXP +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/RFC3918AGGMCTHRUPUT +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/RTRSCALEISISV6 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/Y1564SVCCONF +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/CLOUDSTRESS_NETWORKBENCHMARK_FIXEDCOUNT +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/IEEE8021QOS +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/DHCPPDOPPPOXRATE +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/VSW_FLOWSCALE +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/RFC2889FORWARDINGRATE +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/CS_AUTODEPLOY_AGENTS +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/RTRSCALEBGP +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/PING_TEST +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/SAARATE +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/RFC2544FRAMELOSS +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/PPPOL2TPV2RATE +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/NFVI_ANALYTICS +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/CLOUDSTRESS_MEMORYBENCHMARK_FIXEDLOAD +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/DHCPRATE +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/SAASCALE +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/IEEE8021VLANSCALE +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/RFC2544THROUGHPUT +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/VSW_PERFCONS +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/VSW_PERFCONS_EXP +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/DSCPQOSV6 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/DHCPSCALE +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/RFC2544LATENCY +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/VSW_TPUT_LATENCY +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/RFC2889BROADCASTFORWARDING +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/RFC2889EFF +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/RTRSCALEISIS +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/DHCPPDOPPPOXSCALE +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/ISIS_SR_TE_CONVERGENCE +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/PPPOL2TPV2SCALE +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/RFC6349 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/IPTV_MULTICHANNEL +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/RFC2889ADDRLEARNRATE +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/CLOUDSTRESS_STORAGEBENCHMARK_FIXEDLOAD +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/CLOUDSTRESS_BENCHMARK_FIXEDLOAD +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/VSW_IPPLATENCY_EXP +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/PPPOXSCALE +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/RFC2889BROADCASTLATENCY +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/RFC2889ADDRCACHECAPACITY +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/CLOUDSTRESS_MEMORYBENCHMARK_FIXEDCOUNT +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/PPPOXRATE +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/BFD_ISIS_CONVERGENCE +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/BGPRR_RI4 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/Y1564PERF +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/CS_TVLP_CAPACITY +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/CLOUDSTRESS_STORAGEBENCHMARK_FIXEDCOUNT +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/DSCPQOSV4 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/RFC3918SCGRPFWDMATRIX +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/RTRSCALEBGPV6 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/BGPRR_FS4 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/DDOS +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/CLOUDSTRESS_BENCHMARK_FIXEDCOUNT +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/RFC3918MCLATENCY +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/SILVER_PEAK_POC +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/RTRSCALEOSPFV3 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/Y1564TEST +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/BGPRR_RI6 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/RTRSCALE +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/RFC2889MFR +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Methodology/Packages/BGPRR_RS4 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/lib +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/wizards +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/templates +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/templates/custom +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/locale +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/locale/zh_TW.BIG5 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/locale/zh_TW.BIG5/LC_MESSAGES +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/locale/it +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/locale/it/LC_MESSAGES +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/locale/zh +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/locale/zh/LC_MESSAGES +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/locale/zh.GBK +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/locale/zh.GBK/LC_MESSAGES +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/locale/zh_TW +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/locale/zh_TW/LC_MESSAGES +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/locale/ja +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/locale/ja/LC_MESSAGES +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/locale/fr +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/locale/fr/LC_MESSAGES +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/locale/es +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/locale/es/LC_MESSAGES +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/locale/zh_HK.BIG5HK +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/locale/zh_HK.BIG5HK/LC_MESSAGES +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/locale/ko.UTF-8 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/locale/ko.UTF-8/LC_MESSAGES +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/locale/ko +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/locale/ko/LC_MESSAGES +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/locale/de +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/locale/de/LC_MESSAGES +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/locale/sv +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/locale/sv/LC_MESSAGES +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/im +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/applications +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/mime +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/mime/packages +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/HighContrast +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/HighContrast/16x16 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/HighContrast/16x16/mimetypes +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/HighContrast/16x16/apps +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/HighContrast/48x48 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/HighContrast/48x48/mimetypes +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/HighContrast/48x48/apps +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/HighContrastInverse +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/HighContrastInverse/16x16 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/HighContrastInverse/16x16/mimetypes +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/HighContrastInverse/16x16/apps +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/HighContrastInverse/48x48 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/HighContrastInverse/48x48/mimetypes +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/HighContrastInverse/48x48/apps +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/LowContrast +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/LowContrast/16x16 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/LowContrast/16x16/mimetypes +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/LowContrast/16x16/apps +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/LowContrast/48x48 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/LowContrast/48x48/mimetypes +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/LowContrast/48x48/apps +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/hicolor +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/hicolor/16x16 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/hicolor/16x16/mimetypes +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/hicolor/16x16/apps +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/hicolor/48x48 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/hicolor/48x48/mimetypes +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/desktop/icons/hicolor/48x48/apps +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/zi +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/zi/Indian +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/zi/Europe +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/zi/Etc +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/zi/Pacific +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/zi/SystemV +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/zi/Africa +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/zi/Australia +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/zi/America +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/zi/America/Indiana +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/zi/America/Argentina +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/zi/America/North_Dakota +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/zi/America/Kentucky +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/zi/Atlantic +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/zi/Antarctica +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/zi/Asia +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/audio +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/management +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/servicetag +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/ext +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/amd64 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/amd64/xawt +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/amd64/native_threads +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/amd64/headless +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/amd64/server +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/amd64/motif21 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/amd64/jli +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/deploy +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/oblique-fonts +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/cmm +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/images +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/images/cursors +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/images/icons +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/security +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/lib/fonts +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/bin +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/plugin +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/plugin/desktop +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/.systemPrefs +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/_jvm/javaws +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/bin +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/docs +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/docs/WebHelp +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/docs/WebHelp/Content +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/docs/WebHelp/Content/base +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/docs/WebHelp/Content/SkinSupport +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/docs/WebHelp/Content/images +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/docs/WebHelp/Skin +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/docs/WebHelp/Skin/Images +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/docs/WebHelp/Data +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/results_reporter/docs/WebHelp/Data/Skintoc +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/pkgconfig +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/json +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/json/tests +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/multiprocessing +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/multiprocessing/dummy +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/compiler +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/lib2to3 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/lib2to3/fixes +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/lib2to3/pgen2 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/wsgiref +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/xml +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/xml/parsers +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/xml/etree +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/xml/sax +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/xml/dom +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/hotshot +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/ctypes +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/ctypes/macholib +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/ctypes/test +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/encodings +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/ensurepip +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/ensurepip/_bundled +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/unittest +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/unittest/test +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/idlelib +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/idlelib/idle_test +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/idlelib/Icons +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/sqlite3 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/sqlite3/test +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/plat-linux2 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/logging +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/email +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/email/test +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/email/test/data +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/email/mime +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/lib-tk +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/lib-tk/test +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/lib-tk/test/test_ttk +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/lib-tk/test/test_tkinter +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/curses +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/pydoc_data +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/distutils +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/distutils/tests +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/distutils/command +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/config +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/bsddb +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/bsddb/test +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pytest_cov +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pyflakes-1.6.0.dist-info +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pytest_flakes-2.0.0.dist-info +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/functools32-3.2.3.post2.dist-info +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/funcsigs +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pytest_boxed +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/xdist +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/xdist/scheduler +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pkg_resources +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pkg_resources/extern +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pkg_resources/_vendor +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pkg_resources/_vendor/packaging +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/setuptools-36.6.0.dist-info +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/virtualenv-15.1.0.dist-info +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/_pytest +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/_pytest/_code +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/_pytest/vendored_packages +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/_pytest/assertion +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pytest_forked +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pbr +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pbr/tests +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pbr/tests/testpackage +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pbr/tests/testpackage/doc +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pbr/tests/testpackage/doc/source +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pbr/tests/testpackage/src +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pbr/tests/testpackage/data_files +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pbr/tests/testpackage/pbr_testpackage +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pbr/tests/testpackage/pbr_testpackage/package_data +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pbr/cmd +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pbr/hooks +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pbr-3.1.1.dist-info +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/six-1.11.0.dist-info +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/execnet-1.5.0.dist-info +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/execnet +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/execnet/script +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/mock-2.0.0.dist-info +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/jsonschema +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/jsonschema/schemas +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/jsonschema/tests +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pep8-1.7.1.dist-info +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pytest-3.2.3.dist-info +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/py-1.4.34.dist-info +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/mock +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/mock/tests +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pytest_cache-1.0.dist-info +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/jsonschema-2.6.0.dist-info +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/jsonref-0.1.dist-info +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/virtualenv_support +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/funcsigs-1.0.2.dist-info +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/apipkg-1.4.dist-info +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/coverage +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/coverage/htmlfiles +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/coverage/fullcoverage +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/setuptools +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/setuptools/extern +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/setuptools/command +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pytest_pep8-1.0.6.dist-info +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/functools32 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pyflakes +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pyflakes/test +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pyflakes/scripts +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/cov_core-1.15.0.dist-info +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pytest_cov-2.5.1.dist-info +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/py +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/py/_path +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/py/_log +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/py/_code +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/py/_process +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/py/_io +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pytest_forked-0.2.dist-info +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/pytest_xdist-1.20.1.dist-info +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/site-packages/coverage-4.4.1.dist-info +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/lib-dynload +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/lib/python2.7/importlib +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Python/bin +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKvirtualenvs +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKvirtualenvs/__requirements__ +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/STAKvirtualenvs/__packages__ +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/TLSSampleFiles +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/TLSSampleFiles/ServerFiles +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/TLSSampleFiles/ClientFiles +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/stclib +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/mustache.js +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/mustache.js/wrappers +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/mustache.js/wrappers/dojo +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/mustache.js/wrappers/yui3 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/mustache.js/wrappers/qooxdoo +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/mustache.js/wrappers/mootools +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/mustache.js/wrappers/jquery +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/mustache.js/test +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/mustache.js/test/_files +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/mustache.js/spec +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/mustache.js/spec/_files +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/mustache.js/bin +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/mustache.js/hooks +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/img +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/test +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/test/qunit +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/dist +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/dist/plugins +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/dist/plugins/sigma.layouts.forceLink +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/dist/plugins/sigma.layouts.forceAtlas2 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.plugins.design +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.exporters.svg +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.plugins.edgeSiblings +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.layouts.forceLink +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.layouts.forceLink/tasks +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.renderers.glyphs +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.renderers.edgeLabels +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.layouts.dagre +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.plugins.activeState +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.exporters.spreadsheet +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.plugins.leaflet +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.exporters.json +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.plugins.fullScreen +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.exporters.xlsx +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.plugins.animate +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.plugins.lasso +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.plugins.colorbrewer +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.statistics.HITS +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.parsers.json +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.plugins.keyboard +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.renderers.customEdgeShapes +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.layouts.noverlap +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.exporters.gexf +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.exporters.graphml +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.plugins.poweredBy +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.helpers.graph +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.exporters.image +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.plugins.neighborhoods +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.layouts.forceAtlas2 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.layouts.forceAtlas2/tasks +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.plugins.locate +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.pathfinding.astar +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.plugins.filter +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.parsers.gexf +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.renderers.customShapes +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.plugins.legend +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.renderers.linkurious +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.renderers.linkurious/canvas +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.renderers.linkurious/svg +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.renderers.linkurious/webgl +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.layouts.fruchtermanReingold +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.plugins.dragNodes +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.plugins.relativeSize +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.statistics.louvain +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.plugins.tooltips +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.renderers.halo +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.parsers.cypher +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.plugins.generators +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/plugins/sigma.plugins.select +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/examples +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/examples/lib +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/examples/screens +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/examples/img +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/examples/data +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/examples/fonts +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/src +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/src/middlewares +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/src/utils +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/src/renderers +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/src/renderers/canvas +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/src/renderers/svg +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/src/renderers/webgl +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/src/captors +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/src/misc +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/src/classes +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/topopreviewer/linkurious.js/scripts +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/SampleScripts +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Pga +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Pga/L2l3query +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/NTAF +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/NTAF/tool +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/NTAF/harness +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/NTAF/harness/STC +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/NTAF/harness/STC/3.80 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/NTAF/harness/STC/3.80/Triple Play +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/NTAF/harness/STC/3.80/Carrier Ethernet +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/NTAF/harness/STC/3.80/Access +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/NTAF/harness/STC/3.80/Access/IGMP +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/NTAF/harness/STC/3.80/Switching +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/NTAF/harness/STC/3.80/Routing and MPLS +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/NTAF/harness/STC/3.80/Routing and MPLS/MPLS +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/NTAF/harness/STC/3.80/Application Layer Protocols +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/NTAF/harness/STC/4.20 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/NTAF/harness/STC/4.20/Carrier Ethernet +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/NTAF/harness/STC/4.20/Access +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/NTAF/harness/STC/4.20/Switching +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/NTAF/harness/STC/4.10 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/NTAF/harness/STC/4.10/Carrier Ethernet +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/NTAF/harness/STC/4.10/Access +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/NTAF/harness/STC/4.10/Switching +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/NTAF/harness/STC/4.10/Routing and MPLS +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/NTAF/harness/STC/4.10/Routing and MPLS/MPLS +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/NTAF/harness/STC/4.10/Application Layer Protocols +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/NTAF/harness/STC/4.03 +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/Playlists +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/Playlists/System +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/Playlists/System/Remote_Desktop +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/Playlists/System/Yahoo +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/Playlists/System/Gnutella +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/Playlists/System/Gtalk +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/Playlists/System/base_threats +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/Playlists/System/Exchange +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/Playlists/System/Skype +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/Playlists/System/SMB_DCERPC_for_Printer +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/Playlists/System/Oracle +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/Playlists/System/Bittorrent +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/Playlists/System/SQL +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/Playlists/System/mysql +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/Playlists/System/LDAP +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/Playlists/System/MSN +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/Playlists/System/NFS +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/FlowDefs +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/FlowDefs/System +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/FlowDefs/System/Remote_Desktop +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/FlowDefs/System/Yahoo +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/FlowDefs/System/Gnutella +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/FlowDefs/System/Gtalk +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/FlowDefs/System/base_threats +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/FlowDefs/System/Exchange +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/FlowDefs/System/Skype +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/FlowDefs/System/SMB_DCERPC_for_Printer +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/FlowDefs/System/Oracle +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/FlowDefs/System/Bittorrent +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/FlowDefs/System/SQL +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/FlowDefs/System/mysql +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/FlowDefs/System/LDAP +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/FlowDefs/System/MSN +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/FlowDefs/System/NFS +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/Npe +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/Dpg/Npe/Cifs +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/PDMLMap +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/PDMLMap/HelperFiles +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/PDUs +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/RcmDb +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/API +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/API/Tcl +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/API/C +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/API/Perl +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/API/Python +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/API/CSharp +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/API/Java +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/API/Java/com +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/API/Java/com/spirent +/projects/scid/tgen/stc/4.91/Spirent_TestCenter_Application_Linux/API/Ruby +/projects/scid/tgen/ixia/all/ixia/ +/projects/scid/tgen/ixia/all/ixia/hlapi +/projects/scid/tgen/ixia/all/ixia/hlapi/9.00.1977.39 +/projects/scid/tgen/ixia/all/ixia/hlapi/9.00.1977.39/library +/projects/scid/tgen/ixia/all/ixia/hlapi/9.00.1977.39/library/dependencies +/projects/scid/tgen/ixia/all/ixia/hlapi/9.00.1977.39/library/common +/projects/scid/tgen/ixia/all/ixia/hlapi/9.00.1977.39/library/common/ixiangpf +/projects/scid/tgen/ixia/all/ixia/hlapi/9.00.1977.39/library/common/ixiangpf/tcl +/projects/scid/tgen/ixia/all/ixia/hlapi/9.00.1977.39/library/common/ixiangpf/documentation +/projects/scid/tgen/ixia/all/ixia/hlapi/9.00.1977.39/library/common/ixiangpf/perl +/projects/scid/tgen/ixia/all/ixia/hlapi/9.00.1977.39/library/common/ixiangpf/python +/projects/scid/tgen/ixia/all/ixia/hlapi/9.00.1977.39/library/common/ixiangpf/python/ixiangpf_commands +/projects/scid/tgen/ixia/all/ixia/hlapi/9.00.1977.39/library/common/ixiangpf/python/ixiangpf_commands/__pycache__ +/projects/scid/tgen/ixia/all/ixia/hlapi/9.00.1977.39/library/common/robot_plugin +/projects/scid/tgen/ixia/all/ixia/hlapi/9.00.1977.39/library/common/ixia_hl_lib-9.00 +/projects/scid/tgen/ixia/all/ixia/hlapi/9.00.1977.39/library/utracker +/projects/scid/tgen/ixia/all/ixia/hlapi/9.00.1977.39/bin +/projects/scid/tgen/ixia/all/ixia/hlapi/8.42.0.6 +/projects/scid/tgen/ixia/all/ixia/hlapi/8.42.0.6/library +/projects/scid/tgen/ixia/all/ixia/hlapi/8.42.0.6/library/dependencies +/projects/scid/tgen/ixia/all/ixia/hlapi/8.42.0.6/library/common +/projects/scid/tgen/ixia/all/ixia/hlapi/8.42.0.6/library/common/ixia_hl_lib-8.42 +/projects/scid/tgen/ixia/all/ixia/hlapi/8.42.0.6/library/common/ixiangpf +/projects/scid/tgen/ixia/all/ixia/hlapi/8.42.0.6/library/common/ixiangpf/tcl +/projects/scid/tgen/ixia/all/ixia/hlapi/8.42.0.6/library/common/ixiangpf/documentation +/projects/scid/tgen/ixia/all/ixia/hlapi/8.42.0.6/library/common/ixiangpf/perl +/projects/scid/tgen/ixia/all/ixia/hlapi/8.42.0.6/library/common/ixiangpf/python +/projects/scid/tgen/ixia/all/ixia/hlapi/8.42.0.6/library/common/ixiangpf/python/ixiangpf_commands +/projects/scid/tgen/ixia/all/ixia/hlapi/8.42.0.6/library/common/robot_plugin +/projects/scid/tgen/ixia/all/ixia/hlapi/8.42.0.6/library/utracker +/projects/scid/tgen/ixia/all/ixia/hlapi/8.42.0.6/bin +/projects/scid/tgen/ixia/all/ixia/ixos-api +/projects/scid/tgen/ixia/all/ixia/ixos-api/9.00.0.20 +/projects/scid/tgen/ixia/all/ixia/ixos-api/9.00.0.20/lib +/projects/scid/tgen/ixia/all/ixia/ixos-api/9.00.0.20/lib/Scriptgen +/projects/scid/tgen/ixia/all/ixia/ixos-api/9.00.0.20/lib/ssh +/projects/scid/tgen/ixia/all/ixia/ixos-api/9.00.0.20/lib/sftp +/projects/scid/tgen/ixia/all/ixia/ixos-api/9.00.0.20/lib/ixTclServices +/projects/scid/tgen/ixia/all/ixia/ixos-api/9.00.0.20/lib/ixTclServices/Generic +/projects/scid/tgen/ixia/all/ixia/ixos-api/9.00.0.20/lib/ixTcl1.0 +/projects/scid/tgen/ixia/all/ixia/ixos-api/9.00.0.20/lib/ixTcl1.0/Dap +/projects/scid/tgen/ixia/all/ixia/ixos-api/9.00.0.20/lib/ixTcl1.0/Generic +/projects/scid/tgen/ixia/all/ixia/ixos-api/9.00.0.20/bin +/projects/scid/tgen/ixia/all/ixia/ixos-api/8.42.6.1 +/projects/scid/tgen/ixia/all/ixia/ixos-api/8.42.6.1/lib +/projects/scid/tgen/ixia/all/ixia/ixos-api/8.42.6.1/lib/Scriptgen +/projects/scid/tgen/ixia/all/ixia/ixos-api/8.42.6.1/lib/ssh +/projects/scid/tgen/ixia/all/ixia/ixos-api/8.42.6.1/lib/sftp +/projects/scid/tgen/ixia/all/ixia/ixos-api/8.42.6.1/lib/ixTclServices +/projects/scid/tgen/ixia/all/ixia/ixos-api/8.42.6.1/lib/ixTclServices/Generic +/projects/scid/tgen/ixia/all/ixia/ixos-api/8.42.6.1/lib/ixTcl1.0 +/projects/scid/tgen/ixia/all/ixia/ixos-api/8.42.6.1/lib/ixTcl1.0/Dap +/projects/scid/tgen/ixia/all/ixia/ixos-api/8.42.6.1/lib/ixTcl1.0/Generic +/projects/scid/tgen/ixia/all/ixia/ixos-api/8.42.6.1/bin +/projects/scid/tgen/ixia/all/ixia/ixnetwork +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80 +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PerlApi +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PerlApi/dependencies +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PerlApi/dependencies/JSON +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PerlApi/dependencies/JSON/backportPP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PerlApi/dependencies/JSON/PP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PerlApi/dependencies/LWP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PerlApi/dependencies/LWP/Authen +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PerlApi/dependencies/LWP/Debug +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PerlApi/dependencies/LWP/Protocol +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PerlApi/dependencies/IPv6Sock +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PerlApi/dependencies/Try +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PerlApi/dependencies/HTTP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PerlApi/dependencies/HTTP/Cookies +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PerlApi/dependencies/HTTP/Headers +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PerlApi/dependencies/HTTP/Request +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PerlApi/dependencies/Time +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PerlApi/dependencies/Protocol +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PerlApi/dependencies/Protocol/WebSocket +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PerlApi/dependencies/Protocol/WebSocket/Cookie +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PerlApi/dependencies/Protocol/WebSocket/Handshake +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PerlApi/dependencies/URI +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PerlApi/dependencies/URI/urn +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PerlApi/dependencies/URI/file +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PythonApi +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PythonApi/dependencies +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PythonApi/dependencies/websocket +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PythonApi/dependencies/websocket/tests +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/PythonApi/dependencies/websocket/tests/data +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/RubyApi +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/TclApi +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/TclApi/IxTclNetwork +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/TclApi/IxTclNetwork/dependencies +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/TclApi/IxTclNetwork/dependencies/json +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/TclApi/IxTclNetwork/dependencies/json/c +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/TclApi/IxTclNetwork/dependencies/json/tests +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/TclApi/IxTclNetwork/dependencies/sha1 +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/TclApi/IxTclNetwork/dependencies/websocket +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/TclApi/IxTclNetwork/dependencies/base64 +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/TclApi/IxTclNetwork/dependencies/log +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/TclApi/IxTclNetwork/dependencies/log/msgs +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/TclApi/IxTclNetwork/Generic +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/TclApi/IxPublisher +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/TclApi/IxTclNetworkConnector +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/TclApi/ixtc +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/TclApi/IxTclProtocol +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/TclApi/IxTclProtocol/Generic +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/TclApi/IxTcl1.0 +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/TclApi/scriptgen-protocols +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/lib/TclApi/scriptgen-protocols/Protocols +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/bin +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/CommonUtils +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/AuthAccess +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/AuthAccess/PPP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/AuthAccess/TWAMP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/AuthAccess/IP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/Traffic +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/Traffic/QuickStreams +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/Traffic/CP-DP Convergence +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/Traffic/AdvancedTraffic +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/Traffic/FrameRateOnTheFly +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/Traffic/PacketEditor +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/Traffic/API-TrafficItem +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/Traffic/Dscp-Tos +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/Traffic/SV-Api +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/SDN +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/SDN/OpenFlow +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/RoutingSwitching +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/RoutingSwitching/ISIS +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/RoutingSwitching/ISIS/isisMt_5.50 +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/RoutingSwitching/BGP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/RoutingSwitching/BGP/bgpRouteImport +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/RoutingSwitching/BGP/bgpAd_5.50 +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/RoutingSwitching/CFM-ITU-PBB-TE +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/RoutingSwitching/LACP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/RoutingSwitching/LACP/polacp_5.50 +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/RoutingSwitching/posm_5.50 +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/RoutingSwitching/OSPF +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/RoutingSwitching/rateControl_5.50 +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/Framework +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/Framework/ipv6gateway_5.50 +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/Framework/InterFace&PortManagement +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/Framework/InterFace&PortManagement/Port-Management +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/Framework/InterFace&PortManagement/ipv4-ipv6-interfaces +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/Framework/AdvAES_5.50 +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/Framework/ProtocolStats+LearnedInfo +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/MPLS +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/MPLS/RSVP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/MPLS/RSVP/rsvpEnh_5.50 +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/MPLS/RSVP/RSVP-GR +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/MPLS/MVPN +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/MPLS/MVPN/P2MP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/MPLS/MVPN/mvpnNew_5.50 +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/MPLS/LDP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/MPLS/LDP/LDP-PW +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/IntegtatedTest +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/TestRun +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/IxReporter +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/QuickTest +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/Multicast +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/Multicast/MLD +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/Multicast/MLD/Mld-Router +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/Multicast/IGMP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/Multicast/IGMP/Igmp-Router +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/Multicast/PIM-BSR +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/Statistics +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/Statistics/generic +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Tcl/Statistics/custom-view +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Perl +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Perl/RoutingSwitching +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Perl/RoutingSwitching/OSPF +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Python +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Python/RoutingSwitching +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Python/RoutingSwitching/OSPF +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Ruby +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Ruby/RoutingSwitching +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/Ruby/RoutingSwitching/OSPF +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/REST +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/REST/IxReporter +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/REST/Statistics +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/Classic/REST/Statistics/custom-view +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/VM +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/VM/REST +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/Traffic +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/SDN +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/SDN/ISIS-SR-MS +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/SDN/BGP-EPE +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/SDN/ISIS-SRLG-LINK-PROTECTION +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/SDN/BIER +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/SDN/NGPF-OpenFlowController +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/SDN/Netconf +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/SDN/OSPF-SR +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/SDN/BGP-SR-Policy +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/SDN/L3vpn-Over-SRv6 +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/SDN/OVSDB-Controller +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/SDN/NGPF-OpenFlowSwitch +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/SDN/PCEP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/SDN/BGP-LS +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/SDN/BGP-SR +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/SDN/ISIS-SR +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/RoutingSwitching +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/RoutingSwitching/BFD +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/RoutingSwitching/ISIS +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/RoutingSwitching/BGP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/RoutingSwitching/BFDoverVXLAN +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/RoutingSwitching/OSPF +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/5G +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/5G/eCPRI +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/Timing and Synchronization +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/MPLS-VPN +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/MPLS-VPN/Inter-AS-option-C +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/MPLS-VPN/MPLSOAM +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/MPLS-VPN/MPLSOAM/S-BFD +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/MPLS-VPN/NG-MVPN +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/MPLS-VPN/H-L3VPN +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/MPLS-VPN/EVPN +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/Link Aggregation +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/Access +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/Access/PPP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/Access/L2TP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/Access/IEEE802.1x +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/Access/BondedGRE +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/Access/TLV +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/Access/DHCP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/Framework +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/MPLS +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/MPLS/BGP_RFC3107 +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/MPLS/RSVP_P2MP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/MPLS/RSVP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/MPLS/LDP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/AVB +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/AVB/MSRP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/AVB/gPTP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/QuickTest +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/Capture +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/Multicast +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/Multicast/MLD +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/Multicast/IGMP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/Multicast/PIM +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/Multicast/IPTV +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/Statistics +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/DataCenter +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/DataCenter/EVPN-VxLAN +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/DataCenter/FabricPath +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/DataCenter/VxLAN +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/DataCenter/TRILL +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/DataCenter/VxLANv6 +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Tcl/Interfaces +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/Traffic +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/SDN +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/SDN/ISIS-SR-MS +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/SDN/BGP-EPE +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/SDN/ISIS-SRLG-LINK-PROTECTION +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/SDN/BIER +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/SDN/NGPF-OpenFlowController +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/SDN/Netconf +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/SDN/NGPF_OpenFlowSwitch +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/SDN/OSPF-SR +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/SDN/BGP-SR-Policy +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/SDN/OVSDB-Controller +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/SDN/PCEP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/SDN/BGP-LS +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/SDN/BGP-SR +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/SDN/ISIS-SR +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/RoutingSwitching +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/RoutingSwitching/BFD +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/RoutingSwitching/ISIS +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/RoutingSwitching/BGP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/RoutingSwitching/OSPF +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/Timing and Synchronization +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/MPLS-VPN +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/MPLS-VPN/Inter-AS-option-C +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/MPLS-VPN/NG-MVPN +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/MPLS-VPN/H-L3VPN +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/MPLS-VPN/EVPN +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/Link Aggregation +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/Access +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/Access/PPP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/Access/L2TP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/Access/IEEE802.1x +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/Access/BondedGRE +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/Access/TLV +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/Access/DHCP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/MPLS +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/MPLS/BGP_RFC3107 +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/MPLS/RSVP_P2MP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/MPLS/RSVP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/MPLS/LDP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/AVB +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/AVB/MSRP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/Capture +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/Multicast +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/Multicast/MLD +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/Multicast/IGMP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/Multicast/PIM +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/Multicast/IPTV +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/Statistics +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/DataCenter +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/DataCenter/EVPN-VxLAN +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/DataCenter/FabricPath +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/DataCenter/VxLAN +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/DataCenter/TRILL +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/DataCenter/VxLANv6 +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Perl/Interfaces +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/Traffic +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/SDN +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/SDN/NGPF-OFSwitch +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/SDN/ISIS-SR-MS +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/SDN/BGP-EPE +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/SDN/ISIS-SRLG-LINK-PROTECTION +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/SDN/BIER +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/SDN/NGPF-OFController +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/SDN/Netconf +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/SDN/OSPF-SR +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/SDN/BGP-SR-Policy +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/SDN/L3vpn-over-SRv6 +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/SDN/OVSDB-Controller +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/SDN/PCEP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/SDN/BGP-LS +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/SDN/BGP-SR +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/SDN/ISIS-SR +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/RoutingSwitching +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/RoutingSwitching/BFD +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/RoutingSwitching/ISIS +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/RoutingSwitching/BGP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/RoutingSwitching/BFDoverVXLAN +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/RoutingSwitching/OSPF +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/5G +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/5G/eCPRI +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/Timing and Synchronization +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/MPLS-VPN +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/MPLS-VPN/Inter-AS-option-C +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/MPLS-VPN/MPLSOAM +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/MPLS-VPN/MPLSOAM/S-BFD +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/MPLS-VPN/NG-MVPN +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/MPLS-VPN/H-L3VPN +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/MPLS-VPN/EVPN +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/Link Aggregation +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/Access +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/Access/PPP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/Access/ANCP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/Access/IEEE802.1x +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/Access/BondedGRE +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/Access/TLV +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/Access/DHCP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/Framework +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/Framework/multivalue +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/MPLS +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/MPLS/BGP_RFC3107 +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/MPLS/RSVP_P2MP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/MPLS/RSVP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/MPLS/LDP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/AVB +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/AVB/MSRP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/AVB/gPTP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/Capture +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/Multicast +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/Multicast/MLD +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/Multicast/IGMP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/Multicast/PIM +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/Multicast/IPTV +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/Statistics +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/DataCenter +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/DataCenter/EVPN-VxLAN +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/DataCenter/FabricPath +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/DataCenter/VxLAN +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/DataCenter/TRILL +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/DataCenter/VxLANv6 +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Python/Interfaces +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/Traffic +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/SDN +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/SDN/OSPF-SR +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/SDN/PCEP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/SDN/BGP-LS +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/RoutingSwitching +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/RoutingSwitching/BFD +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/RoutingSwitching/ISIS +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/RoutingSwitching/BGP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/RoutingSwitching/OSPF +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/Link Aggregation +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/Access +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/Access/PPP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/Access/ANCP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/Access/TLV +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/Access/DHCP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/MPLS +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/MPLS/RSVP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/MPLS/LDP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/AVB +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/AVB/MSRP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/AVB/gPTP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/Capture +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/Multicast +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/Multicast/MLD +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/Multicast/IGMP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/Multicast/PIM +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/Multicast/IPTV +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/Statistics +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/DataCenter +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/DataCenter/FabricPath +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/DataCenter/VxLAN +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/DataCenter/TRILL +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/Ruby/Interfaces +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/REST +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/REST/Traffic +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/REST/RoutingSwitching +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/REST/RoutingSwitching/BFD +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/REST/RoutingSwitching/ISIS +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/REST/RoutingSwitching/BGP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/REST/RoutingSwitching/OSPF +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/REST/5G +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/REST/5G/eCPRI +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/REST/Link Aggregation +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/REST/Access +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/REST/Access/DHCP +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/REST/capture +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/REST/Multicast +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/REST/Statistics +/projects/scid/tgen/ixia/all/ixia/ixnetwork/9.00.1919.80/SampleScripts/IxNetwork/NGPF/REST/Interfaces +/projects/scid/tgen/ixia/all/ixia/ixnetwork/8.42.1250.2 +/projects/scid/tgen/ixia/all/ixia/ixnetwork/8.42.1250.2/lib +/projects/scid/tgen/ixia/all/ixia/ixnetwork/8.42.1250.2/lib/IxTclNetwork +/projects/scid/tgen/ixia/all/ixia/ixnetwork/8.42.1250.2/lib/IxTclNetwork/Generic +/projects/scid/tgen/ixia/all/ixia/ixnetwork/8.42.1250.2/lib/IxPublisher +/projects/scid/tgen/ixia/all/ixia/ixnetwork/8.42.1250.2/lib/IxTclNetworkConnector +/projects/scid/tgen/ixia/all/ixia/ixnetwork/8.42.1250.2/lib/PerlApi +/projects/scid/tgen/ixia/all/ixia/ixnetwork/8.42.1250.2/lib/ixtc +/projects/scid/tgen/ixia/all/ixia/ixnetwork/8.42.1250.2/lib/PythonApi +/projects/scid/tgen/ixia/all/ixia/ixnetwork/8.42.1250.2/lib/RubyApi +/projects/scid/tgen/ixia/all/ixia/ixnetwork/8.42.1250.2/lib/IxTclProtocol +/projects/scid/tgen/ixia/all/ixia/ixnetwork/8.42.1250.2/lib/IxTclProtocol/Generic +/projects/scid/tgen/ixia/all/ixia/ixnetwork/8.42.1250.2/lib/IxTcl1.0 +/projects/scid/tgen/ixia/all/ixia/ixnetwork/8.42.1250.2/lib/scriptgen-protocols +/projects/scid/tgen/ixia/all/ixia/ixnetwork/8.42.1250.2/lib/scriptgen-protocols/Protocols +/projects/scid/tgen/ixia/all/ixia/ixnetwork/8.42.1250.2/bin diff --git a/spytest/bin/tools_install.sh b/spytest/bin/tools_install.sh new file mode 100644 index 00000000000..045627044c5 --- /dev/null +++ b/spytest/bin/tools_install.sh @@ -0,0 +1,168 @@ +#!/bin/bash + +set -x + +cat << EOF + The following files are expected to be present in /project/scid/install + ActiveTcl-8.5.19.8519-x86_64-linux-glibc-2.5-403583.tar.gz + ActivePython-2.7.14.2717-linux-x86_64-glibc-2.12-404899.tar.gz + ActivePython-3.7.1.0000-linux-x86_64-glibc-2.12-b2ae37a5.tar.gz +EOF + +dir=$(dirname $0) +dir=$(cd $dir;pwd -P) +scid=$(cd $dir/..;pwd -P) + +# sourde environment +. $dir/env + +if [ -f $dir/.tools_env ]; then + . $dir/.tools_env +fi + +mkdir -p $SCID/install $SCID/tgen/ixia $SCID/tgen/stc +pushd $SCID/install + +untar() +{ + file=$1 + if [ ! -f $file ]; then + echo "$file not exists" + if [ -f $dir/.tools_env ]; then + bfile=$(basename $file) + sshpass -p $PKG_PASS scp -o StrictHostKeyChecking=no $PKG_USER@$PKG_SERVER:$PKG_ROOT/$file /tmp/$bfile + mv /tmp/$bfile $file + else + exit 1 + fi + fi + tar -zxf $file +} + +install_tcl64_85() +{ + pushd $SCID/install + INSTALL=$SCID/tools/ActivTcl/8.5.19; rm -rf $INSTALL + untar ActiveTcl-8.5.19.8519-x86_64-linux-glibc-2.5-403583.tar.gz + pushd ActiveTcl-8.5.19.8519-x86_64-linux-glibc-2.5-403583 + export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./payload/lib + ./payload/bin/tclsh8.5 install.tcl --directory $INSTALL + pushd $SCID/tools/ActivTcl + [ -f current ] || ln -s 8.5.19 current + popd + popd + rm -rf ActiveTcl-8.5.19.8519-x86_64-linux-glibc-2.5-403583 + popd +} + +install_python2() +{ + pushd $SCID/install + INSTALL=$SCID/tools/ActivPython/2.7.14; rm -rf $INSTALL + untar ActivePython-2.7.14.2717-linux-x86_64-glibc-2.12-404899.tar.gz + if [ -d ActivePython-2.7.14.2717-linux-x86_64-glibc-2.12-404899 ]; then + pushd ActivePython-2.7.14.2717-linux-x86_64-glibc-2.12-404899 + ./install.sh -v -I $INSTALL + pushd $SCID/tools/ActivPython + [ -f 2.7.14/bin/python ] || ln -s python3 2.7.14/bin/python + [ -f current ] || ln -s 2.7.14 current + cp -rf $SCID/tools/ActivTcl/current/lib/tclx8.4/ 2.7.14/lib/ + popd + popd + rm -rf ActivePython-2.7.14.2717-linux-x86_64-glibc-2.12-404899 + $dir/upgrade_requirements.sh + fi + popd +} + +reinstall_python2() +{ + src="2.7.14" + pushd $SCID/tools/ActivPython + dst=$(readlink current) + rm -rf $src.old + mv $src $src.old + if [ "$dst" = "$src" ]; then + rm current; ln -s $src.old current + fi + popd + install_python2 + pushd $SCID/tools/ActivPython + if [ "$dst" = "$src" ]; then + rm current;ln -s $src current + fi + rm -rf $src.old + popd + $dir/upgrade_requirements.sh +} + +install_python3() +{ + pushd $SCID/install + INSTALL=$SCID/tools/ActivPython/3.6.6; rm -rf $INSTALL + untar ActivePython-3.6.6.3606-linux-x86_64-glibc-2.12.tar.gz + pushd ActivePython-3.6.6.3606-linux-x86_64-glibc-2.12-* + ./install.sh -v -I $INSTALL + pushd $SCID/tools/ActivPython + [ -f 3.6.6/bin/python ] || ln -s python3 3.6.6/bin/python + cp -rf $SCID/tools/ActivTcl/current/lib/tclx8.4/ 3.6.6/lib/ + popd + popd + rm -rf ActivePython-3.6.6.3606-linux-x86_64-glibc-2.12-* + export SPYTEST_PYTHON_VERSION=3.6.6 + $dir/upgrade_requirements.sh + popd +} + +reinstall_python3() +{ + src="3.6.6" + pushd $SCID/tools/ActivPython + dst=$(readlink current) + rm -rf $src.old + mv $src $src.old + if [ "$dst" = "$src" ]; then + rm current; ln -s $src.old current + fi + popd + install_python3 + pushd $SCID/tools/ActivPython + if [ "$dst" = "$src" ]; then + rm current;ln -s $src current + fi + rm -rf $src.old + popd + $dir/upgrade_requirements.sh +} + +install_ixia_842() +{ + mkdir -p $SCID/tgen/ixia/ + pushd $SCID/tgen/ixia/ + rm -f 8.42 + untar IXIA_8.42EA.tar.gz + ln -s IXIA_8.42EA 8.42 + popd +} + +install_stc_491() +{ + mkdir -p $SCID/tgen/stc/ + pushd $SCID/tgen/stc/ + untar Spirent_TestCenter_4.91.tar.gz + ln -s Spirent_TestCenter_4.91 4.91 + popd +} + +install_tcl64_85 +install_python2 + +if [ -f $dir/.tools_env ]; then + install_ixia_842 + install_stc_491 +fi + +#install_python3 +#reinstall_python3 +#reinstall_python2 + diff --git a/spytest/bin/ubuntu18_deps.sh b/spytest/bin/ubuntu18_deps.sh new file mode 100644 index 00000000000..8bf34a53ed8 --- /dev/null +++ b/spytest/bin/ubuntu18_deps.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +sudo apt install -y python python-pip +sudo apt install -y tcl8.5 tclx +sudo apt install -y iputils-ping diff --git a/spytest/bin/upgrade_requirements.sh b/spytest/bin/upgrade_requirements.sh new file mode 100644 index 00000000000..3639a26d6b9 --- /dev/null +++ b/spytest/bin/upgrade_requirements.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +dir=$(dirname $0) + +# sourde environment +. $dir/env + +export CC=gcc +export CPP=cpp +export CXX=c++ +export LIBS= +export LDSHARED="gcc -pthread -shared" +export PYMSSQL_BUILD_WITH_BUNDLED_FREETDS=1 + +$SPYTEST_PYTHON -m pip install -r $dir/requirements0.txt +#$SPYTEST_PYTHON -m pip install -r $dir/requirements1.txt + +if [ -d $SCID_PYTHON_BIN ]; then + $SPYTEST_PYTHON -m compileall $SCID_PYTHON_BIN/.. + chmod -R go+r $SCID_PYTHON_BIN/.. +fi + diff --git a/spytest/datastore/constants/accton-as5712-54x.yaml b/spytest/datastore/constants/accton-as5712-54x.yaml new file mode 100644 index 00000000000..18bdc67213d --- /dev/null +++ b/spytest/datastore/constants/accton-as5712-54x.yaml @@ -0,0 +1,49 @@ +EGRESS_LOSSLESS_POOL_MODE : "static" +EGRESS_LOSSLESS_POOL_SIZE : "12766208" +EGRESS_LOSSLESS_POOL_TYPE : "egress" +EGRESS_LOSSY_POOL_MODE : "dynamic" +EGRESS_LOSSY_POOL_SIZE : "7326924" +EGRESS_LOSSY_POOL_TYPE : "egress" +INGRESS_LOSSLESS_POOL_MODE : "dynamic" +INGRESS_LOSSLESS_POOL_SIZE : "12766208" +INGRESS_LOSSLESS_POOL_TYPE : "ingress" +EGRESS_LOSSLESS_PROFILE_POOL : "egress_lossless_pool" +EGRESS_LOSSLESS_PROFILE_SIZE : "0" +EGRESS_LOSSLESS_PROFILE_STATIC_TH : "12766208" +EGRESS_LOSSY_PROFILE_POOL : "egress_lossless_pool" +EGRESS_LOSSY_PROFILE_SIZE : "1518" +EGRESS_LOSSY_PROFILE_DYNAMIC_TH : "3" +INGRESS_LOSSY_PROFILE_POOL : "ingress_lossless_pool" +INGRESS_LOSSY_PROFILE_SIZE : "0" +INGRESS_LOSSY_PROFILE_DYNAMIC_TH : "3" +PG_LOSSLESS_10000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_10000_300M_PROFILE_SIZE : "56368" +PG_LOSSLESS_10000_300M_PROFILE_XON : "18432" +PG_LOSSLESS_10000_300M_PROFILE_XOFF : "55120" +PG_LOSSLESS_10000_300M_PROFILE_XON_OFFSET : "2496" +PG_LOSSLESS_10000_300M_PROFILE_DYNAMIC_TH : "-3" +PG_LOSSLESS_25000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_25000_300M_PROFILE_SIZE : "56368" +PG_LOSSLESS_25000_300M_PROFILE_XON : "18432" +PG_LOSSLESS_25000_300M_PROFILE_XOFF : "55120" +PG_LOSSLESS_25000_300M_PROFILE_XON_OFFSET : "2496" +PG_LOSSLESS_25000_300M_PROFILE_DYNAMIC_TH : "-3" +PG_LOSSLESS_100000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_100000_300M_PROFILE_SIZE : "56368" +PG_LOSSLESS_100000_300M_PROFILE_XON : "18432" +PG_LOSSLESS_100000_300M_PROFILE_XOFF : "55120" +PG_LOSSLESS_100000_300M_PROFILE_XON_OFFSET : "2496" +PG_LOSSLESS_100000_300M_PROFILE_DYNAMIC_TH : "-3" +SCHEDULER0_TYPE : "DWRR" +SCHEDULER0_WEIGHT : "14" +SCHEDULER1_TYPE : "DWRR" +SCHEDULER1_WEIGHT : "15" +INGRESS_LOSSY_PROFILE : "ingress_lossy_profile" +INGRESS_LOSSLESS_PROFILE : "pg_lossless_10000_300m_profile" +EGRESS_LOSSY_PROFILE : "egress_lossy_profile" +EGRESS_LOSSLESS_PROFILE : "egress_lossless_profile" +WRED_LOSSLESS_PROFILE : "AZURE_LOSSLESS" +MAX_IPV4_ROUTES_SUPPORTED: '200704' +MAX_IPV6_ROUTES_SUPPORTED: '32768' +MAX_IPV4_ROUTES_FOR_ERROR_HANDLING: '128000' +MAX_IPV6_ROUTES_FOR_ERROR_HANDLING: '128000' diff --git a/spytest/datastore/constants/accton-as7326-56x.yaml b/spytest/datastore/constants/accton-as7326-56x.yaml new file mode 100644 index 00000000000..384fd9f60b5 --- /dev/null +++ b/spytest/datastore/constants/accton-as7326-56x.yaml @@ -0,0 +1,50 @@ +EGRESS_LOSSLESS_POOL_MODE : "static" +EGRESS_LOSSLESS_POOL_SIZE : "33004032" +EGRESS_LOSSLESS_POOL_TYPE : "egress" +EGRESS_LOSSY_POOL_MODE : "dynamic" +EGRESS_LOSSY_POOL_SIZE : "12766208" +EGRESS_LOSSY_POOL_TYPE : "egress" +INGRESS_LOSSLESS_POOL_MODE : "dynamic" +INGRESS_LOSSLESS_POOL_SIZE : "12766208" +INGRESS_LOSSLESS_POOL_TYPE : "ingress" +INGRESS_LOSSLESS_POOL_XOFF : "196608" +EGRESS_LOSSLESS_PROFILE_POOL : "egress_lossless_pool" +EGRESS_LOSSLESS_PROFILE_SIZE : "0" +EGRESS_LOSSLESS_PROFILE_STATIC_TH : "33004032" +EGRESS_LOSSY_PROFILE_POOL : "egress_lossy_pool" +EGRESS_LOSSY_PROFILE_SIZE : "1518" +EGRESS_LOSSY_PROFILE_DYNAMIC_TH : "3" +INGRESS_LOSSY_PROFILE_POOL : "ingress_lossless_pool" +INGRESS_LOSSY_PROFILE_SIZE : "0" +INGRESS_LOSSY_PROFILE_DYNAMIC_TH : "3" +PG_LOSSLESS_10000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_10000_300M_PROFILE_SIZE : "9427" +PG_LOSSLESS_10000_300M_PROFILE_XON : "0" +PG_LOSSLESS_10000_300M_PROFILE_XOFF : "50176" +PG_LOSSLESS_10000_300M_PROFILE_XON_OFFSET : "3584" +PG_LOSSLESS_10000_300M_PROFILE_DYNAMIC_TH : "1" +PG_LOSSLESS_25000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_25000_300M_PROFILE_SIZE : "9427" +PG_LOSSLESS_25000_300M_PROFILE_XON : "0" +PG_LOSSLESS_25000_300M_PROFILE_XOFF : "50176" +PG_LOSSLESS_25000_300M_PROFILE_XON_OFFSET : "3584" +PG_LOSSLESS_25000_300M_PROFILE_DYNAMIC_TH : "1" +PG_LOSSLESS_100000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_100000_300M_PROFILE_SIZE : "9427" +PG_LOSSLESS_100000_300M_PROFILE_XON : "0" +PG_LOSSLESS_100000_300M_PROFILE_XOFF : "50176" +PG_LOSSLESS_100000_300M_PROFILE_XON_OFFSET : "3584" +PG_LOSSLESS_100000_300M_PROFILE_DYNAMIC_TH : "1" +SCHEDULER0_TYPE : "DWRR" +SCHEDULER0_WEIGHT : "14" +SCHEDULER1_TYPE : "DWRR" +SCHEDULER1_WEIGHT : "15" +WRED_ECN : "ecn_all" +INGRESS_LOSSY_PROFILE : "ingress_lossy_profile" +INGRESS_LOSSLESS_PROFILE : "pg_lossless_10000_300m_profile" +EGRESS_LOSSY_PROFILE : "egress_lossy_profile" +EGRESS_LOSSLESS_PROFILE : "egress_lossless_profile" +MAX_IPV4_ROUTES_SUPPORTED: '82944' +MAX_IPV6_ROUTES_SUPPORTED: '32768' +MAX_IPV4_ROUTES_FOR_ERROR_HANDLING: '128000' +MAX_IPV6_ROUTES_FOR_ERROR_HANDLING: '128000' diff --git a/spytest/datastore/constants/accton-as7712-32x.yaml b/spytest/datastore/constants/accton-as7712-32x.yaml new file mode 100644 index 00000000000..cfcb9b09a04 --- /dev/null +++ b/spytest/datastore/constants/accton-as7712-32x.yaml @@ -0,0 +1,85 @@ +EGRESS_LOSSLESS_POOL_MODE : "static" +EGRESS_LOSSLESS_POOL_SIZE : "12766208" +EGRESS_LOSSLESS_POOL_TYPE : "egress" +EGRESS_LOSSY_POOL_MODE : "dynamic" +EGRESS_LOSSY_POOL_SIZE : "7326924" +EGRESS_LOSSY_POOL_TYPE : "egress" +INGRESS_LOSSLESS_POOL_MODE : "dynamic" +INGRESS_LOSSLESS_POOL_SIZE : "12766208" +INGRESS_LOSSLESS_POOL_TYPE : "ingress" +INGRESS_LOSSLESS_POOL_XOFF : "4625920" +EGRESS_LOSSLESS_PROFILE_POOL : "egress_lossless_pool" +EGRESS_LOSSLESS_PROFILE_SIZE : "0" +EGRESS_LOSSLESS_PROFILE_STATIC_TH : "12766208" +EGRESS_LOSSY_PROFILE_POOL : "egress_lossy_pool" +EGRESS_LOSSY_PROFILE_SIZE : "1518" +EGRESS_LOSSY_PROFILE_DYNAMIC_TH : "3" +INGRESS_LOSSY_PROFILE_POOL : "ingress_lossless_pool" +INGRESS_LOSSY_PROFILE_SIZE : "0" +INGRESS_LOSSY_PROFILE_DYNAMIC_TH : "3" +PG_LOSSLESS_10000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_10000_300M_PROFILE_SIZE : "56368" +PG_LOSSLESS_10000_300M_PROFILE_XON : "18432" +PG_LOSSLESS_10000_300M_PROFILE_XOFF : "55120" +PG_LOSSLESS_10000_300M_PROFILE_XON_OFFSET : "2496" +PG_LOSSLESS_10000_300M_PROFILE_DYNAMIC_TH : "-3" +PG_LOSSLESS_25000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_25000_300M_PROFILE_SIZE : "56368" +PG_LOSSLESS_25000_300M_PROFILE_XON : "18432" +PG_LOSSLESS_25000_300M_PROFILE_XOFF : "55120" +PG_LOSSLESS_25000_300M_PROFILE_XON_OFFSET : "2496" +PG_LOSSLESS_25000_300M_PROFILE_DYNAMIC_TH : "-3" +PG_LOSSLESS_100000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_100000_300M_PROFILE_SIZE : "56368" +PG_LOSSLESS_100000_300M_PROFILE_XON : "18432" +PG_LOSSLESS_100000_300M_PROFILE_XOFF : "55120" +PG_LOSSLESS_100000_300M_PROFILE_XON_OFFSET : "2496" +PG_LOSSLESS_100000_300M_PROFILE_DYNAMIC_TH : "-3" +SCHEDULER0_TYPE : "DWRR" +SCHEDULER0_WEIGHT : "14" +SCHEDULER1_TYPE : "DWRR" +SCHEDULER1_WEIGHT : "15" +WRED_ECN : "ecn_all" +WRED_GREEN_DROP_PROBABILITY : "5" +WRED_GREEN_MAX_THRESHOLD : "2097152" +WRED_GREEN_MIN_THRESHOLD : "1048576" +WRED_RED_DROP_PROBABILITY : "5" +WRED_RED_MAX_THRESHOLD : "2097152" +WRED_RED_MIN_THRESHOLD : "1048576" +WRED_YELLOW_DROP_PROBABILITY : "5" +WRED_YELLOW_MAX_THRESHOLD : "2097152" +WRED_YELLOW_MIN_THRESHOLD : "1048576" +WRED_GREEN_ENABLE : "true" +WRED_RED_ENABLE : "true" +WRED_YELLOW_ENABLE : "true" +INGRESS_LOSSY_PROFILE : "ingress_lossy_profile" +INGRESS_LOSSLESS_PROFILE : "pg_lossless_10000_300m_profile" +EGRESS_LOSSY_PROFILE : "egress_lossy_profile" +EGRESS_LOSSLESS_PROFILE : "egress_lossless_profile" +WRED_LOSSLESS_PROFILE : "AZURE_LOSSLESS" +MAX_IPV4_ROUTES_SUPPORTED: '66560' +MAX_IPV6_ROUTES_SUPPORTED: '24576' +MAX_IPV4_ROUTES_FOR_ERROR_HANDLING: '128000' +MAX_IPV6_ROUTES_FOR_ERROR_HANDLING: '128000' +PDDF_FANS_LIST: + - 'FAN1_1' + - 'FAN1_2' + - 'FAN2_1' + - 'FAN2_2' + - 'FAN3_1' + - 'FAN3_2' + - 'FAN4_1' + - 'FAN4_2' + - 'FAN5_1' + - 'FAN5_2' + - 'FAN6_1' + - 'FAN6_2' +PDDF_PSU_LIST: + - 'PSU1' + - 'PSU2' +PDDF_THERMAL_LIST: + - 'TEMP1' + - 'TEMP2' + - 'TEMP3' + - 'TEMP4' +Manufacturer: "accton" diff --git a/spytest/datastore/constants/accton-as7726-32x.yaml b/spytest/datastore/constants/accton-as7726-32x.yaml new file mode 100644 index 00000000000..3ced1d38853 --- /dev/null +++ b/spytest/datastore/constants/accton-as7726-32x.yaml @@ -0,0 +1,50 @@ +MAX_IPV4_ROUTES_SUPPORTED: '82944' +MAX_IPV6_ROUTES_SUPPORTED: '32768' +MAX_IPV4_ROUTES_FOR_ERROR_HANDLING: '128000' +MAX_IPV6_ROUTES_FOR_ERROR_HANDLING: '128000' +EGRESS_LOSSLESS_POOL_MODE : "static" +EGRESS_LOSSLESS_POOL_SIZE : "33004032" +EGRESS_LOSSLESS_POOL_TYPE : "egress" +EGRESS_LOSSY_POOL_MODE : "dynamic" +EGRESS_LOSSY_POOL_SIZE : "12766208" +EGRESS_LOSSY_POOL_TYPE : "egress" +INGRESS_LOSSLESS_POOL_MODE : "dynamic" +INGRESS_LOSSLESS_POOL_SIZE : "12766208" +INGRESS_LOSSLESS_POOL_TYPE : "ingress" +INGRESS_LOSSLESS_POOL_XOFF : "196608" +EGRESS_LOSSLESS_PROFILE_POOL : "egress_lossless_pool" +EGRESS_LOSSLESS_PROFILE_SIZE : "0" +EGRESS_LOSSLESS_PROFILE_STATIC_TH : "33004032" +EGRESS_LOSSY_PROFILE_POOL : "egress_lossy_pool" +EGRESS_LOSSY_PROFILE_SIZE : "1518" +EGRESS_LOSSY_PROFILE_DYNAMIC_TH : "3" +INGRESS_LOSSY_PROFILE_POOL : "ingress_lossless_pool" +INGRESS_LOSSY_PROFILE_SIZE : "0" +INGRESS_LOSSY_PROFILE_DYNAMIC_TH : "3" +PG_LOSSLESS_10000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_10000_300M_PROFILE_SIZE : "9427" +PG_LOSSLESS_10000_300M_PROFILE_XON : "0" +PG_LOSSLESS_10000_300M_PROFILE_XOFF : "50176" +PG_LOSSLESS_10000_300M_PROFILE_XON_OFFSET : "3584" +PG_LOSSLESS_10000_300M_PROFILE_DYNAMIC_TH : "1" +PG_LOSSLESS_25000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_25000_300M_PROFILE_SIZE : "9427" +PG_LOSSLESS_25000_300M_PROFILE_XON : "0" +PG_LOSSLESS_25000_300M_PROFILE_XOFF : "50176" +PG_LOSSLESS_25000_300M_PROFILE_XON_OFFSET : "3584" +PG_LOSSLESS_25000_300M_PROFILE_DYNAMIC_TH : "1" +PG_LOSSLESS_100000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_100000_300M_PROFILE_SIZE : "9427" +PG_LOSSLESS_100000_300M_PROFILE_XON : "0" +PG_LOSSLESS_100000_300M_PROFILE_XOFF : "50176" +PG_LOSSLESS_100000_300M_PROFILE_XON_OFFSET : "3584" +PG_LOSSLESS_100000_300M_PROFILE_DYNAMIC_TH : "1" +SCHEDULER0_TYPE : "DWRR" +SCHEDULER0_WEIGHT : "14" +SCHEDULER1_TYPE : "DWRR" +SCHEDULER1_WEIGHT : "15" +WRED_ECN : "ecn_all" +INGRESS_LOSSY_PROFILE : "ingress_lossy_profile" +INGRESS_LOSSLESS_PROFILE : "pg_lossless_10000_300m_profile" +EGRESS_LOSSY_PROFILE : "egress_lossy_profile" +EGRESS_LOSSLESS_PROFILE : "egress_lossless_profile" \ No newline at end of file diff --git a/spytest/datastore/constants/accton-as7816-64x.yaml b/spytest/datastore/constants/accton-as7816-64x.yaml new file mode 100644 index 00000000000..60bd70d4479 --- /dev/null +++ b/spytest/datastore/constants/accton-as7816-64x.yaml @@ -0,0 +1,62 @@ +EGRESS_LOSSLESS_POOL_MODE : "static" +EGRESS_LOSSLESS_POOL_SIZE : "12766208" +EGRESS_LOSSLESS_POOL_TYPE : "egress" +EGRESS_LOSSY_POOL_MODE : "dynamic" +EGRESS_LOSSY_POOL_SIZE : "7326924" +EGRESS_LOSSY_POOL_TYPE : "egress" +INGRESS_LOSSLESS_POOL_MODE : "dynamic" +INGRESS_LOSSLESS_POOL_SIZE : "12766208" +INGRESS_LOSSLESS_POOL_TYPE : "ingress" +INGRESS_LOSSLESS_POOL_XOFF : "4625920" +EGRESS_LOSSLESS_PROFILE_POOL : "egress_lossless_pool" +EGRESS_LOSSLESS_PROFILE_SIZE : "0" +EGRESS_LOSSLESS_PROFILE_STATIC_TH : "12766208" +EGRESS_LOSSY_PROFILE_POOL : "egress_lossless_pool" +EGRESS_LOSSY_PROFILE_SIZE : "1518" +EGRESS_LOSSY_PROFILE_DYNAMIC_TH : "3" +INGRESS_LOSSY_PROFILE_POOL : "ingress_lossless_pool" +INGRESS_LOSSY_PROFILE_SIZE : "0" +INGRESS_LOSSY_PROFILE_DYNAMIC_TH : "3" +PG_LOSSLESS_10000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_10000_300M_PROFILE_SIZE : "56368" +PG_LOSSLESS_10000_300M_PROFILE_XON : "18432" +PG_LOSSLESS_10000_300M_PROFILE_XOFF : "55120" +PG_LOSSLESS_10000_300M_PROFILE_XON_OFFSET : "2496" +PG_LOSSLESS_10000_300M_PROFILE_DYNAMIC_TH : "-3" +PG_LOSSLESS_25000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_25000_300M_PROFILE_SIZE : "56368" +PG_LOSSLESS_25000_300M_PROFILE_XON : "18432" +PG_LOSSLESS_25000_300M_PROFILE_XOFF : "55120" +PG_LOSSLESS_25000_300M_PROFILE_XON_OFFSET : "2496" +PG_LOSSLESS_25000_300M_PROFILE_DYNAMIC_TH : "-3" +PG_LOSSLESS_100000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_100000_300M_PROFILE_SIZE : "56368" +PG_LOSSLESS_100000_300M_PROFILE_XON : "18432" +PG_LOSSLESS_100000_300M_PROFILE_XOFF : "55120" +PG_LOSSLESS_100000_300M_PROFILE_XON_OFFSET : "2496" +PG_LOSSLESS_100000_300M_PROFILE_DYNAMIC_TH : "-3" +SCHEDULER0_TYPE : "DWRR" +SCHEDULER0_WEIGHT : "14" +SCHEDULER1_TYPE : "DWRR" +SCHEDULER1_WEIGHT : "15" +WRED_ECN : "ecn_all" +WRED_GREEN_DROP_PROBABILITY : "5" +WRED_GREEN_MAX_THRESHOLD : "2097152" +WRED_GREEN_MIN_THRESHOLD : "1048576" +WRED_RED_DROP_PROBABILITY : "5" +WRED_RED_MAX_THRESHOLD : "2097152" +WRED_RED_MIN_THRESHOLD : "1048576" +WRED_YELLOW_DROP_PROBABILITY : "5" +WRED_YELLOW_MAX_THRESHOLD : "2097152" +WRED_YELLOW_MIN_THRESHOLD : "1048576" +WRED_GREEN_ENABLE : "true" +WRED_RED_ENABLE : "true" +WRED_YELLOW_ENABLE : "true" +INGRESS_LOSSY_PROFILE : "ingress_lossy_profile" +INGRESS_LOSSLESS_PROFILE : "pg_lossless_10000_300m_profile" +EGRESS_LOSSY_PROFILE : "egress_lossy_profile" +EGRESS_LOSSLESS_PROFILE : "egress_lossless_profile" +MAX_IPV4_ROUTES_SUPPORTED: '200704' +MAX_IPV6_ROUTES_SUPPORTED: '32768' +MAX_IPV4_ROUTES_FOR_ERROR_HANDLING: '128000' +MAX_IPV6_ROUTES_FOR_ERROR_HANDLING: '128000' diff --git a/spytest/datastore/constants/accton-as9716-32d.yaml b/spytest/datastore/constants/accton-as9716-32d.yaml new file mode 100644 index 00000000000..0989595029b --- /dev/null +++ b/spytest/datastore/constants/accton-as9716-32d.yaml @@ -0,0 +1,46 @@ +EGRESS_LOSSY_POOL_MODE : 'dynamic' +EGRESS_LOSSY_POOL_SIZE : '67108864' +EGRESS_LOSSY_POOL_TYPE : 'egress' +INGRESS_LOSSLESS_POOL_MODE : 'dynamic' +INGRESS_LOSSLESS_POOL_SIZE : '59001152' +INGRESS_LOSSLESS_POOL_TYPE : 'ingress' +INGRESS_LOSSLESS_POOL_XOFF : '7428992' +EGRESS_LOSSY_PROFILE_POOL : 'egress_lossy_pool' +EGRESS_LOSSY_PROFILE_SIZE : '0' +EGRESS_LOSSY_PROFILE_DYNAMIC_TH : '3' +INGRESS_LOSSY_PROFILE_POOL : 'ingress_lossless_pool' +INGRESS_LOSSY_PROFILE_SIZE : '0' +INGRESS_LOSSY_PROFILE_STATIC_TH : '67108864' +EGRESS_LOSSLESS_PROFILE_POOL : 'egress_lossless_pool' +EGRESS_LOSSLESS_PROFILE_SIZE : '0' +EGRESS_LOSSLESS_PROFILE_STATIC_TH : '59001152' +PG_LOSSLESS_10000_40M_PROFILE_POOL : 'ingress_lossless_pool' +PG_LOSSLESS_10000_40M_PROFILE_SIZE : '1270' +PG_LOSSLESS_10000_40M_PROFILE_XON : '0' +PG_LOSSLESS_10000_40M_PROFILE_XOFF : '190500' +PG_LOSSLESS_10000_40M_PROFILE_XON_OFFSET : '2540' +PG_LOSSLESS_10000_40M_PROFILE_DYNAMIC_TH : '-2' +PG_LOSSLESS_25000_40M_PROFILE_POOL : 'ingress_lossless_pool' +PG_LOSSLESS_25000_40M_PROFILE_SIZE : '1270' +PG_LOSSLESS_25000_40M_PROFILE_XON : '0' +PG_LOSSLESS_25000_40M_PROFILE_XOFF : '190500' +PG_LOSSLESS_25000_40M_PROFILE_XON_OFFSET : '2540' +PG_LOSSLESS_25000_40M_PROFILE_DYNAMIC_TH : '-2' +PG_LOSSLESS_100000_40M_PROFILE_POOL : 'ingress_lossless_pool' +PG_LOSSLESS_100000_40M_PROFILE_SIZE : '1270' +PG_LOSSLESS_100000_40M_PROFILE_XON : '0' +PG_LOSSLESS_100000_40M_PROFILE_XOFF : '190500' +PG_LOSSLESS_100000_40M_PROFILE_XON_OFFSET : '2540' +PG_LOSSLESS_100000_40M_PROFILE_DYNAMIC_TH : '-2' +SCHEDULER0_TYPE : 'DWRR' +SCHEDULER0_WEIGHT : '5' +SCHEDULER1_TYPE : 'DWRR' +SCHEDULER1_WEIGHT : '1' +INGRESS_LOSSY_PROFILE : 'ingress_lossy_profile' +INGRESS_LOSSLESS_PROFILE : 'pg_lossless_10000_40m_profile' +EGRESS_LOSSY_PROFILE : 'egress_lossy_profile' +EGRESS_LOSSLESS_PROFILE : 'egress_lossy_profile' +MAX_IPV4_ROUTES_SUPPORTED: '200704' +MAX_IPV6_ROUTES_SUPPORTED: '32768' +MAX_IPV4_ROUTES_FOR_ERROR_HANDLING: '270000' +MAX_IPV6_ROUTES_FOR_ERROR_HANDLING: '270000' \ No newline at end of file diff --git a/spytest/datastore/constants/common.yaml b/spytest/datastore/constants/common.yaml new file mode 100644 index 00000000000..b8035e3e47f --- /dev/null +++ b/spytest/datastore/constants/common.yaml @@ -0,0 +1,122 @@ +ACCTON_AS7712_32X : &AS7712 accton-as7712-32x +ACCTON_AS7816_64X : &AS7816 accton-as7816-64x +ACCTON-AS9716-32D : &AS9716 accton-as9716-32d +DELTA_AG9032V1 : &AG9032 delta-ag9032v1 +QUANTA_IX1B_32X : &IX1B quanta-ix1b-32x +ACCTON_AS5712_54X : &AS5712 accton-as5712-54x +ACCTON_AS7326_56X : &AS7326 accton-as7326-56x +QUANTA_IX8_56X : &IX8 quanta-ix8-56x +QUANTA_IX9_32X : &IX9 quanta-ix9-32x +DELLEMC_Z9332F_O32 : &Z9332f_O32 dellemc-z9332f-o32 +ACCTON_AS7726_32X : &AS7726 accton-as7726-32x +QUANTA-IX4-64X : &IX4 quanta-ix4-64x +DELLEMC-S5232F-C32 : &S5232f_C32 dellemc-s5232f-c32 +DELLEMC-S5232F-P-100G : &S5232f_P_100G dellemc-s5232f-p-100g +DELLEMC-S5232F-P-10G : &S5232f_P_10G dellemc-s5232f-p-10g +DELLEMC-S5232F-P-25G : &S5232f_P_25G dellemc-s5232f-p-25g +DELLEMC-S5248F-P-25G: &S5248f_P_25G dellemc-s5248f-p-25g +DELLEMC-S5296F-P-25G: &S5296f_P_25G dellemc-s5296f-p-25g +QUANTA-IX9-C64O16: &IX9_C64O16 quanta-ix9-c64o16 +DELLEMC-N3248TE: &N3248TE dellemc-n3248te +TH_PLATFORMS : + - *AS7712 + - *AG9032 + - *IX1B +TH2_PLATFORMS : + - *AS7816 + - *IX4 +TD2_PLATFORMS : + - *AS5712 +TD3_PLATFORMS : + - *AS7326 + - *IX8 + - *AS7726 + - *S5232f_C32 + - *S5232f_P_100G + - *S5232f_P_10G + - *S5232f_P_25G + - *S5248f_P_25G + - *S5296f_P_25G + - *N3248TE +TH3_PLATFORMS : + - *IX9 + - *IX9_C64O16 + - *Z9332f_O32 + - *AS9716 +MAX_MAC_ENTRIES : 1024 +MAX_IPV4_ROUTES_SUPPORTED: '32768' +MAX_IPV6_ROUTES_SUPPORTED: '24576' +COPP_CIR_LLDP : 6000 +COPP_CIR_LACP : 6000 +COPP_CIR_DHCP : 6000 +COPP_CIR_NDP : 6000 +COPP_CIR_BGP : 10000 +COPP_CIR_ARP : 6000 +COPP_CIR_IGMP : 6000 +COPP_CIR_SFLOW : 600 +THRESHOLD_FEATURE_PG_HEADROOM_UN_SUPPORTED_PLATFORMS : + - *AS7326 + - *IX8 + - *AS5712 + - *AS7726 + - *S5232f_C32 + - *S5232f_P_100G + - *S5232f_P_10G + - *S5232f_P_25G + - *IX9 + - *Z9332f_O32 +WARM_REBOOT_SUPPORTED_PLATFORMS : + - *AS7816 + - *IX4 + - *IX1B + - *AS7712 + - *AG9032 + - *AS7326 + - *IX8 + - *AS5712 + - *AS7726 + - *S5232f_C32 + - *S5232f_P_100G + - *S5232f_P_10G + - *S5232f_P_25G + - *IX9 + - *Z9332f_O32 + - *AS9716 + - *S5248f_P_25G + - *S5296f_P_25G + - *N3248TE +MAX_IPV4_ROUTES_FOR_ERROR_HANDLING: '128000' +MAX_IPV6_ROUTES_FOR_ERROR_HANDLING: '128000' +BUILD_GENERIC: Generic +BUILD_BROADCOM_CLOUD_BASE: Broadcom Cloud Base +BUILD_BROADCOM_CLOUD_ADVANCED: + - 'Broadcom Cloud Advanced' + - 'Cloud Advanced SONiC OS - Powered by Broadcom' +BUILD_BROADCOM_ENTERPRISE_ADVANCED: + - 'Enterprise Advanced SONiC OS - Powered by Broadcom' +NOS_NAME: + - 'SONiC-OS' +PRODUCT_PACKAGING_OPTIONS: + - 'Cloud_Base' + - 'Cloud_Base-dbg' + - 'Cloud_Base-dbg-cov' + - 'Cloud_Advanced' + - 'Cloud_Advanced-dbg' + - 'Cloud_Advanced-dbg-cov' + - 'Generic' + - 'Generic-dbg' + - 'Generic-dbg-cov' + - 'Enterprise_Base' + - 'Enterprise_Base-dbg' + - 'Enterprise_Base-dbg-cov' + - 'Enterprise_Advanced' + - 'Enterprise_Advanced-dbg' + - 'Enterprise_Advanced-dbg-cov' + - '' +PDDF_SUPPORTED_PLATFORMS: + - *AS7712 +PDDF_BMC_SUPPORTED_PLATFORMS: + - *IX8 +MAX_SUPPORTED_VLANS: '4094' +MAX_VLAN_ID: '4094' +MAX_DROP_MONITOR_FLOWS: '253' \ No newline at end of file diff --git a/spytest/datastore/constants/constants.yaml b/spytest/datastore/constants/constants.yaml new file mode 100644 index 00000000000..caf2778916d --- /dev/null +++ b/spytest/datastore/constants/constants.yaml @@ -0,0 +1,39 @@ + +common: !include common.yaml +accton-as5712-54x: !include accton-as5712-54x.yaml +accton-as7326-56x: !include accton-as7326-56x.yaml +accton-as7712-32x: !include accton-as7712-32x.yaml +accton-as7726-32x: !include accton-as7726-32x.yaml +accton-as7816-64x: !include accton-as7816-64x.yaml +accton-as9716-32d: !include accton-as9716-32d.yaml +dellemc-s5232f-c32: !include dellemc-s5232f-c32.yaml +dellemc-s5232f-p-10g: !include dellemc-s5232f-p-10g.yaml +dellemc-s5232f-p-25g: !include dellemc-s5232f-p-25g.yaml +dellemc-s5232f-p-100g: !include dellemc-s5232f-p-100g.yaml +dellemc-z9332f-o32: !include dellemc-z9332f-o32.yaml +delta-ag9032v1: !include delta-ag9032v1.yaml +quanta-ix1b-32x: !include quanta-ix1b-32x.yaml +quanta-ix4-64x: !include quanta-ix4-64x.yaml +quanta-ix8-56x: !include quanta-ix8-56x.yaml +quanta-ix9-32x: !include quanta-ix9-32x.yaml +quanta-ix9-c64o16: !include quanta-ix9-c64o16.yaml + +constants: + default: [common] + accton-as5712-54x: [common, accton-as5712-54x] + accton-as7326-56x: [common, accton-as7326-56x] + accton-as7712-32x: [common, accton-as7712-32x] + accton-as7726-32x: [common, accton-as7726-32x] + accton-as7816-64x: [common, accton-as7816-64x] + accton-as9716-32d: [common, accton-as9716-32d] + dellemc-s5232f-c32: [common, dellemc-s5232f-c32] + dellemc-s5232f-p-10g: [common, dellemc-s5232f-p-10g] + dellemc-s5232f-p-25g: [common, dellemc-s5232f-p-25g] + dellemc-s5232f-p-100g: [common, dellemc-s5232f-p-100g] + dellemc-z9332f-o32: [common, dellemc-z9332f-o32] + delta-ag9032v1: [common, delta-ag9032v1] + quanta-ix1b-32x: [common, quanta-ix1b-32x] + quanta-ix4-64x: [common, quanta-ix4-64x] + quanta-ix8-56x: [common, quanta-ix8-56x] + quanta-ix9-32x: [common, quanta-ix9-32x] + quanta-ix9-c64o16: [common, quanta-ix9-c64o16] diff --git a/spytest/datastore/constants/dellemc-s5232f-c32.yaml b/spytest/datastore/constants/dellemc-s5232f-c32.yaml new file mode 100644 index 00000000000..3ced1d38853 --- /dev/null +++ b/spytest/datastore/constants/dellemc-s5232f-c32.yaml @@ -0,0 +1,50 @@ +MAX_IPV4_ROUTES_SUPPORTED: '82944' +MAX_IPV6_ROUTES_SUPPORTED: '32768' +MAX_IPV4_ROUTES_FOR_ERROR_HANDLING: '128000' +MAX_IPV6_ROUTES_FOR_ERROR_HANDLING: '128000' +EGRESS_LOSSLESS_POOL_MODE : "static" +EGRESS_LOSSLESS_POOL_SIZE : "33004032" +EGRESS_LOSSLESS_POOL_TYPE : "egress" +EGRESS_LOSSY_POOL_MODE : "dynamic" +EGRESS_LOSSY_POOL_SIZE : "12766208" +EGRESS_LOSSY_POOL_TYPE : "egress" +INGRESS_LOSSLESS_POOL_MODE : "dynamic" +INGRESS_LOSSLESS_POOL_SIZE : "12766208" +INGRESS_LOSSLESS_POOL_TYPE : "ingress" +INGRESS_LOSSLESS_POOL_XOFF : "196608" +EGRESS_LOSSLESS_PROFILE_POOL : "egress_lossless_pool" +EGRESS_LOSSLESS_PROFILE_SIZE : "0" +EGRESS_LOSSLESS_PROFILE_STATIC_TH : "33004032" +EGRESS_LOSSY_PROFILE_POOL : "egress_lossy_pool" +EGRESS_LOSSY_PROFILE_SIZE : "1518" +EGRESS_LOSSY_PROFILE_DYNAMIC_TH : "3" +INGRESS_LOSSY_PROFILE_POOL : "ingress_lossless_pool" +INGRESS_LOSSY_PROFILE_SIZE : "0" +INGRESS_LOSSY_PROFILE_DYNAMIC_TH : "3" +PG_LOSSLESS_10000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_10000_300M_PROFILE_SIZE : "9427" +PG_LOSSLESS_10000_300M_PROFILE_XON : "0" +PG_LOSSLESS_10000_300M_PROFILE_XOFF : "50176" +PG_LOSSLESS_10000_300M_PROFILE_XON_OFFSET : "3584" +PG_LOSSLESS_10000_300M_PROFILE_DYNAMIC_TH : "1" +PG_LOSSLESS_25000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_25000_300M_PROFILE_SIZE : "9427" +PG_LOSSLESS_25000_300M_PROFILE_XON : "0" +PG_LOSSLESS_25000_300M_PROFILE_XOFF : "50176" +PG_LOSSLESS_25000_300M_PROFILE_XON_OFFSET : "3584" +PG_LOSSLESS_25000_300M_PROFILE_DYNAMIC_TH : "1" +PG_LOSSLESS_100000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_100000_300M_PROFILE_SIZE : "9427" +PG_LOSSLESS_100000_300M_PROFILE_XON : "0" +PG_LOSSLESS_100000_300M_PROFILE_XOFF : "50176" +PG_LOSSLESS_100000_300M_PROFILE_XON_OFFSET : "3584" +PG_LOSSLESS_100000_300M_PROFILE_DYNAMIC_TH : "1" +SCHEDULER0_TYPE : "DWRR" +SCHEDULER0_WEIGHT : "14" +SCHEDULER1_TYPE : "DWRR" +SCHEDULER1_WEIGHT : "15" +WRED_ECN : "ecn_all" +INGRESS_LOSSY_PROFILE : "ingress_lossy_profile" +INGRESS_LOSSLESS_PROFILE : "pg_lossless_10000_300m_profile" +EGRESS_LOSSY_PROFILE : "egress_lossy_profile" +EGRESS_LOSSLESS_PROFILE : "egress_lossless_profile" \ No newline at end of file diff --git a/spytest/datastore/constants/dellemc-s5232f-p-100g.yaml b/spytest/datastore/constants/dellemc-s5232f-p-100g.yaml new file mode 100644 index 00000000000..3ced1d38853 --- /dev/null +++ b/spytest/datastore/constants/dellemc-s5232f-p-100g.yaml @@ -0,0 +1,50 @@ +MAX_IPV4_ROUTES_SUPPORTED: '82944' +MAX_IPV6_ROUTES_SUPPORTED: '32768' +MAX_IPV4_ROUTES_FOR_ERROR_HANDLING: '128000' +MAX_IPV6_ROUTES_FOR_ERROR_HANDLING: '128000' +EGRESS_LOSSLESS_POOL_MODE : "static" +EGRESS_LOSSLESS_POOL_SIZE : "33004032" +EGRESS_LOSSLESS_POOL_TYPE : "egress" +EGRESS_LOSSY_POOL_MODE : "dynamic" +EGRESS_LOSSY_POOL_SIZE : "12766208" +EGRESS_LOSSY_POOL_TYPE : "egress" +INGRESS_LOSSLESS_POOL_MODE : "dynamic" +INGRESS_LOSSLESS_POOL_SIZE : "12766208" +INGRESS_LOSSLESS_POOL_TYPE : "ingress" +INGRESS_LOSSLESS_POOL_XOFF : "196608" +EGRESS_LOSSLESS_PROFILE_POOL : "egress_lossless_pool" +EGRESS_LOSSLESS_PROFILE_SIZE : "0" +EGRESS_LOSSLESS_PROFILE_STATIC_TH : "33004032" +EGRESS_LOSSY_PROFILE_POOL : "egress_lossy_pool" +EGRESS_LOSSY_PROFILE_SIZE : "1518" +EGRESS_LOSSY_PROFILE_DYNAMIC_TH : "3" +INGRESS_LOSSY_PROFILE_POOL : "ingress_lossless_pool" +INGRESS_LOSSY_PROFILE_SIZE : "0" +INGRESS_LOSSY_PROFILE_DYNAMIC_TH : "3" +PG_LOSSLESS_10000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_10000_300M_PROFILE_SIZE : "9427" +PG_LOSSLESS_10000_300M_PROFILE_XON : "0" +PG_LOSSLESS_10000_300M_PROFILE_XOFF : "50176" +PG_LOSSLESS_10000_300M_PROFILE_XON_OFFSET : "3584" +PG_LOSSLESS_10000_300M_PROFILE_DYNAMIC_TH : "1" +PG_LOSSLESS_25000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_25000_300M_PROFILE_SIZE : "9427" +PG_LOSSLESS_25000_300M_PROFILE_XON : "0" +PG_LOSSLESS_25000_300M_PROFILE_XOFF : "50176" +PG_LOSSLESS_25000_300M_PROFILE_XON_OFFSET : "3584" +PG_LOSSLESS_25000_300M_PROFILE_DYNAMIC_TH : "1" +PG_LOSSLESS_100000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_100000_300M_PROFILE_SIZE : "9427" +PG_LOSSLESS_100000_300M_PROFILE_XON : "0" +PG_LOSSLESS_100000_300M_PROFILE_XOFF : "50176" +PG_LOSSLESS_100000_300M_PROFILE_XON_OFFSET : "3584" +PG_LOSSLESS_100000_300M_PROFILE_DYNAMIC_TH : "1" +SCHEDULER0_TYPE : "DWRR" +SCHEDULER0_WEIGHT : "14" +SCHEDULER1_TYPE : "DWRR" +SCHEDULER1_WEIGHT : "15" +WRED_ECN : "ecn_all" +INGRESS_LOSSY_PROFILE : "ingress_lossy_profile" +INGRESS_LOSSLESS_PROFILE : "pg_lossless_10000_300m_profile" +EGRESS_LOSSY_PROFILE : "egress_lossy_profile" +EGRESS_LOSSLESS_PROFILE : "egress_lossless_profile" \ No newline at end of file diff --git a/spytest/datastore/constants/dellemc-s5232f-p-10g.yaml b/spytest/datastore/constants/dellemc-s5232f-p-10g.yaml new file mode 100644 index 00000000000..3ced1d38853 --- /dev/null +++ b/spytest/datastore/constants/dellemc-s5232f-p-10g.yaml @@ -0,0 +1,50 @@ +MAX_IPV4_ROUTES_SUPPORTED: '82944' +MAX_IPV6_ROUTES_SUPPORTED: '32768' +MAX_IPV4_ROUTES_FOR_ERROR_HANDLING: '128000' +MAX_IPV6_ROUTES_FOR_ERROR_HANDLING: '128000' +EGRESS_LOSSLESS_POOL_MODE : "static" +EGRESS_LOSSLESS_POOL_SIZE : "33004032" +EGRESS_LOSSLESS_POOL_TYPE : "egress" +EGRESS_LOSSY_POOL_MODE : "dynamic" +EGRESS_LOSSY_POOL_SIZE : "12766208" +EGRESS_LOSSY_POOL_TYPE : "egress" +INGRESS_LOSSLESS_POOL_MODE : "dynamic" +INGRESS_LOSSLESS_POOL_SIZE : "12766208" +INGRESS_LOSSLESS_POOL_TYPE : "ingress" +INGRESS_LOSSLESS_POOL_XOFF : "196608" +EGRESS_LOSSLESS_PROFILE_POOL : "egress_lossless_pool" +EGRESS_LOSSLESS_PROFILE_SIZE : "0" +EGRESS_LOSSLESS_PROFILE_STATIC_TH : "33004032" +EGRESS_LOSSY_PROFILE_POOL : "egress_lossy_pool" +EGRESS_LOSSY_PROFILE_SIZE : "1518" +EGRESS_LOSSY_PROFILE_DYNAMIC_TH : "3" +INGRESS_LOSSY_PROFILE_POOL : "ingress_lossless_pool" +INGRESS_LOSSY_PROFILE_SIZE : "0" +INGRESS_LOSSY_PROFILE_DYNAMIC_TH : "3" +PG_LOSSLESS_10000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_10000_300M_PROFILE_SIZE : "9427" +PG_LOSSLESS_10000_300M_PROFILE_XON : "0" +PG_LOSSLESS_10000_300M_PROFILE_XOFF : "50176" +PG_LOSSLESS_10000_300M_PROFILE_XON_OFFSET : "3584" +PG_LOSSLESS_10000_300M_PROFILE_DYNAMIC_TH : "1" +PG_LOSSLESS_25000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_25000_300M_PROFILE_SIZE : "9427" +PG_LOSSLESS_25000_300M_PROFILE_XON : "0" +PG_LOSSLESS_25000_300M_PROFILE_XOFF : "50176" +PG_LOSSLESS_25000_300M_PROFILE_XON_OFFSET : "3584" +PG_LOSSLESS_25000_300M_PROFILE_DYNAMIC_TH : "1" +PG_LOSSLESS_100000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_100000_300M_PROFILE_SIZE : "9427" +PG_LOSSLESS_100000_300M_PROFILE_XON : "0" +PG_LOSSLESS_100000_300M_PROFILE_XOFF : "50176" +PG_LOSSLESS_100000_300M_PROFILE_XON_OFFSET : "3584" +PG_LOSSLESS_100000_300M_PROFILE_DYNAMIC_TH : "1" +SCHEDULER0_TYPE : "DWRR" +SCHEDULER0_WEIGHT : "14" +SCHEDULER1_TYPE : "DWRR" +SCHEDULER1_WEIGHT : "15" +WRED_ECN : "ecn_all" +INGRESS_LOSSY_PROFILE : "ingress_lossy_profile" +INGRESS_LOSSLESS_PROFILE : "pg_lossless_10000_300m_profile" +EGRESS_LOSSY_PROFILE : "egress_lossy_profile" +EGRESS_LOSSLESS_PROFILE : "egress_lossless_profile" \ No newline at end of file diff --git a/spytest/datastore/constants/dellemc-s5232f-p-25g.yaml b/spytest/datastore/constants/dellemc-s5232f-p-25g.yaml new file mode 100644 index 00000000000..3ced1d38853 --- /dev/null +++ b/spytest/datastore/constants/dellemc-s5232f-p-25g.yaml @@ -0,0 +1,50 @@ +MAX_IPV4_ROUTES_SUPPORTED: '82944' +MAX_IPV6_ROUTES_SUPPORTED: '32768' +MAX_IPV4_ROUTES_FOR_ERROR_HANDLING: '128000' +MAX_IPV6_ROUTES_FOR_ERROR_HANDLING: '128000' +EGRESS_LOSSLESS_POOL_MODE : "static" +EGRESS_LOSSLESS_POOL_SIZE : "33004032" +EGRESS_LOSSLESS_POOL_TYPE : "egress" +EGRESS_LOSSY_POOL_MODE : "dynamic" +EGRESS_LOSSY_POOL_SIZE : "12766208" +EGRESS_LOSSY_POOL_TYPE : "egress" +INGRESS_LOSSLESS_POOL_MODE : "dynamic" +INGRESS_LOSSLESS_POOL_SIZE : "12766208" +INGRESS_LOSSLESS_POOL_TYPE : "ingress" +INGRESS_LOSSLESS_POOL_XOFF : "196608" +EGRESS_LOSSLESS_PROFILE_POOL : "egress_lossless_pool" +EGRESS_LOSSLESS_PROFILE_SIZE : "0" +EGRESS_LOSSLESS_PROFILE_STATIC_TH : "33004032" +EGRESS_LOSSY_PROFILE_POOL : "egress_lossy_pool" +EGRESS_LOSSY_PROFILE_SIZE : "1518" +EGRESS_LOSSY_PROFILE_DYNAMIC_TH : "3" +INGRESS_LOSSY_PROFILE_POOL : "ingress_lossless_pool" +INGRESS_LOSSY_PROFILE_SIZE : "0" +INGRESS_LOSSY_PROFILE_DYNAMIC_TH : "3" +PG_LOSSLESS_10000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_10000_300M_PROFILE_SIZE : "9427" +PG_LOSSLESS_10000_300M_PROFILE_XON : "0" +PG_LOSSLESS_10000_300M_PROFILE_XOFF : "50176" +PG_LOSSLESS_10000_300M_PROFILE_XON_OFFSET : "3584" +PG_LOSSLESS_10000_300M_PROFILE_DYNAMIC_TH : "1" +PG_LOSSLESS_25000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_25000_300M_PROFILE_SIZE : "9427" +PG_LOSSLESS_25000_300M_PROFILE_XON : "0" +PG_LOSSLESS_25000_300M_PROFILE_XOFF : "50176" +PG_LOSSLESS_25000_300M_PROFILE_XON_OFFSET : "3584" +PG_LOSSLESS_25000_300M_PROFILE_DYNAMIC_TH : "1" +PG_LOSSLESS_100000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_100000_300M_PROFILE_SIZE : "9427" +PG_LOSSLESS_100000_300M_PROFILE_XON : "0" +PG_LOSSLESS_100000_300M_PROFILE_XOFF : "50176" +PG_LOSSLESS_100000_300M_PROFILE_XON_OFFSET : "3584" +PG_LOSSLESS_100000_300M_PROFILE_DYNAMIC_TH : "1" +SCHEDULER0_TYPE : "DWRR" +SCHEDULER0_WEIGHT : "14" +SCHEDULER1_TYPE : "DWRR" +SCHEDULER1_WEIGHT : "15" +WRED_ECN : "ecn_all" +INGRESS_LOSSY_PROFILE : "ingress_lossy_profile" +INGRESS_LOSSLESS_PROFILE : "pg_lossless_10000_300m_profile" +EGRESS_LOSSY_PROFILE : "egress_lossy_profile" +EGRESS_LOSSLESS_PROFILE : "egress_lossless_profile" \ No newline at end of file diff --git a/spytest/datastore/constants/dellemc-z9332f-o32.yaml b/spytest/datastore/constants/dellemc-z9332f-o32.yaml new file mode 100644 index 00000000000..f669dbb1c1a --- /dev/null +++ b/spytest/datastore/constants/dellemc-z9332f-o32.yaml @@ -0,0 +1,47 @@ +MAX_IPV4_ROUTES_SUPPORTED: '82944' +MAX_IPV6_ROUTES_SUPPORTED: '32768' +MAX_IPV4_ROUTES_FOR_ERROR_HANDLING: '128000' +MAX_IPV6_ROUTES_FOR_ERROR_HANDLING: '128000' +EGRESS_LOSSY_POOL_MODE : "dynamic" +EGRESS_LOSSY_POOL_SIZE : "67108864" +EGRESS_LOSSY_POOL_TYPE : "egress" +INGRESS_LOSSLESS_POOL_MODE : "dynamic" +INGRESS_LOSSLESS_POOL_SIZE : "59001152" +INGRESS_LOSSLESS_POOL_TYPE : "ingress" +INGRESS_LOSSLESS_POOL_XOFF : "7428992" +EGRESS_LOSSY_PROFILE_POOL : "egress_lossy_pool" +EGRESS_LOSSY_PROFILE_SIZE : "0" +EGRESS_LOSSY_PROFILE_DYNAMIC_TH : "3" +INGRESS_LOSSY_PROFILE_POOL : "ingress_lossless_pool" +INGRESS_LOSSY_PROFILE_SIZE : "0" +INGRESS_LOSSY_PROFILE_STATIC_TH : "67108864" +EGRESS_LOSSLESS_PROFILE_POOL : "egress_lossless_pool" +EGRESS_LOSSLESS_PROFILE_SIZE : "0" +EGRESS_LOSSLESS_PROFILE_STATIC_TH : "59001152" +PG_LOSSLESS_10000_40M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_10000_40M_PROFILE_SIZE : "1270" +PG_LOSSLESS_10000_40M_PROFILE_XON : "0" +PG_LOSSLESS_10000_40M_PROFILE_XOFF : "190500" +PG_LOSSLESS_10000_40M_PROFILE_XON_OFFSET : "2540" +PG_LOSSLESS_10000_40M_PROFILE_DYNAMIC_TH : "-2" +PG_LOSSLESS_25000_40M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_25000_40M_PROFILE_SIZE : "1270" +PG_LOSSLESS_25000_40M_PROFILE_XON : "0" +PG_LOSSLESS_25000_40M_PROFILE_XOFF : "190500" +PG_LOSSLESS_25000_40M_PROFILE_XON_OFFSET : "2540" +PG_LOSSLESS_25000_40M_PROFILE_DYNAMIC_TH : "-2" +PG_LOSSLESS_100000_40M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_100000_40M_PROFILE_SIZE : "1270" +PG_LOSSLESS_100000_40M_PROFILE_XON : "0" +PG_LOSSLESS_100000_40M_PROFILE_XOFF : "190500" +PG_LOSSLESS_100000_40M_PROFILE_XON_OFFSET : "2540" +PG_LOSSLESS_100000_40M_PROFILE_DYNAMIC_TH : "-2" +SCHEDULER0_TYPE : "DWRR" +SCHEDULER0_WEIGHT : "5" +SCHEDULER1_TYPE : "DWRR" +SCHEDULER1_WEIGHT : "1" +WRED_ECN : "ecn_all" +INGRESS_LOSSY_PROFILE : "ingress_lossy_profile" +INGRESS_LOSSLESS_PROFILE : "pg_lossless_10000_40m_profile" +EGRESS_LOSSY_PROFILE : "egress_lossy_profile" +EGRESS_LOSSLESS_PROFILE : "egress_lossy_profile" diff --git a/spytest/datastore/constants/delta-ag9032v1.yaml b/spytest/datastore/constants/delta-ag9032v1.yaml new file mode 100644 index 00000000000..1f4d9e5105f --- /dev/null +++ b/spytest/datastore/constants/delta-ag9032v1.yaml @@ -0,0 +1,65 @@ +MAX_ARP_SUPPORTED: '32768' +MAX_ND_SUPPORTED: '16384' +MAX_IPV4_ROUTES_SUPPORTED: '66560' +MAX_IPV6_ROUTES_SUPPORTED: '24576' +MAX_IPV4_ROUTES_FOR_ERROR_HANDLING: '128000' +MAX_IPV6_ROUTES_FOR_ERROR_HANDLING: '128000' +EGRESS_LOSSLESS_POOL_MODE : "static" +EGRESS_LOSSLESS_POOL_SIZE : "12766208" +EGRESS_LOSSLESS_POOL_TYPE : "egress" +EGRESS_LOSSY_POOL_MODE : "dynamic" +EGRESS_LOSSY_POOL_SIZE : "7326924" +EGRESS_LOSSY_POOL_TYPE : "egress" +INGRESS_LOSSLESS_POOL_MODE : "dynamic" +INGRESS_LOSSLESS_POOL_SIZE : "12766208" +INGRESS_LOSSLESS_POOL_TYPE : "ingress" +INGRESS_LOSSLESS_POOL_XOFF : "4625920" +EGRESS_LOSSLESS_PROFILE_POOL : "egress_lossless_pool" +EGRESS_LOSSLESS_PROFILE_SIZE : "0" +EGRESS_LOSSLESS_PROFILE_STATIC_TH : "12766208" +EGRESS_LOSSY_PROFILE_POOL : "egress_lossy_pool" +EGRESS_LOSSY_PROFILE_SIZE : "1518" +EGRESS_LOSSY_PROFILE_DYNAMIC_TH : "3" +INGRESS_LOSSY_PROFILE_POOL : "ingress_lossless_pool" +INGRESS_LOSSY_PROFILE_SIZE : "0" +INGRESS_LOSSY_PROFILE_DYNAMIC_TH : "3" +PG_LOSSLESS_10000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_10000_300M_PROFILE_SIZE : "56368" +PG_LOSSLESS_10000_300M_PROFILE_XON : "18432" +PG_LOSSLESS_10000_300M_PROFILE_XOFF : "55120" +PG_LOSSLESS_10000_300M_PROFILE_XON_OFFSET : "2496" +PG_LOSSLESS_10000_300M_PROFILE_DYNAMIC_TH : "-3" +PG_LOSSLESS_25000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_25000_300M_PROFILE_SIZE : "56368" +PG_LOSSLESS_25000_300M_PROFILE_XON : "18432" +PG_LOSSLESS_25000_300M_PROFILE_XOFF : "55120" +PG_LOSSLESS_25000_300M_PROFILE_XON_OFFSET : "2496" +PG_LOSSLESS_25000_300M_PROFILE_DYNAMIC_TH : "-3" +PG_LOSSLESS_100000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_100000_300M_PROFILE_SIZE : "56368" +PG_LOSSLESS_100000_300M_PROFILE_XON : "18432" +PG_LOSSLESS_100000_300M_PROFILE_XOFF : "55120" +PG_LOSSLESS_100000_300M_PROFILE_XON_OFFSET : "2496" +PG_LOSSLESS_100000_300M_PROFILE_DYNAMIC_TH : "-3" +SCHEDULER0_TYPE : "DWRR" +SCHEDULER0_WEIGHT : "14" +SCHEDULER1_TYPE : "DWRR" +SCHEDULER1_WEIGHT : "15" +WRED_ECN : "ecn_all" +WRED_GREEN_DROP_PROBABILITY : "5" +WRED_GREEN_MAX_THRESHOLD : "2097152" +WRED_GREEN_MIN_THRESHOLD : "1048576" +WRED_RED_DROP_PROBABILITY : "5" +WRED_RED_MAX_THRESHOLD : "2097152" +WRED_RED_MIN_THRESHOLD : "1048576" +WRED_YELLOW_DROP_PROBABILITY : "5" +WRED_YELLOW_MAX_THRESHOLD : "2097152" +WRED_YELLOW_MIN_THRESHOLD : "1048576" +WRED_GREEN_ENABLE : "true" +WRED_RED_ENABLE : "true" +WRED_YELLOW_ENABLE : "true" +INGRESS_LOSSY_PROFILE : "ingress_lossy_profile" +INGRESS_LOSSLESS_PROFILE : "pg_lossless_10000_300m_profile" +EGRESS_LOSSY_PROFILE : "egress_lossy_profile" +EGRESS_LOSSLESS_PROFILE : "egress_lossless_profile" +WRED_LOSSLESS_PROFILE : "AZURE_LOSSLESS" diff --git a/spytest/datastore/constants/quanta-ix1b-32x.yaml b/spytest/datastore/constants/quanta-ix1b-32x.yaml new file mode 100644 index 00000000000..121af07d82a --- /dev/null +++ b/spytest/datastore/constants/quanta-ix1b-32x.yaml @@ -0,0 +1,4 @@ +MAX_IPV4_ROUTES_SUPPORTED: '66560' +MAX_IPV6_ROUTES_SUPPORTED: '24576' +MAX_IPV4_ROUTES_FOR_ERROR_HANDLING: '128000' +MAX_IPV6_ROUTES_FOR_ERROR_HANDLING: '128000' diff --git a/spytest/datastore/constants/quanta-ix4-64x.yaml b/spytest/datastore/constants/quanta-ix4-64x.yaml new file mode 100644 index 00000000000..e85783f7d9e --- /dev/null +++ b/spytest/datastore/constants/quanta-ix4-64x.yaml @@ -0,0 +1,63 @@ +MAX_IPV4_ROUTES_SUPPORTED: '82944' +MAX_IPV6_ROUTES_SUPPORTED: '32768' +MAX_IPV4_ROUTES_FOR_ERROR_HANDLING: '128000' +MAX_IPV6_ROUTES_FOR_ERROR_HANDLING: '128000' +EGRESS_LOSSLESS_POOL_MODE : "static" +EGRESS_LOSSLESS_POOL_SIZE : "12766208" +EGRESS_LOSSLESS_POOL_TYPE : "egress" +EGRESS_LOSSY_POOL_MODE : "dynamic" +EGRESS_LOSSY_POOL_SIZE : "7326924" +EGRESS_LOSSY_POOL_TYPE : "egress" +INGRESS_LOSSLESS_POOL_MODE : "dynamic" +INGRESS_LOSSLESS_POOL_SIZE : "12766208" +INGRESS_LOSSLESS_POOL_TYPE : "ingress" +INGRESS_LOSSLESS_POOL_XOFF : "4625920" +EGRESS_LOSSLESS_PROFILE_POOL : "egress_lossless_pool" +EGRESS_LOSSLESS_PROFILE_SIZE : "0" +EGRESS_LOSSLESS_PROFILE_STATIC_TH : "12766208" +EGRESS_LOSSY_PROFILE_POOL : "egress_lossy_pool" +EGRESS_LOSSY_PROFILE_SIZE : "1518" +EGRESS_LOSSY_PROFILE_DYNAMIC_TH : "3" +INGRESS_LOSSY_PROFILE_POOL : "ingress_lossless_pool" +INGRESS_LOSSY_PROFILE_SIZE : "0" +INGRESS_LOSSY_PROFILE_DYNAMIC_TH : "3" +PG_LOSSLESS_10000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_10000_300M_PROFILE_SIZE : "56368" +PG_LOSSLESS_10000_300M_PROFILE_XON : "18432" +PG_LOSSLESS_10000_300M_PROFILE_XOFF : "55120" +PG_LOSSLESS_10000_300M_PROFILE_XON_OFFSET : "2496" +PG_LOSSLESS_10000_300M_PROFILE_DYNAMIC_TH : "-3" +PG_LOSSLESS_25000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_25000_300M_PROFILE_SIZE : "56368" +PG_LOSSLESS_25000_300M_PROFILE_XON : "18432" +PG_LOSSLESS_25000_300M_PROFILE_XOFF : "55120" +PG_LOSSLESS_25000_300M_PROFILE_XON_OFFSET : "2496" +PG_LOSSLESS_25000_300M_PROFILE_DYNAMIC_TH : "-3" +PG_LOSSLESS_100000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_100000_300M_PROFILE_SIZE : "56368" +PG_LOSSLESS_100000_300M_PROFILE_XON : "18432" +PG_LOSSLESS_100000_300M_PROFILE_XOFF : "55120" +PG_LOSSLESS_100000_300M_PROFILE_XON_OFFSET : "2496" +PG_LOSSLESS_100000_300M_PROFILE_DYNAMIC_TH : "-3" +SCHEDULER0_TYPE : "DWRR" +SCHEDULER0_WEIGHT : "14" +SCHEDULER1_TYPE : "DWRR" +SCHEDULER1_WEIGHT : "15" +WRED_ECN : "ecn_all" +WRED_GREEN_DROP_PROBABILITY : "5" +WRED_GREEN_MAX_THRESHOLD : "2097152" +WRED_GREEN_MIN_THRESHOLD : "1048576" +WRED_RED_DROP_PROBABILITY : "5" +WRED_RED_MAX_THRESHOLD : "2097152" +WRED_RED_MIN_THRESHOLD : "1048576" +WRED_YELLOW_DROP_PROBABILITY : "5" +WRED_YELLOW_MAX_THRESHOLD : "2097152" +WRED_YELLOW_MIN_THRESHOLD : "1048576" +WRED_GREEN_ENABLE : "true" +WRED_RED_ENABLE : "true" +WRED_YELLOW_ENABLE : "true" +INGRESS_LOSSY_PROFILE : "ingress_lossy_profile" +INGRESS_LOSSLESS_PROFILE : "pg_lossless_10000_300m_profile" +EGRESS_LOSSY_PROFILE : "egress_lossy_profile" +EGRESS_LOSSLESS_PROFILE : "egress_lossless_profile" +WRED_LOSSLESS_PROFILE : "AZURE_LOSSLESS" diff --git a/spytest/datastore/constants/quanta-ix8-56x.yaml b/spytest/datastore/constants/quanta-ix8-56x.yaml new file mode 100644 index 00000000000..038d4c85ef3 --- /dev/null +++ b/spytest/datastore/constants/quanta-ix8-56x.yaml @@ -0,0 +1,76 @@ +MAX_IPV4_ROUTES_SUPPORTED: '82944' +MAX_IPV6_ROUTES_SUPPORTED: '32768' +MAX_IPV4_ROUTES_FOR_ERROR_HANDLING: '128000' +MAX_IPV6_ROUTES_FOR_ERROR_HANDLING: '128000' +EGRESS_LOSSLESS_POOL_MODE : "static" +EGRESS_LOSSLESS_POOL_SIZE : "33004032" +EGRESS_LOSSLESS_POOL_TYPE : "egress" +EGRESS_LOSSY_POOL_MODE : "dynamic" +EGRESS_LOSSY_POOL_SIZE : "12766208" +EGRESS_LOSSY_POOL_TYPE : "egress" +INGRESS_LOSSLESS_POOL_MODE : "dynamic" +INGRESS_LOSSLESS_POOL_SIZE : "12766208" +INGRESS_LOSSLESS_POOL_TYPE : "ingress" +INGRESS_LOSSLESS_POOL_XOFF : "196608" +EGRESS_LOSSLESS_PROFILE_POOL : "egress_lossless_pool" +EGRESS_LOSSLESS_PROFILE_SIZE : "0" +EGRESS_LOSSLESS_PROFILE_STATIC_TH : "33004032" +EGRESS_LOSSY_PROFILE_POOL : "egress_lossless_pool" +EGRESS_LOSSY_PROFILE_SIZE : "1518" +EGRESS_LOSSY_PROFILE_DYNAMIC_TH : "3" +INGRESS_LOSSY_PROFILE_POOL : "ingress_lossless_pool" +INGRESS_LOSSY_PROFILE_SIZE : "0" +INGRESS_LOSSY_PROFILE_DYNAMIC_TH : "3" +PG_LOSSLESS_10000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_10000_300M_PROFILE_SIZE : "9427" +PG_LOSSLESS_10000_300M_PROFILE_XON : "0" +PG_LOSSLESS_10000_300M_PROFILE_XOFF : "50176" +PG_LOSSLESS_10000_300M_PROFILE_XON_OFFSET : "3584" +PG_LOSSLESS_10000_300M_PROFILE_DYNAMIC_TH : "1" +PG_LOSSLESS_25000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_25000_300M_PROFILE_SIZE : "9427" +PG_LOSSLESS_25000_300M_PROFILE_XON : "0" +PG_LOSSLESS_25000_300M_PROFILE_XOFF : "50176" +PG_LOSSLESS_25000_300M_PROFILE_XON_OFFSET : "3584" +PG_LOSSLESS_25000_300M_PROFILE_DYNAMIC_TH : "1" +PG_LOSSLESS_100000_300M_PROFILE_POOL : "ingress_lossless_pool" +PG_LOSSLESS_100000_300M_PROFILE_SIZE : "9427" +PG_LOSSLESS_100000_300M_PROFILE_XON : "0" +PG_LOSSLESS_100000_300M_PROFILE_XOFF : "50176" +PG_LOSSLESS_100000_300M_PROFILE_XON_OFFSET : "3584" +PG_LOSSLESS_100000_300M_PROFILE_DYNAMIC_TH : "1" +SCHEDULER0_TYPE : "DWRR" +SCHEDULER0_WEIGHT : "14" +SCHEDULER1_TYPE : "DWRR" +SCHEDULER1_WEIGHT : "15" +WRED_ECN : "ecn_all" +INGRESS_LOSSY_PROFILE : "ingress_lossy_profile" +INGRESS_LOSSLESS_PROFILE : "pg_lossless_10000_300m_profile" +EGRESS_LOSSY_PROFILE : "egress_lossy_profile" +EGRESS_LOSSLESS_PROFILE : "egress_lossless_profile" +PDDF_FANS_LIST: + - 'FAN1_1' + - 'FAN1_2' + - 'FAN2_1' + - 'FAN2_2' + - 'FAN3_1' + - 'FAN3_2' + - 'FAN4_1' + - 'FAN4_2' + - 'FAN5_1' + - 'FAN5_2' + - 'FAN6_1' + - 'FAN6_2' +PDDF_PSU_LIST: + - 'PSU1' + - 'PSU2' +PDDF_THERMAL_LIST: + - 'Temp_Ambient_0' + - 'Temp_Ambient_1' + - 'Temp_Ambient_2' + - 'Temp_Ambient_3' + - 'Temp_Ambient_4' + - 'Temp_Ambient_5' + - 'Temp_Ambient_6' + - 'Temp_CPU' +Manufacturer: "QSMC" \ No newline at end of file diff --git a/spytest/datastore/constants/quanta-ix9-32x.yaml b/spytest/datastore/constants/quanta-ix9-32x.yaml new file mode 100644 index 00000000000..2381b8b6c2d --- /dev/null +++ b/spytest/datastore/constants/quanta-ix9-32x.yaml @@ -0,0 +1,47 @@ +MAX_IPV4_ROUTES_SUPPORTED: '82944' +MAX_IPV6_ROUTES_SUPPORTED: '32768' +MAX_IPV4_ROUTES_FOR_ERROR_HANDLING: '128000' +MAX_IPV6_ROUTES_FOR_ERROR_HANDLING: '128000' +EGRESS_LOSSY_POOL_MODE : 'dynamic' +EGRESS_LOSSY_POOL_SIZE : '67108864' +EGRESS_LOSSY_POOL_TYPE : 'egress' +INGRESS_LOSSLESS_POOL_MODE : 'dynamic' +INGRESS_LOSSLESS_POOL_SIZE : '59001152' +INGRESS_LOSSLESS_POOL_TYPE : 'ingress' +INGRESS_LOSSLESS_POOL_XOFF : '7428992' +EGRESS_LOSSY_PROFILE_POOL : 'egress_lossy_pool' +EGRESS_LOSSY_PROFILE_SIZE : '0' +EGRESS_LOSSY_PROFILE_DYNAMIC_TH : '3' +INGRESS_LOSSY_PROFILE_POOL : 'ingress_lossless_pool' +INGRESS_LOSSY_PROFILE_SIZE : '0' +INGRESS_LOSSY_PROFILE_STATIC_TH : '67108864' +EGRESS_LOSSLESS_PROFILE_POOL : 'egress_lossless_pool' +EGRESS_LOSSLESS_PROFILE_SIZE : '0' +EGRESS_LOSSLESS_PROFILE_STATIC_TH : '59001152' +PG_LOSSLESS_10000_40M_PROFILE_POOL : 'ingress_lossless_pool' +PG_LOSSLESS_10000_40M_PROFILE_SIZE : '1270' +PG_LOSSLESS_10000_40M_PROFILE_XON : '0' +PG_LOSSLESS_10000_40M_PROFILE_XOFF : '190500' +PG_LOSSLESS_10000_40M_PROFILE_XON_OFFSET : '2540' +PG_LOSSLESS_10000_40M_PROFILE_DYNAMIC_TH : '-2' +PG_LOSSLESS_25000_40M_PROFILE_POOL : 'ingress_lossless_pool' +PG_LOSSLESS_25000_40M_PROFILE_SIZE : '1270' +PG_LOSSLESS_25000_40M_PROFILE_XON : '0' +PG_LOSSLESS_25000_40M_PROFILE_XOFF : '190500' +PG_LOSSLESS_25000_40M_PROFILE_XON_OFFSET : '2540' +PG_LOSSLESS_25000_40M_PROFILE_DYNAMIC_TH : '-2' +PG_LOSSLESS_100000_40M_PROFILE_POOL : 'ingress_lossless_pool' +PG_LOSSLESS_100000_40M_PROFILE_SIZE : '1270' +PG_LOSSLESS_100000_40M_PROFILE_XON : '0' +PG_LOSSLESS_100000_40M_PROFILE_XOFF : '190500' +PG_LOSSLESS_100000_40M_PROFILE_XON_OFFSET : '2540' +PG_LOSSLESS_100000_40M_PROFILE_DYNAMIC_TH : '-2' +SCHEDULER0_TYPE : 'DWRR' +SCHEDULER0_WEIGHT : '5' +SCHEDULER1_TYPE : 'DWRR' +SCHEDULER1_WEIGHT : '1' +WRED_ECN : 'ecn_all' +INGRESS_LOSSY_PROFILE : 'ingress_lossy_profile' +INGRESS_LOSSLESS_PROFILE : 'pg_lossless_10000_40m_profile' +EGRESS_LOSSY_PROFILE : 'egress_lossy_profile' +EGRESS_LOSSLESS_PROFILE : 'egress_lossy_profile' \ No newline at end of file diff --git a/spytest/datastore/constants/quanta-ix9-c64o16.yaml b/spytest/datastore/constants/quanta-ix9-c64o16.yaml new file mode 100644 index 00000000000..db935941812 --- /dev/null +++ b/spytest/datastore/constants/quanta-ix9-c64o16.yaml @@ -0,0 +1,43 @@ +EGRESS_LOSSY_POOL_MODE : 'dynamic' +EGRESS_LOSSY_POOL_SIZE : '67108864' +EGRESS_LOSSY_POOL_TYPE : 'egress' +INGRESS_LOSSLESS_POOL_MODE : 'dynamic' +INGRESS_LOSSLESS_POOL_SIZE : '59001152' +INGRESS_LOSSLESS_POOL_TYPE : 'ingress' +INGRESS_LOSSLESS_POOL_XOFF : '7428992' +EGRESS_LOSSY_PROFILE_POOL : 'egress_lossy_pool' +EGRESS_LOSSY_PROFILE_SIZE : '0' +EGRESS_LOSSY_PROFILE_DYNAMIC_TH : '3' +INGRESS_LOSSY_PROFILE_POOL : 'ingress_lossless_pool' +INGRESS_LOSSY_PROFILE_SIZE : '0' +INGRESS_LOSSY_PROFILE_STATIC_TH : '67108864' +EGRESS_LOSSLESS_PROFILE_POOL : 'egress_lossless_pool' +EGRESS_LOSSLESS_PROFILE_SIZE : '0' +EGRESS_LOSSLESS_PROFILE_STATIC_TH : '59001152' +PG_LOSSLESS_10000_40M_PROFILE_POOL : 'ingress_lossless_pool' +PG_LOSSLESS_10000_40M_PROFILE_SIZE : '1270' +PG_LOSSLESS_10000_40M_PROFILE_XON : '0' +PG_LOSSLESS_10000_40M_PROFILE_XOFF : '190500' +PG_LOSSLESS_10000_40M_PROFILE_XON_OFFSET : '2540' +PG_LOSSLESS_10000_40M_PROFILE_DYNAMIC_TH : '-2' +PG_LOSSLESS_25000_40M_PROFILE_POOL : 'ingress_lossless_pool' +PG_LOSSLESS_25000_40M_PROFILE_SIZE : '1270' +PG_LOSSLESS_25000_40M_PROFILE_XON : '0' +PG_LOSSLESS_25000_40M_PROFILE_XOFF : '190500' +PG_LOSSLESS_25000_40M_PROFILE_XON_OFFSET : '2540' +PG_LOSSLESS_25000_40M_PROFILE_DYNAMIC_TH : '-2' +PG_LOSSLESS_100000_40M_PROFILE_POOL : 'ingress_lossless_pool' +PG_LOSSLESS_100000_40M_PROFILE_SIZE : '1270' +PG_LOSSLESS_100000_40M_PROFILE_XON : '0' +PG_LOSSLESS_100000_40M_PROFILE_XOFF : '190500' +PG_LOSSLESS_100000_40M_PROFILE_XON_OFFSET : '2540' +PG_LOSSLESS_100000_40M_PROFILE_DYNAMIC_TH : '-2' +SCHEDULER0_TYPE : 'DWRR' +SCHEDULER0_WEIGHT : '5' +SCHEDULER1_TYPE : 'DWRR' +SCHEDULER1_WEIGHT : '1' +WRED_ECN : 'ecn_all' +INGRESS_LOSSY_PROFILE : 'ingress_lossy_profile' +INGRESS_LOSSLESS_PROFILE : 'pg_lossless_10000_40m_profile' +EGRESS_LOSSY_PROFILE : 'egress_lossy_profile' +EGRESS_LOSSLESS_PROFILE : 'egress_lossy_profile' \ No newline at end of file diff --git a/spytest/datastore/messages/common.yaml b/spytest/datastore/messages/common.yaml new file mode 100644 index 00000000000..5b4abf4d7dd --- /dev/null +++ b/spytest/datastore/messages/common.yaml @@ -0,0 +1,92 @@ +test_case_passed: Test case Passed. +test_case_failed: Test case Failed. +test_case_failure_message: .{}. +show_system_status_not_ready: "show system status is not ready in time" +test_execution_skipped: Test case execution is skipped {} +test_case_not_executed: Test case Not Executed. +test_case_not_executeds: Test case Not Executed(s). +test_case_not_executed_s_service: Test case Not Executed(s) - Required service parameters is not defined - {}. +test_case_result_with_count: Test cases Passed {} failed {}. +test_case_unsupported: Test case Unsupported. +msg: '{}' +operation_successful : The operation is successful +operation_failed : The operation is failed +operation_failed_msg : The operation is failed {} +invalid_value: The given value {} for {} is invalid +cmds_apply_ok : commands apply successful +exception_file_line: Exception in file {} at line {} +exception_name_file_line: Exception {} in file {} at line {} +command_failed_recovered_using_ctrlc: Command '{}' failed to give prompt, recovered using CTRL+C +simultaneous_commands: Commands should not be executed on same device simultaneously +min_topology_fail: Minimum Topology Check Failed {} +pertest_topology_fail: Per Test Topology Check Failed {} +depedent_test_failed: Prerequisite test function {} Failed +build_upgrade_success: Successfully upgraded the DUT(s) +build_upgrade_failure: Failed to upgrade the DUTS(s) +build_reboot_success: Successfully rebooted the DUT(s) +ssh_connection_failed: SSH connection to provided host {} not successful. +file_transfer_failed: File {} transfer failed. +certificate_generation_failed: Certificate generation failed +error_string_not_found: Error {} not found in log file {} +service_not_running: Service {} not running +service_running_not_expected: Service {} running, which is not expected +scp_file_transfer_failed: SCP file transfer failed from {} to {} +content_not_found: Search content not found +service_missing_in_sonic_services: Service {} not found in sonic_services.yaml +service_not_stopped: Service {} not stopped +service_not_started: Service {} not started +test_case_max_timeout: Testcase max time reached. Exiting. +module_init_max_timeout: Module initialization max time reached. Exiting. +operation_max_timeout: Operation {} taking unexpectedly long time. Exiting. +console_hang_observed: Console hang observed. Exiting. +entry_not_found: Entry not found +invalid_traffic_stats: Invalid traffic statistics +pfc_counters_not_initialized: PFC counters are not initialized even after {} seconds +exception_observed: Exception observed - {} +empty_values_observed: Observed empty values for some fields +error_string_found: Error {} found in log file {} +counters_are_not_initilaized: DUT counters are not initialized +module_not_supported: Module not supported on {} + +## TOPOLOGY +dut_not_found: DUT list not found. +topology_not_matching: Topology not matching, required {} dut, having {} duts. +links_not_matching: Links between {} are not matching the requirement of {} +no_data_found: Data not found. +minimum_links_not_matching: Minimum links of {} not found between DUTs +mismatch_minimun_tg_links: Mismatch in minimum TG links, required {} but observed {} on {} +platform_check_fail: Expected platform not found.Found platform {} + +## CFG +base_config_failed: base configuration failed +base_config_verification_failed: base configuration verification failed +module_config_failed: module configuration failed. {} +module_config_verification_failed: module configuration verification failed +pretest_config_failed: pre-test configuration failed + +config_cmd_error: config command '{}' failed +critical_syncd_exited: Process syncd exited unxepectedly +critical_orchagent_exited: Process orchagent exited unxepectedly +critical_vlanmgrd_exited: Process vlanmgrd exited unxepectedly +critical_terminated_by_sigabrt: terminated by SIGABRT +critical_core_dumped: core dumped +critical_swss_service_failed: Job for swss.service failed. +critical_err_syncd: processEvent, failed to execute api + +test_case_id_passed: testcase {} Passed +test_case_id_failed: testcase {} Failed + +system_not_ready: System not ready + +uicli_pass: Successfully verified CLI tests in '{}'. Commands passed '{}' and failed '{}'. +uicli_fail: Failed to verify CLI tests in '{}'. Commands passed '{}' and failed '{}'. +uicli_tc_status: KLISH '{}' '{}' + +uirest_pass: Successfully verified REST tests in '{}'. Rest calls passed '{}' and failed '{}'. +uirest_fail: Failed to verify REST tests in '{}'. Rest calls passed '{}' and failed '{}'. +uirest_tc_status: REST '{}' '{}' + +uignmi_pass: Successfully verified GNMI tests in '{}'. GNMI calls passed '{}' and failed '{}'. +uignmi_fail: Failed to verify GNMI tests in '{}'. GNMI calls passed '{}' and failed '{}'. +uignmi_tc_status: GNMI '{}' '{}' + diff --git a/spytest/datastore/messages/gearbox.yaml b/spytest/datastore/messages/gearbox.yaml new file mode 100644 index 00000000000..33e7749b526 --- /dev/null +++ b/spytest/datastore/messages/gearbox.yaml @@ -0,0 +1,11 @@ +##COMMON + + +## GEARSYNCD +docker_not_found: Docker {} not found +daemon_not_found: Daemon {} not found +record_not_found: Record {} not found +key_found: Key {} found with {} data. +key_not_found: Key {} not found with {} data. + + diff --git a/spytest/datastore/messages/messages.yaml b/spytest/datastore/messages/messages.yaml new file mode 100644 index 00000000000..08934d21da4 --- /dev/null +++ b/spytest/datastore/messages/messages.yaml @@ -0,0 +1,14 @@ +common: !include common.yaml +tgen: !include tgen.yaml +routing: !include routing.yaml +testbed: !include testbed.yaml +qos: !include qos.yaml +system: !include system.yaml +switching: !include switching.yaml +security: !include security.yaml +gearbox: !include gearbox.yaml +ut_snmp: !include ut_snmp.yaml + + +messages: + default: [common, tgen, routing, testbed, qos, system, switching, security, gearbox, ut_snmp] diff --git a/spytest/datastore/messages/qos.yaml b/spytest/datastore/messages/qos.yaml new file mode 100644 index 00000000000..7d7a97b3997 --- /dev/null +++ b/spytest/datastore/messages/qos.yaml @@ -0,0 +1,70 @@ +##ACL +failed_to_create_acl : Failed to create ACL -{} +failed_to_create_acl_rule : Failed to create ACL rule +unmatched_traffic_dropped: ACL unmatched traffic is not forwarded as per the default permit rule +matched_traffic_forward_fail: Permit acl rule matched traffic is not forwarded +matched_traffic_drop_fail: Deny acl rule matched traffic is forwarded +traffic_match_drop_rule_fail: Traffic matching with deny rule is not dropped +create_acl_successful : Successfully created acl {} +test_case_passed: Test case passed +failed_drop_traffic : Failed to drop valid traffic +failed_forward_traffic: Failed to forward valid traffic +acl_counters_update_fail: ACL counters are not updated for the ACL rule {} +verify_acl_rule_fail: verification of {} ACL rule configuration is failed +clear_acl_counter_fail: Failed to clear acl counter for rule-{} in ACL-{} +verify_clear_acl_counter_fail: ACL counter for rule-{} in acl-{} is not cleared even after the clear operation +acl_counter_output_update_fail: The contents in show acl counters are not updated for the ACL rule {} +verify_acl_rule_delete_fail: ACL rule-{} under the acl-{} is not erased as per the configuration +traffic_failed_as_per_the_drop_rule: Failed to drop traffic as per the configured drop rule +traffic_success_as_per_the_drop_rule: Succesfully dropped the traffic as per the configured ACL rule. +traffic_failed_as_per_the_allow_rule: Failed to allow traffic as per the configured allow rule +old_acl_rule_not_deleted_after_acl_update_full: ACL rule-{} is not deleted in the acl table-{} after acl update full +new_acl_rule_not_created_after_acl_update_full: ACL rule-{} is not created in the acl table-{} after acl update full +old_acl_rule_deleted_after_acl_update_incremental: ACL rule-{} is deleted in the acl table-{} after acl update incremental +new_acl_rule_not_updated_in_config_db: New ACL rule is not updated in the config db +old_acl_rule_not_present_in_config_db: Old ACL rule is not present in the config db +queue_traffic_failed: Traffic is not going to appropriate queue. +queue_map_not_found: Queue {} mapping not found. +ecn_config_not_updated_in_config_db: ECN configuration not updated in the config db +wred_config_not_updated_in_config_db: WRED configuration not updated in the config db +acl_table_name_missing: Acl table name missing +acl_table_not_found: Acl table not found +acl_rule_table_not_found: Acl Table/Rule not found + +#### Port priority shaping +traffic_transmitted_on_the_queue: Traffic {} transmitted on {} at a rate of {} +traffic_transmitted_on_the_port: Traffic {} transmitted on {} at a rate of {} +port_shaping_verify: Port shaper verification {} +queue_shaping_verify: Queue level shaper verification {} +min_not_met_scheduling: Scheduling of queues in min-bandwidth not met case {} +port_shaping_after_profile_removed: Traffic is {} rate-limited after removing the existing port shaper profile configuration +queue_shaping_profile_update: Traffic is {} rate-limited as per the updated value when user updates the existing QoS shaper configuration +queue_and_port_level_shaping: Verification of both queue and port level shaper configuration {} +shaper_scheduler_interaction: QoS shaper interaction with scheduler {} +port_shaping_after_cold_reboot: Port shaping {} after cold reboot +port_shaping_after_fast_reboot: Port shaping {} after fast-reboot +port_shaping_after_config_reload: Port shaping {} after config-reload +port_shaping_after_warm_reboot: Port shaping {} after warm-reboot +queue_shaping_after_cold_reboot: Queue shaping {} after cold reboot +queue_shaping_after_fast_reboot: Queue shaping {} after fast-reboot +queue_shaping_after_config_reload: Queue shaping {} after config-reload +queue_shaping_after_warm_reboot: Queue shaping {} after warm-reboot +queue_shaping_effect_on_other: Verification of queue shaping profile on one queue not effecting other has {} +port_shaping_effect_on_other: Verification of port shaping profile on one port not effecting other has {} +max_supported_scheduler_success: More than max supported scheduler profiles creation success which is not expected +max_supported_scheduler_failed: More than max supported scheduler profiles creation failed as expected + +##ACL_DSCP +classifier_opeartion_failed : classifier configuration failed +policy_operation_failed : policy configuration failed +Flow_operation_failed : Flow configuration failed +classifier_opeartion_successful : classifier configuration successful +policy_operation_successful: policy configuration successful +Flow_operation_successful : Flow configuration successful +priority_check_failed : Priority is failed for interface + +##CoPP +igmp_rate_limit_status: CPU counter check for rate limiting igmp to {} pps is {} +sflow_rate_limit_status: CPU counter check for rate limiting sflow to {} pps is {} +copp_rate_limit_pass: Successfully verified that {} packets hit correct CPU queue {} and were properly rate limited to {} PPS +copp_rate_limit_fail: Failed to verify that {} packets did not hit correct CPU queue {} or were not properly rate limited to {} PPS diff --git a/spytest/datastore/messages/routing.yaml b/spytest/datastore/messages/routing.yaml new file mode 100644 index 00000000000..1a0609c429f --- /dev/null +++ b/spytest/datastore/messages/routing.yaml @@ -0,0 +1,222 @@ +##IP +ip_created: IP created successfully +ping_success: Ping operation is successful +arp_create_fail : Failed to create arp entry for ip address {} +ip_routing_int_create_fail : IPv4 Routing interface {} creation failed. +ip6_routing_int_create_fail : IPv6 Routing interface {} creation failed. +ip_routing_int_delete_fail : IPv4 Routing interface {} deletion failed. +ip6_routing_int_delete_fail : IPv6 Routing interface {} deletion failed. +ip_static_route_create_fail : IPv4 Static Route {} creation failed. +ip6_static_route_create_fail : IPv6 Static Route {} creation failed. +ip_traffic_fail : IP traffic has failed. +route_table_not_cleared_by_withdraw_from_tg : Route table is not cleared by withdrawing of routes on TG +route_table_not_updated_by_advertise_from_tg : Route table is not updated after advertising of routes on TG +ingress_traffic_rate_not_matching_with_egress_rate : Rate of traffic ingressing is not matching with rate of traffic egressing from the DUT +egress_traffic_rate_not_zero : Rate of traffic egressing from the DUT is not Zero. +ip6_link_local_addr_auto_generation_failed : IPv6 Link Local address auto generation failed +ip6_ping_fail_over_link_local_addr : Ipv6 Ping over Link Local address is failed. +ip6_traffic_over_link_local_nh_fail : IPv6 traffic forwarding via link local address as next hop is failed. +ip6_traffic_over_link_local_nh_fail_vrf : IPv6 traffic forwarding via link local address as next hop is failed in non default VRF. +ip6_traffic_over_link_local_nh_fail_manual : IPv6 traffic forwarding via manual configured link local address as next hop is failed +ip6_disable_link_local_failed: Disabling of IPV6 link local address on {} failed. + +##BGP +bgp_router_created: BGP Router created successfully +bgp_ip_peer_establish_fail : Failed to Establish BGP IPv4 peer {} +bgp_ip6_peer_establish_fail : Failed to Establish BGP IPv6 peer {} +bgp_aggregation_pass : BGP prefix {} aggregated +bgp_aggregation_fail : Could not aggregate BGP prefix {} +bgp_route_info : bgp route {} info {} +traffic_verification_failed_during_warm_reboot: traffic loss observed during warm reboot +BGP_established_over_portchannel_after_reboot: BGP established over portchannel after reboot +BGP_failed_to_establish_over_portchannel_after_reboot: BGP failed to establish over portchannel after reboot +BGP_unnumbered_neighbor_establish_status: BGP unnumbered neighbor {} to establish +BGP_unnumbered_neighbor_route_learn_status: Learning of IPV4 route over IPV6 in BGP unnumbered neighbor is {}. + + +##OSPF +ospf_topology_config_fail: OSPF base topology config failed {} +ospf_topology_ipv4_config_fail: failed to configure OSPF link IPv4 addresses {} +ospf_topology_vlan_config_fail: failed to configure OSPF vlan links {} +ospf_topology_portchannel_config_fail: failed to configure OSPF portchannel links {} +ospf_topology_need_fail: OSPF topology requiremnt failed {} +ospf_router_config_fail: OSPF router config failed for {} +ospf_router_mode_config_fail: OSPF router mode config failed for {} +ospf_interface_mode_config_fail: OSPF interface mode config failed for {} +ospf_route_validation_fail: OSPF validation failed for {} +ospf_session_test_fail: OSPF session test failed {} +ospf_session_test_pass: OSPF session test passed {} +ospf_traffic_test_fail: Failed to forward the traffic based on the routes advertised by the OSPF protocol + + +##ARP +ARP_entry_dynamic_entry_fail: arp table is not updated with {} entry in {} +ARP_dynamic_entry_removal_fail: arp {} unable to remove dynamic arp entry when routing interface {} is down +static_arp_create_fail: failed to create static arp in {} +static_arp_delete_fail: failed to delete static arp in {} +arp_ageout_time_config_fail: failed to configure arp ageout time {} +ARP_dynamic_entry_removal_due_to_ageout_fail: dynamic arp entry {} is still present after ageout time +ARP_scale_fail: Failed to create {} ARP entries +ARP_dynamic_entry_clear_arp_fail: arp table {} is not cleared in {} + +##ND +ND_entry_count_fail: ND table is not updated with {} entries +NDP_entry_dynamic_entry_fail: NDP table not updated with the entry {} + +##Ping +ping_fail: ping got failed to IP address {} from {} +ping_fail_from_DUT_to_DUt : ping fail from dut{} to dut{} +traceroute_fail_from_DUT_to_DUT : Traceroute fail from dut{} to dut{} +v6_traceroute_fail_from_DUT_to_DUT : IPv6 Traceroute fail from dut{} to dut{} +v6_ping_fail_from_DUT_to_DUT : IPv6 ping fail from dut{} to dut{} + +##NAT +static_nat_translation_entry_create_fail : Static NAT translation entry creation failed for local ip {} and global ip {} +static_nat_translation_entry_del_fail : Static NAT translation entry deletion failed for local ip {} and global ip {} +snat_translation_failed_in_packet : NAT translation failed in packet, source ip {} not translated to {} +dnat_translation_fail_in_packet : NAT translation failed in packet, destination ip {} not translated to {} +dynamic_snat_translation_entry_create_fail : dynamic SNAT translation entry creation failed for local sip {} and outside sip {} +dynamic_dnat_translation_entry_create_fail : dynamic DNAT translation entry creation failed for outside dip {} and inside dip {} +pool_del_fail : Dynamic pool {} deletion failed +pool_del_success : Dynamic pool {} deletion successful +nat_translation_happening_for_acl_do_not_nat_match_packets : NAT Translation happening for ACL do-not-nat rule matching packets +nat_translation_happening_in_no_nat_case : NAT translation happening for packets, even in case of no nat translation +nat_translation_table_not_cleared : NAT transalation table is not cleared successfully +nat_translation_table_entry_deleted_incorrectly : NAT transalation table is deleted incorrectly +tc_passed : The operation is successful +natmgrd_failed_to_recv : Natmgrd failed to receive {} config from {} +TC_NAT_APP_10_failed: Translation_entry_create Failed {} {} {} TC- NAT-APP-10 FAILED +TC_NAT_APP_11_failed: Traffic translation Failed {} {} {} NAT-APP-11 Failed +dnat_translation_failed: Traffic translation Failed. DNAT Translation Failed {} +TC_NAT_APP_Failed : NAT TC {} Failed +pool_config_fail : Dynamic pool {} {} fail +orchagent_failed_to_recv : Orchaagent failed to receive {} config from {} +natsyncd_failed_to_recv : Natsyncd failed to receive SNAT create notification +failed_global_config: Failed to configure {} +failed_to_configure_zone: Failed to configure zone {} +dynamic_nat_successful : Dynamic NAT translation is successful +dynamic_nat_fail : Dynamic NAT translation failed +twicenat_translation_failed_in_packet: NAT translation is failing for eithe Source or Destination ip address in case of twicenat +nat_translation_global_timeout_verified: NAT Global timeout successfully verified +nat_translation_successful_after_reboot: NAT translation successful after reboot +nat_translation_successful_after_config_reload: NAT translation successful after config save and reload +dynamic_nat_scaling_failed: Failed to learn max supported dynamic nat entries +ping_fail_across_nat_zone: ping failed across nat zones +traceroute_over_nat_failed : Traceroute over NAT translation failed +traceroute_over_nat_translation_successful : Traceroute is successful over a NAT translation +nat_warm_reboot_failed: Warm boot failed in NAT +dynamic_napt_entry_exists_after_docker_restart: Dynamic NAPT entry is not cleared during nat docker restart +static_nat_entry_not_restored_after_docker_restart: Static NAT entry not restored after docker restart +NAT_unsupported_platform : NAT is not supported on this platform {} + +## Error Handling +fib_failure_route_fail : Route {} failed in Fib Failure state. +container_not_running: "Error response from daemon: Container is not running" +failed_to_create_bcm_route: Failed to created BCM route with af {}, ip {}. +failed_to_create_bcm_nbr: Failed to created BCM neighbour with af {}, ip {}. + + +#BFD +single_bfd_peer : In {} BFD peer parameters incorrect for {} +bfd_peer_params : In {} one or more BFD peer parameters is incorrect +bgp_bfd_params : In {} BGP BFD neighbor params failed for {} +bfd_fail_tc : BGP BFD failed over {} port for {} +bfd_fail_tcid : BFD test case id {} failed for mode {} version {} +bfd_pass_tcid : BFD test case id {} passed for mode {} version {} +bfd_convergence_time : Traffic Convergence with BFD failed +bfd_counters_fail: BFD counters verification failed for {} +bfd_fail_reason: Failed {} +bgp_down_reason: In {} BGP down reason incorrect for {} +bgp_neighbor_not_form_error: BGP Neighbor {} formation did not happen +bfd_neighbor_ping_test_error: Ping test to {} failed before BFD neighbor formation +bfd_peer_success: BFD peer {} is successfully up +bfd_peer_error: BFD peer {} is not up +bfd_neighbor_not_form_error: BFD Neighbor {} formation did not happen +bfd_neighbor_port_shutnoshut_error: BFD neighborship {} is not available after port shut/noshut +bfd_neighbor_up_error: BFD neighborship {} is available even though ports are down + +#VRF +vrf_config : Vrf {} config failed on dut {} +vrf_bind : Interface {} not bound to vrf {} +bgp_vrf : VRF BGP {} down on dut {} +vrf_traffic_fail : In vrf {} traffic failed +vrf_ping : Ping failed from vrf {} to vrf {} +v6_vrf_ping : IPv6 Ping failed from vrf {} to vrf {} +vrf_traceroute : Traceroute failed from vrf {} to vrf {} +v6_vrf_traceroute : IPv6 Traceroute failed from vrf {} to vrf {} +v4_vrf_routes : IPv4 routes not present in Vrf {} on dut {} +v6_vrf_routes : IPv6 routes not presnt in Vrf {} on {} +v4_vrf_traffic : IPv4 traffic failed in vrf {} +v6_vrf_traffic : IPv6 traffic failed in vrf {} +vrf_basic_tc_passed : VRF Lite Basic Test Case passed +bgp_vrf_tc_passed : VRF Lite BGP Test Case passed +sag_basic_tc_passed : SAG Basic Test Case passed + +#NDP +ndp_entries_clearing_failed: ndp entries clearing failed +ndp_dynamic_entry_fail: ndp table is not updated with the dynamic entry +static_ndp_create_fail: failed to create static ndp entry +ndp_entry_ip6_link_local_create_fail : NDP table not updated for IPv6 Link Local address +ndp_table_clear_fail : NDP table is not cleared successfully + +#L3 DHCP Relay +IP_Helper_address_config_failed : IP Helper address config failed on Relay agent dut {} +IP_address_assignment_failed: IP address assignment failed on client dut {} +IPv6_address_assignment_failed: IPv6 address assignment failed on client dut {} +client_got_ip_with_invalid_ip_helper_address: client got ip with invalid ip helper address {} +client_got_ip_without_proper_connectivity_to_server: client got ip without proper connectivity to server +fast_reboot_failed: fast reboot failed in relay agent with dhcp relay config +docker_restart_failed: Docker Restart failed in relay agent with dhcp relay config +client_failed_to_renew_ip_address: client failed to renew ip address before lease time +packets_relayed_from_server_to_client_statistics_not_incremented: packets relayed from server to client statistics not incremented +packets_relayed_from_client_to_server_statistics_not_incremented: packets relayed from client to server statistics not incremented +able_to_config_invalid_ip_address_as_IP_Helper_address: able to config invalid ip address as IP Helper address +dhcp_relay_functionality_tc_status: DHCP {} Relay Test Case {} in for {} +dhcp_relay_vrf_functionality_tc_status: DHCP {} Relay Test Case {} in vrf {} for {} +failed_to_clear_relay_statistics: Failed to clear relay statistics on interface {} +dhcp_relay_addr_config_fail : DHCP Relay address config failed on interface {} for {} +dhcp_relay_link_select_src_intf_status_default_vrf : Validation of DHCP relay agent link-select functionality on the {} based routing interface with source interface as {} interface is {} in default VRF. +dhcp_relay_link_select_shut_no_shut_src_intf_status_default_vrf : Validation of DHCP relay agent link-select functionality after shut no shut on the {} based routing interface with source interface as {} interface is {} in default VRF. +dhcp_relay_link_select_src_intf_status_default_vrf_vlan_member_add_remove : Validation of DHCP relay agent link-select functionality on the {} based routing interface with source interface as {} interface is {} in default VRF after vlan member is removed and re added. +dhcp_relay_link_select_src_intf_update_status: Verification of source interface option update for DHCP RELAY Agent {} +client_rcvd_ip_address: Client recevied IP address {} +dhcp_relay_link_select_src_intf_ip_addr_change_status: Verification the DHCP relay agent link-select functionality in default VRF after removing and re-adding the ip address on the Source interface is {} +dhcp_relay_link_select_src_intf_ip_shut_no_shut_status: Verification the DHCP relay agent link-select functionality in default VRF after shut no shut on the source interface is {} +dhcp_relay_stats_verification_status: DHCP relay statistics verification {} +dhcp_relay_link_select_default_vrf_with_diff_boot_status: Verification the DHCP relay agent link-select functionality in default VRF after {} is {}. +dhcp_relay_link_select_ipv6_link_local_status: Verification the DHCP relay agent link-select functionality in default VRF with DHCP server rechability via IPv6 link local address is {} +DHCP_relay_link_select_max_hop_count_status: Verification of max hop count configuration with DHCP relay configuration is {}. +dhcp_relay_verification_rest_klish_status_default_vrf : DHCP{} Relay functionality verification in default VRF through {} is {}. + + +#MCAST +PIM_neighborship_failed : PIM neighborship between {} and {} not established +Mroute_vtysh_failed : Mroute source {} group {} output {} not shown in vtysh +Mroute_appdb_failed : Mroute source {} group {} output {} {} in app-db +Intf_mcast_failed : Interface multicast mode not enabled on {} +IGMP_join_failed : IGMP join entry with output {} not in vtysh +PIM_join_failed : PIM join entry with output {} not in vtysh +counters_mismatch : Counters not matched for {} interface {} +drop_counters_mismatch : Drop counters not matched for {} interface {} +Mroute_bcmcmd_failed : Could not find Mroute source {} group {} in hardware +Mroute_appdb_update_failed : Mroute source {} group {} oif update failed + + +# IP helper +Failed_to_relay_all_default_protocol : Test failed. Test result, stream wise - {} +All_UDP_pkts_relayed_to_server : Test case passed, all streams are relayed from clients to servers. Test result - {} +Dhcp_non_udp_exclude_port_packets_relay_success : Test case Passed - {} broadcast packets are not forwarded +Dhcp_non_udp_exclude_port_packets_relay_msg : Test cases, DHCP/non-UDP/DNS with TTL=1/UDP packet with custom port {} in exclude list {}. Test result {} +Unreachable_helper_IP_or_non_existing_VRF_of_helper_IP : Test cases failed. Test result of each test - {} +Failed_to_relay_UDP_packets_after_restart : Test case failed. Failed to relay UDP packets after {} +Pkts_to_relay_UDP_packets_after_restart : Test case passed. UDP broadcast packet realyed to server after {} +Failed_to_relay_pkts_to_helper_ip : Failed to relay packets to helper address {}, test result, server wise {} +IP_config_verification_failed_on_interfaces : Test case failed. Reason - verification of IP config on interfaces {} failed +UDP_forwarding_status_verification_failed : Test case failed. Reason - UDP forwarding status verification failed +IP_helper_config_verification_failed : Test case failed. Reason - IP helper config verification failed on interfaces {} +Packet_not_relayed_to_Unreachable_helper_IP : Test case passed. Packets not relayed to unreachable helper IP +Packet_not_relayed_to_non_exist_VRF_helper_ip : Test case passed. packets not relayed to non existing VRF of helper IP +All_configured_servers_received_packets : Test case passed. All configured helper IP addresses on interface received packets - {} +IP_helper_service_is_not_up : Test case failed, IP helper is not up +Vrf_Config_verification_failed : VRF config verification failed +IP_helper_test_case_msg_status : Test case {} {} diff --git a/spytest/datastore/messages/security.yaml b/spytest/datastore/messages/security.yaml new file mode 100644 index 00000000000..553bd659dea --- /dev/null +++ b/spytest/datastore/messages/security.yaml @@ -0,0 +1,72 @@ +##USER +Tacacs_server_configs_are_not_successful: Tacacs server {} configs are not successful +DUT_does_not_have_IP_address: DUT does not have IP address +Login_to_DUT_via_SSH_is_failed: Login to DUT via SSH is failed +authentication_failthrough_config_to_tacacs+_is_not_successful: authentication fail through config to tacacs+ is not successful +Ping_to_tacacs_server_is_not_successful: Ping to tacacs server {} is not successful +PAM_file_is_not_created: PAM file is not created +tacacs_global_default_config_fail: tacacs global configs are not having default parameters +tacacs_server_default_config_fail: tacacs server configs are not having default parameters +authentication_default_configs_fail: authentication configs are not having default parameters +authentication_login_config_fail: authentication login config to tacacs+ is not successful +Tacacs_server_ip_delete_fail: Tacacs server ip deletion is not successful +configuring_tacacs_server_parameters_fail: configuring tacacs server parameters failed +tacacs_server_key_config_with_special_characters_is_successful: Verified that Tacacs server key can be configured with more than 4 special characters is successful +SSH_login_using_same_shared_key_success: Verified that SSH login using TACACS+ Server is successful using same shared key +tacacs_serverkey_config_in_appropriate_mode_success: Verified that user is able to execute the TACACS server key command successfully in appropriate mode +tacacs_running_config_fail: tacacs config is not seen in running config +tacacs_config_save_and_reload_success: Verified that TACACS parameters are showing in running config after save and reload + +#Radius +radius_server_config_not_successful: Radius server not configured +radius_global_params_default_failed: Failed to set global radius parameters to default +radius_global_params_config_failed: Radius global parameters configuration failed +ssh_login_with_radius_successful_with_supported_auth_types: SSH authentication using radius server with supported auth type's is successful +radius_max_servers_not_configured: Max radius servers configuration is not successful +ssh_login_failed: SSH authentication using radius server with auth-type {} failed +file_modification_failed: radius_nss.conf file modification failed +radius_fail_copying_failed: Copying radius_nss.conf file to local path failed +radius_server_config_failed: Radius server {} not created +ssh_local_login_status: SSH authentication using local user credentials {} +invalid_passkey_configured: Able to configure invalid passkey {} on the devive +invalid_passkey_config_unsuccessful: Device not allowing invalid characters configuration +ssh_login_success_unreachable_server: SSH authentication successful with unreachable servers +ssh_login_failed_unreachable_server: SSH authentication failed with unreachable radius server and failthrough mechanism is enabled +auth_type_config_failed: Confgiuring auth-type {} failed +invalid_auth_type_configured: Invalid auth type is configured +ping_to_radius_server_is_not_successful: Ping to radius server {} failed +local_user_creation_failed: Failed to create local user {} in the device +ssh_login_with_radius_successful: SSH authentication using radius server with auth type {} successful +security_config_retained_after_save_reboot: Radius and TACACS+ config retained after save and reboot +security_config_retained_after_fast_reboot: Radius and TACACS+ config retained after save and fast-reboot +password_config_failed: password configuration failed +ssh_root_with_radius_status: SSH login with root credentials {} +ssh_cisco_status: SSH login with CISCO ACS server {} +ipv6_source_ip_failed: IPV6 source ip configuration failed +sourceip_override_success: Sourceip override successful +ssh_login_success_invalid: SSH login success with invalid credentails +radius_log_not_found: Radius log is not found in the radius logs file +ssh_radius_login_mgmt_vrf_status: SSH login with radius server with management VRF is {} +invalid_action: Invalid radius action +ssh_ipv6_status: SSH authentication with IPV6 {} +radius_scaling_memory_leak_success: Radius scaling with {} sessions success with no memory leak +free_memory_limit_exceeded_after_radius_ssh: Free memory limit after opening {} sessions exceed max limit +ssh_unnumbered_status: SSH authentication with {} unnumbered interface {} +admin_user_root_privilege: The {} admin user {} root privileges +cmd_not_executed: Command not executed +invalid_config: Invalid config parameter +radius_data_empty: Radius data not configured through {} +radius_data_not_created: Radius server {} not created from {} +ssh_login: SSH login with radius server when configured from {} is {} + + +#AAA +authentication_failthrough_config_fail: Authentication failthrough config is not successful +authentication_fallback_config_fail: Authentication fallback config is not successful +authentication_login_radius_config_fail: Authentication login config to radius is not successful + +# RBAC +rbac_user_config: RBAC User config Failed. +rbac_test_status: RBAC Test_{}- To verify {}-User, {} login using {}, result-{}. +rbac_call_fail: RBAC {} to DUT is failed for mode-{}, type-{}. +rbac_test_jwt_token_fail: RBAC REST call - Unable to Get/Generate the JWT-Token for mode-{}, type-{}. \ No newline at end of file diff --git a/spytest/datastore/messages/switching.yaml b/spytest/datastore/messages/switching.yaml new file mode 100644 index 00000000000..ab958b82a26 --- /dev/null +++ b/spytest/datastore/messages/switching.yaml @@ -0,0 +1,232 @@ +##Vlan +vlan_create_fail : failed to create vlan {} +vlan_name_fail : failed to get vlan {} name +vlan_untagged_member_fail : failed to add untagged member {} to vlan {} +vlan_tagged_member_fail : failed to add tagged member {} to vlan {} +unknown_vlan_untagged_member_add_fail : failed to add untagged member {} to unknown vlan {} +vlan_delete_fail : VLAN {} delete failed +vlan_no_free_id: failed to find free vlan id +portchannel_member_add_success: port channel member(s) addition is successful +portchannel_member_add_failed: port channel member(s) addition is failed +portchannel_member_del_success: port channel member(s) deletion is successful +portchannel_member_del_failed: port channel member(s) deletion is failed +vlan_l2_forwarding_tagged_success: Verified L2 forwarding with tagged traffic is successful +vlan_l2_forwarding_untagged_success: Verified L2 forwarding with untagged traffic is successful +verify_100_plus_vlan_forwarding_with_bum: Verified 100+ VLAN forwarding with BUM traffic is successful +max_vlan_creation_failed: Failed to create max vlans +max_vlan_config_retain_failed_after_save_reload: failed to retain max vlan config after save and reload +vlan_deletion_successfull_albiet_having_member: vlan {} is deleted albiet it has memebers in it +vlan_deletion_failed_albiet_having_nomember: vlan is not deleted albiet it has no memebers in it +vlan_member_deletion_failed: vlan member {} deletion is failed +vlan_member_delete_failed: VLAN {} member {} delete failed +vlan_config_clear_failed: Clearing VLAN configuraiton failed. +vlan_config_clear_success: Clearing VLAN configuraiton is successful. +vlan_member_delete_fail: Failed to delete port-{} from vlan-{} +vlan_member_verification_failed: Vlan member {} in vlan id {} verification failed +vlan_forwarding_with_bum_success: Verified VLAN forwarding with BUM traffic is successful +verify_vlan_tagged_member_fail: Port-{} is not added to VLAN-{} in DUT-{} as a tagged member +Mac_Address_value_not_matching: MAC Address values are not matching +vlan_not_removed_after_cold_boot: VLAN-{} is not removed from DUT-{} after cold boot +vlan_config_verification_failed: VLAN {} config verification failed. +vlan_config_member_add_Fail: Vlan Member addition is failed +vlan_scaling: Vlan scaling '{}' +mac_scaling: mac scaling '{}' +max_vlan_config_retain_after_save_fast_warm_reboot : max vlan config is retained after fast and warm reboot + +##portchannel +portchannel_member_state_failed : PortChannel member state verification failed with expected state +portchannel_create_failed : Portchannel {} on {} creation failed +portchannel_create_successful : Portchannel {} on {} creation successful +portchannel_member_verification_failed: Portchannel {} on {} with member {} verification failed +portchannel_state_fail: Portchannel {} on {} state {} verification failed. +portchannel_member_state_fail: Portchannel {} on {} with member {} state {} verification failed. +portchannel_count_verification_fail: PortChannel count verification failed {} with {} +portchannel_creation_deletion_multiple_pass: Verified that multiple creation / deletion of multiple portchannels. +portchannel_random_link_flap: Verified random link flap in port channel. +portchannel_l2_forwarding_success: Verified basic L2 forwarding with 100 MAC address learned on portchannel is success +portchannel_member_en_dis_success: Successfully verified the traffic hashing when we enable and disable the memberport +portchannel_with_members_deletion_should_not_successful: Portchannel {} having members {} configured should not get deleted. +portchannel_verification_failed : Portchannel {} on {} verification failed +portchannel_deletion_failed: PortChannel {} deletion failed. +portchannel_not_found: PortChannel {} not found. +portchannel_config_clear_failed: Clearing Portchannel configuration failed. +portchannel_config_clear_success: Clearing Portchannel configuration is successful. +add_members_to_portchannel_failed: Failed to add port-{} as member to Port-Channel-{} in DUT-{} +traffic_hashed: Traffic is hashed in DUT-{} +traffic_not_hashed: Traffic not hashed in DUT-{} +PortChannel_came_up_with_ip_address: PortChannel {} came up with ip address +portchannel_verification_db_failed : Portchannel {} on {} verification failed in {} +portchannel_member_verification_db_failed: Portchannel {} on {} member verification failed in {} +portchannel_functionality_during_warmreboot: successfully verified portchannel functionality during warmreboot +portchannel_functionality_after_save_and_reboot: successfully verified portchannel functionality after save and reboot +portchannel_functionality_after_save_and_fastreboot: successfully verified portchannel functionality after fast reboot +portchannel_l3_forwarding_success: successfully verified L3 traffic on {} in {} +portchannel_l3_vlan_routing_success: Successfully verified L3 vlan routing on {} +portchannel_state_with_atlease_one_link: successfully verified {} state with atleast one link is up +add_member_ports_to_other_portchannel: should not add memberport to other portchannel +portchannel_state_with_partner_dut: successfully verified {} state when portchannel is not configured in partner +Enable_disable_portchannel_with_mtu: Successfully enable and disable {} with mtu value +portchannel_delete_with_ip_configured_vlan: successfully verified that user should not able to delete {} with ip configured vlan {} +portchannel_delete_with_ip_configured_portchannel: Successfully verified that user should not able to delete ip assigned portchannel +portchannel_members_with_different_speeds: Successfully verified that user shoud not add member ports with different speeds to portchannel +portchannel_fallback_configuration: Successfully verified that fallback should not configured on static portchannel +portchannel_fallback_configuration_fail: fallback should not configured on static portchannel +portchannel_with_max_members: Successfully verified the maximum members per portchannel +max_portchannels_per_system: Successfully verified the maximum portchannels per system +portchannel_conversion_from_dynamic_to_static_fail: portchannel conversion from dynamic to static is not successful on {} +portchannel_conversion_from_dynamic_to_static_success: portchannel conversion from dynamic to static is successful on {} +lacp_fallback_validation_success: Successfully validated LACP Fallback functionality {} +failed_to_generate_lacp_graceful_restart_log_in_syslog: Failed to generate LACP graceful restart log in syslog +verify_lacp_graceful_restart_success: Successfully verified LACP graceful restart with {} + +##FDB +mac_addr_get_fail: fail to get mac address of {} +mac_failed_to_learn_in_vlan: MAC {} failed to learn in vlan {} +mac_failed_to_learn_in_Particular_vlan: Failed to learn MAC Adresses in vlan {} +mac_address_verification_fail: Learned MAC address verification failed +mac_address_not_clear: Fail to clear Mac Addresses from FDB +max_mac_not_create: Fail to create Max Mac Addresses +mac_not_create: Fail to create Mac Addresses +mac_unknown_entries: Unknown MAC entries are available +mac_not_static : Configured Mac address is not a staic entry +mac_failed_to_learn_on_firt_port: Dut failed to learn mac address on first port +mac_failed_to_learn_on_second_port: Dut failed to learn mac address on second port +mac_aging_time_failed_config : Failed to configure Mac aging time +failed_clear_mac_learned_on_port: Dut failed to clear mac address learnt on {} port after mac move. +failed_to_learn_mac_after_move: Dut failed to learn mac address after mac move. +failed_to_clear_mac_after_ageout: Mac Address is not cleared after ageout +mac_second_port: Second port Entries are not available +mac_seond_vlan: Second vlan id Entries are not available +mac_learning_fail: Failed to learn MAC address-{} on Port-{} of vlan-{} in DUT-{} +failed_to_learn_mac_in_l2_show: failed to learn mac address-{} in vlan {} in "l2 show" command in DUT-{} + +#PVST +invalid_data_for_root_bridge_per_vlan: Invalid data provided to check the root bridge per vlan. +expected_dut_not_root: Expected DUT {} is not root for {} instance +observed_more_than_1_root_bridge: Observed more than 1 root bridge per {} instance. +interface_not_found_in_expected_list: Interface not found in expected list +unexpected_interface_state_for_root: Observed that interface state is {} for root bridge +root_bridge_interface_verification_failed: Root bridge interface state verification failed. +pvst_output_not_found: PVST output not found on {} for {} instance. +stp_output_not_found: STP output not found on {} for {} instance. +no_forwarding_links_highest_mac: Observed no forwarding links on highest mac DUT {} +expected_blocking_links_not_found: Expected number of blocking links not found on {} +eeprom_data_not_found: EEPROM data not found for {} +no_blocking_interfaces_found_on_any: No blocking interfaces found on any of the devices {} +highest_mac_address_not_found: Highest MAC Address DUT not found +other_non_root_dut_data_not_found: OTHER NON ROOT DUT data not found +dut_mac_address_not_found: DUT MAC ADDERSS DATA NOT FOUND +pvst_module_data_check_failed: PVST module data check is failed +pvst_module_config_data_not_found: PVST module config data not found +feature_module_data_check_failed: module data check is failed for {} +feature_module_config_data_not_found: module config data not found for {} +tg_object_not_found: Tg object is not found +exception: exception {} is occured + +# IGMP Snooping +igmp_snooping_verification_successful_for_igmpv2 : IGMP v2 hosts joins are successful and mcast data forwarded to hosts joined ports. +igmp_snooping_verification_fail_for_igmpv2 : IGMP v2 hosts joins failed to learn and mcast data not forwarded to hosts. +igmp_snooping_verification_successful_for_igmpv1 : IGMP v1 hosts joins are successful and mcast data forwarded to hosts joined ports. +igmp_snooping_verification_fail_for_igmpv1 : IGMP v1 hosts joins failed to learn and mcast data not forwarded to hosts. +igmp_snooping_static_mroute_verification_success : IGMP snooping static mrouter addition is successful and traffic also forwarded to mrouter port. +igmp_snooping_static_mroute_verification_fail : IGMP snooping static mrouter addition failed or traffic not forwarded to mrouter port. +igmp_snooping_static_group_verification_success : IGMP snooping static group verification is successful and traffic also forwarded to the static group entry. +igmp_snooping_static_group_verification_fail : IGMP snooping static group verification failed or traffic not forwarded to the static group entry. +igmp_snooping_verification_successful_for_igmpv3 : IGMP v3 hosts joins with specific source ip addr are successful and mcast data with that specific source forwarded to hosts joined ports. +igmp_snooping_verification_fail_for_igmpv3 : IGMP v3 hosts joins with specific source ip addr failed to learn and mcast data with that specific source not forwarded to hosts. +igmp_snooping_verification_successful_for_igmp_pc : IGMP v2 hosts joins on port channel interface successful and mcast data forwarded to hosts joined ports. +igmp_snooping_verification_fail_for_igmp_pc : IGMP v2 hosts joins on port channel interface failed and mcast data forwarded to hosts joined ports. +igmp_snooping_verification_fail_for_vlan_change: IGMP entries are failed to delete or learn after change in vlan association. +igmp_snooping_verification_successful_for_vlan_change: IGMP entries are successfully deleted and learned after change in vlan association. +igmp_snooping_verification_fail_disable_enable: IGMP entries are failed to delete or learn after disable and enable igmp snooping. +igmp_snooping_verification_successful_disable_enable: IGMP entries are successfully deleted and learned after disable and enable igmp snooping. +igmp_snooping_verification_successful_group_query : IGMP Group specific Query generated successfully when a Leave message is received. +igmp_snooping_verification_fail_group_query : IGMP Group specific Query not generated when a Leave message is received. +igmp_snoop_max_entry_status: "{} : To Create MAX IGMP entries." +igmp_snooping_verification_fail_lmqt : IGMP Snooping Last Member Query interval verification failed. +igmp_snooping_verification_successful_lmqt : IGMP Snooping Last Member Query interval verification successful. +igmp_snooping_verification_pim_mrouter_status: On receiving the PIM helo message,{} to update port as mrouter port in IGMP snooping DUT. +igmp_snooping_verification_v2_report_in_v3: On receiving the IGMPv2 join for same group, switch {} in updating the source address learned via IGMPv3 join for the same group. +igmp_snooping_verification_v3_report_w_zero_src_addr: IGMP Snooping switch {} in learning the IGMPv3 join received with 0.0.0.0 as source address. +igmp_snooping_verification_fast_leave : IGMP Snooping fast-leave validation {}. +igmp_snooping_verification_multi_host : IGMP Snooping switch {} in learning/removing multiple hosts from different ports in same vlan. +igmp_snooping_verification_query_interval : IGMP Snooping switch {} in sending the Queries as per configured interval. +igmp_snooping_verification_query_max_response_time : IGMP Snooping switch {} in sending the Queries with configured max response time. +igmp_snooping_verification_query_del_join : IGMP Snooping Querier {} in deleting the join entry after the group membership timer expires as per configured query interval +igmp_snooping_verification_save_reboot : IGMP Snooping verification {} after save reboot. +igmp_snooping_verification_max_vlan : Enabling IGMP Snooping on max supported vlans is {}. +igmp_snooping_verification_shut_no_shut: After shut no shut on the interface IGMP hosts joins learning is {}. +igmp_snooping_verification_warm_boot: IGMP snooping verification with warm boot is {}. +igmp_snooping_verification_rest: IGMP snooping REST operation is {}. +igmp_snooping_verification_gnmi: IGMP snooping gNMI operation is {}. + + +#MCLAG +mclag_state_verification_failed: MCLAG state verification FAILED with {} +mclag_intf_verification_failed: MCLAG interface verification FAILED with {} +mclag_igmp_snooping_sync_to_mclag_peer_successful: "{} snooping entries sync to MCLAG peer is successful." +mclag_igmp_snooping_sync_to_mclag_peer_unsuccessful: "{} snooping entries sync to MCLAG peer is unsuccessful." +mclag_igmp_snooping_entry_check_mclag_down_case_successful: "{} snooping entries check when MCLAG goes down is successful." +mclag_igmp_snooping_entry_check_mclag_down_case_unsuccessful: "{} snooping entries check when MCLAG goes down is unsuccessful." +mclag_igmp_snooping_entry_check_mclag_up_case_successful: "{} snooping entries check when MCLAG comes up is successful." +mclag_igmp_snooping_entry_check_mclag_up_case_unsuccessful: "{} snooping entries check when MCLAG comes up is unsuccessful." +mclag_igmp_snooping_entry_check_split_brain_case_successful: "{} snooping entries check in split brain scenario is successful." +mclag_igmp_snooping_entry_check_split_brain_case_unsuccessful: "{} snooping entries check in split brain scenario is unsuccessful." + +#UDLD PVST +UDLD_PVST_Normal_message_and_multiplier_timers_Failed: UDLD PVST Normal message and multiplier timers Failed +UDLD_PVST_Normal _message_and_multiplier_timers_Passed: UDLD PVST Normal message and multiplier timers Passed +UDLD_PVST_Aggressive_message_and_multiplier_timers_Failed: UDLD PVST Aggressive message and multiplier timers Failed +UDLD_PVST_Aggressive_message_and_multiplier_timers_Passed: UDLD PVST Aggressive message and multiplier timers Passed +UDLD_PVST_Aggressive_and_Normal_mode_Failed: UDLD PVST Aggressive and Normal mode Failed +UDLD_PVST_Aggressive_and_Normal_mode_Passed: UDLD PVST Aggressive and Normal mode Passed +UDLD_PVST_Convergence_Failed: UDLD PVST Convergence Failed +UDLD_PVST_Convergence_Passed: UDLD PVST Convergence Passed +UDLD_PVST_Normal_Config_Reload_Failed: UDLD PVST Normal Config Reload Failed +UDLD_PVST_Normal_Config_Reload_Passed: UDLD PVST Normal Config Reload Passed +UDLD_PVST_Aggressive_Config_Reload_Failed: UDLD PVST Aggressive Config Reload Failed +UDLD_PVST_Aggressive_Config_Reload_Passed: UDLD PVST Aggressive Config Reload Passed +UDLD_PVST_Normal_Fast_Reboot_Failed: UDLD PVST Normal Fast Reboot Failed +UDLD_PVST_Normal_Fast_Reboot_Passed: UDLD PVST Normal Fast Reboot Passed +UDLD_PVST_Aggressive_Fast_Reboot_Failed: UDLD PVST Aggressive Fast Reboot Failed +UDLD_PVST_Aggressive_Fast_Reboot_Passed: UDLD PVST Aggressive Fast Reboot Passed +UDLD_PVST_Normal_Cold_Reboot_Failed: UDLD PVST Normal Cold Reboot Failed +UDLD_PVST_Normal_Cold_Reboot_Passed: UDLD PVST Normal Cold Reboot Passed +UDLD_PVST_Aggressive_Cold_Reboot_Failed: UDLD PVST Aggressive Cold Reboot Failed +UDLD_PVST_Aggressive_Cold_Reboot_Passed: UDLD PVST Aggressive Cold Reboot Passed +UDLD_PVST_Normal_Docker_Restart_Failed: UDLD PVST Normal Docker Restart Failed +UDLD_PVST_Normal_Docker_Restart_Passed: UDLD PVST Normal Docker Restart Passed +UDLD_PVST_Aggressive_Docker_Restart_Failed: UDLD PVST Aggressive Docker Restart Failed +UDLD_PVST_Aggressive_Docker_Restart_Passed: UDLD PVST Aggressive Docker Restart Passed + +#UDLD RPVST +UDLD_RPVST_Normal_message_and_multiplier_timers_Failed: UDLD RPVST Normal message and multiplier timers Failed +UDLD_RPVST_Normal _message_and_multiplier_timers_Passed: UDLD RPVST Normal message and multiplier timers Passed +UDLD_RPVST_Aggressive_message_and_multiplier_timers_Failed: UDLD RPVST Aggressive message and multiplier timers Failed +UDLD_RPVST_Aggressive_message_and_multiplier_timers_Passed: UDLD RPVST Aggressive message and multiplier timers Passed +UDLD_RPVST_Aggressive_and_Normal_mode_Failed: UDLD RPVST Aggressive and Normal mode Failed +UDLD_RPVST_Aggressive_and_Normal_mode_Passed: UDLD RPVST Aggressive and Normal mode Passed +UDLD_RPVST_Convergence_Failed: UDLD RPVST Convergence Failed +UDLD_RPVST_Convergence_Passed: UDLD RPVST Convergence Passed +UDLD_RPVST_Normal_Config_Reload_Failed: UDLD RPVST Normal Config Reload Failed +UDLD_RPVST_Normal_Config_Reload_Passed: UDLD RPVST Normal Config Reload Passed +UDLD_RPVST_Aggressive_Config_Reload_Failed: UDLD RPVST Aggressive Config Reload Failed +UDLD_RPVST_Aggressive_Config_Reload_Passed: UDLD RPVST Aggressive Config Reload Passed +UDLD_RPVST_Normal_Fast_Reboot_Failed: UDLD RPVST Normal Fast Reboot Failed +UDLD_RPVST_Normal_Fast_Reboot_Passed: UDLD RPVST Normal Fast Reboot Passed +UDLD_RPVST_Aggressive_Fast_Reboot_Failed: UDLD RPVST Aggressive Fast Reboot Failed +UDLD_RPVST_Aggressive_Fast_Reboot_Passed: UDLD RPVST Aggressive Fast Reboot Passed +UDLD_RPVST_Normal_Cold_Reboot_Failed: UDLD RPVST Normal Cold Reboot Failed +UDLD_RPVST_Normal_Cold_Reboot_Passed: UDLD RPVST Normal Cold Reboot Passed +UDLD_RPVST_Aggressive_Cold_Reboot_Failed: UDLD RPVST Aggressive Cold Reboot Failed +UDLD_RPVST_Aggressive_Cold_Reboot_Passed: UDLD RPVST Aggressive Cold Reboot Passed +UDLD_RPVST_Normal_Docker_Restart_Failed: UDLD RPVST Normal Docker Restart Failed +UDLD_RPVST_Normal_Docker_Restart_Passed: UDLD RPVST Normal Docker Restart Passed +UDLD_RPVST_Aggressive_Docker_Restart_Failed: UDLD RPVST Aggressive Docker Restart Failed +UDLD_RPVST_Aggressive_Docker_Restart_Passed: UDLD RPVST Aggressive Docker Restart Passed + +#UDLD TX/RX LOOPs +UDLD_TX_RX_loop_Normal_Failed: UDLD TX/RX loop Normal Failed +UDLD_TX_RX_loop_Normal_Passed: UDLD TX/RX loop Normal Passed +UDLD_TX_RX_loop_Aggressive_Failed: UDLD TX/RX loop Aggressive Failed +UDLD_TX_RX_loop_Aggressive_Passed: UDLD TX/RX loop Aggressive Passed diff --git a/spytest/datastore/messages/system.yaml b/spytest/datastore/messages/system.yaml new file mode 100644 index 00000000000..e8efc6f38ca --- /dev/null +++ b/spytest/datastore/messages/system.yaml @@ -0,0 +1,443 @@ +##Port +port_shut_fail : failed to shutdown the port {} +portchannel_member_shut_fail: failed to shutdown the portchannel member {} +portchannel_member_verify_fail: failed to verify given portchannel member {} +failed_to_config_speed : Failed to config speed +Dut_failed_to_get_speed : Dut failed to get speed +failed_to_config_interface : Failed to config on interface +failed_to_receive_valid_traffic : Failed to receive valid traffic +fail_to_configure_mtu_on_device : Fail to configure mtu on Device {} +interface_is_down_on_dut : interface is down on DUT {} +interface_is_up_on_dut : interface is up on dut {} +interface_admin_shut_down_fail: admin shutdown fail on interface {} +interface_admin_startup_fail: fail to bring up interface {} +fail_to_configure_mtu_on_Device: fail to configure mtu on Device {} +interface_is_down_on_partner: interface is down on partner device +interface_down_after_all_iterations: interface is down after all iterations +interfaces_not_listed_after_max_poll: Interfaces are not listed after max polling interval +interface_counters_not_found: Interface counters not found + +##NTP +ntpserver_not_configured: mentioned server ip is not configured +ntp_sync_failed: NTP is not Synchronized +ntp_sync_failed_after_disable_enabled: NTP is not synchronized after disable and enable +time_not_updated: time is not updated +None_of_the_configured_ntp_server_reachable: None of the configured ntp server reachable + +##SSH +dut_not_getting_ip_address : Dut is Not getting IP address +ssh_failed : SSH failed +ssh_is_not_disabled : SSH is not Disabled +ssh_service_not_started : SSH service not started + +##Reboot +After_hard_reboot_DUT_access_fail : Either Fail to login to DUT after hard reboot >>iteration no>>{} or fail to get proper output of 'show platform summary' +After_hard_reboot_DUT_access_pass : After {} hard reboot iterations DUT is stable and accessible +After_soft_reboot_DUT_access_fail : Either DUT is not accessible or Fail to login to DUT after reload iteration number {} or fail to get hwsku +After_soft_reboot_DUT_access_pass : After {} reload iterations DUT is stable and accessible +verify_soft_reboot_show_reboot_cause_fail: After soft reboot 'show reboot-cause' reason is incorrect in {} +verify_hard_reboot_show_reboot_cause_fail: After hards reboot 'show reboot-cause' reason is incorrect in {} +verify_mac_entries_cleared: After Fast reboot mac entries are cleared +clear_config_failed: Clear config failed in {} +fast_or_warm_reboot_failed: Fast or Warm reboot command failed +reboot_failed: Reboot command failed +Warmboot_unsupported_platform : Warm boot is not supported on this platform {} +data_traffic_loss_during_fast_reboot: Traffic loss observed during fast reboot more than 25 seconds + +##Bootup +build_remove_fail: Failed to remove the build {} +verify_additional_builds_remove_fail: Additional builds are not removed successfully +current_active_not_in_available: Current active build is not shown in the available builds list +build_load_fail: Failed to load the build +build_load_unsuccessful: Build is not loaded successfully +set_next_active_build_fail: Failed to configure next active build +set_next_active_build_as_new_build_fail: Failed to configure newly loaded build as next active build +current_active_build_is_not_newly_loaded_build: The current active build is not the newly loaded build as per the configuration +set_next_active_build_as_old_fail: Failed to configure next active build as old build +verify_next_active_build_as_old_build_fail: Next active build is not set to the old build as per the configuration +verify_active_build_as_old_build_fail: DUT is not loaded with old image as per the configuration +set_new_build_as_default_fail: Failed to set the newly loaded image as default image +verify_active_build_as_new_build_fail: DUT is not loaded with newly loaded image as per the configuration +set_old_build_as_default_fail: Failed to set the old image default image +verify_next_active_build_as_new_build_fail: Next boot image is not set to old image as per the configuration +remove_new_build_fail: Failed to remove the new build +verify_remove_new_build_fail: The new build is not removed as per the configuration +sytem_uptime_fail: In DUT {},After 5 min system uptime is not updated with correct value +remove_old_build_fail: Failed to remove the old build +verify_remove_old_build_fail: The old build is not removed as per the configuration + +##Ansible +ansible_host_ping_error: Ansible host {} ping not successful +ansible_fail: Ansible operation failed +ansible_playbook_success: Ansible playbook success +ansible_cmd_success: Ansible Command Successful +ansible_ping_success: Ansible Ping Successful +ansible_ssh_success: Ansible SSH success + +##Chef +client_not_running: Chef client not running +client_running: Chef client is running +error_update_role_chef_server: Error while updating role on chef server +error_update_node_run_list: Error while updating node run list +error_client_pem_file_not_present: Client pem file not present under {} +error_validation_pem_file_not_present: Validation pem file not present under {} +chef_mclag_delete_fail: MCLAG deletion using Chef failed +chef_l2mclag_pass: L2 MCLAG creation and state check successful using Chef +chef_l3mclag_pass: L3 MCLAG creation and state check successful using Chef +chef_mclag_state_fail: MCLAG state check failed after creation using Chef +chef_bootstrap_fail: Bootstrap to DUT failed + +## Config +Config_not_loaded_from_config_db_json: Config from config_db.json is not loaded to running configuration + + +## CRM +threshold_config_success: Successfully configured threshold values. +threshold_config_fail: Failed to configure threshold values. +threshold_exceeded_success: Threshold exceeded - logs generated successfully +threshold_exceeded_fail: Failed to generate 'Threshold exceeded' logs +threshold_clear_success: Threshold logs generated successfully +threshold_clear_fail: Failed to generate 'Threshold clear' logs +polling_interval: Polling interval configured as {} +polling_interval_fail: Polling interval configuration failed. +max_threshold_exceed: Max threshold exceeds the default value. + + +## ZTP +ztp_status_verification_failed: ZTP status verification failed. +ztp_log_verification_failed: ZTP log {} verification failed for message {} +ztp_file_found_error: Error file should not be found under {} +ztp_admin_mode_mismatch_during_reboot: ZTP admin mode not matching with before {} and after {} reboot +ztp_status_mismatch_during_reboot: ZTP status not matching with before {} and after {} reboot +ztp_disable_failed: Disabling ZTP failed +ztp_source_verification_failed: ZTP source verification failed with {} against {} +ztp_max_polling_interval: Max polling interval reached. +ztp_dhclient_error: DHCLIENT on required interface is not found. +snmp_params_verification_failed: SNMP parameter verification failed. + +##logging +failed_to _clear_logs: Failed to clear all logs +logs_are_not_getting_generated_after_reboot: logs are not getting generated after reboot +syslog_server_entry_not_stored_in_config_db: syslog server entry not stored in config_db file +logs_are_getting_generated_after_reboot: logs are getting generated after reboot +logging_severity_level_change_failed: logging severity level change got failed +Tech_support_operation_failed: Tech support operation failed + +##SNMP +snmp_operation_fail: SNMP {} operation is failed due to - {} +sysName_verification_fail : sysname output is not in sync between system and snmp +sysUptime_verification_fail : sysUpTime output is not in sync between system and snmp +sysLocation_verification_fail : sysLocation output is not in sync between system and snmp +sysContact_verification_fail: sysContact output is not in sync between system and snmp +sysObjectId_verification_fail: sysObjectId output is not in sync between system and snmp +sysDescr_verification_fail: sysDescr output is not in sync between system and snmp +ip_verification_fail: Ip address is not a valid one or the ip is not presented on the device +get_snmp_output_fail: SNMP output is not found after walk +snmp_output_failed: SNMP output is not found after walk of {} OID +snmp_service_not_up: SNMP service is not up +sysName_verification_fail_after_docker_restart : sysname output is not in sync between system and snmp after docker restart +snmptrapd_not_running: snmptrapd application is not running on the server. +snmptrap_not_generated: snmp trap {} not received as expected. +snmptrap_generated: snmp trap {} received as expected. +snmp_parameters_config_unsuccessful: Failed to configure SNMP parameters such as contact info-{}, location-{} and sysname-{} +snmp_config_unsuccessful: Failed to configure SNMP parameter {} - {} +snmp_cli_config_verified: snmp cli verification successful after fast-reboot. +snmp_output_status: snmp output stauts of {} after get or walk operations is {}. +snmp_invalid_case_status: snmp{} {} operation with invalid {} is {} +snmp_operation_invalid_credentials_status: snmp operations such as walk and get with invalid credentials is successful. +snmp_trap_informs_status: snmp trap informs status of {} are {} +snmp_community_invalid_error_code: Observed invalid error code {} when snmp community - {} is configured via rest. +snmp_community_config_gnmi_unsuccessful: Failed to {} snmp community - {} via gnmi. +snmp_agent_address_config_verify_unsuccessful: Failed to configure snmp agent addresses - {} with non-default port - {} + +##PMON +pmon_service_not_up : PMON service is not up +pmon_show_command_fail : show command doesn't showed valid output after PMON docker restart + +##running config +running_config_failed: Running config failed + + +### PFC +set_map_pfc_priority_to_queue_fail : Failed to map PFC priority to queue in DUT-{} +set_map_tc_to_queue_fail : Failed to map TC to queue in DUT-{} +set_map_tc_to_pg_fail : Failed to map TC to priority group in DUT-{} +set_map_dscp_to_tc_fail : Failed to map DSCP to TC in DUT-{} +set_pfc_enable_fail : Failed to enable PFC on the Queues-{} of the ports-{} in DUT-{} +asymmetric_instead_of_symmetric_in_interface_status: Asymmetric PFC configuration shown instead of symmetric on port-{} of DUT-{} in interfaces status output +set_asymmetric_pfc_enable_fail: Failed to enable asymmetric PFC on port-{} of DUT-{} +verify_asymmetric_pfc_enable_fail: Asymmetric PFC is not enabled on port-{} of DUT-{} as per the configuration +verify_asymmetric_pfc_in_interface_status_fail: Asymmetric PFC is not shown as enabled in interface status on port-{} of DUT-{} as per the configuration +set_pfc_counters_clear_fail: Failed to clear PFC counters on DUT-{} +verify_pfc_counters_clear_fail: PFC counters on the Port-{} in DUT-{} are not cleared as per the configuration +traffic_loss_for_loss_less_traffic: Traffic loss is observed for the loss less traffic(priority-{} traffic) +invalid_pfc_rx_counter: invalid PFC RX counters are observed on port-{} in DUT-{} +invalid_pfc_tx_counter: invalid PFC TX counters are observed on port-{} in DUT-{} +pfc_rx_counter_fail: PFC RX counter for lossless priority-{} is not incremented on port-{} in DUT-{} +pfc_tx_counter_fail: PFC RX counter for lossless priority-{} is not incremented on port-{} in DUT-{} +no_traffic_loss_for_lossy_traffic: No traffic loss is observed for the lossy traffic(priority-{} traffic) +verify_pfc_priority_to_queue_map_fail: PFC priority-{} is not mapped to queue-{} as per the configuration in DUT-{} +verify_tc_to_queue_map_fail: TC-{} is not mapped to queue-{} as per the configuration in DUT-{} +verify_tc_to_pg_map_fail: TC-{} is not mapped to priority-group-{} as per the configuration in DUT-{} +verify_pfc_enable_fail: PFC is not enabled on the ports-{} and on the queues-{} as per the configuration in DUT-{} +pfc_priority_to_queue_map_config_exist_after_cold_boot: Mapping for pfc priority-{} to queue-{} still exist in DUT-{} after cold boot +tc_to_queue_map_config_exist_after_cold_boot: Mapping for tc-{} to queue-{} still exist in DUT-{} after cold boot +tc_to_pg_map_config_exist_after_cold_boot: Mapping for tc-{} to priority-group-{} still exist in DUT-{} after cold boot +pfc_enabled_after_cold_boot: PFC is still enabled on the ports-{} and on the queues-{} in DUT-{} +invalid_platform_for_buffer_config: Invalid platform for buffer configuration +pfc_counters_not_initialized: PFC counters are not initialized +verify_asymmetric_pfc_mode_fail: Verification of asymmetric PFC mode as {} on port {} in DUT {} is failed +pfc_validation_fail: PFC validation failed {} + +#docker +docker_operation_failed: Failed to {} docker {} + +###lldp +lldp_snmp_not_matching: LLDP and SNMP values are not matching +no_lldp_entries_are_available : No LLDP entries are available +both_are_not_matching : DUT Management IP and LLDP entries Management IP is not matching +hostname_is_not_matching : DUT hostname and LLDP entries hostname is not matching +interfaces_not_up_after_poll : Interfaces are not up even after polling +lldp_neighbors_info_not_found_after_poll : LLDP neighbors information is not found after polling +lldp_invalid : Invalid LLDP output +LLDP_non_default_config_is_failed : LLDP nondefault configuration on neighbor is not reflecting properly +lldp_service_not_up : LLDP service is not Up + +#SPAN + +mirror_session_verification_failed: Mirror session not configured or observed some other error +mirror_prt_func_with_dir_as_rx_not_working : Mirroring with port functionality is not working on rx direction +mirroring_with_port_rx_working_fine: Mirroring with port functionality is working on rx direction +mirror_prt_func_with_dir_as_tx_not_working : Mirroring with port functionality is not working on tx direction +mirroring_with_port_tx_working_fine: Mirroring with port functionality is working on tx direction +mirror_prt_func_with_dir_as_both_not_working : Mirroring with port functionality is not working on both direction +mirroring_with_port_both_working_fine: Mirroring with port functionality is working on direction as both +mirror_prt_channel_func_with_dir_as_rx_not_working: Mirroring with port channel functionality is not working on rx + direction +mirroring_with_port_channel_rx_working_fine: Mirroring with portchannel functionality is working on rx direction +mirror_vlan_func_with_dir_as_rx_not_working: Mirroring with VLAN functionality is not working on rx direction +mirroring_with_vlan_tx_working_fine: Mirroring with VLAN functionality is working on tx direction +mirror_vlan_func_with_dir_as_tx_not_working: Mirroring with VLAN functionality is not working on tx direction +mirroring_with_vlan_both_working_fine: Mirroring with VLAN functionality is working on both direction +mirror_vlan_func_with_dir_as_both_not_working: Mirroring with VLAN functionality is not working on both direction +mirroring_with_vlan_rx_working_fine: Mirroring with VLAN functionality is working on rx direction +mirror_stat_prtchannel_inactive: Mirroring with port channel with no members avialable is showing active which is false +mirror_stat_prtchannel_status_pass: Mirroring with port channel status when no members is working fine +mirror_negative_case_same_dest_sourece_fail: Mirroring with Negative scenario configuring source and destination as + same is failing +mirror_negative_case_same_dest_prtchannel_fail: Mirroring with Negative scenario configuring destination as portchannel + is Failed +mirror_config_same_twice_failed: Mirroring configured twice with same ports should failed and should throw error message +mirror_config_with_wrong_direction_fail: Mirroring with direction other than rx,tx and both should fail +mirror_config_as_source_and_dst_with_same_intf_fail: Mirroring with Source port used in one session cannot be used as + the destination port in another session +mirror_with_negative_scenarios_pass : Mirroring with negative scenarios passed +mirror_erspan_func_status : Mirroring configured with ERSPAN functionality {} +mirror_erspan_span_func_pass : Mirroring with ERSPAN to SPAN to ERSPAN functionality is passed +mirror_erspan_mode_status : Mirroring configured as ERSPAN with {} as source port is {} +mirror_max_sessions_not_formed : Mirroring with max mirror sessions are not formed +mirror_func_with_max_session_status : Mirroring functionality is {} with max session configured +mirroring_with_multi_source_verify_pass_fail : Mirroring with mutiple source direction {} verification is {} +mirroring_gnmi_get_or_set_operation_has_invalid_values : Mirroring {} value has invalid data +mirroring_after_gnmi_config_status : Mirroring config after using gnmi is {} +mirror_traffic_is_not_forwarded : Traffic is not forwareded correctly for mirroing , please check tg +mirroring_destination_CPU_both_working_fine : Mirroing with {} as source and CPU as destination is {} +# ERSPAN +ip_config_fail : Failed to configure ip address {} on interface {} +mirror_session_fail : Failed to create mirror session {} +acl_table_fail : Failed to create acl table as {} +acl_rule_fail : Failed to create acl rule +ping_fail : Failed to ping ip address {} +traffic_verification_fail : Traffic verification failed at {} +neg_traffic_verification_fail : Invalid Traffic verification failed at {} + +#Error Handling +entry_not_found_in_error_db: Entry for {} not found in error db +entry_found_in_error_db: Entry for {} found in error db +entry_not_found_in_show_error_db: Entry for {} not found in show error database +entry_found_in_show_error_db: Entry for {} found in show error database +test_case_pass: Test case {} passed +Failed_to_clear_error_db: Failed to clear entries in error db +failed_to_recv_notification: Bgp failed to recv notification + +#IFA +device_id_is_persent: ifa device id is not cleared +flow_is_present: ifa flow is not cleared +device_id_is_not_created: failed to create the ifa device id +flow_is_not_created: failed to create a ifa flow +collector_is_not_created: failed to created the ifa eggress collector +collector_is_present: failed to clear the ifa eggress collector +ifa_Status_is_not_correct: ifa status is not correct +ifa_status_params_are_not_correct: ifa status parms are not correct +get_ifa_status_failed: failed to get ifa status +get_ifa_flow_failed: failed to get flow +get_ifa_session_failed: failed to get ifa session +get_ifa_collector_failed: failed to get ifa collector info +configure_ifa_feature_failed: failed to configure ifa feature +create_ifa_flow_failed: failed to create a ifa flow +ifa_flow_parameters_are_not_correct: ifa flow params are not correct +create_ifa_session_failed: failed to create the ifa session +ifa_session_params_are_not_correct: ifa session params are not correct +create_ifa_collector_failed: create ifa collector failed +ifa_collector_parameteres_are_not_correct: ifa collector parameters are not correct +start_ifa_session_failed: start ifa session failed +stop_ifa_session_failed: stop ifa session failed +remove_ifa_collector_failed: remove ifa collector failed +config_clear_failed: ifa config clear failed +sampled_packets_count_not_correct : sampled packet count is not correct +ifa_int_header_meta_data_not_correct: ifa int header and meta data in sampled packet are not correct +invalid_rest_request_successfull: invalid rest request is successfull +ifa_is_unsupported_on_this_platform: ifa is unsupported on this platform +config_is_failed_ableit_on_supported_build_platform: ifa config is failed on supported platform with supported build +config_is_successful_ableit_on_unsupported_build_platform: ifa config is successful on unsupported platform with unsupported build +ifa_firmware_is_not_running: IFA Firmware is not running in Supported Platform and Build. +ifa_statistics_not_incrementing: ifa statistics not incrementing + +#Branding +invalid_build_version_format: Build version format is invalid in {} output +found_string_dirty: The string dirty is found in {} output + +#PDDF +pddf_mode_set_failed: "{0}: Failed to {1} {0} mode on device." +empty_values_observed: "{}: Observed empty values for some or all fields {}." +value_mismatch: "{}: Observed value mismatch in sys-eeprom field {}." +show_platform_syseeprom_command_verified: "{}: Successfully verified the output of 'show platform syseeprom' command." +decode_syseeprom_command_verified: "{}: Successfully verified the output of 'decode-syseeprom' command." +fan_params_verification_failed: "{}: Verification of fan parameters failed." +pddf_fanutil_verification_failed: "{}: FAN util '{}' verification failed." +pddf_fanstatus_verification_success: "{}: Platform FAN status / util verification success." +platform_psu_summary_verification_failed: "{}: Platform PSU summary verification failed." +pddf_psu_summary_util_verification_success: "{}: Platform PSU UTIL summary verification success." +pddf_psu_util_verification_failed: "{}: Failed to verify PDDF PSU-UTIL data." +pddf_psu_mfr_info_verification_failed: "{}: Failed to verify PDDF_PSU_MFR_info." +sfp_lp_mode_x_failed: "{}: Failed to {} sfp lp mode on the interface-{}." +sfp_presence_not_found: "{}: Unable to find sfp presence on up interface {}." +sfp_reset_failed: "{}: Failed to perform sfp reset on interface {}." +sfp_eeprom_detection_failed: "{}: Unable to find sfp EEPROM status on up interface {}." +pddf_show_sfputil_verification_success: "{}: show sfputil verification successful." +pddf_led_validation_failed: "{}: Failed to validate LED commands." +pddf_led_validation_success: "{}: LED commands validation is successful." +pddf_thermalutil_validation_failed: "{}: Failed to validate thermalutil - {}." +pddf_thermalutil_validation_success: "{}: Successfully validated thermalutil commands." +show_environment_o/p_validation_failed: "{}: Failed to validate - show environment o/p" +show_environment_o/p_validation_success: "{}: Validation of show environment o/p is successful." +system_down_status_observed: "{}: System is Down - post executing pddf debug commands." +system_status_up_post_pddf_debug: "{}: No Change in system status post executing pddf debug commands." +pddf_reboot_cause_validation_failed: "{}: Failed to validate reboot cause for reboot type - {}." +pddf_reboot_cause_validation_success: "{}: Successfully validated reboot cause for reboot type - {}." +up_interface_not_found: "{}: Unable to find any UP ports in the device." +pddf_unsupported: "{}: Feature is not supported for platform - {}." +pddf_get_constanc_fail: "{}: Failed to get constants for platform - {}." + +#Domain +domain_names_present: Broadcom domain names are present in the {} file +domain_names_not_present: Broadcom domain names are not Present in the {} file + + +#storm-control +storm_control_traffic_verification_successful: Storm control traffic verification got passed +storm_control_traffic_verification_failed: Storm control traffic verification is failed +storm_control_config_verify_failed: Storm control config verification failed for {} on interface {} +storm_control_negative_verification_successful: Storm control negative scenarios verification got passed +storm_control_negative_verification_failed: Storm control negative scenarios verification is failed +storm_control_portchannel_verification_failed : Stormcontrol portchannel verification got failed +storm_control_portchannel_verification_successful : Stormcontrol portchannel verification is passed +storm_control_traffic_incremental_bps_max_vlan_successful: Storm control incremental bps value and max vlan traffic verification got passed +storm_control_traffic_incremental_bps_max_vlan_failed: Storm control incremental bps value and max vlan traffic verification got failed +storm_control_reboot_successful: Storm Control config save reboot operation is successful +storm_control_reboot_failed: Storm Control config save reboot operation got failed +storm_control_unsupported : Stormcontrol control is unsupported on this platform + +#sFlow +failed_to_config_sflow: failed to config sflow configuration. +failed_to_receive_sflow_packets: sFlow collector failed to receive sflow sample packets with {} {}. +failed_to_generate_sflow_log_in_syslog: failed to generate sflow log in syslog +failed_to_config_non_default_sampling_rate: failed to config non default sampling_rate +sflow_test_case_passed: Successfully verified sFlow sampling functionality. +sflow_negative_verifcation_failed: Sflow negative scenario verification failed +sflow_docker_restart_status: Sflow collector {} to receive sflow packets after sflow docker restart. +sflow_fast_reboot_status: Sflow collector {} to receive sflow packets after fast reboot. +sflow_warm_reboot_status: Sflow collector {} to receive sflow packets after warm reboot. +sflow_control_plane_traffic_status: Sflow collector {} to receive sflow packets for control plane traffic +sflow_invalid_config_status: DUT is {} throwing error when tried to config invalid sflow {} +sflow_REST_config_status: through rest {} to config sflow configuration +sflow_gNMI_config_status: sflow admin {} using gNMI {} is {} + +#Tail time stamping +tail_ts_is_unsupported_on_this_platform: Tail Time-Stamping is unsupported on this platform +device_id_is_not_present: tam device id is not created +ts_flow_is_not_created: failed to create a TS flow +failed_to_observe_timestamps: failed to observe timestamps at captured result +statistics_are_not_incremented: TS flow statistics are not incremented +tail_ts_status: Verification of tail time-stamping {} functionality got {} + + +#Drop-monitor +sample_cfg_fail: Failed to configure sample. +drop_monitor_aging_interval_cfg_pass: Drop monitor aging interval configured successfully. +drop_monitor_aging_interval_cfg_fail: Failed to configure aging interval. +drop_monitor_aging_interval_clear_pass: Drop monitor aging interval cleared successfully. +drop_monitor_aging_interval_clear_fail: Drop monitor aging interval clear failed. +drop_monitor_flow_cfg_pass: Drop monitor flow configured successfully. +drop_monitor_flow_cfg_fail: Failed to configure drop monitor flow. +drop_monitor_flow_delete_pass: Drop monitor flow delete successfully. +drop_monitor_flow_delete_fail: Drop monitor flow delete failed. +drop_monitor_appl_db_entry_fail: Drop monitor flow APPL_DB entry verification failed. +drop_monitor_appl_db_entry_pass: Drop monitor flow APPL_DB entry verification passed. +drop_monitor_collector_cfg_fail: Drop monitor collector config failed. +drop_monitor_stats_fail: Failed to get drop monitor stats. +drop_monitor_stats_pass: Verified drop monitor stats. +drop_monitor_unsupported_platform: Drop Monitor feature is not supported on {} +drop_monitor_unsupported_build: This build does not support drop monitor feature +no_mod_drop_events_observed: MOD drop events are not observed +verify_flow_configuration_failed: Verification of flow configuration for Drop Monitor feature failed +mod_support: MOD support is set to {} +mod_feature_enable_fail: Failed to enable drop monitor feature +drop_monitor_max_flows_config_successful: Drop Monitor maximum flows configuration is successful +mod_config_fail: Drop monitor configuration failed +verify_failed: Verification of {} failed +drop_reason_validation_pass: Successfully validated the drop reason {} + + + +#Management VRF +mgmt_vrf_eth0_bind_fail: Failure to bind eth0 to Managemnt VRF +mgmt_vrf_eth0_bind_success: Successful binding of eth0 to Management VRF +mgmt_vrf_ssh_fail: SSH over Management VRF failed +mgmt_vrf_ssh_pass: Successful to SSH over Management VRF +mgmt_vrf_ping_fail: PING over Management VRF failed +mgmt_vrf_ping_pass: PING successful using Management VRF +mgmt_vrf_tr_fail: Traceroute over Management VRF failed +mgmt_vrf_tr_pass: Traceroute successful using Management VRF +mgmt_vrf_dataport_bind: Front panel data ports bind to Management VRF +mgmt_vrf_dataport_nobind: No front-panel/data ports bind to Management VRF +mgmt_vrf_reboot_fail: Management VRF bind failed after reboot +mgmt_vrf_reboot_pass: Management VRF bind success after reboot +mgmt_vrf_cfgreload_fail: Management VRF bind failed after config reload +mgmt_vrf_cfgreload_pass: Management VRF bind success after config reload +mgmt_vrf_http_fail: HTTP/CURL un-successful with eth0 bind to Management VRF +mgmt_vrf_http_pass: HTTP/CURL successful with eth0 bind to Management VRF +mgmt_vrf_snmp_fail: SNMP operation NOT successful with eth0 in MGMT VRF +mgmt_vrf_snmp_pass: SNMP operation successful with eth0 in MGMT VRF +mgmt_vrf_ntp: NTP synchronized {} in Management VRF +mgmt_vrf_warmboot: After warmboot, {} network conenctivity over eth0 in Management VRF +mgmt_vrf_tacacs: SSH login using TACACS {} over Management VRF +mgmt_vrf_static_dhcp: Static and DHCP address assigment to eth0 {} in Management VRF + +#snapshot +snapshot_telemetry_interval_config_and_reset : verification of configured telemetry interval and reset to default interval is {} +snapshot_interval_config : configuring snapshot interval is {} +snapshot_interval_config_and_reset : verification of configured snapshot interval and reset to default interval is {} +snapshot_verify_default_interval: verification of snapshot default interval is {} +snapshot_tc_verify : verification of {} counter is {} +snapshot_clear_verify : verification of {} is {} +snapshot_all_buffer_counters : verification of all buffer counters using {} traffic is {} +snapshot_tc_counter_DB_verify : verification of {} counter using counter DB is {} +snapshot_clear_counter_DB_verify : verification of {} counter using counter DB is {} + +#BARE BONE OC YANG VERSION +rest_error_observed: REST error response {} status code observed +no_response_found: No response found with {} +bare_bone_ocyang_version_status: Verification the bare bone oc yang version using correct, no version and incorrect version using {} call is {}. +ocyang_unsupported_build_version: OCYANG version is unsupported on this build. +ocyang_error_is_not_as_per_the_design: OCYANG ERROR {} is not as per the design. \ No newline at end of file diff --git a/spytest/datastore/messages/testbed.yaml b/spytest/datastore/messages/testbed.yaml new file mode 100644 index 00000000000..29ae836f14c --- /dev/null +++ b/spytest/datastore/messages/testbed.yaml @@ -0,0 +1,6 @@ +testbed_no_rps_info : Failed to get RPS information from testbed +testbed_no_rps_model: Failed to get RPS model from testbed +testbed_no_rps_ip: Failed to get RPS IP address from testbed +testbed_no_rps_outlet: Failed to get RPS outlet from testbed +testbed_no_rps_username: Failed to get RPS outlet from testbed + diff --git a/spytest/datastore/messages/tgen.yaml b/spytest/datastore/messages/tgen.yaml new file mode 100644 index 00000000000..ff5bfcf345c --- /dev/null +++ b/spytest/datastore/messages/tgen.yaml @@ -0,0 +1,26 @@ + +traffic_verification_success: traffic verification is successful +traffic_verification_failed: traffic verification is failed +traffic_transmission_failed: Traffic is not transmitted from IXIA-{} +traffic_verification_failed_from_to: Traffic verification failed from {} to {} +traffic_verification_failed_msg: traffic verification is failed {} +traffic_stream_config_failed: Traffic stream configuration failed +traffic_action_control_failed: Traffic action control failed. +tgen_exception: Traffic generator exception occured {} +traffic_loss_observed: Traffic loss observed + +tgen_vlan_packets_received: All packets with VLAN ID {} received on {} +tgen_vlan_packets_dropped: All packets with VLAN ID {} NOT received on {} +tgen_failed_add_endpoint_sets: Failed to add endpointsets +tgen_failed_api: Traffic generator API Failed with status {} +tgen_failed_oversubscription: Traffic generator Failed with Oversubscription +tgen_failed_invalid_option: Traffic generator Failed with invalid option usage +tgen_failed_runtime_error: Traffic generator Failed with runtime error +tgen_failed_start_capture: Traffic generator Failed with unable to start capture + +tgen_failed_start_protocols: Failed to start TGen Protocols +tgen_failed_configure_stack: Could not configure TGen stack +tgen_failed_apply_changes: Port must be selected to apply TGen changes +tgen_failed_missing_traffic_item: Could not find TGen Traffic Item Statistics view +tgen_failed_invalid_value: Invalid value for TGen parameter +tgen_failed_abort: Traffic generator abort {} diff --git a/spytest/datastore/messages/ut_snmp.yaml b/spytest/datastore/messages/ut_snmp.yaml new file mode 100644 index 00000000000..f98918a4c41 --- /dev/null +++ b/spytest/datastore/messages/ut_snmp.yaml @@ -0,0 +1,34 @@ +##COMMON + + +## SNMP UT +ut_snmp_init_pass : UT SNMP initialization passed +ut_snmp_init_fail : UT SNMP initialization failed +ut_snmp_strings_pass : UT SNMP strings passed +ut_snmp_strings_fail : UT SNMP strings failed +ut_snmp_community_pass : UT SNMP community passed +ut_snmp_community_fail : UT SNMP community failed +ut_snmp_group_pass : UT SNMP group passed +ut_snmp_group_fail : UT SNMP group failed +ut_snmp_host_v2_pass : UT SNMP host v2 passed +ut_snmp_host_v2_fail : UT SNMP host v2 failed +ut_snmp_host_v3_pass : UT SNMP host v3 passed +ut_snmp_host_v3_fail : UT SNMP host v3 failed +ut_snmp_user_pass : UT SNMP user passed +ut_snmp_user_fail : UT SNMP user failed +ut_snmp_view_pass : UT SNMP view passed +ut_snmp_view_fail : UT SNMP view failed +ut_snmp_pid_snmpd_pass : UT SNMP snmpd pid check passed +ut_snmp_pid_snmpd_fail : UT SNMP snmpd pid check failed +ut_snmp_md5_snmpd_conf_pass : UT SNMP /etc/snmp/snmpd.conf md5sum check passed +ut_snmp_md5_snmpd_conf_fail : UT SNMP /etc/snmp/snmpd.conf md5sum check failed +ut_snmp_client_pass : UT SNMP client passed +ut_snmp_client_fail : UT SNMP client failed +ut_snmp_engineid_pass : UT SNMP agentaddress v2c passed +ut_snmp_engineid_fail : UT SNMP agentaddress v2c failed +ut_snmp_agentaddress_ipv4_pass : UT SNMP agent address ipv4 passed +ut_snmp_agentaddress_ipv4_fail : UT SNMP agent address ipv4 failed +ut_snmp_agentaddress_ipv6_pass : UT SNMP agent address ipv6 passed +ut_snmp_agentaddress_ipv6_fail : UT SNMP agent address ipv6 failed +ut_snmp_trap_v2c_pass : UT SNMP trap v2c passed +ut_snmp_trap_v2c_fail : UT SNMP trap v2c failed diff --git a/spytest/datastore/prompts/Prompts_Support.md b/spytest/datastore/prompts/Prompts_Support.md new file mode 100644 index 00000000000..da8f8e5215e --- /dev/null +++ b/spytest/datastore/prompts/Prompts_Support.md @@ -0,0 +1,34 @@ +# Prompts Support: + +We have placed a file "sonic_prompts.yaml" under "datastore/prompts" directory in spytest clone. +Little bit of help/comments are also provided in that file. + +This file contains 3 sections: +#### **patterns:** +Where users will define each pattern with a unique name. Each pattern name and value should be unique. +For sonic management framework, “**--sonic-mgmt--**” is a default value used by framework, please don’t change that and add your pattern using the above default value. + +#### **modes:** +Whatever name we have given to a pattern in the above section, we have to provide a mechanism to enter into that prompt. +So here, for each pattern, we have to provide the parent pattern and the command which need to be executed to enter into that mode and a command which need to be executed to come out of that mode. +There are some areas where we have to provide some values along with the command to enter into a mode. For such scenarios, keep a place holder({}) for that. + +#### **required_args:** +In the above section, for some commands, we added place holders. Names for those place holders will be added here. +We have written an example script "**tests/infra_ut/test_ut_modes.py**" as part of our unit testing. +Look for functions which match "**test_mgmt_cli_mode_\***", "**test_vtysh_prompt_modes_\***", "**test_vtysh_mgmt_prompt_modes_\***" and "**test_all_modes_\***" + +# Example: + +To add support for acl prompts, following is the way: + +To enter into ACL prompt, we have to execute “**ip access-list ACL_NAME**” and to come out of it is “**exit**”. After entering the prompt will be like “**--sonic-mgmt--(config-ipv4-acl)#**” + +So add as follow in pattern section: +**`mgmt-ipv4-acl-config: '--sonic-mgmt--\(config-ipv4-acl\)#'`** + +And in modes section: +**`mgmt-ipv4-acl-config: ['mgmt-config', 'ip access-list {}', 'exit']`** + +For entering into that mode, ACL_NAME should be provided as input. So, need to add the following in required args +**`mgmt-ipv4-acl-config: ['aclname']`** diff --git a/spytest/datastore/prompts/sonic_prompts.yaml b/spytest/datastore/prompts/sonic_prompts.yaml new file mode 100644 index 00000000000..d343bbcf455 --- /dev/null +++ b/spytest/datastore/prompts/sonic_prompts.yaml @@ -0,0 +1,218 @@ +########################################################################## +# Note: +# Use only single quotes for enclosing the regular expression patterns. +########################################################################## + +########################################################################## +# PATTERNS: +# Mapping different prompt patterns to a name(same as in modes) +########################################################################## +patterns: + login_prompt: '\S+\s+login:\s*$' + onie_prompt: '\s*ONIE:/ #\s*$' + onie_resque_prompt: '\s+Please press Enter to activate this console.\s*$' + # ===================================================================== + # Generic Patterns + # ===================================================================== + normal-user: 'admin@(sonic|{}):.*[#|\$]\s*$' + root-user: 'root@(sonic|{}):.*[#|\$]\s*$' + # ===================================================================== + # VTYSH Patterns + # ===================================================================== + vtysh-user: '(sonic|{})#' + vtysh-config: '(sonic|{})\(config\)#' + vtysh-intf-config: '(sonic|{})\(config-if\)#' + vtysh-bfd-config: '(sonic|{})\(config-bfd\)#' + vtysh-bfd-peer-config: '(sonic|{})\(config-bfd-peer\)#' + vtysh-key-chain-config: '(sonic|{})\(config-keychain\)#' + vtysh-key-chain-Identifier-config: '(sonic|{})\(config-keychain-key\)#' + vtysh-line-vty-config: '(sonic|{})\(config-line\)#' + vtysh-l2vpn-config: '(sonic|{})\(config-l2vpn\)#' + vtysh-nhgroup-config: '(sonic|{})\(config-nh-group\)#' + vtysh-pbr-map-config: '(sonic|{})\(config-pbr-map\)#' + vtysh-pseudowire-config: '(sonic|{})\(config-pw\)#' + vtysh-route-map-config: '(sonic|{})\(config-route-map\)#' + vtysh-router-config: '(sonic|{})\(config-router\)#' + vtysh-router-af-config: '(sonic|{})\(config-router-af\)#' + vtysh-vrf-config: '(sonic|{})\(config-vrf\)#' + vtysh-any-config: '(sonic|{})\(config[^)]*\)#' + # ===================================================================== + # LLDP Patterns + # ===================================================================== + lldp-user: '\[lldpcli\]\s*#\s*$' + # ===================================================================== + # MGMT-CLI Patterns + # ===================================================================== + mgmt-user: '--sonic-mgmt--#' + mgmt-config: '--sonic-mgmt--\(config\)#' + mgmt-intf-config: '--sonic-mgmt--\(conf-if-Ethernet.*\)#' + mgmt-ipv4-acl-config: '--sonic-mgmt--\(config-ipv4-acl\)#' + mgmt-mirror-session-config: '--sonic-mgmt--\(config-mirror-.*\)#' + mgmt-vlan-config: '--sonic-mgmt--\(conf-if-Vlan.*\)#' + mgmt-lag-config: '--sonic-mgmt--\(conf-if-po.*\)#' + mgmt-management-config: '--sonic-mgmt--\(conf-if-eth.*\)#' + + mgmt-evpn-view: '--sonic-mgmt--\(conf-if-evpn\)#' + mgmt-vxlan-view: '--sonic-mgmt--\(conf-if-Vxlan-.*\)#' + + mgmt-tam-view: '--sonic-mgmt--\(config-tam\)#' + mgmt-tam-int-ifa-view: '--sonic-mgmt--\(config-tam-int-ifa\)#' + mgmt-tam-int-ifa-ts-view: '--sonic-mgmt--\(config-int-ifa-ts\)#' + mgmt-tam-drop-monitor-view: '--sonic-mgmt--\(config-drop-monitor\)#' + + mgmt-nat-view: '--sonic-mgmt--\(config-nat\)#' + mgmt-mclag-view: '--sonic-mgmt--\(config-mclag-domain-.*\)#' + mgmt-lo-view: '--sonic-mgmt--\(conf-if-lo.*\)#' + + mgmt-bfd-view: '--sonic-mgmt--\(conf-bfd\)#' + mgmt-bfd-peer-view: '--sonic-mgmt--\(conf-bfd-peer\)#' + + mgmt-route-map-view: '--sonic-mgmt--\(config-route-map\)#' + + mgmt-link-state-track-view: '--sonic-mgmt--\(config-link-track\)#' + + mgmt-router-bgp-view: '--sonic-mgmt--\(config-router-bgp\)#' + mgmt-router-bgp-af-view: '--sonic-mgmt--\(config-router-bgp-af\)#' + mgmt-router-bgp-nbr-view: '--sonic-mgmt--\(config-router-bgp-neighbor\)#' + mgmt-router-bgp-nbr-af-view: '--sonic-mgmt--\(config-router-bgp-neighbor-af\)#' + mgmt-router-bgp-template-view: '--sonic-mgmt--\(config-router-bgp-pg\)#' + mgmt-router-bgp-template-af-view: '--sonic-mgmt--\(config-router-bgp-pg-af\)#' + mgmt-router-bgp-l2vpn-vni-view: '--sonic-mgmt--\(config-router-bgp-af-vni\)#' + mgmt-router-bgp-vrf-view: '--sonic-mgmt--\(config-router-bgp-.*-vrf\)#' + + mgmt-any-config: '--sonic-mgmt--\((config|conf[ig]*-[^)]*)\)#' + +########################################################################## +# MODES: +# Mapping of commands to enter and exit from a parent mode +# Commands may be static or dynamic. If a command needs a value, +# keep a place holder and provide the names for them in required_args. +########################################################################## +modes: + # ===================================================================== + # Generic modes + # ===================================================================== + normal-user: ['', '', ''] + root-user: ['normal-user', 'sudo su', 'exit'] + # ===================================================================== + # VTYSH modes + # ===================================================================== + vtysh-user: ['normal-user', 'sudo vtysh', 'exit'] + vtysh-config: ['vtysh-user', 'configure terminal', 'exit'] + vtysh-intf-config: ['vtysh-config', 'interface {}', 'exit'] + vtysh-bfd-config: ['vtysh-config', 'bfd', 'exit'] + vtysh-bfd-peer-config: ['vtysh-bfd-config', 'peer {}', 'exit'] + vtysh-key-chain-config: ['vtysh-config', 'key chain {}', 'exit'] + vtysh-key-chain-Identifier-config: ['vtysh-key-chain-config', 'key {}', 'exit'] + vtysh-line-vty-config: ['vtysh-config', 'line vty', 'exit'] + vtysh-l2vpn-config: ['vtysh-config', 'l2vpn {} type vpls', 'exit'] + vtysh-nhgroup-config: ['vtysh-config', 'nexthop-group {}', 'exit'] + vtysh-pbr-map-config: ['vtysh-config', 'pbr-map {} seq {}', 'exit'] + vtysh-pseudowire-config: ['vtysh-config', 'pseudowire {}', 'exit'] + vtysh-route-map-config: ['vtysh-config', 'route-map {} {} {}', 'exit'] + vtysh-router-config: ['vtysh-config', 'router {} {}', 'exit'] + vtysh-router-af-config: ['vtysh-router-config', 'address-family {} {}', 'exit'] + vtysh-vrf-config: ['vtysh-config', 'vrf {}', 'exit'] + vtysh-any-config: ['vtysh-user', '', 'end'] + # ===================================================================== + # LLDP modes + # ===================================================================== + lldp-user: ['normal-user', 'docker exec -it lldp lldpcli', 'exit'] + # ===================================================================== + # MGMT-CLI modes + # ===================================================================== + mgmt-user: ['normal-user', 'sonic-cli prompt=--sonic-mgmt--', 'exit'] + mgmt-config: ['mgmt-user', 'configure terminal', 'exit'] + mgmt-intf-config: ['mgmt-config', 'interface {}', 'exit'] + mgmt-ipv4-acl-config: ['mgmt-config', 'ip access-list {}', 'exit'] + mgmt-mirror-session-config: ['mgmt-config', 'mirror-session {}', 'exit'] + mgmt-vlan-config: ['mgmt-config', 'interface Vlan {}', 'exit'] + mgmt-lag-config: ['mgmt-config', 'interface PortChannel {}', 'exit'] + mgmt-management-config: ['mgmt-config', 'interface Management {}', 'exit'] + + mgmt-evpn-view: ['mgmt-config', 'evpn {}', 'exit'] + mgmt-vxlan-view: ['mgmt-config', 'interface Vxlan {}', 'exit'] + + mgmt-tam-view: ['mgmt-config', 'tam', 'exit'] + mgmt-tam-int-ifa-view: ['mgmt-tam-view', 'int-ifa', 'exit'] + mgmt-tam-int-ifa-ts-view: ['mgmt-tam-view', 'int-ifa-ts', 'exit'] + mgmt-tam-drop-monitor-view: ['mgmt-tam-view', 'drop-monitor', 'exit'] + + mgmt-nat-view: ['mgmt-config', 'nat', 'exit'] + mgmt-mclag-view: ['mgmt-config', 'mclag domain {}', 'exit'] + mgmt-lo-view: ['mgmt-config', 'interface Loopback {}', 'exit'] + + mgmt-bfd-view: ['mgmt-config', 'bfd', 'exit'] + mgmt-bfd-peer-view: ['mgmt-bfd-view', 'peer {}', 'exit'] + + mgmt-route-map-view: ['mgmt-config', 'route-map {} {} {}', 'exit'] + + mgmt-link-state-track-view: ['mgmt-config', 'link state track {}', 'exit'] + + mgmt-router-bgp-view: ['mgmt-config', 'router bgp {} vrf {}', 'exit'] + mgmt-router-bgp-af-view: ['mgmt-router-bgp-view', 'address-family {} {}', 'exit'] + mgmt-router-bgp-nbr-view: ['mgmt-router-bgp-view', 'neighbor {}', 'exit'] + mgmt-router-bgp-nbr-af-view: ['mgmt-router-bgp-nbr-view', 'address-family {} {}', 'exit'] + mgmt-router-bgp-template-view: ['mgmt-router-bgp-view', 'peer-group {}', 'exit'] + mgmt-router-bgp-template-af-view: ['mgmt-router-bgp-template-view', 'address-family {} {}', 'exit'] + mgmt-router-bgp-l2vpn-vni-view: ['mgmt-router-bgp-af-view', 'vni {}', 'exit'] + + mgmt-any-config: ['mgmt-user', '', 'end'] + +########################################################################## +# REQUIRED_ARGS: +# To enter a mode, some times, we may need a mandatory value, +# to provide along with the command. +########################################################################## +required_args: + vtysh-intf-config: ['interface'] + vtysh-bfd-peer-config: ['peer_ip'] + vtysh-key-chain-config: ['key_chain'] + vtysh-key-chain-Identifier-config: ['key_id'] + vtysh-l2vpn-config: ['l2vpn_name'] + vtysh-nhgroup-config: ['group_name'] + vtysh-pbr-map-config: ['map_name', 'seq_id'] + vtysh-pseudowire-config: ['interface'] + vtysh-route-map-config: ['tag_name', 'action', 'seq_num'] + vtysh-router-config: ['router', 'instance'] + vtysh-router-af-config: ['addr_family', 'modifier'] + vtysh-vrf-config: ['vrf_name'] + + mgmt-intf-config: ['interface'] + mgmt-ipv4-acl-config: ['aclname'] + mgmt-vlan-config: ['vlan'] + mgmt-lag-config: ['portchannel'] + mgmt-management-config: ['management'] + mgmt-mirror-session-config: ['session_name'] + + mgmt-evpn-view: ['evpnname'] + mgmt-vxlan-view: ['vxlan'] + + mgmt-mclag-view: ['domain_id'] + mgmt-lo-view: ['loopback_id'] + + mgmt-bfd-peer-view: ['peer_ip'] + + mgmt-route-map-view: ['map_name', 'action', 'seq_num'] + + mgmt-link-state-track-view: ['track_name'] + + mgmt-router-bgp-view: ['bgp_instance', 'bgp_vrf_name'] + mgmt-router-bgp-af-view: ['af_type', 'af_family'] + mgmt-router-bgp-nbr-view: ['ip_address'] + mgmt-router-bgp-nbr-af-view: ['nbr_af_type', 'nbr_af_family'] + mgmt-router-bgp-template-view: ['group_name'] + mgmt-router-bgp-template-af-view: ['tpl_af_type', 'tpl_af_family'] + mgmt-router-bgp-l2vpn-vni-view: ['vxlan_id'] + +########################################################################## +# SUDO_INCLUDE_PROMPTS: +# Prompts where config commands should be executed with SUDO. +########################################################################## +sudo_include_prompts: ['normal-user'] + +########################################################################## +# do_exclude_prompts: +# Prompts where show commands should be not be executed with DO. +########################################################################## +do_exclude_prompts: ['normal-user', 'vtysh-user', 'mgmt-user', 'root-user'] diff --git a/spytest/datastore/vervars/3.0.1.yaml b/spytest/datastore/vervars/3.0.1.yaml new file mode 100644 index 00000000000..cbc8305163a --- /dev/null +++ b/spytest/datastore/vervars/3.0.1.yaml @@ -0,0 +1 @@ +UI_TYPE : "click" diff --git a/spytest/datastore/vervars/3.0.x.0.yaml b/spytest/datastore/vervars/3.0.x.0.yaml new file mode 100644 index 00000000000..cbc8305163a --- /dev/null +++ b/spytest/datastore/vervars/3.0.x.0.yaml @@ -0,0 +1 @@ +UI_TYPE : "click" diff --git a/spytest/datastore/vervars/common.yaml b/spytest/datastore/vervars/common.yaml new file mode 100644 index 00000000000..dd2ff7867e4 --- /dev/null +++ b/spytest/datastore/vervars/common.yaml @@ -0,0 +1 @@ +UI_TYPE : "" diff --git a/spytest/datastore/vervars/vervars.yaml b/spytest/datastore/vervars/vervars.yaml new file mode 100644 index 00000000000..9b8777fbcd2 --- /dev/null +++ b/spytest/datastore/vervars/vervars.yaml @@ -0,0 +1,9 @@ + +common: !include common.yaml +3.0.x.0: !include 3.0.x.0.yaml +3.0.1: !include 3.0.1.yaml + +vervars: + default: [common] + 3.0.x.0: [common, 3.0.x.0] + 3.0.1: [common, 3.0.1] diff --git a/spytest/reporting/tcmap.csv b/spytest/reporting/tcmap.csv new file mode 100644 index 00000000000..299521e586a --- /dev/null +++ b/spytest/reporting/tcmap.csv @@ -0,0 +1,415 @@ +Release,RunType,Feature,TestCaseID,FunctionName +Arlo+,daily,Regression,ft_acl_egress_fwd_src_ipv4,test_ft_acl_egress_ipv4 +Arlo+,daily,Regression,ft_acl_ip_in_forward_port_channel,test_ft_acl_port_channel_ingress +Arlo+,daily,Regression,ft_acl_ipv6_in_forward_port_channel,test_ft_acl_port_channel_ingress +Arlo+,daily,Regression,ft_acl_rule_update_with_acl_loader_update_full,test_ft_acl_loader +Arlo+,daily,Regression,ft_acl_rule_update_with_acl_loader_update_increment,test_ft_acl_loader +Arlo+,daily,Regression,ft_acl_rule_upgrade,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_acl_show_commands,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_deny_all_after_forward_rule,test_ft_acl_egress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_eg_drop_dst_ip,test_ft_acl_egress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_eg_drop_src_ip,test_ft_acl_egress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_eg_drop_src_ip_and_dst_port,test_ft_acl_egress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_eg_fwd_dst_ip,test_ft_acl_egress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_eg_fwd_src_and_dst_ip,test_ft_acl_egress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_eg_fwd_src_ip,test_ft_acl_egress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_in_drop_dst_ip,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_in_drop_dst_port_range,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_in_drop_ip_proto_src_dst_ip,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_in_drop_ip_proto_src_ip,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_in_drop_src_ip,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_in_drop_src_port,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_in_drop_src_port_range,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_in_fwd_dst_ip,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_in_fwd_dst_port_range,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_in_fwd_ip_proto_src_dst_ip,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_in_fwd_ip_proto_src_dst_port,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_in_fwd_ip_proto_src_ip,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_in_fwd_ip_proto_src_port,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_in_fwd_l4_dst_port,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_in_fwd_l4_src_port,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_in_fwd_src_and_dst_ip,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_in_fwd_src_ip,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_in_fwd_src_port_range,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_in_fwd_vlan_l4_dst_port_ip_proto,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_ing_drop_dst_port,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_ingress_create,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_ingress_drop_ip_protocol_dst_ip,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_ingress_drop_ip_protocol_src_port,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_ingress_forward_ip_protocol_dst_ip,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_acl_v6_eg_drop_dst_ip,test_ft_acl_egress_ipv6 +Arlo+,daily,Regression,ft_acl_v6_eg_drop_src_ip,test_ft_acl_egress_ipv6 +Arlo+,daily,Regression,ft_acl_v6_eg_fwd_dst_ip,test_ft_acl_egress_ipv6 +Arlo+,daily,Regression,ft_acl_v6_eg_fwd_src_dst_ip,test_ft_acl_egress_ipv6 +Arlo+,daily,Regression,ft_acl_v6_eg_fwd_src_ip,test_ft_acl_egress_ipv6 +Arlo+,daily,Regression,ft_acl_v6_in_create,test_ft_acl_egress_ipv6 +Arlo+,daily,Regression,ft_acl_v6_in_drop_dst_ip,test_ft_acl_ingress_ipv6 +Arlo+,daily,Regression,ft_acl_v6_in_drop_src_ip,test_ft_acl_ingress_ipv6 +Arlo+,daily,Regression,ft_acl_v6_in_fwd_dst_ip,test_ft_acl_ingress_ipv6 +Arlo+,daily,Regression,ft_acl_v6_in_fwd_dstip_ip_protocol,test_ft_acl_ingress_ipv6 +Arlo+,daily,Regression,ft_acl_v6_in_fwd_src_dst_ip,test_ft_acl_ingress_ipv6 +Arlo+,daily,Regression,ft_acl_v6_in_fwd_src_ip,test_ft_acl_ingress_ipv6 +Arlo+,daily,Regression,ft_acl_v6_in_fwd_srcip_ip_protocol,test_ft_acl_ingress_ipv6 +Arlo+,daily,Regression,ft_acl_v6_ing_drop_ip_proto_src_dst_ip,test_ft_acl_ingress_ipv6 +Arlo+,daily,Regression,ft_acl_v6_ing_fwd_eth_type,test_ft_acl_ingress_ipv6 +Arlo+,daily,Regression,ft_acl_v6_ing_fwd_ip_proto_src_dst_ip,test_ft_acl_ingress_ipv6 +Arlo+,daily,Regression,ft_acl_v6_ingress_drop_eth_type,test_ft_acl_ingress_ipv6 +Arlo+,daily,Regression,ft_acl_v6_ingress_drop_ip_protocol,test_ft_acl_ingress_ipv6 +Arlo+,daily,Regression,ft_acl_v6_ingress_drop_ip_protocol_dst_port,test_ft_acl_ingress_ipv6 +Arlo+,daily,Regression,ft_acl_v6_ingress_drop_src_ip_ip_protocol,test_ft_acl_ingress_ipv6 +Arlo+,daily,Regression,ft_acl_v6_ingress_drop_tcp_flags,test_ft_acl_ingress_ipv6 +Arlo+,daily,Regression,ft_acl_v6_ingress_fwd_tcp_flags,test_ft_acl_ingress_ipv6 +Arlo+,daily,Regression,ft_acl_v6_rule_update_full,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_aclshow,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_ipv4_acl_in_srcip_dstip_port_redirect,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_ipv4_acl_in_srcip_dstport_redirect,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ipv4acl_ingress_l4srcport_range_l4dstport_range_forward,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ipv4acl_ingress_srcip_ipprotocol_forward,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ipv4acl_ingress_srcip_l4dst_port_drop,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ipv4acl_ingress_dstip_l4dstport_range_forward,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,FtOpSoQosAclFn097,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ipv6acl_ingress_tcpflags_forward,test_ft_acl_ingress_ipv6 +Arlo+,daily,Regression,ipv6acl_ingress_tcpflags_drop,test_ft_acl_ingress_ipv6 +Arlo+,daily,Regression,ipv4acl_ingress_tcpflas_drop,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,FtOpSoQosAclFn102,test_ft_acl_port_channel_V4_ingress +Arlo+,daily,Regression,FtOpSoQosAclFn103,test_ft_acl_port_channel_V4_ingress +Arlo+,daily,Regression,FtOpSoQosAclFn104,test_ft_acl_port_channel_V4_ingress +Arlo+,daily,Regression,FtOpSoQosAclFn105,test_ft_acl_port_channel_ingress +Arlo+,daily,Regression,FtOpSoQosAclFn106,test_ft_acl_port_channel_ingress +Arlo+,daily,Regression,ipv4acl_ingress_ip_protocol,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ipv4acl_ingress_l4ports_forward,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ipv4acl_ingress_srcip_dstip_drop,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ipv4acl_ingress_srcip_dstip_forward,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ipv4acl_ingress_srcip_dstip_redirect,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ipv6acl_ingress_ip_protocol,test_ft_acl_ingress_ipv6 +Arlo+,daily,Regression,ipv6acl_ingress_srcip_dstip_drop,test_ft_acl_ingress_ipv6 +Arlo+,daily,Regression,ipv6acl_ingress_srcip_dstip_forward,test_ft_acl_ingress_ipv6 +Arlo+,daily,Regression,ft_v4_v6_queue,test_ft_cos_cpu_counters +Arlo+,daily,Regression,ft_cos_routing,test_ft_cos_cpu_counters +Arlo+,daily,Regression,ft_cos_switching,test_ft_cos_cpu_counters +Arlo+,daily,Regression,ft_arp_clear_cache_static_and_dynamic_entries,test_ft_arp_clear_cache_static_and_dynamic_entries +Arlo+,daily,Regression,ft_arp_dynamic,test_ft_arp_clear_cache_static_and_dynamic_entries +Arlo+,daily,Regression,ft_arp_dynamic_renew_traffic_test,test_ft_arp_dynamic_renew_traffic_test +Arlo+,daily,Regression,ft_arp_entry_link_failure,test_ft_arp_entry_link_failure +Arlo+,daily,Regression,ft_arp_static,test_ft_arp_clear_cache_static_and_dynamic_entries +Arlo+,daily,Regression,ft_bgp_v6peer_auth_simple,TestBGPRif.test_ft_bgp_clear +Arlo+,daily,Regression,ft_bgp_v6peer_sonic_clear,TestBGPRif.test_ft_bgp_clear +Arlo+,daily,Regression,ft_bgp_v6peer_vtysh_clear,TestBGPRif.test_ft_bgp_clear +Arlo+,daily,Regression,ft_bgp_sonic_show_summary_neigh,TestBGPRif.test_ft_bgp_clear +Arlo+,daily,Regression,ft_bgp_v4peer_asn4,TestBGPRif.test_ft_bgp_clear +Arlo+,daily,Regression,ft_bgp_v4peer_auth_simple,TestBGPRif.test_ft_bgp_clear +Arlo+,daily,Regression,ft_bgp_v4peer_sonic_clear,TestBGPRif.test_ft_bgp_clear +Arlo+,daily,Regression,ft_bgp_v4peer_vtysh_clear,TestBGPRif.test_ft_bgp_clear +Arlo+,daily,Regression,ft_ip6_connected_host_traffic_forward,test_ft_ip6_static_route_traffic_forward_blackhole +Arlo+,daily,Regression,ft_ip_static_route_traffic_forward,test_ft_ip_static_route_traffic_forward +Arlo+,daily,Regression,ft_tacacs_enable_disable_failthrough,test_ft_tacacs_enable_disable_failthrough +Arlo+,daily,Regression,ft_tacacs_failthrough_with_multiple_servers,test_ft_tacacs_enable_disable_failthrough +Arlo+,daily,Regression,ft_tacacs_modify_server_parameters,test_ft_tacacs_modify_server_parameters +Arlo+,daily,Regression,ft_tacacs_nondefault_global_key_timeout,test_ft_tacacs_modify_server_parameters +Arlo+,daily,Regression,ft_tacacs_ssh_login_highest_priorityserver,test_ft_tacacs_ssh_login_highest_priorityserver +Arlo+,daily,Regression,ft_tacacs_ssh_login_with_nondefaultpriority_servers,test_ft_tacacs_ssh_login_highest_priorityserver +Arlo+,daily,Regression,ft_ipv4_bgp_ibgp_ebgp_traffic,TestBGPRif.test_ft_bgp_peer_traffic_check +Arlo+,daily,Regression,tacacs_key_config_with_special_characters,test_ft_tacacs_maximum_servers +Arlo+,daily,Regression,fallback_mode_on_portchannel,test_ft_portchannel_fallback_traffic +Arlo+,daily,Regression,traffic_on_portchannel_up_with_fallback,test_ft_portchannel_fallback_traffic +Arlo+,daily,Regression,l3_fwding ,test_l3_fwding +Arlo+,daily,Regression,base_line_multiple_portchannel_create_delete,test_base_line_portchannel_create_delete +Arlo+,daily,Regression,base_line_random_link_flap_portchannel,test_base_line_random_link_flap_portchannel +Arlo+,daily,Regression,po_ft_lag_down_when_not_created_in_another_DUT,test_ft_portchannel_fallback_traffic +Arlo+,daily,Regression,ft_lag_l2_hash_smac_dmac_vlan,test_ft_portchannel_behavior_with_tagged_traffic +Arlo+,daily,Regression,ft_lag_l3_hash_sip_dip_l4port,test_ft_lag_l3_hash_sip_dip_l4port +Arlo+,daily,Regression,ft_lag_member_remove_add,test_ft_portchannel_behavior_with_tagged_traffic +Arlo+,daily,Regression,ft_lag_remove_vlan,test_ft_portchannel_behavior_with_tagged_traffic +Arlo+,daily,Regression,ft_lag_withone_member,test_ft_portchannel_behavior_with_tagged_traffic +Arlo+,daily,Regression,ft_portchannel_del_members,test_ft_portchannel_behavior_with_tagged_traffic +Arlo+,daily,Regression,FtOpSoSwLagFn021,test_base_line_random_link_flap_portchannel +Arlo+,daily,Regression,traffic_loss_when_portchannel_member_removed_and_added,test_ft_portchannel_behavior_with_tagged_traffic +Arlo+,daily,Regression,portchannel_status_when_all_members_are_shutdown,test_ft_portchannel_behavior_with_tagged_traffic +Arlo+,daily,Regression,shutdown_on_portchannel_member,test_ft_portchannel_behavior_with_tagged_traffic +Arlo+,daily,Regression,tagged_traffic_on_portchannel,test_ft_portchannel_behavior_with_tagged_traffic +Arlo+,daily,Regression,traffic_distribution_on_new_portchannel_member,test_ft_portchannel_behavior_with_tagged_traffic +Arlo+,daily,Regression,traffic_on_disabled_portchannel,test_ft_portchannel_behavior_with_tagged_traffic +Arlo+,daily,Regression,untagged_traffic_on_portchannel,test_ft_untagged_traffic_on_portchannel +Arlo+,daily,Regression,ft_lldp_interaction_with_lag,test_ft_portchannel_behavior_with_tagged_traffic +Arlo+,daily,Regression,ft_lldp_ConfigManAddrEntry,test_ft_lldp_ConfigManAddrEntry +Arlo+,daily,Regression,ft_lldp_lldplocportid,test_ft_lldp_lldplocportid +Arlo+,daily,Regression,ft_lldp_lldplocsysdesc,test_ft_lldp_lldplocsysdesc +Arlo+,daily,Regression,ft_lldp_lldplocsysname,test_ft_lldp_lldplocsysname +Arlo+,daily,Regression,ft_lldp_LocManAddrEntry,test_ft_lldp_LocManAddrEntry +Arlo+,daily,Regression,ft_lldp_LocManAddrLen,test_ft_lldp_LocManAddrLen +Arlo+,daily,Regression,ft_lldp_LocManAddrlfld,test_ft_lldp_LocManAddrlfld +Arlo+,daily,Regression,ft_lldp_LocManAddrOID,test_ft_lldp_LocManAddrOID +Arlo+,daily,Regression,ft_port_frame_fwd_diff_mtu,test_ft_port_frame_fwd_diff_mtu +Arlo+,daily,Regression,base_line_port_move_from_vlan_a_to_vlan_b,test_base_line_port_move_from_vlan_a_to_vlan_b +Arlo+,daily,Regression,base_line_vlan_port_association,test_base_line_vlan_port_association +Arlo+,daily,Regression,ft_add_unknownvlan_interface,test_ft_add_unknownvlan_interface +Arlo+,daily,Regression,ft_show_vlan_brief,test_ft_vlan_trunk_tagged +Arlo+,daily,Regression,ft_vlan_delete_with_member,test_ft_vlan_delete_with_member +Arlo+,daily,Regression,ft_vlan_trunk_tagged,test_ft_vlan_trunk_tagged +Arlo+,long,Regression,mirror_action_config_upload,test_ft_system_config_mgmt_verifying_config_with_save_reboot +Arlo+,daily,Regression,ft_mirror_action_ip_addr,test_ft_erspan_action_ip_protocol_ip_addr_l4_ports +Arlo+,daily,Regression,mirror_action_encapsulation,test_ft_erspan_action_encapsulation +Arlo+,daily,Regression,mirror_action_l4_ports,test_ft_erspan_action_ip_protocol_ip_addr_l4_ports +Arlo+,daily,Regression,mirror_action_ip_addr,test_ft_erspan_action_ip_protocol_ip_addr_l4_ports +Arlo+,daily,Regression,ft_ping_vlan ,test_ft_ping_v4_v6_vlan +Arlo+,daily,Regression,mirror_action_ether_type,test_ft_erspan_action_ethertype_dscp_tcp_flags_l4range +Arlo+,daily,Regression,mirror_action_tcp_flags,test_ft_erspan_action_ethertype_dscp_tcp_flags_l4range +Arlo+,daily,Regression,mirror_action_dscp,test_ft_erspan_action_ethertype_dscp_tcp_flags_l4range +Arlo+,daily,Regression,base_line_l2_taggged_forwarding_with_portchannel,test_base_line_l2_taggged_forwarding_with_portchannel +Arlo+,daily,Regression,base_line_mac_move_single_vlan,test_base_line_mac_move_single_vlan +Arlo+,daily,Regression,ft_port_mtu_change_verify,test_ft_port_frame_fwd_diff_mtu +Arlo+,daily,Regression,ft_ntp_disable_ntp_enable_ntp,test_ft_ntp_disable_enable_with_message_log +Arlo+,daily,Regression,ft_ntp_message_log_display_with_correct_time,test_ft_ntp_disable_enable_with_message_log +Arlo+,daily,Regression,ft_ssh_add_user_verify,test_ft_ssh_add_user_verify +Arlo+,daily,Regression,ft_ssh_service_disable,test_ft_ssh_service_disable +Arlo+,daily,Regression,ft_port_fec_nofec,test_ft_port_fec_nofec +Arlo+,daily,Regression,ft_specific_port_shutdown,test_ft_port_frame_fwd_diff_mtu +Arlo+,daily,Regression,FtOpSoSwLagFn041,test_ft_portchannel_with_vlan_variations +Arlo+,daily,Regression,FtOpSoSwLagFn042,test_ft_portchannel_with_vlan_variations +Arlo+,daily,Regression,ft_snmp_sysDescr,test_ft_snmp_sysDescr +Arlo+,daily,Regression,ft_snmp_sysLocation,test_ft_snmp_sysLocation +Arlo+,daily,Regression,ft_snmp_sysName,test_ft_snmp_sysName +Arlo+,daily,Regression,ft_snmp_test_syUpTime,test_ft_snmp_test_syUpTime +Arlo+,daily,Regression,base_line_portchannel_create_delete,test_base_line_portchannel_create_delete +Arlo+,daily,Regression,base_line_vlan_create_delete_and_mac_learning_with_bum,test_base_line_vlan_create_delete_and_mac_learning_with_bum +Arlo+,daily,Regression,l2_to_l3_port,test_l2_to_l3_port +Arlo+,daily,Regression,ft_ipv6_neighbor_entry,test_ft_ipv6_neighbor_entry +Arlo+,daily,Regression,ft_acl_v6_eg_redirect_sip_dip_routingports,test_ft_acl_egress_ipv6 +Arlo+,daily,Regression,ft_acl_v6_eg_redirect_srcip_dstip,test_ft_acl_egress_ipv6 +Arlo+,daily,Regression,mirror_action_ip_addr_l4_ports,test_ft_erspan_action_ip_protocol_ip_addr_l4_ports +Arlo+,daily,Regression,test_mirror_action_l4_port_range,test_ft_erspan_action_ethertype_dscp_tcp_flags_l4range +Arlo+,daily,Regression,ft_ip6_static_route_traffic_fwd_blackhole ,test_ft_ip6_static_route_traffic_forward_blackhole +Arlo+,daily,Regression,ft_ipv6_address_check ,test_ft_ping_v4_v6_vlan +Arlo+,daily,Regression,mirror_action_ip_protocol_src_ip,test_ft_erspan_action_ip_protocol_ip_addr_l4_ports +Arlo+,daily,Regression,ft_ipv4_address_check ,test_ft_ping_v4_v6_vlan +Arlo+,daily,Regression,base_line_mac_move_across_vlans,test_base_line_mac_move_across_vlans +Arlo+,daily,Regression,ft_snmp_sysContact,test_ft_snmp_sysContact +Arlo+,daily,Regression,auth_shared_keys ,test_ft_tacacs_ssh_login_with_tacacs_operations +Arlo+,daily,Regression,portchannel_member_vlan_participation,test_ft_portchannel_behavior_with_tagged_traffic +Arlo+,daily,Regression,executing_server_key_in_appropriate_mode,test_ft_tacacs_ssh_login_with_tacacs_operations +Arlo+,daily,Regression,ft_system_uptime,test_ft_system_uptime +Arlo+,daily,Regression,ft_ping_after_ip_change,test_ft_ping__v4_v6_after_ip_change_pc +Arlo+,daily,Regression,ft_ping6_after_ip_change,test_ft_ping__v4_v6_after_ip_change_pc +Arlo+,daily,Regression,ft_ip6_address_check_pc,test_ft_ping__v4_v6_after_ip_change_pc +Arlo+,daily,Regression,ft_ip_L2_L3_translation,test_ft_ip_v4_v6_L2_L3_translation +Arlo+,daily,Regression,ft_ip6_L2_L3_translation,test_ft_ip_v4_v6_L2_L3_translation +Arlo+,daily,Regression,ft_ip6_static_route_traffic_forward ,test_ft_ip6_static_route_traffic_forward_blackhole +Arlo+,daily,Regression,ft_tacacs_default_login,test_ft_tacacs_ssh_login_with_tacacs_operations +Arlo+,daily,Regression,ft_tacacs_add_multiple_servers,test_ft_tacacs_maximum_servers +Arlo+,daily,Regression,ft_tacacs_single_server_for_auth,test_ft_tacacs_ssh_login_with_tacacs_operations +Arlo+,daily,Regression,ft_tacacs_ssh_login,test_ft_tacacs_ssh_login_with_tacacs_operations +Arlo+,daily,Regression,ft_tacacs_maximum_ipv4_servers ,test_ft_tacacs_maximum_servers +Arlo+,daily,Regression,ft_port_speed_fn ,test_ft_port_frame_fwd_diff_mtu +Arlo+,daily,Regression,ft_port_traffic_fn,test_ft_port_frame_fwd_diff_mtu +Arlo+,daily,Regression,ft_tacacs_nondefault_timeout,test_ft_tacacs_modify_server_parameters +Arlo+,daily,Regression,ft_tacacs_remove_highpriority_server,test_ft_tacacs_ssh_login_highest_priorityserver +Arlo+,daily,Regression,ft_tacacsauth_enable_pamfile_created,test_ft_tacacs_ssh_login_with_tacacs_operations +Arlo+,daily,Regression,ft_port_config_mtu,test_ft_port_frame_fwd_diff_mtu +Arlo+,daily,Regression,ft_port_save_reload,test_ft_port_frame_fwd_diff_mtu +Arlo+,daily,Regression,ft_port_fn_config_checking,test_ft_port_frame_fwd_diff_mtu +Arlo+,daily,Regression,ft_port_check,test_ft_port_frame_fwd_diff_mtu +Arlo+,daily,Regression,ft_vlan_based_arp_entry_link_failure,test_ft_arp_entry_link_failure +Arlo+,daily,Regression,ft_ip_address_check_pc,test_ft_ping__v4_v6_after_ip_change_pc +Arlo+,daily,Regression,ft_mirror_action_encapsulation,test_ft_erspan_action_encapsulation +Arlo+,daily,Regression,FtOpSoSySnFn008,test_ft_snmp_mib_2 +Arlo+,daily,Regression,FtOpSoSySnFn009,test_ft_snmp_if_mib_all +Arlo+,daily,Regression,FtOpSoSySnFn010,test_ft_snmp_entity_mib_all +Arlo+,daily,Regression,FtOpSoSySnFn011,test_ft_snmp_entity_sensor_mib +Arlo+,daily,Regression,FtOpSoSySnFn012,test_ft_snmp_dot1q_dot1db_mib +Arlo+,daily,Regression,FtOpSoSySnFn013,test_ft_snmp_dot1q_dot1db_mib +Arlo+,daily,Regression,FtOpSoSySnFn015,test_ft_snmp_root_node_walk +Arlo+,daily,Regression,ft_in_acl_v6_src_dst_ip_fwd_port_channel,test_ft_acl_port_channel_ingress +Arlo+,daily,Regression,ft_in_acl_v6_src_dst_ip_protocol_fwd_port_channel,test_ft_acl_port_channel_ingress +Arlo+,daily,Regression,ft_in_acl_v6_src_dst_port_drop_port_channel,test_ft_acl_port_channel_ingress +Arlo+,daily,Regression,ft_in_acl_v6_src_dst_port_fwd_port_channel,test_ft_acl_port_channel_ingress +Arlo+,daily,Regression,ft_in_acl_v6_src_dst_port_range_fwd_port_channel,test_ft_acl_port_channel_ingress +Arlo+,daily,Regression,ft_in_acl_v6_src_ip_dst_port_drop_port_channel,test_ft_acl_port_channel_ingress +Arlo+,daily,Regression,ft_in_acl_v6_src_port_dst_port_range_drop_port_channel,test_ft_acl_port_channel_ingress +Arlo+,daily,Regression,ft_in_acl_v6_src_tcpflags_drop_port_channel,test_ft_acl_port_channel_ingress +Arlo+,daily,Regression,ft_in_acl_v4_src_dst_ip_drop_port_channel,test_ft_acl_port_channel_V4_ingress +Arlo+,daily,Regression,ft_in_acl_v4_src_dst_ip_fwd_port_channel,test_ft_acl_port_channel_V4_ingress +Arlo+,daily,Regression,ft_in_acl_v4_src_dst_ip_src_port_fwd_port_channel,test_ft_acl_port_channel_V4_ingress +Arlo+,daily,Regression,ft_in_acl_v4_src_dst_port_drop_port_channel,test_ft_acl_port_channel_V4_ingress +Arlo+,daily,Regression,ft_in_acl_v4_src_dst_port_range_drop_port_channel,test_ft_acl_port_channel_V4_ingress +Arlo+,daily,Regression,ft_in_acl_v4_src_dst_port_range_fwd_port_channel,test_ft_acl_port_channel_V4_ingress +Arlo+,daily,Regression,ft_in_acl_v4_src_tcpflags_drop_port_channel,test_ft_acl_port_channel_V4_ingress +Arlo+,long,Regression,ft_sytem_bootup_logging_debug,test_ft_sytem_bootup_logging_debug +Arlo+,long,Regression,ft_sys_hard_reboot_multiple_iter,test_ft_sys_hard_reboot_multiple_iter +Arlo+,long,Regression,ft_sys_soft_reboot_multiple_iter,test_ft_sys_soft_reboot_multiple_iter +Arlo+,long,Regression,ft_sys_show_reboot_cause,test_ft_sys_hard_reboot_multiple_iter +Arlo+,long,Regression,FtFpSoQoSCoSCfg001,test_ft_qos_config_mgmt_verifying_config_with_save_reboot +Arlo+,long,Regression,FtOpSoQosAclCmFn002,test_ft_qos_config_mgmt_verifying_config_with_save_reboot +Arlo+,long,Regression,ft_ecn_config_db_to_running_config_after_save_and_reload,test_ft_qos_config_mgmt_verifying_config_with_save_reboot +Arlo+,long,Regression,ft_ecn_config_to_config_db_json_after_save_and_reboot,test_ft_qos_config_mgmt_verifying_config_with_save_reboot +Arlo+,long,Regression,FtOpSoQosEcnCfg001,test_ft_qos_config_mgmt_verifying_config_with_save_reboot +Arlo+,long,Regression,FtOpSoQosWredCfg001,test_ft_qos_config_mgmt_verifying_config_with_save_reboot +Arlo+,long,Regression,FtOpSoQosAclCmFn001,test_ft_qos_config_mgmt_verifying_config_with_save_reboot +Arlo+,long,Regression,ft_acl_configvalues_save_reload,test_ft_qos_config_mgmt_verifying_config_with_save_reboot +Arlo+,daily,Regression,FtOpSoSyInIpFn002,test_ft_port_fn_verify_shut_noshut +Arlo+,daily,Regression,ft_ipv4_acl_loader_add,test_ft_acl_loader +Arlo+,daily,Regression,FtOpSoSySnFn017,test_ft_snmp_ipAddressStorageType_ipv6 +Arlo+,daily,Regression,FtOpSoSySnFn020,test_ft_snmp_ipv6_If_Forward_default_HopLimit +Arlo+,daily,Regression,ft_ipv6_ndp_clear_cache,test_ft_ipv6_neighbor_entry +Arlo+,daily,Regression,ft_ipv6_neighbor_static_entry,test_ft_ipv6_neighbor_entry +Arlo+,daily,Regression,ft_ip_static_ip_on_mgmt_intrf,test_ft_ip_static_ip_on_mgmt_intrf +Arlo+,daily,Regression,FtOpSoSySnFn016,test_ft_snmp_ipAddressRowStatus_ipv6 +Arlo+,daily,Regression,FtOpSoSySnFn021,test_ft_snmp_ipv6scope_index_table +Arlo+,daily,Regression,ft_bgp_update_delay_timer,TestBGPRif.test_ft_bgp_peer_traffic_check +Arlo+,daily,Regression,FtOpSoSwLagFn005,test_ft_lag_l3_hash_sip_dip_l4port +Arlo+,daily,Regression,FtOpSoSwLagFn010,test_ft_member_state_after_interchanged_the_members_across_portchannels +Arlo+,daily,Regression,FtOpSoSwVlFn010,test_ft_untagged_traffic_on_portchannel +Arlo+,daily,Regression,FtOtSoRtBgpPlFn032,TestBGPVeLag.test_ft_bgp_peer_traffic_check +Arlo+,daily,Regression,FtOtSoRtBgp4Fn070,TestBGPVeLag.test_ft_bgp_peer_traffic_check +Arlo+,daily,Regression,FtOtSoRtBgp4Fn069,TestBGPVeLag.test_ft_bgp_peer_traffic_check +Arlo+,daily,Regression,FtOpSoQosAclFn156,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,FtOpSoQosAclFn157,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_acl_v4_rule_update_full,test_ft_acl_loader +Arlo+,daily,Regression,ft_acl_v4_rule_update_incremental,test_ft_acl_loader +Arlo+,daily,Regression,FtOpSoQosAclFn160,test_ft_acl_loader +Arlo+,daily,Regression,FtOpSoQoSCosFn001,test_ft_cos_tc_queue_map +Arlo+,daily,Regression,FtOpSoQsWdFn012,test_ft_wred_functionality +Arlo+,daily,Regression,FtOpSoQosAclFn093,test_ft_acl_ingress_ipv6_l3_forwarding +Arlo+,daily,Regression,FtOpSoQosAclFn096,test_ft_acl_ingress_ipv6_l3_forwarding +Arlo+,daily,Regression,FtOpSoQosAclFn098,test_ft_acl_ingress_ipv6_l3_forwarding +Arlo+,daily,Regression,FtOtSoRtBgpPlFn004,TestBGPRif.test_ft_bgp_v6_dyn_nbr +Arlo+,daily,Regression,FtOtSoRtBgpPlFn006,TestBGPRif.test_ft_bgp_v6_link_local_bgp +Arlo+,daily,Regression,FtOtSoRtBgp4Fn001,TestBGPRif.test_ft_bgp_ipv4_no_route_aggregation_for_exact_prefix_match +Arlo+,daily,Regression,FtOtSoRtBgp4Fn002,TestBGPRif.test_ft_bgp_ipv4_route_aggregation_atomic_aggregate_without_as_set +Arlo+,daily,Regression,FtOtSoRtBgp4Fn019,TestBGPRif.test_ft_bgp_v4_max_dyn_nbr +Arlo+,daily,Regression,FtOtSoRtBgp4Fn049,TestBGPRif.test_ft_bgp_peer_traffic_check +Arlo+,daily,Regression,FtOtSoRtBgp4Fn060,TestBGPRif.test_ft_bgp_v4_dyn_nbr +Arlo+,daily,Regression,FtOtSoRtBgp4Fn063,TestBGPRif.test_ft_bgp_graceful_restart_and_aware_routers +Arlo+,daily,Regression,FtOtSoRtBgp4Fn078,TestBGPRif.test_ft_bgp_rmap +Arlo+,daily,Regression,FtOtSoRtBgp4Fn088,TestBGPRif.test_ft_bgp_rmap_out +Arlo+,daily,Regression,ft_bgp_v6peer_route_redist_static_conn,TestBGPIPvxRouteAdvertisementFilter.test_redistribute_connected_ipv6 +Arlo+,daily,Regression,FtOtSoRtBgpPlFn007,TestBGPIPvxRouteAdvertisementFilter.test_filter_list_in_ipv6 +Arlo+,daily,Regression,FtOtSoRtBgpPlFn012,TestBGPIPvxRouteAdvertisementFilter.test_distribute_list_in_ipv6 +Arlo+,daily,Regression,FtOtSoRtBgpPlFn014,TestBGPIPvxRouteAdvertisementFilter.test_distribute_list_in_ipv6 +Arlo+,daily,Regression,FtOtSoRtBgpPlFn016,TestBGPIPvxRouteAdvertisementFilter.test_prefix_list_out_ipv6 +Arlo+,daily,Regression,FtOtSoRtBgpPlFn018,TestBGPIPvxRouteAdvertisementFilter.test_filter_list_out_ipv6 +Arlo+,daily,Regression,FtOtSoRtBgp4Fn018,TestBGPIPvxRouteAdvertisementFilter.test_distribute_list_in_ipv4 +Arlo+,daily,Regression,FtOtSoRtBgp4Fn027,TestBGPIPvxRouteAdvertisementFilter.test_route_map_in_ipv4 +Arlo+,daily,Regression,FtOtSoRtBgp4Fn032,TestBGPIPvxRouteAdvertisementFilter.test_default_originate_ipv4 +Arlo+,daily,Regression,FtOtSoRtBgp4Fn033,TestBGPIPvxRouteAdvertisementFilter.test_redistribute_static_ipv4 +Arlo+,daily,Regression,FtOtSoRtBgp4Fn034,TestBGPIPvxRouteAdvertisementFilter.test_redistribute_connected_ipv4 +Arlo+,daily,Regression,FtOtSoRtBgp4Fn036,TestBGPIPvxRouteAdvertisementFilter.test_prefix_list_out_ipv4 +Arlo+,daily,Regression,FtOtSoRtBgp4Fn039,TestBGPIPvxRouteAdvertisementFilter.test_filter_list_in_ipv4 +Arlo+,daily,Regression,FtOtSoRtBgp4Fn077,TestBGPIPvxRouteAdvertisementFilter.test_filter_list_in_ipv4 +Arlo+,long,Regression,ft_bgp_v6peer_asn4_cfg_fast_reboot,test_ft_bgp_fast_reboot +Arlo+,long,Regression,ft_bgp_v6peer_asn4_save_reboot,test_ft_bgp_save_reboot +Arlo+,long,Regression,ft_bgp_v4peer_asn4_cfg_fast_reboot,test_ft_bgp_fast_reboot +Arlo+,long,Regression,ft_bgp_v4peer_asn4_save_reboot,test_ft_bgp_save_reboot +Arlo+,long,Regression,tacacs_save_config,test_ft_security_config_mgmt_verifying_config_with_save_reboot +Arlo+,long,Regression,FtOpSoSyCmFn003,test_ft_system_config_mgmt_verifying_config_with_save_reboot +Arlo+,long,Regression,ft_system_hardreboot_nounwantedlogs,test_ft_sytem_bootup_logging_debug +Arlo+,long,Regression,ft_system_boxservices_clearconfig,test_ft_sys_soft_reboot_multiple_iter +Arlo+,long,Regression,ft_system_repetitive_power_OFF_ON,test_ft_sys_hard_reboot_multiple_iter +Arlo+,long,Regression,ft_system_system_hard_softreboot,test_ft_sys_hard_reboot_multiple_iter +Arlo+,long,Regression,ft_system_savereload,test_ft_sys_soft_reboot_multiple_iter +Arlo+,daily,Regression,FtOtSoRtBgp4Fn025,TestBGPRif.test_ft_bgp_peer_traffic_check +Arlo+,daily,Regression,FtOpSoSyErspanFn015,test_ft_erspan_basic_functionality +Arlo+,daily,Regression,FtOpSoSyErspanFn011,test_ft_erspan_basic_functionality +Arlo+,daily,Regression,FtOpSoSwlldpFn004,test_ft_lldp_non_default_config +Arlo+,daily,Regression,FtOpSoSwlldpFn008,test_ft_lldp_non_default_config +Arlo+,daily,Regression,FtOpSoSwlldpFn009,test_ft_lldp_non_default_config +Arlo+,daily,Regression,FtOpSoSwlldpFn010,test_ft_lldp_non_default_config +Arlo+,daily,Regression,FtOpSoSwlldpFn011,test_ft_lldp_non_default_config +Arlo+,daily,Regression,FtOpSoSyErspanFn019,test_ft_erspan_basic_functionality +Arlo+,daily,Regression,FtOpSoQosAclFn136,test_ft_acl_ingress_ipv4 +Arlo+,daily,Regression,ft_bgp_v6_as_path_acl,TestBGPIPvxRouteAdvertisementFilter.test_distribute_list_in_ipv6 +Arlo+,daily,Regression,FtOtSoRtBgp4Fn014,TestBGPIPvxRouteAdvertisementFilter.test_bgp_ebgp4_nbr_update_source +Arlo+,daily,Regression,FtOtSoRtBgp4Fn015,TestBGPIPvxRouteAdvertisementFilter.test_bgp_ebgp4_nbr_authentication +Arlo+,daily,Regression,FtOtSoRtBgp4Fn016,TestBGPIPvxRouteAdvertisementFilter.test_bgp_route_map_with_community +Arlo+,daily,Regression,FtOpSoRtIpv4Fn003,test_l3_v4_route_po_1 +Arlo+,daily,Regression,FtOpSoRtIpv6Fn003,test_l3_v6_route_po_1 +Arlo+,daily,Regression,FtOtSoRtBgpPlFn038,TestBGPIPvxRouteAdvertisementFilter.test_route_map_in_ipv6 +Arlo+,daily,Regression,FtOpSoSyErspanFn018,test_ft_erspan_action_ethertype_dscp_tcp_flags_l4range +Arlo+,long,Regression,FtOpSoSyErspanCm001,test_ft_system_config_mgmt_verifying_config_with_save_reboot +Arlo+,long,Regression,FtOpSoSyErspanCm002,test_ft_system_config_mgmt_verifying_config_with_save_reboot +Arlo+,long,Regression,FtOpSoSyErspanCm003,test_ft_system_config_mgmt_verifying_config_with_save_reboot +Arlo+,daily,Regression,FtOpSoQsCoPPFn002,test_ft_copp_bgp +Arlo+,daily,Regression,FtOpSoQsCoPPFn003,test_ft_copp_lldp +Arlo+,daily,Regression,FtOpSoQsCoPPFn005,test_ft_copp_lacp +Arlo+,daily,Regression,FtOpSoQsCoPPFn006,test_ft_copp_dhcp +Arlo+,daily,Regression,FtOpSoQsCoPPFn004,test_copp_ndp +Arlo+,daily,Regression,ft_bgp_outbound_routefilter,TestBGPIPvxRouteAdvertisementFilter.test_prefix_list_out_ipv4 +Arlo+,daily,Regression,FtOtSoRtBgpPlFn005,TestBGPIPvxRouteAdvertisementFilter.test_static_blackhole_rt_redistribute_with_routemap_ipv6 +Arlo+,long,Regression,ft_bgp_v6peer_asn4_cfg_save_reload,test_ft_bgp_save_reboot +Arlo+,long,Regression,ft_bgp_v4peer_asn4_cfg_save_reload,test_ft_bgp_save_reboot +Arlo+,daily,Regression,FtOtSoRtBgp4Fn008,TestBGP_LINEAR_EBGP_IBGP.test_bgp_linear_ebgp_ibgp_route_advanced +Arlo+,daily,Regression,FtOtSoRtBgp4Fn013,TestBGP_LINEAR_EBGP_IBGP.test_bgp_linear_ebgp_ibgp_route_advanced +Arlo+,daily,Regression,FtOtSoRtBgp4Fn029,TestBGP_LINEAR_EBGP_IBGP.test_bgp_linear_ebgp_ibgp_route_advanced +Arlo+,daily,Regression,FtOtSoRtBgp4Fn076,TestBGP_LINEAR_EBGP_IBGP.test_bgp_linear_ebgp_ibgp_route_advanced +Arlo+,long,Regression,FtOpSoSysFRFn004,test_ft_system_verify_traffic_fast_reboot +Arlo+,daily,Regression,FtOpSoQsCoPPFn001,test_ft_copp_arp +Arlo+,daily,Regression,FtOtSoRtArpFn011,test_ft_lag_l3_hash_sip_dip_l4port +Arlo+,daily,Regression,FtOtSoRtBgp4Fn028,TestBGP_LINEAR_EBGP_IBGP.test_bgp_linear_ebgp_ibgp_route_advanced +Arlo+,daily,Regression,FtOtSoRtBgp4Fn043,TestBGP_LINEAR_EBGP_IBGP.test_bgp_linear_ebgp_ibgp_route_advanced +Arlo+,daily,Regression,FtOtSoRtBgp4Fn074,TestBGP_LINEAR_EBGP_IBGP.test_bgp_linear_ebgp_ibgp_route_advanced +Arlo+,daily,Regression,FtOtSoRtBgp4Fn061,TestBGPRif.test_bgp_route_aggregation_4byteASN +Arlo+,long,Regression,FtOpSoRtStRtFn014,test_ft_arp_static_route_config_mgmt_verifying_config_with_save_reboot +Arlo+,long,Regression,ft_test_arp_after_fast_reboot,test_ft_arp_static_route_config_mgmt_verifying_config_with_save_fast_reboot +Arlo+,long,Regression,FtOpSoRtStRtFn015,test_ft_arp_static_route_config_mgmt_verifying_config_with_save_fast_reboot +Arlo+,daily,Regression,FtOtSoRtBgpPlFn029,TestBGP_LINEAR_EBGP_IBGP.test_bgp_linear_ebgp_ibgp_route_advanced +Arlo+,daily,Regression,FtOtSoRtBgpPlFn013,TestBGP_LINEAR_EBGP_IBGP.test_bgp_linear_ebgp_ibgp_route_advanced +Arlo+,daily,Regression,FtOtSoRtEcmpFn008,test_max_v6_route_with_max_paths +Arlo+,daily,Regression,FtOtSoRtEcmpFn009,test_max_v4_route_with_max_paths +Arlo+,daily,Regression,FtOtSoRtBgpPlFn019,TestBGP_LINEAR_EBGP.test_bgp_sp_three_node_linear_ebgp_med_rmap +Arlo+,daily,Regression,FtOtSoRtBgpPlFn022,TestBGP_STAR_IBGP.test_bgp_sp_star_ibgp_route_reflector_ipv46 +Arlo+,daily,Regression,FtOtSoRtBgp4Fn010,TestBGP_LINEAR_EBGP.test_bgp_sp_three_node_linear_ebgp_med_rmap +Arlo+,daily,Regression,FtOtSoRtBgp4Fn047,TestBGP_LINEAR_IBGP.test_bgp_sp_four_node_bgp_cluster_route +Arlo+,daily,Regression,FtOtSoRtBgp4Fn048,TestBGP_STAR_IBGP.test_bgp_sp_star_ibgp_route_reflector_bgp_sess_flap +Arlo+,daily,Regression,FtOtSoRtBgp4Fn082,TestBGP_STAR_IBGP.test_bgp_sp_star_ibgp_route_reflector_ipv46 +Arlo+,daily,Regression,FtOtSoRtBgp4Fn083,TestBGP_STAR_IBGP.test_bgp_sp_star_ibgp_route_reflector_bgp_clear +Arlo+,daily,Regression,FtOpSoSySnFn014,test_ft_snmp_ipcidr_route_table +Arlo+,daily,Regression,ft_bgp_v4peer_confed_route_maps,TestBGPRif.test_ft_bgp_ebgp_confed +Arlo+,daily,Regression,FtOtSoRtBgpPlFn002,TestBGPIPvxRouteAdvertisementFilter.test_route_aggregate_ipv6 +Arlo+,daily,Regression,FtOtSoRtBgpPlFn023,TestBGP_LINEAR_IBGP.test_bgp_sp_four_node_bgp_cluster_route +Arlo+,daily,Regression,ft_bgp_confedv6_config_adv,TestBGPConfed.test_ipv6_confed_route_distribution +Arlo+,daily,Regression,ft_bgp_confedv6_route_maps_filter,TestBGPConfed.test_confed_route_distribution_with_rmap +Arlo+,daily,Regression,ft_bgp_confedv6_route_reflector,TestBGPConfed.test_ipv6_confed_with_rr +Arlo+,daily,Regression,FtOtSoRtBgpPlFn001,TestBGPRif.test_ft_bgp_ipv6_route_aggregation_with_as_set +Arlo+,daily,Regression,FtOtSoRtBgpPlFn028,TestBGPIPvxRouteAdvertisementFilter.test_bgp_ebgp6_traffic +Arlo+,daily,Regression,FtOtSoRtBgp4Fn057,test_ft_bgp_ebgp_multihop_4byteASN +Arlo+,daily,Regression,FtOtSoRtBgpPlFn025,TestBGPRrTraffic.test_ft_bgp6_rr_traffic_check +Buzznik,buzznik,Regression,FtOpSoSwLagFn045,test_ft_portchannel_interface_after_save_reload +Buzznik,buzznik,Regression,ipv4_intf_order,test_ft_verify_interfaces_order +Buzznik,buzznik,Regression,ipv6_intf_order,test_ft_verify_interfaces_order +Buzznik,buzznik,Regression,FtOpSoSysFRFn009,test_ft_system_verify_traffic_during_fast_reboot +Buzznik,buzznik,Regression,FtOpSoSysFRFn010,test_ft_system_verify_traffic_through_port_channel_during_fast_reboot +Buzznik,buzznik,Regression,FtOpSoSysPoFn010,test_ft_ovr_counters +Arlo+,daily,Regression,ft_verify_acl_conf_rest,test_acl_rest +Arlo+,daily,Regression,ft_eg_acl_conf_rest,test_acl_rest +Arlo+,daily,Regression,ft_verify_acl_conf_gNMI,test_ft_acl_gnmi +Arlo+,daily,Regression,ft_port_frame_fwd_diff_mtu_klish,test_ft_port_frame_fwd_diff_mtu_klish +Arlo+,daily,Regression,ft_port_mtu_change_verify_klish,test_ft_port_frame_fwd_diff_mtu_klish +Arlo+,daily,Regression,ft_specific_port_shutdown_klish,test_ft_port_frame_fwd_diff_mtu_klish +Arlo+,daily,Regression,ft_port_traffic_fn_klish,test_ft_port_frame_fwd_diff_mtu_klish +Arlo+,daily,Regression,ft_port_config_mtu_klish,test_ft_port_frame_fwd_diff_mtu_klish +Arlo+,daily,Regression,ft_port_check_klish,test_ft_port_frame_fwd_diff_mtu_klish +Arlo+,daily,Regression,FtOpSoSyInIpFn002_klish,test_ft_port_fn_verify_shut_noshut_klish +Arlo+,daily,Regression,ft_add_unknownvlan_interface_klish,test_ft_add_unknownvlan_interface_klish +Arlo+,daily,Regression,ft_vlan_delete_with_member_klish,test_ft_vlan_delete_with_member_klish +Arlo+,daily,Regression,ft_show_vlan_brief_klish,test_ft_vlan_trunk_tagged_klish +Arlo+,daily,Regression,ft_vlan_trunk_tagged_klish,test_ft_vlan_trunk_tagged_klish +Arlo+,daily,Regression,ft_lag_member_remove_add_klish,test_ft_portchannel_behavior_with_tagged_traffic_klish +Arlo+,daily,Regression,ft_lag_withone_member_klish,test_ft_portchannel_behavior_with_tagged_traffic_klish +Arlo+,daily,Regression,ft_portchannel_del_members_klish,test_ft_portchannel_behavior_with_tagged_traffic_klish +Arlo+,daily,Regression,portchannel_status_when_all_members_are_shutdown_klish,test_ft_portchannel_behavior_with_tagged_traffic_klish +Arlo+,daily,Regression,shutdown_on_portchannel_member_klish,test_ft_portchannel_behavior_with_tagged_traffic_klish +Arlo+,daily,Regression,FtOpSoSwVlFn010_klish,test_ft_untagged_traffic_on_portchannel_klish +Arlo+,daily,Regression,FtOpSoSwLagFn010_klish,test_ft_member_state_after_interchanged_the_members_across_portchannels_klish +Arlo+,daily,Regression,fallback_mode_on_portchannel_klish,test_ft_portchannel_fallback_traffic_klish +Arlo+,daily,Regression,traffic_on_portchannel_up_with_fallback_klish,test_ft_portchannel_fallback_traffic_klish +Arlo+,daily,Regression,po_ft_lag_down_when_not_created_in_another_DUT_klish,test_ft_portchannel_fallback_traffic_klish +Arlo+,daily,Regression,ft_bgp_sonic_config_commands_set_2_test_v4,test_ft_bgp_ipv4_klish +Arlo+,daily,Regression,ft_bgp_sonic_config_commands_set_2_test_v6,test_ft_bgp_ipv6_klish +Arlo+,daily,Regression,FtOpSoRtPerfFn053,test_ft_l3_performance_enhancements_v4_route_intstall_withdraw +Arlo+,daily,Regression,FtOpSoRtPerfFn052,test_ft_l3_performance_enhancements_v4_route_intstall_withdraw +Arlo+,daily,Regression,FtOpSoRtPerfFn054,test_cli_validation_ip_address +Arlo+,daily,Regression,FtOpSoRtPerfFn055,test_cli_validation_ip_address +Arlo+,daily,Regression,FtOpSoRtPerfFn056,test_cli_validation_bgp_router_config +Arlo+,daily,Regression,FtOpSoRtPerfFn057,test_cli_validation_bgp_router_config +Arlo+,daily,Regression,ft_lag_l3_hash_sip_dip_l4port_klish,test_ft_lag_l3_hash_sip_dip_l4port_klish +Arlo+,daily,Regression,FtOpSoSwLagFn005_klish,test_ft_lag_l3_hash_sip_dip_l4port_klish +Arlo+,daily,Regression,FtOtSoRtArpFn011_klish,test_ft_lag_l3_hash_sip_dip_l4port_klish diff --git a/spytest/spytest/__init__.py b/spytest/spytest/__init__.py new file mode 100644 index 00000000000..872596e1a99 --- /dev/null +++ b/spytest/spytest/__init__.py @@ -0,0 +1,9 @@ +import spytest.infra as st + +import spytest.tgen_api as tgapi +import utilities.common as cutils +import utilities.parallel as putils + +from spytest.dicts import SpyTestDict + +__all__ = ['st','tgapi', 'cutils', 'putils', 'SpyTestDict'] diff --git a/spytest/spytest/access/__init__.py b/spytest/spytest/access/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/spytest/spytest/access/connection.py b/spytest/spytest/access/connection.py new file mode 100644 index 00000000000..922f463dae1 --- /dev/null +++ b/spytest/spytest/access/connection.py @@ -0,0 +1,184 @@ +import os +import copy +import socket +import logging +import netmiko + +from .sonic_connection import SonicBaseConnection +from .sonic_connection import SonicSshConnection +from .fastpath_connection import FastpathBaseConnection +from .fastpath_connection import FastpathSshConnection +from .icos_connection import IcosBaseConnection +from .icos_connection import IcosSshConnection + +def log_msg(logger, lvl, msg): + if logger: + logger.log(lvl, msg) + else: + print(msg) + +class DeviceConnectionTimeout(netmiko.ssh_exception.NetMikoTimeoutException): + pass + + +class DeviceAuthenticationFailure(netmiko.ssh_exception.NetMikoAuthenticationException): + pass + + +def _DeviceConnection(ip_port, logger, **kwargs): + device_type = kwargs['device_type'] + try: + if device_type == "sonic_terminal": + kwargs['device_type'] = "sonic_terminal_telnet" + return SonicBaseConnection(**kwargs) + if device_type == "sonic_sshcon": + return SonicSshConnection(**kwargs) + if device_type == "sonic_ssh": + return SonicSshConnection(**kwargs) + if device_type == "fastpath_terminal": + kwargs['device_type'] = "fastpath_terminal_telnet" + return FastpathBaseConnection(**kwargs) + if device_type == "fastpath_ssh": + return FastpathSshConnection(**kwargs) + if device_type == "icos_terminal": + kwargs['device_type'] = "icos_terminal_telnet" + return IcosBaseConnection(**kwargs) + if device_type == "icos_ssh": + return IcosSshConnection(**kwargs) + return netmiko.ConnectHandler(**kwargs) + except netmiko.ssh_exception.NetMikoTimeoutException as e1: + log_msg(logger, logging.WARNING, "Timeout({}): {}".format(ip_port, e1)) + raise DeviceConnectionTimeout(e1) + except netmiko.ssh_exception.NetMikoAuthenticationException as e2: + #log_msg(logger, logging.WARNING, "Failure({}): {}".format(ip_port, e2)) + raise DeviceAuthenticationFailure(e2) + except Exception as e3: + log_msg(logger, logging.WARNING, "Exception({}): {}".format(ip_port, e3)) + raise e3 + +def initDeviceConnectionDebug(file_prefix): + logging.getLogger("paramiko").setLevel(logging.INFO) + + root = logging.getLogger('netmiko') + if not os.getenv("SPYTEST_NETMIKO_DEBUG", None): + root.setLevel(logging.INFO) + return + root.propagate = False + fmt = logging.Formatter('%(asctime)s %(levelname)s: %(message)s') + root.setLevel(logging.DEBUG) + logfile = "netmiko.log" + + if file_prefix: + logfile = "{}_{}".format(file_prefix, logfile) + + handler = logging.FileHandler(logfile, 'w') + handler.setFormatter(fmt) + root.addHandler(handler) + +def DeviceConnection(**kws): + + if os.getenv("SPYTEST_FILE_MODE", None): + return None + + kwargs = copy.copy(kws) + + logger = kwargs.pop("logger", None) + + ip = kwargs.get('ip', "0.0.0.0") + port = kwargs.get('port', "0") + ip_port = "{}:{}".format(ip, port) + + auth = [] + if "addl_auth" in kwargs: + auth.extend(kwargs["addl_auth"]) + del kwargs["addl_auth"] + if "password" in kwargs: + auth.append([kwargs["username"], kwargs["password"]]) + if "altpassword" in kwargs: + auth.append([kwargs["username"], kwargs["altpassword"]]) + if "mgmt_ipmask" in kwargs: + del kwargs["mgmt_ipmask"] + if "mgmt_gw" in kwargs: + del kwargs["mgmt_gw"] + if "access_model" in kwargs: + kwargs["device_type"] = kwargs["access_model"] + del kwargs["access_model"] + + # use sshcon_username and sshcon_password if provided + if "sshcon_password" in kwargs and "sshcon_username" in kwargs: + auth = [[kwargs["sshcon_username"], kwargs["sshcon_password"]]] + kwargs["password"] = kwargs["sshcon_password"] + del kwargs["sshcon_username"] + del kwargs["sshcon_password"] + del kwargs["altpassword"] + + last_exception = "" + run_passwd_cmd = False + for [username, password] in auth: + try: + if last_exception: + msg = "TRY ALT Authentication: {} {} {}".format(ip_port, username, password) + else: + msg = "TRY Authentication: {} {} {}".format(ip_port, username, password) + log_msg(logger, logging.INFO, msg) + kwargs["username"] = username + if "altpassword" in kwargs and kwargs["altpassword"] == password: + tmp_pwd = kwargs["password"] + kwargs["password"] = kwargs["altpassword"] + kwargs["altpassword"] = tmp_pwd + hndl = _DeviceConnection(ip_port, logger, **kwargs) + if "altpassword" in kwargs and run_passwd_cmd: + hndl.change_password(kwargs["username"], kwargs["altpassword"]) + hndl.password = kwargs["altpassword"] + hndl.altpassword = kwargs["password"] + return hndl + except DeviceAuthenticationFailure as e1: + last_exception = e1 + except socket.error as e2: + # Needed to check for the message where passwd change is done. + if "Spytest: socket is closed abruptly" in e2: + if "altpassword" in kwargs and not run_passwd_cmd: + auth.append([username, kwargs["altpassword"]]) + run_passwd_cmd = True + last_exception = e2 + except Exception as e3: + raise e3 + if last_exception: + raise last_exception + +def DeviceFileUpload(net_connect, src_file, dst_file, connection_param): + if connection_param["mgmt-ip"]: + dev = { + 'device_type': 'sonic_ssh', + 'username': connection_param["username"], + 'password': connection_param["password"], + 'altpassword': connection_param["altpassword"], + 'ip': connection_param["mgmt-ip"], + } + if "altpassword" in connection_param: + dev["altpassword"] = connection_param["altpassword"] + net_connect = DeviceConnection(**dev) + scp_conn = netmiko.SCPConn(net_connect) + scp_conn.scp_transfer_file(src_file, dst_file) + scp_conn.close() + if connection_param["mgmt-ip"]: + net_connect.disconnect() + +def DeviceFileDownload(net_connect, src_file, dst_file, connection_param): + if connection_param["mgmt-ip"]: + dev = { + 'device_type': 'sonic_ssh', + 'username': connection_param["username"], + 'password': connection_param["password"], + 'altpassword': connection_param["altpassword"], + 'ip': connection_param["mgmt-ip"], + } + if "altpassword" in connection_param: + dev["altpassword"] = connection_param["altpassword"] + net_connect = DeviceConnection(**dev) + scp_conn = netmiko.SCPConn(net_connect) + scp_conn.scp_get_file(src_file, dst_file) + scp_conn.close() + if connection_param["mgmt-ip"]: + net_connect.disconnect() + diff --git a/spytest/spytest/access/fastpath_connection.py b/spytest/spytest/access/fastpath_connection.py new file mode 100644 index 00000000000..f6a38dd99fc --- /dev/null +++ b/spytest/spytest/access/fastpath_connection.py @@ -0,0 +1,371 @@ +from __future__ import unicode_literals + +import re +import time +import logging +import socket +from netmiko.cisco_base_connection import CiscoBaseConnection + +show_trace = 0 +if show_trace > 1: + logger = logging.getLogger('netmiko_connection') + logger.setLevel(logging.DEBUG) + +def trace(fmt, *args): + if show_trace <= 0: + return + msg = fmt % args + if show_trace > 1: + logger.info(msg) + else: + print(msg) + +def dtrace(*args): + if show_trace > 2: + print(args) + +class FastpathBaseConnection(CiscoBaseConnection): + + def __init__(self, **kwargs): + self.net_login = kwargs.pop("net_login", None) + self.net_devname = kwargs.pop("net_devname", None) + if "altpassword" in kwargs: + self.altpassword = kwargs["altpassword"] + del kwargs["altpassword"] + super(FastpathBaseConnection, self).__init__(**kwargs) + + def set_logger(self, logger): + self.logger = logger + + def log_warn(self, msg): + if not getattr(self, "logger", None): + print(msg) + else: + self.logger.warning(msg) + + def session_preparation(self): + trace("============================== session_preparation ============================") + self.ansi_escape_codes = True + if self.protocol == "ssh": + return self.ssh_session_preparation() + else: + return super(FastpathBaseConnection, self).session_preparation() + + + def ssh_session_preparation(self): + """ + Added extended_login to the base function for handling the password change in ssh scenario. + """ + output = self._test_channel_read() + output = self.extended_login(output) + self.set_base_prompt() + self.disable_paging() + self.set_terminal_width() + + # Clear the read buffer + time.sleep(0.3 * self.global_delay_factor) + self.clear_buffer() + + def _enter_shell(self): + return '' + + def _return_cli(self): + return '' + + def disable_paging(self, command="terminal length 0", delay_factor=1): + return "" + + def set_base_prompt(self, pri_prompt_terminator='>', + alt_prompt_terminator='#', delay_factor=1): + trace("============================== set_base_prompt ============================") + return super(FastpathBaseConnection, self).set_base_prompt( + pri_prompt_terminator=pri_prompt_terminator, + alt_prompt_terminator=alt_prompt_terminator, + delay_factor=delay_factor) + + def change_password(self, username, new_password, output=None): + if output is None: + output = self.send_command("sudo passwd {}".format(username), expect_string="Enter new UNIX password:", strip_command=False, strip_prompt=False) + if "Enter new UNIX password:" in output: + output += self.send_command(new_password, expect_string="Retype new UNIX password:", strip_command=False, strip_prompt=False) + if "Retype new UNIX password:" in output: + pri_prompt_terminator_new = r"(#|\$|passwd: password updated successfully)\s*$" + output += self.send_command(new_password, expect_string=pri_prompt_terminator_new, strip_command=False, strip_prompt=False) + return [True, output] + return [False, output] + + def extended_login(self, output): + if "(current) UNIX password:" in output: + output = self.send_command(self.password, expect_string="Enter new UNIX password:", strip_command=False, strip_prompt=False) + retval = self.change_password(self.username, self.altpassword, output) + output += retval[1] + if retval[0]: + if self.protocol == "ssh" and "passwd: password updated successfully" in retval[1]: + self.disconnect() + raise socket.error("Spytest: socket is closed abruptly") + else: + retval2 = self.change_password(self.username, self.password) + output += retval2[1] + return output + + def telnet_login(self, pri_prompt_terminator=r'#\s*$', alt_prompt_terminator=r'>\s*$', + username_pattern=r"(?:[Uu]ser:|sername|ogin|User Name)", + pwd_pattern=r"assword", + delay_factor=1, max_loops=20): + trace("============================== telnet_login ============================") + setattr(self, "in_sonic_login", 1) + pri_prompt_terminator_new = r"(#|>|\(current\) UNIX password:)\s*$" + output = super(FastpathBaseConnection, self).telnet_login( + pri_prompt_terminator_new, alt_prompt_terminator, username_pattern, + pwd_pattern, delay_factor, max_loops) + output = self.extended_login(output) + setattr(self, "in_sonic_login", None) + return output + + def add_cached_read_data(self, output): + if not output: + return + try: + self.cached_read_data.append(output) + except: + self.cached_read_data = [output] + + def clear_cached_read_data(self): + self.cached_read_data = [] + + def get_cached_read_data(self): + retval = self.cached_read_data + self.cached_read_data = [] + return retval + + def sysrq_trace(self): + trace("============================== sysrq_trace ============================") + if self.remote_conn is None: + return None + if self.protocol != "telnet": + return None + import telnetlib + self.remote_conn.sock.sendall(telnetlib.IAC + telnetlib.BRK) + time.sleep(1) + self.remote_conn.sock.sendall(chr(108)) + output = self.send_command_timing("", delay_factor=1) + trace(output) + return output + + def read_channel(self): + trace("============================== read_channel ============================") + output = super(FastpathBaseConnection, self).read_channel() + self.add_cached_read_data(output) + if not getattr(self, "in_sonic_login", None): + return output + if re.search("1 - Assume the main session", output, flags=re.I): + self.log_warn("Terminal server is used by someone else '{}'".format(output)) + self.write_channel("1") + elif re.search("1 - Initiate a regular session", output, flags=re.I): + self.log_warn("Terminal server is used by someone else '{}'".format(output)) + #self.write_channel("1") + self.write_channel("4") + elif re.search("Enter session PID or 'all'", output, flags=re.I): + self.log_warn("Terminal server is used by someone else '{}'".format(output)) + self.write_channel("all") + self.write_channel("\n") + elif re.search(r"Assumed the main session\(open_rw_session\)", output, flags=re.I): + self.log_warn("Terminal server is used by someone else '{}'".format(output)) + self.write_channel("\n") + elif re.search("WARNING: New user connected to this port", output, flags=re.I): + self.log_warn("Terminal server is used by someone else '{}'".format(output)) + return output + + def verify_prompt(self, prompt2): + regex_onie_resque = r"\s+Please press Enter to activate this console.\s*$" + prompt = prompt2.replace("\\", "") + if re.compile(r"(.*[#|\$]\s*$)").match(prompt): + return True + if re.compile(r".*\(config.*\)#\s*$").match(prompt): + return True + if re.compile(r"\S+\s+login:\s*$").match(prompt): + return True + if re.compile(r"^\s*ONIE:/ #\s*$").match(prompt): + return True + if re.compile(regex_onie_resque).match(prompt): + return True + if re.compile(r"[Pp]assword:\s*$").match(prompt): + return True + if re.compile(r"\(Broadcom FASTPATH Routing\)\s*>\s*$").match(prompt): + return True + if re.compile(r"\(dhcp-\d+-\d+-\d+-\d+\)\s*>\s*$").match(prompt): + return True + return False + + def find_prompt(self, delay_factor=1): + trace("============================== find_prompt ============================") + try: + self.clear_cached_read_data() + rv = super(FastpathBaseConnection, self).find_prompt(delay_factor) + rv = re.escape(rv) + self.clear_cached_read_data() + return rv + except Exception as exp: + raise exp + + def strip_ansi_escape_codes(self, string_buffer): + rv = super(FastpathBaseConnection, self).strip_ansi_escape_codes(string_buffer) + trace("================== strip_ansi_escape_codes ============================") + try: + callback, arg = getattr(self, "trace_callback", [None, None]) + if callback: + callback(arg, rv) + else: + trace(rv) + except: + pass + trace("=======================================================================") + return rv + + def trace_callback_set(self, callback, arg): + setattr(self, "trace_callback", [callback, arg]) + + def send_command(self, command_string, expect_string=None, delay_factor=1, + max_loops=500, auto_find_prompt=True, strip_prompt=True, + strip_command=True, normalize=True, use_textfsm=False, + use_genie=False): + self.clear_cached_read_data() + retval = super(FastpathBaseConnection, self).send_command(command_string, + expect_string, delay_factor, max_loops, auto_find_prompt, + strip_prompt, strip_command, normalize, use_textfsm) + self.clear_cached_read_data() + return retval + + def send_command_new(self, fcli, command_string, expect_string=None, delay_factor=1, + max_loops=500, auto_find_prompt=True, strip_prompt=True, + strip_command=True, normalize=True, use_textfsm=False): + + if delay_factor > 2 or fcli == 0: + if "\n" in command_string: + cmd_list = [] + for cmd in command_string.split("\n"): + cmd_list.append(self.normalize_cmd(cmd)) + command_string = "".join(cmd_list) + return self.send_command(command_string, + expect_string, delay_factor, max_loops, auto_find_prompt, + strip_prompt, strip_command, normalize, use_textfsm) + + self.clear_cached_read_data() + # Time to delay in each read loop + loop_delay = 0.2 + + # Default to making loop time be roughly equivalent to self.timeout (support old max_loops + # and delay_factor arguments for backwards compatibility). + delay_factor = self.select_delay_factor(delay_factor) + if delay_factor == 1 and max_loops == 500: + # Default arguments are being used; use self.timeout instead + max_loops = int(self.timeout / loop_delay) + + # Find the current router prompt + if expect_string is None: + if auto_find_prompt: + try: + prompt = self.find_prompt(delay_factor=delay_factor) + except ValueError: + prompt = self.base_prompt + else: + prompt = self.base_prompt + search_pattern = re.escape(prompt.strip()) + else: + search_pattern = expect_string + + if normalize: + command_string = self.normalize_cmd(command_string) + + self.clear_buffer() + + self.write_channel(command_string) + + # trying to use agressive loop delay + loop_delay = loop_delay/4 + max_loops = max_loops * 4 + # trying to use agressive loop delay + + loop_sleep = loop_delay * delay_factor + + # trying to use constant loop sleep + loop_sleep = loop_delay + max_loops = max_loops * delay_factor + # trying to use constant loop sleep + + i = 1 + output = "" + mark_seen = False + + # Keep reading data until search_pattern is found or until max_loops is reached. + dbg_msg = [] + dtrace("command_string:", command_string) + while i <= max_loops: + new_data = self.read_channel() + if new_data: + if self.ansi_escape_codes: + new_data = self.strip_ansi_escape_codes(new_data) + + output += new_data + + dtrace("output", mark_seen, output) + dbg_msg.append("{}:{}".format(mark_seen, output)) + + if not mark_seen: + if re.search(command_string, output): + parts = output.partition(command_string) + dtrace("Mark-1", parts) + mark_seen = True + output = "".join(parts[2:]) + dbg_msg.append("Mark-1:'{}'".format(output)) + out_lines = [] + else: + out_lines = output.split("\r\n") + for index,line in enumerate(out_lines): + line2 = line.strip() + dtrace("LINE", line, line2, search_pattern, re.search(search_pattern, line2)) + if not line2: + dtrace("BLANK LINE") + continue + if re.match(search_pattern, line2): + dtrace("PROMPT LINE") + continue + dtrace("NON PROMPT LINE", line2) + if not re.search(search_pattern, line2): + mark_seen = True + output = "\r\n".join(out_lines[index:]) + dtrace("Mark-2", index, output) + dbg_msg.append("Mark-2:'{}'".format(output)) + break + if not mark_seen: + time.sleep(loop_sleep) + i += 1 + continue + + # Check if we have already found our pattern + dtrace("CMP-1", expect_string, search_pattern, output) + if re.search(search_pattern, output): + dtrace("MATCH-1") + break + + output2 = re.sub(r"\r", repl="", string=output) + if re.search(search_pattern, output2): + dtrace("MATCH-2") + break + + time.sleep(loop_sleep) + i += 1 + else: # nobreak + msg = "Search pattern: '{}' never detected CMD: '{}'\nDBG: '{}'" + msg = msg.format(search_pattern, command_string, "\n".join(dbg_msg)) + raise IOError(msg) + + self.clear_cached_read_data() + + output = self.normalize_linefeeds(output) + + return output + +class FastpathSshConnection(FastpathBaseConnection): + pass + diff --git a/spytest/spytest/access/icos_connection.py b/spytest/spytest/access/icos_connection.py new file mode 100644 index 00000000000..8d1f5bedd2e --- /dev/null +++ b/spytest/spytest/access/icos_connection.py @@ -0,0 +1,367 @@ +from __future__ import unicode_literals + +import re +import time +import logging +import socket +from netmiko.cisco_base_connection import CiscoBaseConnection + +show_trace = 0 +if show_trace > 1: + logger = logging.getLogger('netmiko_connection') + logger.setLevel(logging.DEBUG) + +def trace(fmt, *args): + if show_trace <= 0: + return + msg = fmt % args + if show_trace > 1: + logger.info(msg) + else: + print(msg) + +def dtrace(*args): + if show_trace > 2: + print(args) + +class IcosBaseConnection(CiscoBaseConnection): + + def __init__(self, **kwargs): + if "altpassword" in kwargs: + self.altpassword = kwargs["altpassword"] + del kwargs["altpassword"] + super(IcosBaseConnection, self).__init__(**kwargs) + + def set_logger(self, logger): + self.logger = logger + + def log_warn(self, msg): + if not getattr(self, "logger", None): + print(msg) + else: + self.logger.warning(msg) + + def session_preparation(self): + trace("============================== session_preparation ============================") + self.ansi_escape_codes = True + if self.protocol == "ssh": + return self.ssh_session_preparation() + else: + return super(IcosBaseConnection, self).session_preparation() + + + def ssh_session_preparation(self): + """ + Added extended_login to the base function for handling the password change in ssh scenario. + """ + output = self._test_channel_read() + output = self.extended_login(output) + self.set_base_prompt() + self.disable_paging() + self.set_terminal_width() + + # Clear the read buffer + time.sleep(0.3 * self.global_delay_factor) + self.clear_buffer() + + def _enter_shell(self): + return '' + + def _return_cli(self): + return '' + + def disable_paging(self, command="terminal length 0", delay_factor=1): + return "" + + def set_base_prompt(self, pri_prompt_terminator='>', + alt_prompt_terminator='#', delay_factor=1): + trace("============================== set_base_prompt ============================") + return super(IcosBaseConnection, self).set_base_prompt( + pri_prompt_terminator=pri_prompt_terminator, + alt_prompt_terminator=alt_prompt_terminator, + delay_factor=delay_factor) + + def change_password(self, username, new_password, output=None): + if output is None: + output = self.send_command("sudo passwd {}".format(username), expect_string="Enter new UNIX password:", strip_command=False, strip_prompt=False) + if "Enter new UNIX password:" in output: + output += self.send_command(new_password, expect_string="Retype new UNIX password:", strip_command=False, strip_prompt=False) + if "Retype new UNIX password:" in output: + pri_prompt_terminator_new = r"(#|\$|passwd: password updated successfully)\s*$" + output += self.send_command(new_password, expect_string=pri_prompt_terminator_new, strip_command=False, strip_prompt=False) + return [True, output] + return [False, output] + + def extended_login(self, output): + if "(current) UNIX password:" in output: + output = self.send_command(self.password, expect_string="Enter new UNIX password:", strip_command=False, strip_prompt=False) + retval = self.change_password(self.username, self.altpassword, output) + output += retval[1] + if retval[0]: + if self.protocol == "ssh" and "passwd: password updated successfully" in retval[1]: + self.disconnect() + raise socket.error("Spytest: socket is closed abruptly") + else: + retval2 = self.change_password(self.username, self.password) + output += retval2[1] + return output + + def telnet_login(self, pri_prompt_terminator=r'#\s*$', alt_prompt_terminator=r'>\s*$', + username_pattern=r"(?:[Uu]ser:|sername|ogin|User Name)", + pwd_pattern=r"assword", + delay_factor=1, max_loops=20): + trace("============================== telnet_login ============================") + setattr(self, "in_sonic_login", 1) + pri_prompt_terminator_new = r"(#|>|\(current\) UNIX password:)\s*$" + output = super(IcosBaseConnection, self).telnet_login( + pri_prompt_terminator_new, alt_prompt_terminator, username_pattern, + pwd_pattern, delay_factor, max_loops) + output = self.extended_login(output) + setattr(self, "in_sonic_login", None) + return output + + def add_cached_read_data(self, output): + if not output: + return + try: + self.cached_read_data.append(output) + except: + self.cached_read_data = [output] + + def clear_cached_read_data(self): + self.cached_read_data = [] + + def get_cached_read_data(self): + retval = self.cached_read_data + self.cached_read_data = [] + return retval + + def sysrq_trace(self): + trace("============================== sysrq_trace ============================") + if self.remote_conn is None: + return None + if self.protocol != "telnet": + return None + import telnetlib + self.remote_conn.sock.sendall(telnetlib.IAC + telnetlib.BRK) + time.sleep(1) + self.remote_conn.sock.sendall(chr(108)) + output = self.send_command_timing("", delay_factor=1) + trace(output) + return output + + def read_channel(self): + trace("============================== read_channel ============================") + output = super(IcosBaseConnection, self).read_channel() + self.add_cached_read_data(output) + if not getattr(self, "in_sonic_login", None): + return output + if re.search("1 - Assume the main session", output, flags=re.I): + self.log_warn("Terminal server is used by someone else '{}'".format(output)) + self.write_channel("1") + elif re.search("1 - Initiate a regular session", output, flags=re.I): + self.log_warn("Terminal server is used by someone else '{}'".format(output)) + #self.write_channel("1") + self.write_channel("4") + elif re.search("Enter session PID or 'all'", output, flags=re.I): + self.log_warn("Terminal server is used by someone else '{}'".format(output)) + self.write_channel("all") + self.write_channel("\n") + elif re.search(r"Assumed the main session\(open_rw_session\)", output, flags=re.I): + self.log_warn("Terminal server is used by someone else '{}'".format(output)) + self.write_channel("\n") + elif re.search("WARNING: New user connected to this port", output, flags=re.I): + self.log_warn("Terminal server is used by someone else '{}'".format(output)) + return output + + def verify_prompt(self, prompt2): + regex_onie_resque = r"\s+Please press Enter to activate this console.\s*$" + prompt = prompt2.replace("\\", "") + if re.compile(r"(.*[#|\$]\s*$)").match(prompt): + return True + if re.compile(r".*\(config.*\)#\s*$").match(prompt): + return True + if re.compile(r"\S+\s+login:\s*$").match(prompt): + return True + if re.compile(r"^\s*ONIE:/ #\s*$").match(prompt): + return True + if re.compile(regex_onie_resque).match(prompt): + return True + if re.compile(r"[Pp]assword:\s*$").match(prompt): + return True + if re.compile(r"\(Broadcom FASTPATH Routing\)\s*>\s*$").match(prompt): + return True + return False + + def find_prompt(self, delay_factor=1): + trace("============================== find_prompt ============================") + try: + self.clear_cached_read_data() + rv = super(IcosBaseConnection, self).find_prompt(delay_factor) + rv = re.escape(rv) + self.clear_cached_read_data() + return rv + except Exception as exp: + raise exp + + def strip_ansi_escape_codes(self, string_buffer): + rv = super(IcosBaseConnection, self).strip_ansi_escape_codes(string_buffer) + trace("================== strip_ansi_escape_codes ============================") + try: + callback, arg = getattr(self, "trace_callback", [None, None]) + if callback: + callback(arg, rv) + else: + trace(rv) + except: + pass + trace("=======================================================================") + return rv + + def trace_callback_set(self, callback, arg): + setattr(self, "trace_callback", [callback, arg]) + + def send_command(self, command_string, expect_string=None, delay_factor=1, + max_loops=500, auto_find_prompt=True, strip_prompt=True, + strip_command=True, normalize=True, use_textfsm=False, + use_genie=False): + self.clear_cached_read_data() + retval = super(IcosBaseConnection, self).send_command(command_string, + expect_string, delay_factor, max_loops, auto_find_prompt, + strip_prompt, strip_command, normalize, use_textfsm) + self.clear_cached_read_data() + return retval + + def send_command_new(self, fcli, command_string, expect_string=None, delay_factor=1, + max_loops=500, auto_find_prompt=True, strip_prompt=True, + strip_command=True, normalize=True, use_textfsm=False): + + if delay_factor > 2 or fcli == 0: + if "\n" in command_string: + cmd_list = [] + for cmd in command_string.split("\n"): + cmd_list.append(self.normalize_cmd(cmd)) + command_string = "".join(cmd_list) + return self.send_command(command_string, + expect_string, delay_factor, max_loops, auto_find_prompt, + strip_prompt, strip_command, normalize, use_textfsm) + + self.clear_cached_read_data() + # Time to delay in each read loop + loop_delay = 0.2 + + # Default to making loop time be roughly equivalent to self.timeout (support old max_loops + # and delay_factor arguments for backwards compatibility). + delay_factor = self.select_delay_factor(delay_factor) + if delay_factor == 1 and max_loops == 500: + # Default arguments are being used; use self.timeout instead + max_loops = int(self.timeout / loop_delay) + + # Find the current router prompt + if expect_string is None: + if auto_find_prompt: + try: + prompt = self.find_prompt(delay_factor=delay_factor) + except ValueError: + prompt = self.base_prompt + else: + prompt = self.base_prompt + search_pattern = re.escape(prompt.strip()) + else: + search_pattern = expect_string + + if normalize: + command_string = self.normalize_cmd(command_string) + + self.clear_buffer() + + self.write_channel(command_string) + + # trying to use agressive loop delay + loop_delay = loop_delay/4 + max_loops = max_loops * 4 + # trying to use agressive loop delay + + loop_sleep = loop_delay * delay_factor + + # trying to use constant loop sleep + loop_sleep = loop_delay + max_loops = max_loops * delay_factor + # trying to use constant loop sleep + + i = 1 + output = "" + mark_seen = False + + # Keep reading data until search_pattern is found or until max_loops is reached. + dbg_msg = [] + dtrace("command_string:", command_string) + while i <= max_loops: + new_data = self.read_channel() + if new_data: + if self.ansi_escape_codes: + new_data = self.strip_ansi_escape_codes(new_data) + + output += new_data + + dtrace("output", mark_seen, output) + dbg_msg.append("{}:{}".format(mark_seen, output)) + + if not mark_seen: + if re.search(command_string, output): + parts = output.partition(command_string) + dtrace("Mark-1", parts) + mark_seen = True + output = "".join(parts[2:]) + dbg_msg.append("Mark-1:'{}'".format(output)) + out_lines = [] + else: + out_lines = output.split("\r\n") + for index,line in enumerate(out_lines): + line2 = line.strip() + dtrace("LINE", line, line2, search_pattern, re.search(search_pattern, line2)) + if not line2: + dtrace("BLANK LINE") + continue + if re.match(search_pattern, line2): + dtrace("PROMPT LINE") + continue + dtrace("NON PROMPT LINE", line2) + if not re.search(search_pattern, line2): + mark_seen = True + output = "\r\n".join(out_lines[index:]) + dtrace("Mark-2", index, output) + dbg_msg.append("Mark-2:'{}'".format(output)) + break + if not mark_seen: + time.sleep(loop_sleep) + i += 1 + continue + + # Check if we have already found our pattern + dtrace("CMP-1", expect_string, search_pattern, output) + if re.search(search_pattern, output): + dtrace("MATCH-1") + break + + output2 = re.sub(r"\r", repl="", string=output) + if re.search(search_pattern, output2): + dtrace("MATCH-2") + break + + time.sleep(loop_sleep) + i += 1 + else: # nobreak + msg = "Search pattern: '{}' never detected CMD: '{}'\nDBG: '{}'" + msg = msg.format(search_pattern, command_string, "\n".join(dbg_msg)) + raise IOError(msg) + + self.clear_cached_read_data() + + output = self.normalize_linefeeds(output) + + return output + +class IcosSshConnection(IcosBaseConnection): + pass + diff --git a/spytest/spytest/access/sonic_connection.py b/spytest/spytest/access/sonic_connection.py new file mode 100644 index 00000000000..94df5167767 --- /dev/null +++ b/spytest/spytest/access/sonic_connection.py @@ -0,0 +1,419 @@ +from __future__ import unicode_literals + +import re +import time +import socket +import logging +import telnetlib +from netmiko.cisco_base_connection import CiscoBaseConnection + +show_trace = 0 +if show_trace > 1: + logger = logging.getLogger('netmiko_connection') + logger.setLevel(logging.DEBUG) + +def trace(fmt, *args): + if show_trace <= 0: + return + msg = fmt % args + if show_trace > 1: + logger.info(msg) + else: + print(msg) + +def dtrace(*args): + if show_trace > 2: + print(args) + +class SonicBaseConnection(CiscoBaseConnection): + + def __init__(self, **kwargs): + self.net_login = kwargs.pop("net_login", None) + self.net_devname = kwargs.pop("net_devname", None) + if "altpassword" in kwargs: + self.altpassword = kwargs["altpassword"] + del kwargs["altpassword"] + super(SonicBaseConnection, self).__init__(**kwargs) + + def set_logger(self, logger): + self.logger = logger + + def log_warn(self, msg): + if not getattr(self, "logger", None): + print(msg) + else: + self.logger.warning(msg) + + def session_preparation(self): + trace("============================== session_preparation ============================") + self.ansi_escape_codes = True + if self.protocol == "ssh": + return self.ssh_session_preparation() + else: + return super(SonicBaseConnection, self).session_preparation() + + def ssh_session_preparation(self): + """ + Added extended_login to the base function for handling the password change in ssh scenario. + """ + output = self._test_channel_read() + output = self.extended_login(output) + self.set_base_prompt() + self.disable_paging() + self.set_terminal_width() + + # Clear the read buffer + time.sleep(0.3 * self.global_delay_factor) + self.clear_buffer() + + def _enter_shell(self): + return '' + + def _return_cli(self): + return '' + + def disable_paging(self, command="terminal length 0", delay_factor=1): + return "" + + def set_base_prompt(self, pri_prompt_terminator='$', + alt_prompt_terminator='#', delay_factor=1): + trace("============================== set_base_prompt ============================") + return super(SonicBaseConnection, self).set_base_prompt( + pri_prompt_terminator=pri_prompt_terminator, + alt_prompt_terminator=alt_prompt_terminator, + delay_factor=delay_factor) + + def change_password(self, username, new_password, output=None): + retype_expect = r"(#|\$)\s*$" + if output is None: + retype_expect = r"passwd: password updated successfully\s*" + output = self.send_command("sudo passwd {}".format(username), + expect_string="Enter new UNIX password:", + strip_command=False, strip_prompt=False) + trace("========= change_password_1: {} =========".format(output)) + if "Enter new UNIX password:" in output: + output += self.send_command(new_password, expect_string="Retype new UNIX password:", + strip_command=False, strip_prompt=False) + trace("========= change_password_2: {} =========".format(output)) + if "Retype new UNIX password:" in output: + output += self.send_command(new_password, expect_string=retype_expect, + strip_command=False, strip_prompt=False) + trace("========= change_password_3: {} =========".format(output)) + return [True, output] + return [False, output] + + def extended_login(self, output): + trace("========= extended_login_1: {} =========".format(output)) + if self.device_type == "sonic_sshcon": + if self.net_login and self.net_devname: + self.net_login(self.net_devname, self) + if "(current) UNIX password:" in output: + output = self.send_command(self.password, expect_string="Enter new UNIX password:", + strip_command=False, strip_prompt=False) + retval = self.change_password(self.username, self.altpassword, output) + output += retval[1] + if retval[0]: + if self.protocol == "ssh" and "passwd: password updated successfully" in retval[1]: + self.disconnect() + raise socket.error("Spytest: socket is closed abruptly") + else: + retval2 = self.change_password(self.username, self.password) + output += retval2[1] + trace("========= extended_login_2: {} =========".format(output)) + return output + + def telnet_login(self, pri_prompt_terminator=r'#\s*$', alt_prompt_terminator=r'\$\s*$', + username_pattern=r"(?:[Uu]ser:|sername|ogin|User Name)", + pwd_pattern=r"assword", + delay_factor=1, max_loops=20): + trace("============================== telnet_login ============================") + setattr(self, "in_sonic_login", 1) + pri_prompt_terminator_new = r"(#|\$|\(current\) UNIX password:)\s*$" + output = super(SonicBaseConnection, self).telnet_login( + pri_prompt_terminator_new, alt_prompt_terminator, username_pattern, + pwd_pattern, delay_factor, max_loops) + output = self.extended_login(output) + setattr(self, "in_sonic_login", None) + trace("========= telnet_login: {} =========".format(output)) + return output + + def add_cached_read_data(self, output): + if not output: + return + try: + self.cached_read_data.append(output) + except: + self.cached_read_data = [output] + + def clear_cached_read_data(self): + self.cached_read_data = [] + + def get_cached_read_data(self): + retval = self.cached_read_data + self.cached_read_data = [] + return retval + + def sysrq_trace(self): + trace("============================== sysrq_trace ============================") + if self.remote_conn is None: + return None + if self.protocol != "telnet": + return None + self.remote_conn.sock.sendall(telnetlib.IAC + telnetlib.BRK) + time.sleep(1) + self.remote_conn.sock.sendall(chr(108)) + output = self.send_command_timing("", delay_factor=1) + trace(output) + return output + + def read_channel(self): + trace("============================== read_channel ============================") + output = super(SonicBaseConnection, self).read_channel() + self.add_cached_read_data(output) + if not getattr(self, "in_sonic_login", None): + return output + if re.search("1 - Assume the main session", output, flags=re.I): + self.log_warn("Terminal server is used by someone else '{}'".format(output)) + self.write_channel("1") + elif re.search("1 - Initiate a regular session", output, flags=re.I): + self.log_warn("Terminal server is used by someone else '{}'".format(output)) + self.write_channel("4") + elif re.search("Enter session PID or 'all'", output, flags=re.I): + self.log_warn("Terminal server is used by someone else '{}'".format(output)) + self.write_channel("all") + self.write_channel("\n") + elif re.search(r"Assumed the main session\(open_rw_session\)", output, flags=re.I): + self.log_warn("Terminal server is used by someone else '{}'".format(output)) + self.write_channel("\n") + elif re.search("WARNING: New user connected to this port", output, flags=re.I): + self.log_warn("Terminal server is used by someone else '{}'".format(output)) + return output + + def verify_prompt(self, prompt2): + regex_onie_resque = r"\s+Please press Enter to activate this console.\s*$" + prompt = prompt2.replace("\\", "") + if re.compile(r"(.*[#|\$]\s*$)").match(prompt): + return True + if re.compile(r".*\(config.*\)#\s*$").match(prompt): + return True + if re.compile(r"\S+\s+login:\s*$").match(prompt): + return True + if re.compile(r"^\s*ONIE:/ #\s*$").match(prompt): + return True + if re.compile(regex_onie_resque).match(prompt): + return True + if re.compile(r"[Pp]assword:\s*$").match(prompt): + return True + return False + + def find_prompt(self, delay_factor=1): + trace("============================== find_prompt ============================") + try: + self.clear_cached_read_data() + rv = super(SonicBaseConnection, self).find_prompt(delay_factor) + rv = re.escape(rv) + self.clear_cached_read_data() + return rv + except Exception as exp: + raise exp + + def strip_ansi_escape_codes(self, string_buffer): + rv = super(SonicBaseConnection, self).strip_ansi_escape_codes(string_buffer) + trace("================== strip_ansi_escape_codes ============================") + try: + callback, arg = getattr(self, "trace_callback", [None, None]) + if callback: + callback(arg, rv) + else: + trace(rv) + except: + pass + trace("=======================================================================") + return rv + + def trace_callback_set(self, callback, arg): + setattr(self, "trace_callback", [callback, arg]) + + def is_strip_prompt(self, strip_prompt): + if getattr(self, "in_sonic_login", None): + return False + return strip_prompt + + def send_command(self, command_string, expect_string=None, delay_factor=1, + max_loops=500, auto_find_prompt=True, strip_prompt=True, + strip_command=True, normalize=True, use_textfsm=False, + use_genie=False): + strip_prompt = self.is_strip_prompt(strip_prompt) + self.clear_cached_read_data() + retval = super(SonicBaseConnection, self).send_command(command_string, + expect_string, delay_factor, max_loops, auto_find_prompt, + strip_prompt, strip_command, normalize, use_textfsm) + self.clear_cached_read_data() + return retval + + def send_command_new(self, fcli, command_string, expect_string, delay_factor, + max_loops=500, auto_find_prompt=True, strip_prompt=True, + strip_command=True, normalize=True, use_textfsm=False): + + if delay_factor > 2 or fcli == 0 or expect_string is None: + if "\n" in command_string: + cmd_list = [] + for cmd in command_string.split("\n"): + cmd_list.append(self.normalize_cmd(cmd)) + command_string = "\n".join(cmd_list) + return self.send_command(command_string, + expect_string, delay_factor, max_loops, auto_find_prompt, + strip_prompt, strip_command, normalize, use_textfsm) + + search_pattern = expect_string + self.clear_cached_read_data() + + # Time to delay in each read loop + loop_delay = 0.2 + + # Default to making loop time be roughly equivalent to self.timeout (support old max_loops + # and delay_factor arguments for backwards compatibility). + delay_factor = self.select_delay_factor(delay_factor) + if delay_factor == 1 and max_loops == 500: + # Default arguments are being used; use self.timeout instead + max_loops = int(self.timeout / loop_delay) + + self.clear_buffer() + + # remove multiple spaces + command_string = self.remove_spaces(command_string) + + command_string = self.normalize_cmd(command_string) + self.write_channel(command_string) + + # trying to use agressive loop delay + loop_delay = loop_delay/4 + max_loops = max_loops * 4 + # trying to use agressive loop delay + + loop_sleep = loop_delay * delay_factor + + # trying to use constant loop sleep + loop_sleep = loop_delay + max_loops = max_loops * delay_factor + # trying to use constant loop sleep + + i = 1 + output = "" + cmd_issued = False + + # Keep reading data until search_pattern is found or until max_loops is reached. + dbg_msg = [] + command_string = self.normalize_linefeeds(command_string) + self.dmsg_append(dbg_msg, "command_string:", self.dmsg_fmt(command_string)) + self.dmsg_append(dbg_msg, "search_pattern:", self.dmsg_fmt(search_pattern)) + #self.dmsg_append(dbg_msg, "command_string_hex:", self.dmsg_hex(command_string)) + #self.dmsg_append(dbg_msg, "search_pattern_hex:", self.dmsg_hex(search_pattern)) + while i <= max_loops: + new_data = self.read_channel() + if new_data: + new_data = self.normalize_linefeeds(new_data) + new_data = self.strip_ansi_escape_codes(new_data) + output += new_data + output = self.strip_ansi_escape_codes(output) + output = self.normalize_linefeeds(output) + + self.dmsg_append(dbg_msg, "cmd_issued:", cmd_issued) + if not cmd_issued and "\n" in output: + self.dmsg_append(dbg_msg, "CMP-OUT:", self.dmsg_fmt(output)) + self.dmsg_append(dbg_msg, "CMP-CMD:", self.dmsg_fmt(command_string)) + #self.dmsg_append(dbg_msg, "CMP-OUT:", self.dmsg_hex(output)) + #self.dmsg_append(dbg_msg, "CMP-CMD:", self.dmsg_hex(command_string)) + if command_string in output: + parts = output.partition(command_string) + cmd_issued = True + output = "".join(parts[2:]) + self.dmsg_append(dbg_msg, "ECHOED ", "output:", self.dmsg_fmt(output)) + out_lines = [] + else: + out_lines = output.split("\r\n") + for index,line in enumerate(out_lines): + line2 = line.strip() + if not line2: + self.dmsg_append(dbg_msg, "BLANK LINE") + continue + if re.match(search_pattern, line2): + self.dmsg_append(dbg_msg, "MATCH PROMPT LINE: ", line2) + continue + if not re.search(search_pattern, line2): + cmd_issued = True + output = "\r\n".join(out_lines[index:]) + self.dmsg_append(dbg_msg, "NON-PROMPT ", "INDEX:", index, + "output:", self.dmsg_fmt(output)) + break + self.dmsg_append(dbg_msg, "HAS-PROMPT:", line2) + + if cmd_issued: + # Check if we have already found our pattern + self.dmsg_append(dbg_msg, "CMP-PROMPT:", self.dmsg_fmt(search_pattern)) + self.dmsg_append(dbg_msg, "CMP-OUTPUT:", self.dmsg_fmt(output)) + if re.search(search_pattern, output): + self.dmsg_append(dbg_msg, "MATCH-1") + break + + output2 = re.sub(r"\r", repl="", string=output) + if re.search(search_pattern, output2): + self.dmsg_append(dbg_msg, "MATCH-2") + break + + time.sleep(loop_sleep) + i += 1 + else: # nobreak + msg = "Search pattern: '{}' never detected".format(search_pattern) + raise IOError(self.dmsg_str(dbg_msg, msg)) + + self.clear_cached_read_data() + + output = self.normalize_linefeeds(output) + + #print(self.dmsg_str(dbg_msg)) + + return output + + def dmsg_fmt(self, data): + msg = "'{}'".format(data) + msg = msg.replace("\r", "") + msg = msg.replace("\n", "") + return msg + + def dmsg_hex(self, data): + msg = ":".join("{:02x}".format(ord(c)) for c in data) + return "'{}'".format(msg) + + def dmsg_str(self, dbg_msg, s = ""): + s = s + "\n=======================FASTER-CLI-DBG: ===================" + s = s + "\n" + "\n".join(dbg_msg) + s = s + "\n==========================================================" + return s + + def dmsg_append(self, dbg_msg, *args): + try: + msg = " ".join(map(str,args)) + except UnicodeEncodeError as exp: + msg = " ".join(map(unicode,args)) + dtrace(msg) + dbg_msg.append(msg) + + def remove_spaces(self, cmd): + chlist = [ch for ch in cmd] + retlist, inq, prev = [], False, None + for ch in chlist: + if ch in ["'", "\""]: + inq = bool(not inq) + if prev and not inq: + if ch in [" ", "\t"]: + if prev == ch: + continue + prev = ch + retlist.append(ch) + return "".join(retlist) + + +class SonicSshConnection(SonicBaseConnection): + pass + diff --git a/spytest/spytest/ansible.py b/spytest/spytest/ansible.py new file mode 100644 index 00000000000..dafa557b499 --- /dev/null +++ b/spytest/spytest/ansible.py @@ -0,0 +1,65 @@ +import os +import sys +import subprocess +import argparse +import tempfile + +import utilities.common as utils + +def _fp_write(fp, data): + fp.write(data.encode()) + +def ansible_playbook(playbook, host_list, username, password, logs_path=None): + + ansible_cfg = os.path.join(os.path.dirname(__file__), '..', "ansible", "ansible.cfg") + ansible_cfg = os.path.abspath(ansible_cfg) + ansible_dir = os.path.dirname(sys.executable) + ansible_exe = os.path.join(ansible_dir, "ansible-playbook") + + if logs_path: os.environ["ANSIBLE_LOCAL_TEMP"] = logs_path + os.environ["ANSIBLE_CONFIG"] = ansible_cfg + # added the SSH_ARGS as environment variable to supress host checking as the nodes + # in the case would be dut's with dynamic inventory. + os.environ["ANSIBLE_SSH_ARGS"] = "-o ControlMaster=auto -o ControlPersist=60s -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" + fp = tempfile.NamedTemporaryFile(delete=False) + _fp_write(fp, "[hosts]\n") + for host in host_list: + _fp_write(fp, "{}\n".format(host)) + _fp_write(fp, "[hosts:vars]\n") + _fp_write(fp, "ansible_user={}\n".format(username)) + _fp_write(fp, "ansible_password={}\n".format(password)) + fp.close() + configs="\n".join(utils.read_lines(fp.name)) + + cmd = "{} -i {} {}".format(ansible_exe, fp.name, playbook) + #print("Executing", cmd) + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + out, err = proc.communicate() + proc.wait() + os.unlink(fp.name) + if proc.returncode != 0: + msg = ["Error: Failed to execute ansible playbook '{}'".format(playbook)] + msg.append("errcode: {} error: ('{}')".format(proc.returncode, err.strip())) + msg.append("output: {}".format(out)) + msg.append("config: {}".format(configs)) + return "\n".join(msg) + for line in out.splitlines(): + print(line) + return out + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='SpyTest ansible helper.') + + parser.add_argument("--playbook", action="store", default=None, + required=True, help="execute given ansible playbook yaml.") + parser.add_argument("--hosts", action="store", default=None, nargs="+", + required=True, help="ansible hosts.") + parser.add_argument("--username", action="store", default=None, + required=True, help="ansible username for all hosts.") + parser.add_argument("--password", action="store", default=None, + required=True, help="ansible password for all hosts.") + + args, unknown = parser.parse_known_args() + + ansible_playbook(args.playbook, args.hosts, args.username, args.password) + diff --git a/spytest/spytest/batch.py b/spytest/spytest/batch.py new file mode 100644 index 00000000000..65f2d02c0a4 --- /dev/null +++ b/spytest/spytest/batch.py @@ -0,0 +1,765 @@ +import os +import sys +import csv +import shutil +from operator import itemgetter +from spytest.dicts import SpyTestDict +from spytest.testbed import Testbed +import spytest.spydist as dist +import utilities.common as utils + +wa = SpyTestDict() +wa.parse_logs = [] +wa.buckets = [] +wa.print_func = None +wa.custom_scheduling = False +wa.debug_level = 0 +wa.logs_path = "" +wa.executed = SpyTestDict() +wa.trace_file = None +wa.logger = None + +def ftrace(msg): + if not wa.trace_file: + wa.trace_file = os.path.join(wa.logs_path, "batch_debug.log") + utils.write_file(wa.trace_file, "") + utils.write_file(wa.trace_file, "{}\n".format(msg), "a") + +def debug(*args, **kwargs): + if wa.debug_level > 0: + trace(*args, **kwargs) + else: + ftrace(" ".join(map(str,args))) + +def trace(*args, **kwargs): + msg = " ".join(map(str,args)) + if wa.logger: + wa.logger.info(msg) + if wa.print_func is None: + wa.parse_logs.append(msg) + return + if wa.parse_logs: + wa.print_func("\n") + for log in wa.parse_logs: + wa.print_func("{}\n".format(log)) + wa.parse_logs = [] + wa.print_func("\n{}\n".format(msg)) + ftrace(msg) + +def save_report(): + + # show running + (header, rows) = (['#', "Module", "Function", "TestCase", "Node", "Status"], []) + for index, nodeid in enumerate(wa.executed): + [node_name, status] = wa.executed[nodeid] + if not node_name: continue + parts = nodeid.split("::", 1) + (module, func) = (parts[0], parts[1]) + if "tclist" not in wa.tcmap: + rows.append([len(rows)+1, module, func, func, node_name, status]) + elif func not in wa.tcmap.tclist: + rows.append([len(rows)+1, module, func, func, node_name, status]) + else: + for tcid in wa.tcmap.tclist[func]: + rows.append([len(rows)+1, module, func, tcid, node_name, status]) + + filepath = os.path.join(wa.logs_path, "batch_progress.csv") + utils.write_csv_file(header, rows, filepath) + filepath = os.path.splitext(filepath)[0]+'.html' + utils.write_html_table(header, rows, filepath) + + # show pending + (header, rows) = (['#', "Module", "Function", "TestCase", "Status"], []) + for index, nodeid in enumerate(wa.executed): + [node_name, status] = wa.executed[nodeid] + if node_name: continue + parts = nodeid.split("::", 1) + (module, func) = (parts[0], parts[1]) + if "tclist" not in wa.tcmap: + rows.append([len(rows)+1, module, func, func, status]) + elif func not in wa.tcmap.tclist: + rows.append([len(rows)+1, module, func, func, status]) + else: + for tcid in wa.tcmap.tclist[func]: + rows.append([len(rows)+1, module, func, tcid, status]) + + filepath = os.path.join(wa.logs_path, "batch_pending.csv") + utils.write_csv_file(header, rows, filepath) + filepath = os.path.splitext(filepath)[0]+'.html' + utils.write_html_table(header, rows, filepath) + +def report(op, nodeid, node_name): + if op == "load": + wa.executed[nodeid] = ["", "Pending"] + return + elif op == "add": + wa.executed[nodeid] = [node_name, "Queued"] + return + elif op == "remove": + if nodeid in wa.executed: + del wa.executed[nodeid] + return + elif op == "finish": + if nodeid in wa.executed: + wa.executed[nodeid] = [node_name, "Completed"] + try: + save_report() + except Exception as exp: + print(exp) + +def shutdown(): + pass + +class SpyTestScheduling(object): + def __init__(self, config, wa, log=None): + self.config = config + self.count = len(config.getvalue("tx")) + self.node_modules = {} + self.collection_is_completed = False + self.all_modules = SpyTestDict() + self.buckets = {} + self.order = {} + self.order_support = True + self.topo = {} + self.topo_support = True + self.base_names = {} + self.wa = wa + self.default_bucket = 4 + self.default_order = 2 + self.default_topo = "" + self.max_order = self.default_order + self._load_buckets() + + def _load_buckets(self): + root = os.path.join(os.path.dirname(__file__), '..', "reporting") + root = os.path.abspath(root) + module_csv = os.getenv("SPYTEST_MODULE_CSV_FILENAME", "modules.csv") + csv_file = os.path.join(root, module_csv) + rows = [] + if os.path.exists(csv_file): + with open(csv_file, 'r') as fd: + for row in csv.reader(fd): + rows.append(row) + fd.close() + for row in rows: + if len(row) < 3: continue + topo = self.default_topo + if len(row) > 3: topo = ",".join([str(i).strip() for i in row[3:]]) + bucket, order, name = [str(i).strip() for i in row[:3]] + if bucket.startswith("#"): continue + self.buckets[name] = utils.integer_parse(bucket, self.default_bucket) + self.order[name] = utils.integer_parse(order, self.default_order) + self.topo[name] = topo + if self.max_order < self.order[name]: + self.max_order = self.order[name] + basename = os.path.basename(name) + if basename in self.base_names: + trace("duplicate basename {}".format(basename)) + else: + self.base_names[basename] = name + + def add_node(self, node): + self.node_modules[node] = [] + + def remove_node(self, node): + self.node_modules.pop(node) + + @property + def tests_finished(self): + if not self.collection_is_completed: + return False + for node, modules in self.node_modules.items(): + name = node.gateway.id + if not self.wa.slaves[name].completed: return False + if not self.wa.slaves[name].started: return False + if len(modules) >= 2: return False + debug("================ tests_finished ===========") + return True + + @property + def nodes(self): + return list(self.node_modules.keys()) + + @property + def has_pending(self): + debug("================ has_pending ===========") + for node, modules in self.node_modules.items(): + name = node.gateway.id + if not self.wa.slaves[name].completed: return True + if not self.wa.slaves[name].started: return True + if modules: + return True + return False + + def add_nodeid(self, nodeid, init): + report("load", nodeid, "") + mname = nodeid.split("::", 1)[0] + if mname not in self.buckets: + mname = os.path.basename(mname) + if mname in self.base_names: + mname = self.base_names[mname] + if mname not in self.all_modules: + self.all_modules[mname] = SpyTestDict() + module = self.all_modules[mname] + module.node_indexes = [] + module.nodes = [] + if mname in self.topo: + module.topo = self.topo[mname] + else: + module.topo = self.default_topo + if mname in self.order: + module.order = self.order[mname] + else: + module.order = self.default_order + if mname in self.buckets: + module.bucket = self.buckets[mname] + else: + module.bucket = self.default_bucket + if init: + trace("Module {} is not found in modules.csv".format(mname)) + self.all_modules[mname].node_indexes.append(self.collection.index(nodeid)) + + def add_node_collection(self, node, collection): + self.count = self.count - 1 + if self.count > 0: + return + + # last node has registered the collection + # generate module list + self.collection_is_completed = True + self.collection = collection + for nodeid in collection: + self.add_nodeid(nodeid, True) + report("save", "", "") + + # identify the matching testbeds for custom topo + for mname, minfo in self.all_modules.items(): + minfo.nodes = [] + for slave in wa.slaves.values(): + if minfo.bucket <= slave.bucket and \ + minfo.bucket >= slave.min_bucket: + if minfo.topo: + [errs, p] = slave.tb_obj.ensure_min_topology(minfo.topo) + slave.tb_obj.reset_derived_devices() + if errs: continue + minfo.nodes.append(slave.name) + if not minfo.nodes: + # check if we can find match in higher buckets + msg = "NO suitable testbed in bucket {} to execute {}" + trace(msg.format(minfo.bucket, mname)) + for bucket in range(minfo.bucket+1, 100): + if minfo.nodes: break + for slave in wa.slaves.values(): + if slave.bucket != bucket: continue + [errs, p] = slave.tb_obj.ensure_min_topology(minfo.topo) + slave.tb_obj.reset_derived_devices() + if not errs: + msg = "Using testbed {} to execute {}" + trace(msg.format(slave.name, mname)) + minfo.nodes.append(slave.name) + break + if not minfo.nodes: + # execute it gw0 as last option + msg = "Using testbed {} to execute {}" + trace(msg.format("gw0", mname)) + minfo.nodes.append("gw0") + self._show_module_info() + _show_testbed_info() + + def _show_module_info(self, show=True): + header = ["Module", "Bucket", "Tests", "Topology", "Nodes"] + (mcount, tcount, rows) = (0, 0, []) + for mname, minfo in self.all_modules.items(): + count = len(minfo.node_indexes) + mcount = mcount + 1 + tcount = tcount + count + nodes = ",".join(minfo.nodes) + rows.append([mname, minfo.bucket, count, minfo.topo, nodes]) + rows = sorted(rows, key=itemgetter(1), reverse=True) + retval = utils.sprint_vtable(header, rows) + if show: + trace("Modules: {} Functions: {}".format(mcount, tcount)) + trace(retval) + return retval + + def mark_test_complete(self, node, item_index, duration=0): + debug("Remove", item_index, "From", node, self.node_modules[node]) + if item_index in self.node_modules[node]: + self.node_modules[node].remove(item_index) + report("finish", self.collection[item_index], node.gateway.id) + debug("============== completed", item_index, self.collection[item_index]) + else: + trace("============== already completed", item_index, self.collection[item_index]) + self._schedule_node(node) + debug("NewList", node, self.node_modules[node]) + + def _assign_test(self, node, name): + slave = self.wa.slaves[name] + for order in range(0, self.max_order + 1): + for mname,minfo in self.all_modules.items(): + if name not in minfo.nodes: continue + if minfo.order != order and self.order_support: + continue + del self.all_modules[mname] + self.node_modules[node].extend(minfo.node_indexes) + slave.executed = slave.executed + len(minfo.node_indexes) + debug("ASSIGNED", name, minfo.order, mname, minfo.node_indexes) + for item_index in minfo.node_indexes: + report("add", self.collection[item_index], node.gateway.id) + report("save", "", "") + return True + return False + + def _schedule_node(self, node): + name = node.gateway.id + debug("================ _schedule_node =========== {}".format(node)) + if self.wa.slaves[name].completed: return + if not self.wa.slaves[name].started: return + if node.shutting_down: return + prev_count = len(self.node_modules[node]) + if prev_count >= 2: return + debug("================ load =========== {} {}".format(node, prev_count)) + for i in range(0,2): + if len(self.node_modules[node]) < 2: + self._assign_test(node, name) + indexes = self.node_modules[node][prev_count:] + if indexes: node.send_runtest_some(indexes) + if len(self.node_modules[node]) >= 2: + return + debug("================ shutdown =========== {} {}".format(node, indexes)) + node.shutdown() + + def unfinished(self, node): + return len(self.node_modules[node]) + + def schedule(self, finished=None, error=None): + debug("start", utils.get_proc_name()) + for node in self.node_modules: + if node.gateway.id != finished: + self._schedule_node(node) + continue + unfinished_tests = self.unfinished(node) + if unfinished_tests <= 0: + continue + slave = wa.slaves[node.gateway.id] + lines = [] + for item_index in self.node_modules[node]: + report("remove", self.collection[item_index], node.gateway.id) + lines.append(self.collection[item_index]) + report("save", "", "") + if slave.executed <= unfinished_tests: + for nodeid in lines: + self.add_nodeid(nodeid, False) + msg = "{} finished without executing any tests." + #msg = msg + " Adding back to pool" + lines.insert(0, msg.format(node.gateway.id)) + trace("\n - ".join(lines)) + slave.executed = 0 + else: + msg = "unfinished test cases" + lines.insert(0, msg) + trace("\n - ".join(lines)) + + +def _show_testbed_topo(show=True): + header = ["Node", "Topology"] + rows = [] + for slave in wa.slaves.values(): + topo = slave.tb_obj.get_topo() + rows.append([slave.name, topo]) + retval = utils.sprint_vtable(header, rows) + if show: trace(retval) + return retval + +def _read_pid(wa): + file_prefix = os.getenv("SPYTEST_FILE_PREFIX", "results") + for slave in wa.slaves.values(): + filepath = os.path.join(wa.logs_path, slave.name, "{}_pid.txt".format(file_prefix)) + try: slave.pid = utils.read_lines(filepath)[0] + except: pass + +def _show_testbed_info(show=True): + header = ["Node", "Buckets", "Node Testbed", + "Status", "Devices", "Parent Testbed", "PID"] + rows = [] + _read_pid(wa) + for slave in wa.slaves.values(): + fname = os.path.basename(slave.testbed) + if slave.completed is None: status = "Dead" + elif slave.completed: status = "Completed" + elif slave.started: status = "Running" + else: status = "Waiting" + devices = ",".join(slave.tb_obj.get_device_names("DUT")) + parent_testbed = slave.parent_testbed + if parent_testbed: parent_testbed = os.path.basename(parent_testbed) + min_bucket = 1 if slave.min_bucket == 0 else slave.min_bucket + if min_bucket == slave.bucket: + bucket_range = "{}".format(slave.bucket) + else: + bucket_range = "{}-{}".format(min_bucket, slave.bucket) + rows.append([slave.name, bucket_range, fname, + status, devices, parent_testbed, slave.pid]) + retval = utils.sprint_vtable(header, rows) + if show: trace(retval) + return retval + +def init_stdout(config, logs_path): + + file_prefix = os.getenv("SPYTEST_FILE_PREFIX", "results") + + if is_slave(): + # create the stdout for the slaves + filepath = "{}_stdout.log".format(file_prefix) + filepath = os.path.join(logs_path, filepath) + sys.stdout = open(filepath, 'w') + sys.stderr = sys.stdout + + if is_master(): + # create the console file for the master + tr = config.pluginmanager.getplugin('terminalreporter') + if tr is not None: + folder = os.path.join(logs_path, "master") + if not os.path.exists(folder): + os.makedirs(folder) + filepath = os.path.join(folder, "{}_stdout.log".format(file_prefix)) + config._pytestsessionfile = open(filepath, 'w') + oldwrite = tr._tw.write + def tee_write(s, **kwargs): + oldwrite(s, **kwargs) + config._pytestsessionfile.write(str(s)) + tr._tw.write = tee_write + wa.print_func = tee_write + +def configure(config, logs_path): + debug("============== batch configure =====================") + wa.tcmap = dict() + wa.logs_path = logs_path + init_stdout(config, logs_path) + dist.configure(config, logs_path, is_slave()) + return is_master() + +def set_logger(logger): + wa.logger = logger + +def set_tcmap(tcmap): + wa.tcmap = tcmap + +def unconfigure(config): + debug("============== batch unconfigure =====================") + return is_master() + +def finish(): + return is_master() + +def make_scheduler(config, log): + debug("============== batch make_scheduler =====================") + if wa.custom_scheduling: + wa.sched = SpyTestScheduling(config, wa, log) + else: + from xdist.scheduler import LoadFileScheduling + wa.sched = LoadFileScheduling(config, log) + return wa.sched + +def configure_nodes(config): + debug("============== batch configure_nodes =====================") + +def configure_node(node): + debug("============== batch configure_node =====================") + if not wa.custom_scheduling: + return + if node.gateway.id in wa.slaves: + slave = wa.slaves[node.gateway.id] + slave.completed = False + debug("configure_node {} testbed: {}".format(node, slave.testbed)) + else: + trace("configure_node--unknown {}".format(node)) + +def begin_node(gateway): + debug("============== batch begin_node =====================") + if not wa.custom_scheduling: + return + if gateway.id in wa.slaves: + debug("begin_node {}".format(gateway)) + slave = wa.slaves[gateway.id] + slave.completed = False + else: + trace("begin_node--unknown {}".format(gateway)) + +def finish_node(node, error): + msg = "============== batch finish {} =====================" + trace(msg.format(node.gateway.id)) + if not wa.custom_scheduling: + return + if node.gateway.id not in wa.slaves: + trace("finish_node--unknown {}".format(node)) + return + debug("finish_node {}".format(node)) + slave = wa.slaves[node.gateway.id] + slave.completed = None if error else True + # add the devices in the current testbed into free devices list + devices = slave.tb_obj.get_device_names("DUT") + + # add the device info loaded devices if they have executed tests + unfinished_tests = wa.sched.unfinished(node) + debug("Node {} executed {} unfinished {}".format(node.gateway.id, slave.executed, unfinished_tests)) + if slave.executed > unfinished_tests: + wa.loaded_devices.extend(devices) + + if slave.parent_testbed: + wa.free_devices[slave.parent_testbed].extend(devices) + else: + wa.free_devices[slave.testbed].extend(devices) + for name,slave in wa.slaves.items(): + if slave.started: continue + ptestbed = slave.parent_testbed + pdevices = wa.free_devices[ptestbed] + devices = slave.tb_obj.get_device_names("DUT") + # search if all devices in the current slave are in free pool + slave.started = all(dut in pdevices for dut in devices) + loaded = all(dut in wa.loaded_devices for dut in devices) + if slave.started: + debug("Starting slave {} loaded {}".format(name, loaded)) + wa.free_devices[ptestbed] = utils.filter_list(pdevices, devices) + else: + debug("Waiting slave {} loaded {}".format(name, loaded)) + debug("wa.free_devices", slave.name, slave.started, ptestbed, wa.free_devices[ptestbed]) + + # indicate if we need to skip the image loading + if loaded: + utils.write_file(os.path.join(wa.logs_path, slave.name, "slave_used"), "") + else: + utils.delete_file(os.path.join(wa.logs_path, slave.name, "slave_used")) + + wa.sched.schedule(node.gateway.id, error) + _show_testbed_info() + + if error: + trace("=======================================") + trace("============ NODE DEAD {} =============".format(node.gateway.id)) + trace("=======================================") + +topologies = { + 1 : "D1T1:2", + 2 : "D1T1:4 D1D2:6 D2T1:2", + 3 : "D1 D2 D3", + 4 : "D1T1:2 D2T1:2 D3T1:2 D4T1:2 D1D2:4 D2D3:4 D3D4:4 D4D1:4", + 5 : "D1 D2 D3 D4 D5", + 6 : "D1D3:4 D1D4:4 D1D5:2 D1D6:4 D2D3:4 D2D4:4 D2D5:4 D2D6:4 D3T1:2 D4T1:2 D5T1:2 D6T1:2", + 7 : "D1 D2 D3 D4 D5 D6 D7" +} + +def _create_bucket_testbeds(tb_objs, buckets, logs_path): + + ret_list = [] + + testbed_files = [] + + topologies[1] = os.getenv("SPYTEST_TOPO_1", topologies[1]) + topologies[2] = os.getenv("SPYTEST_TOPO_2", topologies[2]) + topologies[3] = os.getenv("SPYTEST_TOPO_3", topologies[3]) + topologies[4] = os.getenv("SPYTEST_TOPO_4", topologies[4]) + topologies[5] = os.getenv("SPYTEST_TOPO_5", topologies[5]) + topologies[6] = os.getenv("SPYTEST_TOPO_6", topologies[6]) + topologies[7] = os.getenv("SPYTEST_TOPO_7", topologies[7]) + + def copy_file(src, index, bucket, inst): + dst = "testbed_{}_{}_{}.yaml".format(index, bucket, inst) + new_filename = os.path.join(logs_path, dst) + shutil.copy(src, new_filename) + testbed_files.append(new_filename) + return (dst, new_filename) + + # testbed fort default bucket + prev_bucket = 0 if not buckets else buckets[0] + 1 + for index, tb in enumerate(tb_objs): + file_path = tb.get_file_path() + (bucket, min_bucket, testbed) = (100, prev_bucket, file_path) + (fname, fpath) = copy_file(testbed, index+1, min_bucket, 0) + trace("Copying Testbed {} as {}".format(testbed, [fname])) + msg = "Use {} for Buckets(>={}) with devices {}" + trace(msg.format(fname, bucket, tb.get_device_names("DUT"))) + ret_list.append([bucket, min_bucket, None, [fpath]]) + + # create mini testbed files for each bucket + for i,bucket in enumerate(buckets): + try: min_bucket = buckets[i+1] + 1 + except: min_bucket = 1 + if bucket not in topologies: + msg = "bucket {} is not found in supported, using higher bucket {} testbed" + trace(msg.format(bucket, prev_bucket)) + continue + for index, tb in enumerate(tb_objs): + topo = topologies[bucket] + [slist, props, errs] = tb.identify_topology(None, tb, None, 100, topo) + if not slist or len(slist) <= 0: + msg = "Failed to create testbed for bucket {} topo {} using higher bucket {} testbed" + trace(msg.format(bucket, topo, prev_bucket)) + continue + tmp_tbs = [] + for j,duts_dict in enumerate(slist): + dut_list = Testbed.get_dut_list(duts_dict) + mini_testbed = tb.rebuild_topo_file(dut_list, props) + yaml_file = Testbed.write_file(mini_testbed, "batch_testbed_", ".yaml") + (fname, fpath) = copy_file(yaml_file, index+1, bucket, j+1) + msg = "Created {} for bucket({}-{}) with devices {}" + trace(msg.format(fname, min_bucket, bucket, duts_dict)) + tmp_tbs.append(fpath) + ret_list.append([bucket, min_bucket, testbed_files[index], tmp_tbs]) + prev_bucket = bucket + + return ret_list + +def parse_args(numprocesses, buckets, logs_path): + if os.getenv("SPYTEST_BATCH_DEBUG"): + wa.debug_level = 1 + if os.getenv("PYTEST_XDIST_WORKER"): + return [] + filename = os.getenv("SPYTEST_TESTBED_FILE", "testbed.yaml") + parts = filename.split(",") + count = len(parts) + if numprocesses and count < numprocesses and os.getenv("SPYTEST_FILE_MODE"): + for index in range(count, numprocesses): + parts.append(parts[0]) + count = numprocesses + [count, parts] = parse_buckets(count, parts, buckets, logs_path) + for i,part in enumerate(parts): + os.environ["SPYTEST_TESTBED_FILE_gw{}".format(i)] = part + os.environ["SPYTEST_TESTBED_FILE"] = parts[0] + if numprocesses or count > 1: + os.environ["SPYTEST_BATCH_RUN"] = "1" + return dist.parse_args(count, ["-n", str(count), "--max-worker-restart", str(0)]) + return [] + +def parse_buckets(count, testbeds, buckets_csv, logs_path): + wa.testbeds = testbeds + wa.count = count + + if buckets_csv: + # force to use custom scheduling + wa.custom_scheduling = True + else: + wa.custom_scheduling = bool(os.getenv("SPYTEST_SCHEDULING", None)) + + if not wa.custom_scheduling: + return [count, testbeds] + + trace("============== parsing batch info =====================") + trace("Buckets = {}".format(buckets_csv)) + + # init return values + (tb_objs) = ([]) + + # concat all the bucket args + buckets_csv = ",".join(buckets_csv) if buckets_csv else "" + + # read the buckets in reverse order + bucket_list = [] + for bucket in buckets_csv.split(","): + if bucket: + bucket_list.append(int(bucket)) + bucket_list = sorted(bucket_list, key=int, reverse=True) + trace("Buckets - Sorted = {}".format(bucket_list)) + + # create testbed objects for testbed files + for j,testbed in enumerate(testbeds): + tb = Testbed(testbed, flex_dut=True) + if not tb.is_valid(): + msg = "testbed file {} is not valid" + trace(msg.format(testbed)) + os._exit(100) + #trace("Testbed: {}".format(testbed)) + #trace(" Devices: {}".format(tb.get_device_names("DUT"))) + tb_objs.append(tb) + + # initialize collected lists + for key in ["testbeds", "buckets", "min_buckets", "parent_testbeds", "testbed_objs"]: + wa[key] = [] + for key in ["free_devices"]: + wa[key] = dict() + + # use given testbeds for tests needs higher topology + # than the spefified buckets + data = _create_bucket_testbeds(tb_objs, bucket_list, logs_path) + for bucket, min_bucket, parent_testbed, tbs in sorted(data, reverse=True): + for index,testbed in enumerate(tbs): + wa.buckets.append(bucket) + wa.min_buckets.append(min_bucket) + wa.testbeds.append(testbed) + wa.parent_testbeds.append(parent_testbed) + + for testbed in wa.testbeds: + fname = os.path.basename(testbed) + tb = Testbed(testbed, flex_dut=True) + if not tb.is_valid(): + msg = "testbed file {} is not valid" + trace(msg.format(fname)) + os._exit(100) + msg = "Topology({}): {}" + trace(msg.format(fname, tb.get_topo())) + wa.testbed_objs.append(tb) + + wa.slaves = SpyTestDict() + wa.count = len(wa.testbeds) + wa.logs_path = logs_path + + min_bucket = wa.buckets[0] + for i in range(0, wa.count): + node = "gw{}".format(i) + bucket = wa.buckets[i] + testbed = wa.testbeds[i] + tb_obj = wa.testbed_objs[i] + slave = SpyTestDict() + slave.name = node + slave.bucket = bucket + slave.min_bucket = wa.min_buckets[i] + slave.testbed = testbed + slave.parent_testbed = wa.parent_testbeds[i] + slave.tb_obj = tb_obj + slave.completed = False + slave.executed = 0 + slave.pid = 0 + slave.started = True if bucket >= wa.buckets[0] else False + wa.slaves[node] = slave + wa.free_devices[testbed] = [] + wa.loaded_devices = [] + if bucket < min_bucket: + min_bucket = bucket + + # let the testbeds in min bucket handled all lower buckets + for name, slave in wa.slaves.items(): + if slave.bucket <= min_bucket: + slave.min_bucket = 0 + + trace("=======================================================") + _show_testbed_topo() + _show_testbed_info() + return [len(wa.testbeds), wa.testbeds] + +def verify_bucket(nodeid, used, fails): + if fails != 0: + trace("SKIP verify bucket", nodeid, fails, used) + elif not wa.custom_scheduling: + debug("Not valid for current scheduling type") + elif nodeid not in wa.sched.all_modules: + trace("Module not found in modules.csv", nodeid) + elif wa.sched.all_modules[nodeid].bucket != used: + trace("Mismatch bucket information", nodeid, used, wa.sched.all_modules[nodeid].bucket) + +def is_batch(): + return bool(os.getenv("SPYTEST_BATCH_RUN")) + +def is_master(): + return bool(not is_slave() and is_batch()) + +def get_slave_id(): + return os.getenv("PYTEST_XDIST_WORKER") + +def is_slave(): + return bool(get_slave_id()) + +def is_member(): + return bool(is_slave() or not is_batch()) + +def get_member_count(): + return wa.count + diff --git a/spytest/spytest/batch.sh b/spytest/spytest/batch.sh new file mode 100644 index 00000000000..a847895ddd5 --- /dev/null +++ b/spytest/spytest/batch.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +dir=$(dirname $0) +spytest=$dir/../bin/spytest +TYPE=$1;shift + +export SPYTEST_BATCH_RUN_NEW=2 +if [ "$TYPE" == "slave" ]; then + export PYTEST_XDIST_WORKER=1 + export SPYTEST_NO_CONSOLE_LOG=1 +elif [ "$TYPE" != "master" ]; then + echo "USAGE: $0 [master|slave] ..." + exit 0 +fi + +exec $spytest $* + diff --git a/spytest/spytest/datamap.py b/spytest/spytest/datamap.py new file mode 100644 index 00000000000..4bc6877f02e --- /dev/null +++ b/spytest/spytest/datamap.py @@ -0,0 +1,62 @@ +import os +from spytest.ordyaml import OrderedYaml +import utilities.common as utils + +root = os.path.join(os.path.dirname(__file__), '..', "datastore") + +class DataMap(object): + """ + todo: Update Documentation + """ + + def __init__(self, name, version=None): + self.name = name + self.version = version + self.valid = False + self.dmap = dict() + self.errs = [] + self.file_path = os.path.join(root, name, "{}.yaml".format(name)) + if not os.path.isfile(self.file_path): + self.errs.append("Failed to locate: {}".format(self.file_path)) + print(self.errs) + return + oyaml = OrderedYaml(self.file_path) + self.data = oyaml.get_data() + if name not in self.data: + self.errs.append("Failed to locate {} section".format(self.name)) + print(self.errs) + return + self.valid = True + + def __del__(self): + pass + + def _load(self, d, version): + for section in self.data[self.name][version]: + for tok, value in self.data[section].items(): + d[tok] = value + + def get(self, version=None): + if not self.valid: + print(self.errs) + return None + if not version: + version = "default" + if version in self.dmap: + return self.dmap[version] + if version not in self.data[self.name]: + print("version {} missing in {} section".format(version, self.name)) + return None + self.dmap[version] = dict() + self._load(self.dmap[version], "default") + self._load(self.dmap[version], version) + return self.dmap[version] + + +if __name__ == "__main__": + #dmap = DataMap("messages") + #utils.print_yaml(dmap.get(), "default") + import pdb;pdb.set_trace() + dmap = DataMap("vervars", "3.0.1") + utils.print_yaml(dmap.get("3.0.1"), "3.0.1") + diff --git a/spytest/spytest/dicts.py b/spytest/spytest/dicts.py new file mode 100644 index 00000000000..c98e4d3f176 --- /dev/null +++ b/spytest/spytest/dicts.py @@ -0,0 +1,34 @@ +from collections import OrderedDict + +class SpyTestDict(OrderedDict): + """ + todo: Update Documentation + """ + def __getattr__(self, name): + try: + return self[name] + except KeyError: + raise AttributeError(name) + + def __setattr__(self, name, value): + if not name.startswith('_OrderedDict__'): + self[name] = value + else: + OrderedDict.__setattr__(self, name, value) + + def __delattr__(self, name): + try: + self.pop(name) + except KeyError: + OrderedDict.__delattr__(self, name) + + # compare + def __eq__(self, other): + return dict.__eq__(self, other) + + # stringify + def __str__(self): + return '{%s}' % ', '.join('%r: %r' % item for item in self.items()) + + # for PrettyPrinter + __repr__ = OrderedDict.__repr__ diff --git a/spytest/spytest/framework.py b/spytest/spytest/framework.py new file mode 100644 index 00000000000..d916d2da27d --- /dev/null +++ b/spytest/spytest/framework.py @@ -0,0 +1,4242 @@ +import os +import sys +import pdb +import csv +import time +import copy +import glob +from inspect import currentframe +from collections import OrderedDict +from operator import itemgetter +import traceback +import textwrap +import logging +import socket +import signal +import pytest + +from apis.common.init import apis_register +from apis.common.init import apis_common_init +from apis.common.init import apis_common_clean + +import utilities.common as utils +import utilities.parallel as putil +from spytest.net import Net +from spytest.logger import Logger +from spytest.result import Result +from spytest.testbed import Testbed +from spytest.rps import RPS +from spytest.dicts import SpyTestDict +from spytest.tgen import tg as tgapi +from spytest.version import get_git_ver +from spytest.datamap import DataMap +from spytest import batch +from spytest.st_time import get_timenow +from spytest.st_time import get_elapsed +from spytest.st_time import get_timestamp + +bg_results = putil.ExecuteBackgroud() +min_time = 0 +tcmap = SpyTestDict() +missing_test_names_msg = "" +collected_items = dict() +selected_test_items = OrderedDict() +must_fail_items = OrderedDict() +nodeid_test_names = dict() +selected_test_results = OrderedDict() +reused_test_results = OrderedDict() +current_test = SpyTestDict() +current_module = SpyTestDict() +gWorkArea = None +result_vals = ["PASS", "FAIL", "ENVFAIL", "SCRIPTERROR", "DEPFAIL", \ + "CONFIGFAIL", "SKIPPED", "TIMEOUT", "TOPOFAIL", \ + "TGENFAIL", "DUTFAIL", "UNSUPPORTED"] +report_cols = ["Execution Started", "Execution Completed", "Execution Time", \ + "Session Init Time", "Tests Time"] +report_cols.extend(["Module Count", "Function Count", "Test Count", \ + "SysLog Count", "Pass Count", "Pass Rate", "Software Versions"]) +report_cols.extend(sorted(result_vals)) +syslog_levels = ['emerg', 'alert', 'crit', 'err', 'warning', 'notice', 'info', 'debug', 'none'] +mail_build = "UNKNOWN Build" +def set_mail_build(val): + global mail_build + mail_build = val + +ftrace_file = None +def ftrace(*args): + global ftrace_file + if not ftrace_file: + [user_root, logs_path, slave_id] = _get_logs_path() + ftrace_file = get_file_path("ftrace", "txt", logs_path) + utils.write_file(ftrace_file, "") + l_args = [] + for arg in args: + l_args.append(str(arg)) + utils.write_file(ftrace_file, " ".join(l_args) + "\n", "a") + +def dtrace(*args): + dbg = False + if not dbg: + return + wa = get_work_area() + if wa: + wa.log(args) + else: + print(args) + +def _get_logs_path(): + user_root = os.getenv("SPYTEST_USER_ROOT", os.getcwd()) + logs_path = os.getenv("SPYTEST_LOGS_PATH", user_root) + slave_id = batch.get_slave_id() + if slave_id: + logs_path = os.path.join(logs_path, slave_id) + if not os.path.isabs(logs_path): + logs_path = os.path.join(user_root, logs_path) + if not os.path.exists(logs_path): + os.makedirs(logs_path) + return [user_root, logs_path, slave_id] + +def get_file_path(suffix, extn, prefix=None, consolidated=False): + file_prefix = os.getenv("SPYTEST_FILE_PREFIX", "results") + results_prefix = os.getenv("SPYTEST_RESULTS_PREFIX", file_prefix) + if not consolidated: + filename = "{}_{}.{}".format(results_prefix, suffix, extn) + else: + filename = "{}_{}_all.{}".format(results_prefix, suffix, extn) + if prefix: + filename = os.path.join(prefix, filename) + return filename + +def get_results_csv(prefix=None, consolidated=False): + return get_file_path("result", "csv", prefix, consolidated) + +def get_tc_results_csv(prefix=None, consolidated=False): + return get_file_path("tcresult", "csv", prefix, consolidated) + +def get_syslog_csv(prefix=None, consolidated=False): + return get_file_path("syslog", "csv", prefix, consolidated) + +def get_report_txt(prefix=None, consolidated=False): + return get_file_path("report", "txt", prefix, consolidated) + +def create_pid_file(): + [user_root, logs_path, slave_id] = _get_logs_path() + pid_file = get_file_path("pid", "txt", logs_path) + utils.write_file(pid_file, "{}".format(os.getpid())) + +class Context(object): + + def __init__(self, wa, cfg): + self.stats_txt = None + self.stats_csv = None + self.wa = wa + self.cfg = cfg + self.tc_results = dict() + self.all_tc_executed = 0 + self.shutting_down = False + self.sent_first_progress = False + self.file_prefix = None + self.skip_tgen = cfg.skip_tgen + self.version_msg = "VERSION: {}".format(get_git_ver()) + self.hostname = "HOSTNAME: {}".format(socket.gethostname()) + self.cmdline_args = os.getenv("SPYTEST_CMDLINE_ARGS", "") + self.cmdline_args = "ARGS: {}".format(self.cmdline_args) + [self.user_root, self.logs_path, self.slave_id] = _get_logs_path() + if not self.user_root: + print("SPYTEST_USER_ROOT env not found") + os._exit(1) + self.net = None + self._log_init() + if self.slave_id: + self.log.info("SPYTEST_SLAVE_ID = {}".format(self.slave_id)) + gwtestbed = "SPYTEST_TESTBED_FILE_{}".format(self.slave_id) + gwtestbed = os.getenv(gwtestbed) + self.log.info("using testbed file {}".format(gwtestbed)) + if gwtestbed: + cfg.testbed = gwtestbed + self.execution_start_time = get_timenow() + self.log.info("") + self.log.info("Execution Start Time: {}".format(self.execution_start_time)) + self.log.info(self.version_msg) + self.log.info(self.hostname) + self.log.info("Python: {}.{}.{}".format(sys.version_info.major, + sys.version_info.minor, sys.version_info.micro)) + self.log.info(self.cmdline_args) + self.log.info("LOGS PATH: {}".format(self.logs_path)) + if os.path.exists(os.path.join(self.logs_path, "slave_used")): + self.cfg.skip_load_image = True + self.execution_end_time = None + self.session_start_time = get_timenow() + self.session_init_time_taken = None + self.total_tc_start_time = None + for name, value in cfg.env: + self.log.warning("setting environment {} = {}".format(name, value)) + os.environ[name] = value + + # log the reused test results + if reused_test_results: + self.log.info("Reusing previous test results:") + for tc in reused_test_results.keys(): + self.log.info(" {}".format(tc)) + + if cfg.log_lvl: + self.log.set_lvl(cfg.log_lvl) + + self._tb = Testbed(cfg.testbed, self.log, self.cfg) + if not self._tb.is_valid(): + print("Error: testbed file is not found or contains errors") + os._exit(2) + + topo_str = self._tb.get_topo() + self.log.info("Topology: {}".format(topo_str)) + + # register for signal handlers + if self.cfg.graceful_exit: + self._handle_signals() + + # nothing to do in batch master + if batch.is_master(): + self.report_txt = get_report_txt(self.logs_path, True) + self.results_csv = get_results_csv(self.logs_path, True) + self.tc_results_csv = get_tc_results_csv(self.logs_path, True) + self.result = Result(self.file_prefix, False) + return + + if cfg.rps_reboot: + # nothing further when we are just rebooting devices + return + + self.result = Result(self.file_prefix) + + has_non_scapy = self._load_topo() + if self.cfg.filemode and has_non_scapy: + self.skip_tgen = True + tgapi.init_tgen(self.wa, self.log, self.skip_tgen) + + # create net module and register devices + self.net = Net(self.cfg, self.file_prefix, self.log, self._tb) + self.net.set_workarea(self.wa) + if self._tb.is_valid(): + self.net.register_devices(self.topo) + + # move the connect to session init + wa._context = self + wa.net = self.net + set_work_area(wa) + self._connect() + + # copy testbed files + testbed_info = "{0}_{1}".format(self.file_prefix, "testbed_info.txt") + with open(os.path.join(self.logs_path, testbed_info), "w") as ofh: + for filepath in self._tb.get_all_files(): + ofh.write("########## {} ##########".format(filepath)) + with open(filepath) as ifh: + for line in ifh: + ofh.write(line) + ofh.write("###################################") + + # report missing test cases + missing_tests = "{0}_{1}".format(self.file_prefix, "missing_tests.txt") + with open(os.path.join(self.logs_path, missing_tests), "w") as ofh: + ofh.write(missing_test_names_msg) + + self.results_csv = get_results_csv(self.logs_path) + self.tc_results_csv = get_tc_results_csv(self.logs_path) + self.report_txt = get_report_txt(self.logs_path) + self.stats_txt = get_file_path("stats", "txt", self.logs_path, False) + self.stats_csv = get_file_path("stats", "csv", self.logs_path, False) + Result.write_report_csv(self.stats_csv, [], 3, is_batch=False) + utils.delete_file(self.stats_txt) + + def _cleanup_gracefully(self): + if not self.shutting_down: + self.shutting_down = True + putil.set_shutting_down() + self._disconnect() + self._tgen_close() + batch.shutdown() + + def _exit_gracefully(self, signum, frame): + self.log.warning("shutting down - signal {}".format(signum)) + self._cleanup_gracefully() + time.sleep(2) + os._exit(0) + + def _handle_signals(self): + signal.signal(signal.SIGINT, self._exit_gracefully) + signal.signal(signal.SIGTERM, self._exit_gracefully) + + def log_time(self, name, mode="a"): + filepath = "{}_time.log".format(self.file_prefix) + msg = "{} = {}\n".format(get_timenow(), name) + utils.write_file(filepath, msg, mode) + + def _init_log_path(self, prefix="results"): + self.file_prefix = os.getenv("SPYTEST_FILE_PREFIX", None) + if not self.file_prefix: + if self.cfg.filemode: + # for filemode overwrite the existing log file + self.file_prefix = "{0}".format(prefix) + else: + self.file_prefix = "{0}_{1}".format( + prefix, time.strftime("%H_%M_%m_%d_%Y")) + if batch.is_master(): + self.file_prefix = os.path.join(self.logs_path, "master", self.file_prefix) + else: + self.file_prefix = os.path.join(self.logs_path, self.file_prefix) + self.wa.file_prefix = self.file_prefix + + def _log_init(self): + self._init_log_path() + lvl_name = os.getenv("SPYTEST_LOGS_LEVEL", "info").lower() + if lvl_name == "debug": + lvl = logging.DEBUG + elif lvl_name == "warn": + lvl = logging.WARN + else: + lvl = logging.INFO + self.log = Logger(self.file_prefix, "logs.log", level=lvl, tlog=self.cfg.tc_log_support) + self.log_time("START", "w") + + def _load_topo(self): + duts = OrderedDict() + links = OrderedDict() + for dut in self._tb.get_device_names("DUT"): + duts[dut] = self._tb.get_dut_access(dut) + tgens = OrderedDict() + has_non_scapy = False + for tgen_name in self._tb.get_device_names("TG"): + tgen_dict = self._tb.get_tg_info(tgen_name) + if tgen_dict: + tgen_ports = list() + tgen_links = self._tb.get_links(tgen_name) + for linkElem in tgen_links: + port = linkElem[0] + tgen_ports.append(port) + tgen_dict['ports'] = tgen_ports + tgens[tgen_name] = tgen_dict + if tgen_dict["type"] != "scapy": + has_non_scapy = True + self.topo = {"duts": duts, "links": links, "tgens": tgens} + return has_non_scapy + + def _tgen_close(self): + failures = False + try: + for tgen_dict in self.topo["tgens"].values(): + if not tgapi.close_tgen(tgen_dict): + failures = True + except: + pass + return False if failures and not self.skip_tgen else True + + def _tgen_init(self): + failures = False + for tgen_dict in self.topo["tgens"].values(): + if not tgapi.load_tgen(tgen_dict): + failures = True + return False if failures and not self.skip_tgen else True + + def _tgen_instrument(self, phase, data): + for tgen_dict in self.topo["tgens"].values(): + try: + tgapi.instrument_tgen(tgen_dict, phase, data) + except Exception as exp: + self.log.debug("TGEN instrument {} {} failed: {}".format(phase, data, exp)) + + def _connect(self): + self.log.debug("\nconnect().....\n") + funcs = [ + [self._tgen_init], + [self.net.connect_all_devices, self.cfg.faster_init] + ] + self.log_time("device connections start") + [[rv1, rv2], [e1, e2]] = putil.exec_all(self.cfg.faster_init, funcs, True) + self.log_time("device connections finish") + if rv2 is False or e2 is not None: + self.log.error("Failed to connect one or more devices in topology") + if e2 is not None: + for msg in utils.stack_trace(e2): + self.log.error(msg, split_lines=True) + os._exit(4) + if rv1 is False or e1 is not None: + ignore = False + if e1 is not None: + for msg in utils.stack_trace(e1): + if "DeprecationWarning" in msg: + ignore = True + self.log.warning(msg, split_lines=True) + else: + self.log.error(msg, split_lines=True) + msg = "Failed to connect one or more TGEN devices in topology" + if not ignore: + self.log.error(msg) + os._exit(5) + self.log.warning(msg) + + def _disconnect(self): + try: + if self.net: + self.net.unregister_devices() + except: + pass + + def email(self, subject=None): + filepath = os.path.join(self.logs_path, "build.txt") + build_info = 'build:{0}\nuitype:{1}\nExecution Started:{2}\nExecution Completed:{3}'.format(mail_build, + self.cfg.ui_type, self.execution_start_time, get_timenow()) + utils.write_file(filepath, build_info) + + if not self.cfg.email_csv: + return + + (report_file, is_html, add_png) = (None, True, False) + html_file = os.path.splitext(self.report_txt)[0]+'.html' + if os.path.exists(html_file): + report_file = html_file + + # get the first DUT name to read the services info + first_dut = None + for dut in self._tb.get_device_names("DUT"): + first_dut = dut + break + + body = os.getenv("SPYTEST_EMAIL_BODY_PREFIX", "") + body = body + textwrap.dedent("""\ + {3}VERSION : {0} + {3}{1} + {3}{2} + """.format(get_git_ver(), self.hostname, self.cmdline_args, + "

" if is_html else "")) + + attchments = [] + if self.cfg.email_attachments: + if self.tc_results_csv and os.path.exists(self.tc_results_csv): + attchments.append(self.tc_results_csv) + html_file = os.path.splitext(self.tc_results_csv)[0]+'.html' + if os.path.exists(html_file): + attchments.append(html_file) + png_file = os.path.splitext(self.tc_results_csv)[0]+'.png' + if add_png and os.path.exists(png_file): + attchments.append(png_file) + if self.results_csv and os.path.exists(self.results_csv): + attchments.append(self.results_csv) + html_file = os.path.splitext(self.results_csv)[0]+'.html' + if os.path.exists(html_file): + attchments.append(html_file) + png_file = os.path.splitext(self.results_csv)[0]+'.png' + if add_png and os.path.exists(png_file): + attchments.append(png_file) + html_file = os.path.splitext(self.results_csv)[0]+'_modules.html' + if os.path.exists(html_file): + attchments.append(html_file) + + html_file = os.path.splitext(self.tc_results_csv)[0]+'_components.html' + if os.path.exists(html_file): + lines = utils.read_lines(html_file) + body = body + "\n".join(lines) + body = body + "\n
\n" + + # build mail body from report file + if report_file: + lines = utils.read_lines(report_file) + body = body + "\n".join(lines) + + # get the servics for first DUT for SMTP details + server = self._tb.get_service(first_dut, "smtp") + mailcfg = SpyTestDict({ + "recipients": self.cfg.email_csv, + "subject": subject or self.cfg.email_subject, + "body": body, + "server": server + }) + + date=self.execution_start_time.strftime('%b-%d') + + mailcfg.subject = utils.j2_apply(mailcfg.subject, + build=mail_build, date=date, uitype=self.cfg.ui_type) + + self.result.email(mailcfg, attchments, is_html) + + + def set_default_error(self, res, msgid, *args): + self.result.set_default_error(res, msgid, *args) + + def report_tc(self, tcid, res, msgid, *args): + try: + desc = self.result.build_msg(msgid, *args) + except Exception as e: + desc = "Invalid error code {} : {}".format(msgid, e) + res = "Fail" + self.tc_results[tcid] = (res, desc) + + def report(self, res, msgid, *args): + """ + todo: Update Documentation + :param res: pass/fail/envfail/depfail + :param msgid: message identifier from /messages/*.yaml files + :param args: arguments required in message identifier specification + :return: + """ + if current_test.name in must_fail_items: + res = "Fail" if res in ["Pass"] else "Pass" + retval = self.result.set(res, msgid, *args) + self.log.info("========= Report({}): {} =========".format(res, retval)) + set_current_result(res, retval) + return retval + + def publish2(self, nodeid, func, tcid, time_taken, comp=None, + result=None, desc=None, rtype="Executed"): + if not comp and func and func in selected_test_results: + if selected_test_results[func] != None: + return False + + syslogs = self.net.get_syslogs() + fcli = self.net.get_fcli() + tryssh = self.net.get_tryssh() + dut_list = self._tb.get_device_names("DUT") + res = self.result.publish(nodeid, func, tcid, time_taken, comp, + result, desc, rtype, syslogs, + fcli, tryssh, dut_list) + if not comp and func: + selected_test_results[func] = res["Result"] + self.all_tc_executed = self.all_tc_executed + 1 + if func in selected_test_items: + item = selected_test_items[func] + if "TimeTaken" not in res: + res["TimeTaken"] = "0:00:00" + row = ["", res["Module"], func, res["Result"], res["TimeTaken"], + res["ExecutedOn"], res["Description"]] + item.user_properties.append(row) + if not self.slave_id: + self.run_progress_report(self.all_tc_executed) + return True + + def publish(self, nodeid, func, time_taken): + if not self.publish2(nodeid, func, None, time_taken): + return + if func in tcmap.tclist: + for tcid in tcmap.tclist[func]: + if tcid not in self.tc_results: + comp = tcmap.comp[tcid] + self.publish2(nodeid, func, tcid, "0:00:00", + comp, None, None, "Mapped") + for tcid in self.tc_results.keys(): + (res, desc) = self.tc_results[tcid] + if tcid in tcmap.comp: + comp = tcmap.comp[tcid] + elif func in tcmap.comp: + comp = tcmap.comp[func] + else: + continue + self.publish2(nodeid, func, tcid, "0:00:00", comp, + res, desc, "SubTest") + + def run_progress_report(self, executed): + if executed < 1 or self.cfg.run_progress_report < 1: + return + if not self.sent_first_progress: + subject = "Progress(First) - " + else: + subject = "Progress({}) - ".format(executed) + subject = subject + self.cfg.email_subject + if self.cfg.run_progress_report == 1 or not self.sent_first_progress: + self.email(subject) + self.sent_first_progress = True + elif (executed % self.cfg.run_progress_report) == 0: + self.email(subject) + +class WorkArea(object): + """ + todo: Update Documentation + """ + + def __init__(self, cfg): + """ + Construction of WorkArea object + :param cfg: + :type cfg: + """ + self.cli_file = None + self.cli_records = OrderedDict() + self.syslog_levels = syslog_levels + self.dmaps = dict() + self.vsonic_map = dict() + self.app_vars = dict() + self.module_vars = dict() + self._context = None + self.file_prefix = None + self.all_ports = OrderedDict() + self.connected_ports = OrderedDict() + self.reserved_ports = OrderedDict() + self.free_ports = OrderedDict() + self.hooks = dict() + self.swver = OrderedDict() + self.cfg = cfg + self.hooks = apis_register() + self.session_init_completed = False + self.current_tc_start_time = None + self._context = Context(self, cfg) + self.net = self._context.net + + # RPS REBOOT + if cfg.rps_reboot: + if cfg.rps_reboot == "__all__": + devices = self.get_dut_names() + else: + devices = utils.split_byall(cfg.rps_reboot, True) + devlist = [] + for d in self.get_dut_names(): + dinfo = self._context._tb.get_device_info(d) + if d in devices or dinfo.alias in devices: + devlist.append(d) + + [rvs, exps] = self._foreach(devlist, self.do_rps, "reset", recon=False) + self._trace_exceptions(devlist, "exception doing RPS reboot", exps) + os._exit(1) + + try: self.do_rps_new(devlist, "reset", recon=False) + finally: os._exit(1) + + self.base_config_verified = False + self.module_config_verified = False + self.current_module_verifier = None + self.module_tc_fails = 0 + self.module_get_tech_support = False + self.module_fetch_core_files = False + self.stats_count = 0 + self.module_tc_executed = 0 + self.min_topo_called = False + self.abort_module_msg = None + self.tgen_reconnect = False + + def __del__(self, name=None): + """ + todo: Update Documentation + :param name: + :type name: + :return: + :rtype: + """ + if self._context: + self._context._disconnect() + self._context._tgen_close() + + def _foreach (self, items, func, *args, **kwargs): + return putil.exec_foreach(self.cfg.faster_init, items, func, *args, **kwargs) + + def _foreach_dev (self, func, *args, **kwargs): + return self._foreach(self.get_dut_names(), func, *args, **kwargs) + + def is_dry_run(self): + return self.cfg.filemode + + def is_community_build(self, dut=None): + if os.getenv("SPYTEST_COMMUNITY_BUILD_FEATURES", "0") != "0": + return True + return self.cfg.community_build + + def is_vsonic(self, dut=None): + if not dut: + dut = self.get_dut_names()[0] + if dut not in self.vsonic_map: + self.vsonic_map[dut] = self.net.is_vsonic(dut) + return self.vsonic_map[dut] + + def is_valid_base_config(self): + return bool(self.cfg.skip_load_config not in ["base"]) + + def get_logs_path(self, for_file=None): + [user_root, logs_path, slave_id] = _get_logs_path() + if for_file: + file_path = "{0}_{1}".format(self.file_prefix, for_file) + return os.path.join(logs_path, file_path) + return logs_path + + def profiling_start(self, msg, max_time): + return self.net.profiling_start(msg, max_time) + + def profiling_stop(self, pid): + return self.net.profiling_stop(pid) + + def banner(self, msg, width=80, delimiter="#", wrap=True, tnl=True, lnl=True): + utils.banner(msg, width, delimiter, wrap, self.log, tnl, lnl) + + def debug(self, msg, dut=None, split_lines=False): + self._context.log.debug(msg, dut, split_lines) + + def event(self, *args): + msg = "" + for arg in args: + msg = msg + " " + str(arg) + msg = msg.strip() + self.log("\n================== {} ==================".format(msg)) + + def log_time(self, name): + self._context.log_time(name) + + def log(self, msg, dut=None, split_lines=False): + self._context.log.info(msg, dut, split_lines) + + def exception(self, msg, dut=None, split_lines=True): + self._context.log.exception(msg, dut, split_lines) + + def warn(self, msg, dut=None, split_lines=False): + self._context.log.warning(msg, dut, split_lines) + + def error(self, msg, dut=None, split_lines=False): + self._context.log.error(msg, dut, split_lines) + + def dut_log(self, dut, msg, skip_general=False, lvl=logging.INFO, cond=True): + self._context.net.dut_log(dut, msg, skip_general, lvl, cond) + + def wait(self, val, msg=None): + if msg: + self.log("Sleep for {} sec(s)...{}".format(val, msg)) + else: + self.log("Sleep for {} sec(s)...".format(val)) + self._context.net.wait(val) + + def tg_wait(self, val, msg=None): + if msg: + self.log("TG Sleep for {} sec(s)...{}".format(val, msg)) + else: + self.log("TG Sleep for {} sec(s)...".format(val)) + self._context.net.tg_wait(val) + + def report_tc_pass(self, tcid, msgid, *args): + self._context.report_tc(tcid, "Pass", msgid, *args) + + def onfail_debug_dump(self): + if os.getenv("SPYTEST_ONFAIL_TGEN_STATS", "0") != "0": + self.tgen_debug_show() + + def report_tc_fail(self, tcid, msgid, *args): + self.onfail_debug_dump() + self._context.report_tc(tcid, "Fail", msgid, *args) + + def report_tc_unsupported(self, tcid, msgid, *args): + self._context.report_tc(tcid, "Unsupported", msgid, *args) + + def report_pass(self, msgid, *args): + """ + Infrastructure API used by test scripts to report pass + :param msgid: message identifier from /messages/*.yaml files + :param args: arguments required in message identifier specification + :return: + """ + self._context.report("Pass", msgid, *args) + + def report_pdb(self): + if self.cfg.pdb_on_error: + pdb.set_trace() + HELP = " NOTE: execute 'up' command thrice to go to the failure line ==== " + + def report_env_fail(self, msgid, *args): + desc = self._context.report("EnvFail", msgid, *args) + self.report_pdb() + pytest.skip(desc) + + def report_timeout(self, msgid, *args): + desc = self._context.report("Timeout", msgid, *args) + self.report_pdb() + pytest.skip(desc) + + def report_topo_fail(self, msgid, *args): + desc = self._context.report("TopoFail", msgid, *args) + self.report_pdb() + pytest.skip(desc) + + def tgen_ftrace(self, *args): + ftrace("TGEN:", args, current_module.name, current_test.name) + + def report_tgen_exception(self, ex): + ignore = False + for msg in utils.stack_trace(ex): + if "DeprecationWarning" in msg: + self.warn(msg, split_lines=True) + ignore = True + else: + self.error(msg, split_lines=True) + if not ignore: + desc = self._context.report("TGenFail", "tgen_exception", "{}".format(ex)) + self.report_pdb() + pytest.skip(desc) + + def report_tgen_fail(self, msgid, *args): + desc = self._context.report("TGenFail", msgid, *args) + self.report_pdb() + pytest.skip(desc) + + def report_tgen_abort(self, msgid, *args): + desc = self._context.report("TGenFail", msgid, *args) + self.report_pdb() + self.abort_module_msg = "TGen connection aborted" + self.tgen_reconnect = True + pytest.skip(desc) + + def report_fail(self, msgid, *args): + msg = self._context.report("Fail", msgid, *args) + self.report_pdb() + self.onfail_debug_dump() + pytest.xfail(msg) + + def report_dut_fail(self, msgid, *args): + msg = self._context.report("DUTFail", msgid, *args) + self.report_pdb() + self.onfail_debug_dump() + pytest.xfail(msg) + + def report_unsupported(self, msgid, *args): + msg = self._context.report("Unsupported", msgid, *args) + self.report_pdb() + pytest.xfail(msg) + + def report_scripterror(self, msgid, *args): + msg = self._context.report("ScriptError", msgid, *args) + self.report_pdb() + pytest.xfail(msg) + + def report_config_fail(self, msgid, *args): + msg = self._context.report("ConfigFail", msgid, *args) + self.report_pdb() + self.onfail_debug_dump() + pytest.xfail(msg) + + def set_default_error(self, res, msgid, *args): + self._context.set_default_error(res, msgid, *args) + + def apply_script(self, dut, cmdlist): + """ + todo: Update Documentation + :param cmdlist: + :type cmdlist: + :param dut: + :type dut: + :return: + :rtype: + """ + return self._context.net.apply_script(dut, cmdlist) + + def apply_json(self, dut, json): + """ + todo: Update Documentation + :param json: + :type json: + :param dut: + :type dut: + :return: + :rtype: + """ + return self._context.net.apply_json(dut, json) + + def apply_json2(self, dut, json): + """ + todo: Update Documentation + :param json: + :type json: + :param dut: + :type dut: + :return: + :rtype: + """ + return self._context.net.apply_json2(dut, json) + + def apply_files(self, dut, file_list, method="incremental"): + """ + todo: Update Documentation + :param dut: + :type dut: + :param file_list: + :type file_list: + :return: + :rtype: + """ + return self._context.net.apply_files(dut, file_list, method) + + def run_script(self, dut, timeout, script_path, *args): + """ + todo: Update Documentation + :param dut: + :type dut: + :param timeout: in secs + :type timeout: + :param script_path: + :type script_path: + :return: + :rtype: + """ + return self._context.net.run_script(dut, timeout, script_path, *args) + + def enable_disable_console_debug_msgs(self, dut, flag): + """ + todo: Update Documentation + :param dut: + :type dut: + :return: + :rtype: + """ + return self._context.net.enable_disable_console_debug_msgs(dut, flag) + + def clear_config(self, dut): + """ + todo: Update Documentation + :param dut: + :type dut: + :return: + :rtype: + """ + retval_1 = self._context.net.clear_config(dut) + + # wait for system ready + retval_2 = self.wait_system_status(dut) + + if retval_1 and retval_2: + return True + return False + + def config_db_reload(self, dut, save=False): + """ + todo: Update Documentation + :param dut: + :type dut: + :param save: + :type save: + :return: + :rtype: + """ + retval_1 = self._context.net.config_db_reload(dut, save) + + # wait for system ready + retval_2 = self.wait_system_status(dut) + + if isinstance(retval_1, bool): + if retval_1 and retval_2: + return True + return False + return [retval_1, retval_2] + + def upgrade_image(self, dut, url, skip_reboot=False, port_break=True, port_speed=True, max_ready_wait=0): + """ + Upgrade the software in the given DUT from given URL + :param dut: + :type dut: + :param url: URL string used to upgrade + :type url: String + :param skip_reboot: Flag to avoid rebooting device after upgrade + :type url: boolean (default False) + :return: + :rtype: + """ + upgrd_retval = False + pb_retval = True + if self.cfg.load_image == "onie1": + upgrd_retval = self.net.upgrade_onie_image1(dut, url,max_ready_wait=max_ready_wait) + elif self.cfg.load_image == "onie": + upgrd_retval = self.net.upgrade_onie_image2(dut, url,max_ready_wait=max_ready_wait) + elif self.cfg.load_image == "installer-without-migration": + upgrd_retval = self.net.upgrade_image(dut, url, skip_reboot, False,max_ready_wait=max_ready_wait) + else: + upgrd_retval = self.net.upgrade_image(dut, url, skip_reboot,max_ready_wait=max_ready_wait) + if port_break or port_speed: + pb_retval = self.set_port_defaults(dut, port_break, port_speed) + + # read app vars again + self.app_vars[dut] = self.hooks.get_vars(dut) + + return bool(upgrd_retval and (pb_retval or port_speed)) + + def reboot(self, dut, method="normal", skip_port_wait=False, skip_exception=False, skip_fallback=False): + if not dut: + self.log("reboot all {}".format(self.cfg.faster_init)) + self._foreach_dev(self._context.net.reboot, method, skip_port_wait, + skip_exception, skip_fallback) + else: + self._context.net.reboot(dut, method, skip_port_wait, skip_exception, skip_fallback) + + def _apply_config_file_list(self, dut, files): + for filename in utils.make_list(files): + if not isinstance(filename, list): + file_path = self._context._tb.get_config_file_path(filename) + if not file_path: + self.warn("failed to locate {}".format(filename)) + else: + self.apply_files(dut, [file_path], "full") + continue + # create new list with full paths + inner_list = [] + for inner_list_file in filename: + file_path = self._context._tb.get_config_file_path(inner_list_file) + if not file_path: + self.warn("failed to locate {}".format(filename)) + else: + inner_list.append(file_path) + if inner_list: + self.apply_files(dut, inner_list, "full") + + def is_shutting_down(self): + return self._context.shutting_down + + def wait_system_reboot(self, dut): + return self._context.net.wait_system_reboot(dut) + + def wait_system_status(self, dut, max_time=0): + if self.cfg.pde: return True + max_time = self.cfg.port_init_wait if max_time == 0 else max_time + apply_args = [max_time, self.cfg.poll_for_ports] + if self.cfg.community_build: + apply_args.append(True) + return self._context.net._apply_remote(dut, "wait-for-ports", apply_args) + + line = currentframe().f_back.f_lineno + t = time.time() + max_time + msg = "system is not online - waited for {} sec Ref: {}".format(max_time, line) + while not self.cfg.pde: + if "get_system_status" not in self.hooks: + msg = "get_system_status API not found" + break + rv = self.hooks.get_system_status(dut, skip_error_check=True) + if rv or self.cfg.filemode: return True + if rv == None: + # run without --community-build when device has community build + apply_args.append(True) + return self._context.net._apply_remote(dut, "wait-for-ports", apply_args) + time.sleep(3) + if time.time() > t: + break + self.dut_log(dut, msg, False, logging.WARNING) + return False + + def ensure_system_ready(self, dut, scope="", name=""): + + # un-comment to simulate system not ready + #if scope in ["pre-test", "module"]: + #self.abort_module_msg = " show system status is not ready in time." + #return + + if self.wait_system_status(dut): + return self.net.show_dut_time(dut) + + # recover the system by config reload + # this is not working as getting messages while saving + #msg = "Trying to recover the DUT with save and reload" + #self.dut_log(dut, msg, False, logging.WARNING) + #if self.config_db_reload(dut): + #return + + # system not ready - aborting current module + self.abort_module_msg = " show system status is not ready in time." + msg = self.abort_module_msg + " Trying to recover the DUT with reboot." + self.dut_log(dut, msg, False, logging.WARNING) + + # the module is bailing out - collect the syslog and tech-support + if scope in ["pre-test", "module"] and name: + self.net.do_memory_checks(dut, "post-module-epilog", name) + self.net.do_syslog_checks(dut, "post-module-epilog", name) + self.net.generate_tech_support(dut, name) + + # recover the system by reboot - for next module + rv = self.net.reboot(dut, "fast", skip_exception=True) + if rv: + msg = "Successfully rebooted the DUT to recover." + msg = msg + " Final verification if show system status also ready." + self.dut_log(dut, msg, False, logging.WARNING) + rv = self.wait_system_status(dut, 10) + if rv: + if self.session_init_completed: + msg = "show system status is ready after recovery." + msg = msg + " abort current module and continue with next module" + self.dut_log(dut, msg, False, logging.WARNING) + self.report_dut_fail("show_system_status_not_ready") + return self.net.show_dut_time(dut) + + # failed to recover devices - bailout the run + msg = "show systemn status is not ready even after recovery - bailout run" + self.dut_log(dut, msg, False, logging.ERROR) + os._exit(6) + + def _fill_hooks_data(self, dut): + try: + self.swver[dut] = self.hooks.get_swver(dut) + except: + self.swver[dut] = "UNKNOWN" + msg = "Failed to get the software version" + self.dut_log(dut, msg, False, logging.WARNING) + self.app_vars[dut] = self.hooks.get_vars(dut) + set_mail_build(self.swver[dut]) + + def _noshutdown_connected(self, dut): + if dut in self.connected_ports and self.connected_ports[dut]: + msg = "noshutdown connected ports:{}".format(self.connected_ports[dut]) + self.dut_log(dut, msg, False, logging.INFO) + self.hooks.port_noshutdown(dut, self.connected_ports[dut]) + + def _shutdown_reserved(self, dut): + if dut in self.reserved_ports and self.reserved_ports[dut]: + msg = "shutdown reserved ports:{}".format(self.reserved_ports[dut]) + self.dut_log(dut, msg, False, logging.WARNING) + self.hooks.port_shutdown(dut, self.reserved_ports[dut]) + self._context.net._apply_remote(dut, "update-reserved-ports", [self.reserved_ports[dut]]) + + def _save_base_config_dut(self, dut): + + # noshut connected ports so that they get saved as up in config + self._noshutdown_connected(dut) + + # shut reserved ports so that they get saved as down in config + self._shutdown_reserved(dut) + + if not self.cfg.skip_init_config: + # save the configuration as TA default configuration + self._context.net._apply_remote(dut, "save-base-config") + + def _apply_config_profile_dut(self, dut): + # Apply the profile configuration + profile_name = self._context._tb.get_config_profile() + if self.cfg.config_profile: + profile_name = self.cfg.config_profile + largs_list = [profile_name] + self._context.net._apply_remote(dut, "config-profile", largs_list) + + def _load_image_dut(self, dut, scope): + build = self.get_build(dut, scope) + if self.cfg.build_url != None and self.cfg.build_url.strip() == "": + msg = "Given build url is not valid..." + self.dut_log(dut, msg, False, logging.ERROR) + raise ValueError(msg) + if self.cfg.build_url and scope == "current": + build = self.cfg.build_url + if build: + if self.cfg.port_breakout or self.cfg.port_speed: + if os.getenv("SPYTEST_SYSTEM_READY_AFTER_PORT_SETTINGS", "0") == "1": + self.upgrade_image(dut, build, False, False, False, 1) + else: + self.upgrade_image(dut, build, False, False, False, 0) + else: + self.upgrade_image(dut, build, False, False, False, 0) + return True + msg = "testbed file does not contain {} build".format(scope) + self.dut_log(dut, msg, False, logging.WARNING) + return False + + def _load_config_dut(self, dut, scope, pertest=False): + if not pertest: + # create TA default configuration + self._create_init_ta_config(dut) + + # apply configs as given in template + files = self.get_config(dut, scope) + if not files: + if not pertest: + msg = "testbed file does not contain {} configs".format(scope) + self.dut_log(dut, msg, False, logging.WARNING) + files = [] + + # apply all the config groups given in the template for current scope + self._apply_config_file_list(dut, files) + + def _create_init_ta_config(self, dut): + profile_name = self._context._tb.get_config_profile() + if self.cfg.config_profile: + profile_name = self.cfg.config_profile + largs_list = [self.cfg.fetch_core_files, self.cfg.get_tech_support, profile_name] + self._context.net._apply_remote(dut, "init-ta-config", largs_list) + + def _get_interfaces_all(self, dut): + if not self.cfg.filemode: + return self.hooks.get_interfaces_all(dut) + retval = [] + for port in range(0,200): + retval.append("Ethernet{}".format(port)) + return retval + + def _build_port_list(self, dut, retry): + alias = self._context._tb.get_device_alias(dut) + self.free_ports[dut] = [] + self.all_ports[dut] = [] + self.connected_ports[dut] = [] + self.reserved_ports[dut] = self._context._tb.get_rerved_links(dut) + for local, partner, remote in self._get_device_links(dut, None, None): + self.connected_ports[dut].append(local) + all_ports = self._get_interfaces_all(dut) + errs = [] + err_msg = "Failed to display interfaces in show command in {}".format(dut) + if all_ports: + for port in self.connected_ports[dut]: + if port not in all_ports: + errs.append("invalid connected port {}/{}".format(alias, port)) + errs.append(" should be one of {}".format(all_ports)) + for port in self.reserved_ports[dut]: + if port not in all_ports: + errs.append("invalid reserved port {}/{}".format(alias, port)) + errs.append(" should be one of {}".format(all_ports)) + for port in all_ports: + if port not in self.reserved_ports[dut]: + self.all_ports[dut].append(port) + if port not in self.connected_ports[dut]: + if "Ethernet" in port: + self.free_ports[dut].append(port) + elif retry > 0: + retry = retry - 1 + msg = "{} - retry {}".format(err_msg, retry) + self.dut_log(dut, msg, False, logging.WARNING) + errs = self._build_port_list(dut, retry) + else: + errs.append(err_msg) + return errs + + # the sequence is init, transfer and save + # init triggers init-ta-config + # transfer triggers apply-files + # save triggers save-base-config + # Note: init and transfer can't be compibed because + # we support config.cmds which need to be executed from framework + def _session_common_dut(self, dut, scope): + + if not self.net.is_sonic_device(dut): + return + + if scope == "restore": + if self.cfg.get_tech_support not in ["none"]: + self.net.generate_tech_support(dut, "session") + if self.cfg.fetch_core_files not in ["none"]: + self.net._apply_remote(dut, "fetch-core-files", ["session"]) + + # Load Image + if self.cfg.skip_load_image: + msg = "SKIP loading {} image".format(scope) + self.dut_log(dut, msg, False, logging.WARNING) + apply_port_defaults = True + else: + apply_port_defaults = self._load_image_dut(dut, scope) + + # Apply Profile + if scope == "current": + self._apply_config_profile_dut(dut) + + # load user configuration + if os.getenv("SPYTEST_PORT_DEFAULTS_CONFIG"): + self._load_config_dut2(dut, scope) + + # port speed and breakout + if scope == "current" or apply_port_defaults: + if os.getenv("SPYTEST_SKIP_RESERVED_PORT_SPEED"): + self.set_port_defaults(dut, speed=False) + else: + self.set_port_defaults(dut) + + # load user configuration + if not os.getenv("SPYTEST_PORT_DEFAULTS_CONFIG"): + self._load_config_dut2(dut, scope) + + # save configuration as TA configuration + self._save_base_config_dut(dut) + + def _load_config_dut2(self, dut, scope): + if self.cfg.pde: + msg = "SKIP loading configuration" + self.dut_log(dut, msg, False, logging.WARNING) + elif self.cfg.skip_init_config: + msg = "SKIP loading configuration" + self.dut_log(dut, msg, False, logging.WARNING) + largs = [self.cfg.fetch_core_files, self.cfg.get_tech_support, self.cfg.clear_tech_support] + self.net._apply_remote(dut, "init-clean", largs) + if self.cfg.config_profile: + self._create_init_ta_config(dut) + else: + self._load_config_dut(dut, scope, False) + + def _session_common_dut2(self, dut, no_recovery=True): + errs = [] + + # read the port list + if not self.cfg.pde: + errs.extend(self._build_port_list(dut, 3)) + + # check if there are any issues reported if not all is well + if not errs: + if os.getenv("SPYTEST_SKIP_RESERVED_PORT_SPEED"): + # set the port speeed after building port list + self.set_port_defaults(dut, breakout=False) + self._fill_hooks_data(dut) + self._save_base_config_dut(dut) + return True + + # bail out on testbed issues + if self.all_ports[dut]: + self._trace_errors("invalid ports in testbed file", errs) + return False + + # nothing to do if we are not recovering + if no_recovery: + msg = "ports are not created - bailout run" + self.dut_log(dut, msg, False, logging.ERROR) + return False + + # recover the DUT with reboot + msg = " ports are not created - trying to recover the DUT with reboot." + self.dut_log(dut, msg, False, logging.WARNING) + rv = self.net.reboot(dut, "fast", skip_exception=True) + if not rv: + msg = "Failed to reboot the DUT to recover - bailout run" + self.dut_log(dut, msg, False, logging.ERROR) + return False + + # reboot is OK, check ports again + msg = "Successfully rebooted the DUT to recover - verify testbed ports" + self.dut_log(dut, msg, False, logging.WARNING) + if not self._session_common_dut2(dut, True): + msg = "ports are not ready even after recovery - bailout run" + self.dut_log(dut, msg, False, logging.ERROR) + return False + + # all is well atleast now + msg = "Successfully verified testbed ports after recovery" + self.dut_log(dut, msg, False, logging.INFO) + return True + + def _trace_errors(self, msg, errs): + if msg: self.error(msg) + for err in errs: self.error(err) + return False + + def _trace_exceptions(self, dut_list, msg, exceptions): + errs = [] + for dut_index, ex in enumerate(exceptions): + if not ex: continue + self.dut_log(dut_list[dut_index], str(ex), False, logging.ERROR) + errs.extend(ex) + if errs: + self.error("exception loading image or init config") + return errs + + def _session_init(self): + self.log("session init start") + self.log_time("session init start") + + dut_list = self.get_dut_names() + + # init software versions + for dut in dut_list: + self.swver[dut] = "" + + # load current image, config and perform + [retvals, exceptions] = self._foreach(dut_list, self._session_common_dut, "current") + if self._trace_exceptions(dut_list, "exception loading image or init config", exceptions): + os._exit(6) + + apis_common_init("session", None) + + # identify invalid port names given in testbed file + self.log("building port list and save base config") + no_recovery = bool(os.getenv("SPYTEST_RECOVER_INITIAL_SYSTEM_NOT_READY", "0") == "1") + [retvals, exceptions] = self._foreach(dut_list, self._session_common_dut2, no_recovery) + if self._trace_exceptions(dut_list, "exception saving base config", exceptions): + os._exit(6) + + # bail out if there are erros detected in topology + if not all(retvals): + self.error("invalid ports in topology - please check testbed file") + if not self.cfg.filemode: os._exit(6) + + # get application vars + for dut in self.get_dut_names(): + self.app_vars[dut] = self.hooks.get_vars(dut) + self.module_vars[dut] = dict() + + # perform topology check + if self.cfg.topology_check not in ["skip", "status2", "status3", "status4"]: + [retval, header, rows] = self.hooks.verify_topology(self.cfg.topology_check) + topo_status = utils.sprint_vtable(header, rows) + if not retval: + self.error("Topology verification failed") + self.error(topo_status, split_lines=True) + if self.cfg.topology_check in ["abort"]: + os._exit(7) + else: + self.log("Topology verification successful") + self.log(topo_status, split_lines=True) + else: + self.warn("SKIP Topology verification") + + # flag to run module init + self.base_config_verified = True + self._context.session_init_time_taken = get_elapsed(self._context.session_start_time, True) + self._context.total_tc_start_time = get_timenow() + + if batch.is_member(): + self._report_file_generation() + + self.log_time("session init end") + + def _session_clean(self): + self.log_time("session clean start") + + #self.log("session clean {}".format(self.cfg.faster_init)) + if batch.is_member(): + data = self._report_file_generation() + + if self.cfg.tgen_module_init: + funcs = [ + [self._module_init_tgen], + [self._foreach_dev, self._session_common_dut, "restore"] + ] + putil.exec_all(self.cfg.faster_init, funcs, True) + else: + self._foreach_dev(self._session_common_dut, "restore") + + apis_common_clean("session", None) + if self.net: + self.net.session_close() + if batch.is_member(): + self.log("=================== Final Report =========================") + self.log(data, split_lines=True) + self.log(" ================== Software Versions ====================") + ver_dut_map = utils.invert_dict(self.swver) + for swver, dut in ver_dut_map.items(): + self.log(" ============ {} = {}".format(dut, swver)) + self.log("==========================================================") + rlist = [i for i in selected_test_results.values()] + from collections import Counter + self.log("============ Results : {}".format(list(Counter(rlist).items()))) + self.log("==========================================================") + self.log_time("session clean end") + + def _module_init_dut(self, dut, filepath): + self.clear_module_vars(dut) + if self.cfg.skip_load_config not in ["base"]: + largs = [self.cfg.load_config_method] + self._context.net._apply_remote(dut, "apply-base-config", largs) + else: + msg = "SKIP appying base configuration" + self.dut_log(dut, msg, False, logging.WARNING) + + # ensure system is ready + module_str = "module_{}".format(filepath.split('.')[0]) + self.ensure_system_ready(dut, "module", module_str) + + def _module_init_tgen(self): + for tgen in self._context.topo["tgens"]: + tgapi.module_init(self._context.topo["tgens"][tgen]) + + def _module_init(self, filepath): + base_filename = os.path.basename(filepath) + #self.log("module init start") + self.log_time("module {} init start".format(filepath)) + self._context._tb.reset_derived_devices() + self.clear_tc_results() + + # per module faster-cli + if self.cfg.faster_cli == 1: + fcli = 1 + elif self.cfg.faster_cli == 2: + fcli = tcmap.faster_cli.get(base_filename, 0) + elif self.cfg.faster_cli == 3: + fcli = tcmap.faster_cli.get(base_filename, 1) + else: + fcli = 0 + + # per module tryssh + if self.cfg.tryssh == 1: + tryssh = 1 + elif self.cfg.tryssh == 2: + tryssh = tcmap.tryssh.get(base_filename, 0) + elif self.cfg.tryssh == 3: + tryssh = tcmap.tryssh.get(base_filename, 1) + else: + tryssh = 0 + + # ajdust Module MAX Timeout using data from tcmap + try: module_max_timeout = tcmap.module_max_timeout.get(base_filename, 0) + except: module_max_timeout = 0 + if module_max_timeout > 0 and module_max_timeout > self.cfg.module_max_timeout: + self.net.module_init_start(module_max_timeout, fcli, tryssh) + else: + self.net.module_init_start(self.cfg.module_max_timeout, fcli, tryssh) + + if not self.base_config_verified: + self.warn("base config verification already failed - no need to run any modules") + self.module_config_verified = False + return "SKIP" + + apis_common_init("module", filepath) + self._context.result.clear() + self.cli_records.clear() + + self.min_topo_called = False + self.module_tc_executed = 0 + self.module_tc_fails = 0 + self.module_get_tech_support = False + self.module_fetch_core_files = False + self.current_module_verifier = "NA" + self.abort_module_msg = None + + if self.tgen_reconnect: + self.warn("Reconnecting to TGen") + self._context._tgen_close() + if not self._context._tgen_init(): + self.error("Failed to reconnect to tgen") + os._exit(6) + self.warn("Reconnected to TGen") + self.tgen_reconnect = False + + self.log("applying base configuration: {}".format(self.abort_module_msg)) + if self.cfg.tgen_module_init: + funcs = [ + [self._module_init_tgen], + [self._foreach_dev, self._module_init_dut, filepath] + ] + [[rv1, rv2], [e1, e2]] = putil.exec_all(self.cfg.faster_init, funcs, True) + if e2 is not None: + self.error("Failed to module init one or more devices in topology") + for msg in utils.stack_trace(e2): + self.error(msg, split_lines=True) + if e1 is not None: + ignore = False + for msg in utils.stack_trace(e1): + if "DeprecationWarning" in msg: + ignore = True + self.warn(msg, split_lines=True) + else: + self.error(msg, split_lines=True) + msg = "Failed to module init one or more TGEN devices in topology" + if not ignore: + self.error(msg) + self.warn(msg) + else: + [rvs, exps] = self._foreach_dev(self._module_init_dut, filepath) + self.log("RVs: {} EXPs: {} CHK: {}".format(rvs, exps, self.abort_module_msg)) + + self.log("base configuration applied: {}".format(self.abort_module_msg)) + + # ensure system is ready to proceed further + if self.abort_module_msg: + self.error(self.abort_module_msg) + self.module_config_verified = False + return "SKIP" + + if self.cfg.topology_check in ["status2", "status3", "status4"]: + self.log("verify/show port status before module") + [retval, header, rows] = self.hooks.verify_topology(self.cfg.topology_check) + topo_status = utils.sprint_vtable(header, rows) + self.log(topo_status, split_lines=True) + # TODO: Swap ports if they are down + + if self.cfg.skip_load_config not in ["base"] and \ + self.cfg.skip_verify_config not in ["base", "both"]: + verifier = self._context._tb.get_verifier() + self.log("base config verification - {}".format(verifier)) + verifiers = self.hooks.verifiers() + if verifier in verifiers: + self.base_config_verified = verifiers[verifier]() + if not self.base_config_verified: + self.error("base config verification failed - no need to run any modules") + self.module_config_verified = False + return "SKIP" + else: + self.log("base config verification successful") + else: + self.base_config_verified = False + self.log("base config verifier '{}' not found".format(verifier)) + else: + self.warn("base config verification is skipped") + + self.module_config_verified = self.base_config_verified + self.log_time("module {} init end".format(filepath)) + + def _clear_devices_usage_list(self): + self.net.clear_devices_usage_list() + + def _get_devices_usage_list(self): + return self.net.get_devices_usage_list() + + def _set_device_usage_collection(self, collect_flag): + self.net.set_device_usage_collection(collect_flag) + + def save_sairedis(self, phase, name): + if self.cfg.save_sairedis not in [None]: + self._foreach_dev(self.net.save_sairedis, phase, name) + + def _do_memory_checks(self, phase, name): + if self.cfg.memory_check not in ["none"]: + self._foreach_dev(self.net.do_memory_checks, phase, name) + + def _do_syslog_checks(self, phase, name): + if self.cfg.syslog_check not in ["none"]: + self._foreach_dev(self.net.do_syslog_checks, phase, name) + + def _pre_module_prolog(self, name): + self._do_memory_checks("pre-module-prolog", name) + self._do_syslog_checks("pre-module-prolog", name) + self.save_sairedis("pre-module-prolog", name) + self._context._tgen_instrument("pre-module-prolog", name) + + def _post_module_prolog(self, name, success_status): + + # fetch debug info when module config failed + if not success_status: + if self.cfg.get_tech_support in ["onfail", "onfail-epilog"]: + self._foreach_dev(self.net.generate_tech_support, name) + if self.cfg.fetch_core_files in ["onfail", "onfail-epilog"]: + self._foreach_dev(self.net._apply_remote, "fetch-core-files", [name]) + + self._do_memory_checks("post-module-prolog", name) + self._do_syslog_checks("post-module-prolog", name) + self.save_sairedis("post-module-prolog", name) + self._context._tgen_instrument("post-module-prolog", name) + + if success_status and self.cfg.skip_load_config not in ["base"]: + self._foreach_dev(self._save_module_config) + + def _post_module_epilog(self, name, success_status): + if not self.abort_module_msg: + self._do_memory_checks("post-module-epilog", name) + self._do_syslog_checks("post-module-epilog", name) + self.save_sairedis("post-module-epilog", name) + self._context._tgen_instrument("post-module-epilog", name) + + def _pre_function_prolog(self, name): + pass + + def _post_function_prolog(self, name): + pass + + def _pre_function_epilog(self, name): + (res, desc) = self._context.result.get() + if res.lower() not in ["fail", "xfail", "dutfail"]: + return + if self.cfg.get_tech_support in ["onfail-epilog"]: + self._foreach_dev(self.net.generate_tech_support, name) + if self.cfg.fetch_core_files in ["onfail-epilog"]: + self._foreach_dev(self.net._apply_remote, "fetch-core-files", [name]) + + def _post_function_epilog(self, name): + pass + + def _save_module_config(self, dut=None): + # we MUST save the module config even though we don;t need to call + # apply-module-config, because if reboot happens the device should + # start with module config + # save the module configuration before executing any test cases in the module + msg = "save the module config - needed if device reboots " + if self.cfg.skip_load_config not in ["module"]: + msg = " and for restore across test cases" + self.dut_log(dut, msg) + self._context.net._apply_remote(dut, "save-module-config") + + def set_module_lvl_action_flags(self, action): + if action == "core-dump": + self.module_fetch_core_files = True + elif action == "tech-support": + self.module_get_tech_support = True + + def _module_complete_dut(self, dut, filepath): + module_str = "module_{}".format(filepath.split('.')[0]) + + if self.module_get_tech_support and self.cfg.get_tech_support in ["module-onerror"]: + self.net.generate_tech_support(dut, module_str) + self.net._apply_remote(dut, "init-clean", ["none", self.cfg.get_tech_support, self.cfg.clear_tech_support]) + elif self.cfg.get_tech_support in ["module-onfail"] and self.module_tc_fails > 0: + self.net.generate_tech_support(dut, module_str) + self.net._apply_remote(dut, "init-clean", ["none", self.cfg.get_tech_support, self.cfg.clear_tech_support]) + elif self.cfg.get_tech_support in ["module-always"]: + self.net.generate_tech_support(dut, module_str) + self.net._apply_remote(dut, "init-clean", ["none", self.cfg.get_tech_support, self.cfg.clear_tech_support]) + + if self.module_fetch_core_files and self.cfg.fetch_core_files in ["module-onerror"]: + self.net._apply_remote(dut, "fetch-core-files", [module_str]) + self.net._apply_remote(dut, "init-clean", [self.cfg.fetch_core_files, "none", self.cfg.clear_tech_support]) + elif self.cfg.fetch_core_files in ["module-onfail"] and self.module_tc_fails > 0: + self.net._apply_remote(dut, "fetch-core-files", [module_str]) + self.net._apply_remote(dut, "init-clean", [self.cfg.fetch_core_files, "none", self.cfg.clear_tech_support]) + elif self.cfg.fetch_core_files in ["module-always"]: + self.net._apply_remote(dut, "fetch-core-files", [module_str]) + self.net._apply_remote(dut, "init-clean", [self.cfg.fetch_core_files, "none", self.cfg.clear_tech_support]) + + def _module_complete(self, filepath): + self._save_cli() + dut_list = self.get_dut_names() + [retvals, exceptions] = self._foreach(dut_list, self._module_complete_dut, filepath) + errs = [] + dut_index = 0 + for ex in exceptions: + if ex: + self.dut_log(dut_list[dut_index], str(ex), False, logging.ERROR) + errs.extend(ex) + dut_index = dut_index + 1 + if errs: + self.error("exception collecting module level core and dump files") + os._exit(6) + + def _module_clean(self, filepath): + #self.log("module clean start") + if not self.min_topo_called: + self.error("Module {} Minimum Topology is not specified".format(filepath)) + self.log_time("module {} clean start".format(filepath)) + apis_common_clean("module", filepath) + self._module_complete(filepath) + self.log_time("module {} clean end".format(filepath)) + # update the node report files for every module + # if we are not reporting run progress + if self.cfg.run_progress_report == 0: + self._report_file_generation() + + def _check_dut_state(self, dut): + self.dut_log(dut, "Check DUT state") + return self._context.net._check_dut_state(dut) + + def _check_dut_port_state(self, dut): + self.dut_log(dut, "Check DUT port state connected to TG/DUT") + return_states = [] + for port in self.connected_ports[dut]: + status = self.hooks.get_interface_status(dut, port) + return_states.append(status) + return return_states + + def _function_common_dut(self, dut, scope, res=None, func_name=None): + + if scope != "post-test": + self.ensure_system_ready(dut, "pre-test", func_name) + self.net.do_memory_checks(dut, "pre-test", func_name) + self.net.do_syslog_checks(dut, "pre-test", func_name) + self.net.save_sairedis(dut, "pre-test", func_name) + return + + if not self.abort_module_msg: + self.net.do_memory_checks(dut, "post-test", func_name) + self.net.do_syslog_checks(dut, "post-test", func_name) + self.net.save_sairedis(dut, "post-test", func_name) + self.net.do_audit("post-test", dut, func_name, res) + + if self.cfg.skip_load_config in ["base", "module"]: + return + + # check for any configuration cleanup not done as part of testcase + # if found any difference apply the module configuration at the end of every test case + largs = [self.cfg.load_config_method] + self.net._apply_remote(dut, "apply-module-config", largs) + self.ensure_system_ready(dut) + + def tc_log_init(self, func_name): + self._context.log.tc_log_init(func_name) + if self.cfg.tc_log_support and func_name: + self.log(self._context.version_msg) + self.log(self._context.cmdline_args) + self.log("Topology: {}".format(self._context._tb.get_topo())) + + def module_log_init(self, func_name): + self._context.log.module_log_init(func_name) + if func_name: + #self.log("Execution Start Time: {}".format(self._context.execution_start_time)) + self.log(self._context.version_msg) + self.log(self._context.cmdline_args) + self.log("Topology: {}".format(self._context._tb.get_topo())) + + def _test_log_init(self, nodeid, func_name, show_trace=False): + self._context.result.clear() + msg = "\n================== {} ==================\n".format(nodeid) + if show_trace: + self.log(msg) + for dut in self.get_dut_names(): + self.dut_log(dut, msg, True) + + def _function_init(self, nodeid, func_name): + + self.clear_tc_results() + #self.log("function init start") + self.log_time("function {} init start".format(nodeid)) + self.current_tc_start_time = get_timenow() + + # ajdust TC MAX Timeout using data from tcmap + try: tc_max_timeout = tcmap.tc_max_timeout.get(func_name, 0) + except: tc_max_timeout = 0 + + if tc_max_timeout > 0 and tc_max_timeout > self.cfg.tc_max_timeout: + self.net.function_init_start(tc_max_timeout) + else: + self.net.function_init_start(self.cfg.tc_max_timeout) + + self._test_log_init(nodeid, func_name) + + if self.abort_module_msg: + desc = self._context.report("SKIPPED", "test_execution_skipped", self.abort_module_msg) + self._function_clean(nodeid, func_name, min_time) + pytest.skip(desc) + + if self.cfg.first_test_only and self.module_tc_executed > 0: + desc = self._context.report("SKIPPED", "test_execution_skipped", "as the ask for to run first test only") + self._function_clean(nodeid, func_name, min_time) + pytest.skip(desc) + + # report as failure if the base config is not verified + if not self.base_config_verified: + self.error("base config verification failed - no need to run {}".format(func_name)) + desc = self._context.report("ConfigFail", "base_config_verification_failed") + self._function_clean(nodeid, func_name, min_time) + pytest.skip(desc) + + # report as failure if the module config is not verified + if not self.module_config_verified: + self.error("module config verification failed - no need to run {}".format(func_name)) + desc = self._context.report("ConfigFail", "module_config_verification_failed") + self._function_clean(nodeid, func_name, min_time) + pytest.skip(desc) + + # check if the dependent test case is failed + if not self.cfg.ignore_dep_check: + errs = check_dependency(func_name) + if errs: + self.error("dependent test case failed - no need to run {}".format(func_name)) + desc = self._context.report("DepFail", "depedent_test_failed", errs) + self._function_clean(nodeid, func_name, min_time) + pytest.skip(desc) + + #if self.cfg.topology_check in ["status4"] and not self.cfg.filemode: + if self.cfg.topology_check in ["status4"]: + self.log("verify/show port status before function") + [retval, header, rows] = self.hooks.verify_topology(self.cfg.topology_check) + topo_status = utils.sprint_vtable(header, rows) + self.log(topo_status, split_lines=True) + + # per-testcase topology checking + if self.cfg.pertest_topo_check: + self.log("perform topology check per test case") + self._context._tb.pertest_topo_checking = True + + # get the dut states + [retvals, exceptions] = self._foreach_dev(self._check_dut_state) + for dut in self.get_dut_names(): + self._context._tb.devices_state[dut] = retvals.pop(0) + + # get the dut-port-ixia states + [retvals, exceptions] = self._foreach_dev(self._check_dut_port_state) + for dut in self.get_dut_names(): + dut_port_states = retvals.pop(0) + for port in self.connected_ports[dut]: + self._context._tb.devices_port_state["{}:{}".format(dut,port)] = dut_port_states.pop(0) + + #self.log("save/apply module config if not skipped") + self._foreach_dev(self._function_common_dut, "pre-test", func_name=func_name) + + # ensure system is ready to proceed further + if self.abort_module_msg: + self.error(self.abort_module_msg) + return "SKIP" + + self._context._tgen_instrument("pre-test", func_name) + + apis_common_init("function", func_name) + if self.cfg.skip_load_config not in ["base", "module"] and \ + self.cfg.skip_verify_config not in ["module", "both"]: + verifier = self.current_module_verifier + self.log("performing module config verification - {}".format(verifier)) + verifiers = self.hooks.verifiers() + if verifier in verifiers: + self.module_config_verified = verifiers[verifier]() + if not self.module_config_verified: + self.error("module config verification failed - no need to run {}".format(func_name)) + else: + self.log("module config verification successful") + else: + self.module_config_verified = False + self.log("module module config verifier '{}' not found".format(verifier)) + + if not self.module_config_verified: + # bail out current test + desc = self._context.report("ConfigFail", "module_config_verification_failed") + self._function_clean(nodeid, func_name, min_time) + pytest.skip(desc) + return + else: + self.warn("module config verification is skipped") + self._context.net.tc_start(self.current_tc_start_time) + self.log_time("function {} init end".format(nodeid)) + + def _function_clean(self, nodeid, func_name, time_taken=None): + + #self.log("function clean start") + self.log_time("function {} clean start".format(nodeid)) + self._context.net.set_prev_tc(func_name) + apis_common_clean("function", func_name) + + # Get result and description to print in log files. + (res, desc) = self._context.result.get() + + self._foreach_dev(self._function_common_dut, "post-test", res, func_name) + self._context._tgen_instrument("post-test", func_name) + + if time_taken is None: + time_taken = get_elapsed(self.current_tc_start_time, True, min_time) + elif isinstance(time_taken, int): + time_taken = utils.time_format(time_taken) + + self._test_log_finish(nodeid, func_name, res, desc, time_taken) + self.module_tc_executed = self.module_tc_executed + 1 + if self.cfg.run_progress_report > 1: + self._report_file_generation() + if res.lower() != "pass": + self.module_tc_fails = self.module_tc_fails + 1 + if self.cfg.maxfail and self.module_tc_fails >= self.cfg.maxfail: + os._exit(0) + self.log_time("function {} clean end".format(nodeid)) + + def _report_file_generation(self): + self.log_time("report file generation start") + + self._context.execution_end_time = get_timenow() + total_tc_time_taken = get_elapsed(self._context.total_tc_start_time, True) + tcresults_csv = get_tc_results_csv(self._context.logs_path) + results_csv = get_results_csv(self._context.logs_path) + syslog_csv = get_syslog_csv(self._context.logs_path) + + data = _report_data_generation(self._context.execution_start_time, + self._context.execution_end_time, + self._context.session_init_time_taken, + total_tc_time_taken, + results_csv, tcresults_csv, syslog_csv) + with open(self._context.report_txt, "w") as ofh: + ofh.write(data) + last_dut_version = "" + duts_sw_versions = [] + ver_dut_map = utils.invert_dict(self.swver) + for swver, dut in ver_dut_map.items(): + duts_sw_versions.append("{} : {}".format(dut, swver)) + last_dut_version = swver + ofh.write("\nSoftware Versions = {}".format(",".join(duts_sw_versions))) + ofh.write("\nSoftware Version = {}".format(last_dut_version)) + + self.log_time("report file generation end") + return data + + def _test_log_finish(self, nodeid, func_name, res, desc, time_taken): + + if isinstance(time_taken, int): + time_taken = utils.time_format(time_taken) + + # trace missing parallel operations + stats = self._context.net.get_stats() + if stats.canbe_parallel: + msg = "yet to be parallized: {}".format(nodeid) + utils.banner(msg, func=ftrace) + for [start_time, msg, dut1, dut2] in stats.canbe_parallel: + ftrace(start_time, msg, dut1, dut2) + utils.banner(None, func=ftrace) + + #Construct the final result log message to print in all log files. + msg = "\n================== Report: {} : {} : {} : {} =========\n" + msg = msg.format(nodeid, res, time_taken, desc) + self.log(msg) + + self._write_stats(nodeid, res, desc, time_taken) + self._context.net.tc_start() + for dut in self.get_dut_names(): + self.dut_log(dut, msg, True) + self._context.publish(nodeid, func_name, time_taken) + self.log_time("Test Time ({} - {}) Published".format(self.current_tc_start_time, get_timenow())) + + def _write_stats(self, nodeid, res, desc, time_taken): + if not self._context.stats_txt: + return + with open(self._context.stats_txt, "a") as ofh: + ofh.write("\n======================= STATS: {} ===========================".format(nodeid)) + stats = self._context.net.get_stats() + ofh.write("\nRESULT = {}".format(res)) + ofh.write("\nDESCRIPTION = {}".format(desc)) + ofh.write("\nTOTAL Test Time = {}".format(time_taken)) + ofh.write("\nTOTAL Sleep Time = {} sec".format(stats.tc_total_wait)) + ofh.write("\nTOTAL TG Sleep = {} sec".format(stats.tg_total_wait)) + ofh.write("\nTOTAL CMD Time = {}".format(stats.tc_cmd_time)) + ofh.write("\nTOTAL INFRA Time = {}".format(stats.infra_cmd_time)) + ofh.write("\nTOTAL TG Time = {}".format(stats.tg_cmd_time)) + ofh.write("\nTOTAL PROMPT NFOUND = {}".format(stats.pnfound)) + for [start_time, thid, ctype, dut, cmd, ctime] in stats.cmds: + start_msg = "\n{} {}".format(get_timestamp(this=start_time), thid) + if ctype == "CMD": + ofh.write("{}CMD TIME: {} {} = {}".format(start_msg, ctime, dut, cmd)) + elif ctype == "INFRA": + ofh.write("{}INFRA TIME: {} {} = {}".format(start_msg, ctime, dut, cmd)) + elif ctype == "TG": + ofh.write("{}TG TIME: {} = {}".format(start_msg, ctime, cmd)) + elif ctype == "WAIT": + ofh.write("{}WAIT TIME: {} = {}".format(start_msg, ctime, cmd)) + elif ctype == "TGWAIT": + ofh.write("{}TGWAIT TIME: {} = {}".format(start_msg, ctime, cmd)) + elif ctype == "PROMPT_NFOUND": + ofh.write("{}PROMPT NFOUND: {}".format(start_msg, cmd)) + ofh.write("\n=========================================================\n") + self.stats_count = self.stats_count + 1 + row = [self.stats_count, nodeid, res, time_taken, stats.infra_cmd_time, + stats.tc_cmd_time, stats.tg_cmd_time, stats.tc_total_wait, + stats.tg_total_wait, stats.pnfound, desc.replace(",", " ")] + Result.write_report_csv(self._context.stats_csv, [row], 3, False, True) + + def get_device_names(self, dtype): + """ + This method is used to get all device names of given type + + :return: all device names or device names of given type + :rtype: list + """ + return self._context._tb.get_device_names(dtype) + + def get_dut_names(self): + """ + This method is used to get all the DUT names + + :return: names of all the duts + :rtype: list + """ + return self.get_device_names("DUT") + + def get_tg_names(self): + """ + This method is used to get all the TG names + + :return: names of all the tg + :rtype: list + """ + return self.get_device_names("TG") + + def get_free_ports(self, dut): + """ + This method gets all the ports that are not connected to either + partner DUT or Traffic Generator + + :param dut: device under test + :type dut: + :return: all the free ports + :rtype: list + """ + return self.free_ports[dut] + + def get_all_ports(self, dut): + """ + This method gets all the ports that are not connected to either + partner DUT or Traffic Generator + + :param dut: device under test + :type dut: + :return: all the free ports + :rtype: list + """ + return self.all_ports[dut] + + def get_service_info(self, dut, name): + """ + todo: Update Documentation + :param name: + :type name: + :param dut: + :type dut: + :return: + :rtype: + """ + return self._context._tb.get_service(dut, name) + + def get_links(self, dut, peer=None): + return self._context._tb.get_links(dut, peer, None) + + def get_dut_links_local(self, dut, peer=None, index=None): + retval = [] + for local, partner, remote in self.get_dut_links(dut, peer): + retval.append(local) + if index is None: + return retval + try: + return [retval[int(index)]] + except: + return [] + + def _get_device_links(self, dut, peer=None, dtype="DUT"): + return self._context._tb.get_links(dut, peer, dtype) + + def get_dut_links(self, dut, peer=None): + return self._get_device_links(dut, peer, "DUT") + + def get_tg_links(self, tg, peer=None): + return self._get_device_links(tg, peer, "TG") + + def get_tg_info(self, tg): + return self._context._tb.get_tg_info(tg) + + def get_device_alias(self, dut): + return self._context._tb.get_device_alias(dut) + + def set_device_alias(self, dut, name): + return self.net.set_device_alias(dut, name) + + def do_rps(self, dut, op, on_delay=None, off_delay=None, recon=True): + """ + This method performs the RPS operations such as on/off/reset. + RPS models supported are Raritan, ServerTech, Avocent + and all are through telnet. + The RPS information is obtained from the testbed file. + :param dut: DUT identifier + :type dut: basestring + :param op: operation i.e. on/off/reset + :return: True if the operation is successful else False + """ + rinfo = self._context._tb.get_rps(dut) + retval = False + if not rinfo: + self.report_env_fail("testbed_no_rps_info") + elif "model" not in rinfo or not rinfo.model or rinfo.model in ["None"]: + self.report_env_fail("testbed_no_rps_model") + elif "ip" not in rinfo or not rinfo.ip: + self.report_env_fail("testbed_no_rps_ip") + elif "outlet" not in rinfo or not rinfo.outlet: + self.report_env_fail("testbed_no_rps_outlet") + elif "username" not in rinfo or rinfo.username is None: + self.report_env_fail("testbed_no_rps_username") + elif not self.cfg.filemode: + if "port" not in rinfo: rinfo.port = 23 + rps = RPS(rinfo.model, rinfo.ip, rinfo.port, rinfo.outlet, + rinfo.username, rinfo.password, desc=str(dut)) + if "pdu_id" in rinfo: rps.set_pdu_id(rinfo.pdu_id) + if recon: self.net.do_pre_rps(dut, op.lower()) + if not rps.do_op(op, on_delay, off_delay): + self.error("Failed to perform RPS {}".format(op)) + retval = True + if recon: self.net.do_post_rps(dut, op.lower()) + else: + retval = True + return retval + + def do_rps_new(self, dut, op, on_delay=None, off_delay=None, recon=True): + dut_list = utils.make_list(dut) + dut_mrinfo = OrderedDict() + for d in dut_list: + rinfo = self._context._tb.get_rps(d) + if not rinfo: + self.report_env_fail("testbed_no_rps_info") + if "model" not in rinfo or not rinfo.model or rinfo.model in ["None"]: + self.report_env_fail("testbed_no_rps_model") + elif "ip" not in rinfo or not rinfo.ip: + self.report_env_fail("testbed_no_rps_ip") + elif "outlet" not in rinfo or not rinfo.outlet: + self.report_env_fail("testbed_no_rps_outlet") + elif "username" not in rinfo or rinfo.username is None: + self.report_env_fail("testbed_no_rps_username") + + if "port" not in rinfo: rinfo.port = 23 + rps = RPS(rinfo.model, rinfo.ip, rinfo.port, rinfo.outlet, + rinfo.username, rinfo.password, desc=str(d)) + if "pdu_id" in rinfo: rps.set_pdu_id(rinfo.pdu_id) + if rps.has_multi_support(): + key = "_".join(map(str,[rinfo.model, rinfo.ip, rinfo.port])) + else: + key = "_".join(map(str,[rinfo.model, rinfo.ip, rinfo.port, d])) + if key not in dut_mrinfo: + dut_mrinfo[key] = [rps, [rinfo.outlet]] + else: + dut_mrinfo[key][1].append(rinfo.outlet) + + if self.cfg.filemode: + return True + + # perform pre-rps operations + if recon: + self._foreach(dut_list, self.net.do_pre_rps, op.lower()) + + def f(key): + [rps, outlets] = dut_mrinfo[key] + if not rps.do_op(op.lower(), on_delay, off_delay, outlets): + self.error("Failed to perform RPS {}".format(op)) + return False + return True + (rvs, exps) = self._foreach(dut_mrinfo.keys(), f) + retval = all(rvs) + + # perform post-rps operations + if recon: + self._foreach(dut_list, self.net.do_post_rps, op.lower()) + + return retval + + def lock_topology(self, spec): + """ + locks the topology to specified specification though + current testbed topology has more than specified + :param spec: needed topology specification + :type spec: basestring + :return: True if the operation is successful else False + :rtype: bool + """ + + def ensure_min_topology(self, *args): + """ + verifies if the current testbed topology satifies the + minimum topology required by test script + :param spec: needed topology specification + :type spec: basestring + :return: True if current topology is good enough else False + :rtype: bool + """ + self.log("ensure_min_topology: {}".format(args)) + self.min_topo_called = True + ftrace("ensure_min_topology", args, current_module.name, current_test.name) + [errs, properties] = self._context._tb.ensure_min_topology(*args) + if not errs: + if None in properties and "CONSOLE_ONLY" in properties[None]: + self.net.set_console_only(True) + return self.get_testbed_vars() + + if self.cfg.pertest_topo_check: + if "dut_down" in errs or "link_down" in errs: + self.report_env_fail("pertest_topology_fail", errs) + + self.report_topo_fail("min_topology_fail", errs) + + def get_testbed_vars(self): + """ + returns the testbed variables in a dictionary + :return: testbed variables dictionary + :rtype: dict + """ + rv = self._context._tb.get_testbed_vars() + for dut in self.get_dut_names(): + for name in self.app_vars[dut]: + if name not in rv: + rv[name] = dict() + rv[name][dut] = self.app_vars[dut][name] + for name in self.module_vars[dut]: + if name not in rv: + rv[name] = dict() + rv[name][dut] = self.module_vars[dut][name] + rv["config"] = self.cfg + return rv + + def clear_tc_results(self): + self._context.tc_results.clear() + + def clear_module_vars(self, dut): + self.module_vars[dut].clear() + + def add_module_vars(self, dut, name, value): + dut_list = [dut] if dut else self.get_dut_names() + for d in dut_list: + self.module_vars[d][name] = value + + def get_mgmt_ip(self, dut): + return self._context.net.get_mgmt_ip(dut) + + def get_config(self, dut, scope="current"): + return self._context._tb.get_config(dut, scope) + + def get_build(self, dut, scope="current"): + return self._context._tb.get_build(dut, scope) + + def get_param(self, name, default): + return self._context._tb.get_param(name, default) + + def get_device_param(self, dut, name, default): + return self._context._tb.get_device_param(dut, name, default) + + def get_link_param(self, dut, local, name, default): + return self._context._tb.get_link_param(dut, local, name, default) + + def tgen_debug_show(self, name=None, tg=None, port=None, msg=""): + for (c, p) in self.get_tgen_handles(name=name, tg=tg, port=port).values(): + if c and p: + c.debug_show(p, msg) + + def get_tgen(self, name, port=None, tg=None): + tbvars = self.get_testbed_vars() + if name is not None: + if name not in tbvars.tgen_ports: + return (None, None) + [tg, ctype, port] = tbvars.tgen_ports[name] + return tgapi.get_tgen(port, tbvars[tg]) + if port is not None: + return tgapi.get_tgen(port, tg) + return (None, None) + + def get_tgen_handles(self, max_ports=0, name=None, tg=None, port=None): + rv = SpyTestDict() + tbvars = self.get_testbed_vars() + if tg is None and port is not None: + return (None, None) + if tg is not None and port is None: + return (None, None) + if tg is not None and port is not None: + return tgapi.get_tgen(port, tg) + count = 0 + for name1, value in tbvars.tgen_ports.items(): + [tg, ctype, port] = value + rv[name1] = tgapi.get_tgen(port, tbvars[tg]) + if max_ports != 0: + count = count + 1 + if count >= max_ports: + break + if name is None: return rv + if name in rv: return rv[name] + return (None, None) + + def get_run_config(self): + return self.cfg + + def get_args(self, arg): + if arg in self.cfg: + return self.cfg[arg] + return None + + def get_ui_type(self, dut=None): + if dut and dut in self.app_vars: + vervars = self.app_vars[dut].get("vervars") + if vervars: + val = vervars.get("UI_TYPE") + if val and self.cfg["ui_type"] != val: + self.dut_log(dut, "UI-TYPE Forced to {}".format(val), logging.DEBUG) + return val + return self.cfg["ui_type"] + + def get_datastore(self, dut, name, scope): + if dut not in self.dmaps: + self.dmaps[dut] = dict() + dmaps = self.dmaps[dut] + if name not in dmaps: + dmaps[name] = DataMap(name) + return dmaps[name].get(scope) + + def exec_ssh(self, dut, username=None, password=None, cmdlist=[]): + return self._context.net.exec_ssh(dut, username, password, cmdlist) + + def exec_remote(self, ipaddress, username, password, scriptpath, wait_factor=2): + return self._context.net.exec_remote(ipaddress, username, password, scriptpath, wait_factor) + + def change_passwd(self, dut, username, password): + return self._context.net.change_passwd(dut, username, password) + + def upload_file_to_dut(self, dut, src_file, dst_file): + return self._context.net.upload_file_to_dut(dut, src_file, dst_file) + + def download_file_from_dut(self, dut, src_file, dst_file): + return self._context.net.download_file_from_dut(dut, src_file, dst_file) + + def set_module_verifier (self, verifier): + verifiers = self.hooks.verifiers() + if verifier not in verifiers: + self.warn("Verifier '{}' is not registered".format(verifier)) + self.current_module_verifier = verifier + + def ansible_dut(self, dut, playbook): + return self._context.net.ansible_dut(dut, playbook) + + def ansible_service(self, service, playbook): + tbvars = self.get_testbed_vars() + service_data = self.get_service_info(tbvars.D1, service) + service_data["filemode"] = self.get_args("filemode") + return self._context.net.ansible_service(service_data, playbook) + + def add_addl_auth(self, dut, username, password): + self._context.net.add_addl_auth(dut, username, password) + + def set_port_defaults(self, dut, breakout=True, speed=True): + + # init applicable ports and arguments + (all_ports, apply_args) = ([], [[],[]]) + + # fill breakout settings arguments + if self.cfg.port_breakout and breakout: + + # get the breakout infor from testbed file + breakout_info = self._context._tb.get_breakout(dut) + if not breakout_info: breakout_info = [] + + for [l_port, l_breakout] in breakout_info: + all_ports.append(l_port) + apply_args[0].append(l_port) + apply_args[0].append(l_breakout) + + # fill speed settings arguments + if self.cfg.port_speed and speed: + + # get the spped infor from testbed file + speed_info = self._context._tb.get_speed(dut) + if not speed_info: speed_info = dict() + + ###################################################### + # remove the speed info for reserved ports + if os.getenv("SPYTEST_SKIP_RESERVED_PORT_SPEED"): + speed_info = copy.copy(speed_info) + for l_port in self.reserved_ports[dut]: + if l_port in speed_info: + msg = "skip speed setting for reserved port {}" + self.dut_log(dut, msg.format(l_port), False, logging.DEBUG) + del speed_info[l_port] + else: + msg = "no speed setting for reserved port {}" + self.dut_log(dut, msg.format(l_port), False) + for l_port in self.connected_ports[dut]: + if l_port in speed_info: + msg = "speed setting for connected port {} = {}" + msg = msg.format(l_port, speed_info[l_port]) + self.dut_log(dut, msg, False, logging.DEBUG) + else: + msg = "no speed setting for connected port {}" + self.dut_log(dut, msg.format(l_port), False, logging.DEBUG) + for l_port in self.free_ports[dut]: + if l_port in speed_info: + msg = "speed setting for free port {} = {}" + self.dut_log(dut, msg.format(l_port, speed_info[l_port]), False) + #msg = "skip speed setting for unconnected port {}" + #self.dut_log(dut, msg.format(l_port), False, logging.DEBUG) + #del speed_info[l_port] + else: + msg = "no speed setting for unconnected port {}" + self.dut_log(dut, msg.format(l_port), False, logging.DEBUG) + ###################################################### + + apply_args[1] = [] + for l_port, l_speed in speed_info.items(): + all_ports.append(l_port) + apply_args[1].append(l_port) + apply_args[1].append(l_speed) + + if all_ports: + # trace interfaces to debug settings before port breakout + self.hooks.get_interface_status(dut, ",".join(all_ports)) + + if apply_args[0] or apply_args[1]: + retval_1 = self.net._apply_remote(dut, "port-defaults", apply_args) + retval_2 = self.ensure_system_ready(dut) + if not retval_1 or not retval_2: + return False + + return True + + def add_prevent(self, what): + return self._context.net.add_prevent(what) + + def instrument(self, dut, scope): + dut_list = [dut] if dut else self.get_dut_names() + for d in dut_list: + inst = self._context._tb.get_instrument(d, scope) + if not inst: + continue + op = inst[0] + rem_args = inst[1:] + if op == "info": + self.log(" ".join(rem_args)) + elif op == "warn": + self.warn(" ".join(rem_args)) + elif op == "sh" or op == "cmds": + self._apply_config_file_list(dut, rem_args) + else: + self.log("INSTRUMENT: {} = {}".format(scope, rem_args)) + + def change_prompt(self, dut, mode, **kwargs): + return self._context.net.change_prompt(dut, mode, **kwargs) + + def cli_config(self, dut, cmd, mode=None, skip_error_check=False, delay_factor=0, **kwargs): + return self._context.net.cli_config(dut, cmd, mode, skip_error_check, delay_factor, **kwargs) + + def cli_show(self, dut, cmd, mode=None, skip_tmpl=False, skip_error_check=False, **kwargs): + return self._context.net.cli_show(dut, cmd, mode, skip_tmpl, skip_error_check, **kwargs) + + def get_config_profile(self): + return self._context._tb.get_config_profile() + + def get_device_type(self, dut): + return self._context._tb.get_device_type(dut) + + def rest_init(self, dut, username, password, altpassword): + return self.net.rest_init(dut, username, password, altpassword) + + def rest_create(self, dut, path, data, *args, **kwargs): + return self.net.rest_create(dut, path, data, *args, **kwargs) + + def rest_update(self, dut, path, data, *args, **kwargs): + return self.net.rest_update(dut, path, data, *args, **kwargs) + + def rest_modify(self, dut, path, data, *args, **kwargs): + return self.net.rest_modify(dut, path, data, *args, **kwargs) + + def rest_read(self, dut, path, *args, **kwargs): + return self.net.rest_read(dut, path, *args, **kwargs) + + def rest_delete(self, dut, path, *args, **kwargs): + return self.net.rest_delete(dut, path, *args, **kwargs) + + def rest_parse(self, dut, filepath=None, all_sections=False, paths=[], **kwargs): + return self.net.rest_parse(dut, filepath, all_sections, paths, **kwargs) + + def rest_apply(self, dut, data): + return self.net.rest_apply(dut, data) + + def parse_show(self, dut, cmd, output): + return self.net.parse_show(dut, cmd, output) + + def show_new(self, dut, cmd, **kwargs): + return self.net.show_new(dut, cmd, **kwargs) + + def config_new(self, dut, cmd, **kwargs): + return self.net.config_new(dut, cmd, **kwargs) + + def exec_ssh_remote_dut(self, dut, ipaddress, username, password, command=None, timeout=30): + return self.net.exec_ssh_remote_dut(dut, ipaddress, username, password, command, timeout) + + def run_uicli_script(self, dut, scriptname): + return self.net.run_uicli_script(dut, scriptname) + + def run_uirest_script(self, dut, scriptname): + return self.net.run_uirest_script(dut, scriptname) + + def run_uignmi_script(self, dut, scriptname, **kwargs): + return self.net.run_uignmi_script(dut, scriptname, **kwargs) + + def generate_tech_support(self, dut, name): + self.net.generate_tech_support(dut, name) + + def get_credentials(self, dut): + return self.net.get_credentials(dut) + + def _trace_cli(self, dut, mode, cmd): + if dut not in self.cli_records: + self.cli_records[dut] = [] + self.cli_records[dut].append([mode, cmd]) + + def _save_cli(self): + if not self.cli_file: + [user_root, logs_path, slave_id] = _get_logs_path() + self.cli_file = get_file_path("cli", "txt", logs_path) + utils.write_file(self.cli_file, "") + for dut, mode_cmd_list in self.cli_records.items(): + for entry in mode_cmd_list: + [mode, cmd] = entry + utils.write_file(self.cli_file, "{},{}\n".format(mode, cmd), "a") + + def dump_all_commands(self, dut, type='click'): + return self.net.dump_all_commands(dut, type) + +def add_options(parser): + group = parser.getgroup("SpyTest") + group.addoption("--testbed-file", action="store", default=None, + metavar="", + help="testbed file path -- default: ./testbed.yaml") + group.addoption("--ignore-tcmap-errors", action="store", default=0, type=int, + choices=[0, 1], help="Ignore errors in tcmap -- default: 0") + group.addoption("--tclist-map", action="store", + default=None, help="use test case map file") + group.addoption("--tclist-bucket", action="append", + default=None, help="use test cases from buckets") + group.addoption("--tclist-file", action="store", + metavar="", + default=None, help="test case list file path") + group.addoption("--tclist-csv", action="store", + metavar="", + default=None, help="test case list csv") + group.addoption("--logs-path", action="store", default=None, + metavar="", + help="logs folder -- default: .") + group.addoption("--file-mode", action="store_true", default=False, + help="Execute in file mode -- default: false") + group.addoption("--quick-test", action="store_true", default=False, + help="Disable options for a quick test -- default: false") + group.addoption("--email", action="append", default=None, + help="Email address(es) to send report to.") + group.addoption("--email-subject", action="store", default="Run Report", + help="Email subject to be used to send report") + group.addoption("--email-attachments", action="store", default=0, type=int, + choices=[0, 1], help="Enable email attachments -- default: 0") + group.addoption("--skip-tgen", action="store_true", default=False, + help="Skip connecting to traffic generator(s) -- default: false") + group.addoption("--tgen-module-init", action="store", default=1, type=int, + choices=[0, 1], help="Call TGEN module init -- default: 1") + group.addoption("--native-port-breakout", action="store", default=1, type=int, + choices=[0, 1], help="Use port breakout script from device -- default: 1") + group.addoption("--port-defaults", action="store", default="both", + choices=['breakout', 'speed', 'both', 'none'], + help="set port defaults -- default: none") + group.addoption("--topology-check", action="store", default="status3", + choices=['abort', 'report', 'skip', 'status', 'status2', 'status3', 'status4'], + help="Topology Check -- default: status3") + group.addoption("--load-config-method", action="store", default="reload", + choices=['reload', 'replace', 'reboot', 'force_reload', 'force_reboot'], + help="Method to be used to load config -- default: reload.") + group.addoption("--skip-init-config", action="store_true", default=False, + help="Skip loading initial configuration before and after execution -- default: false") + group.addoption("--skip-load-config", action="store", default="module", + choices=['base', 'module', 'none'], + help="Skip loading configuration before and after test case execution -- default: false") + group.addoption("--load-image", action="store", default="onie", + choices=['installer', 'onie', "none", "installer-without-migration"], + help="Loading image before and after execution using specified method -- default: onie") + group.addoption("--skip-verify-config", action="store", default="none", choices=['base', 'module', "both", "none"], + help="Skip verifying base and/or module configuration") + group.addoption("--ignore-dep-check", action="store", default=0, type=int, + choices=[0, 1], help="Ignore depends mark in test cases -- default: 0") + group.addoption("--memory-check", action="store", default="none", + choices=['test', 'module', 'none'], + help="read memory usage default: none") + group.addoption("--syslog-check", action="store", default="err", + choices=syslog_levels, + help="read syslog messages of given level and clear all syslog messages default: err") + group.addoption("--save-sairedis", action="store", default="none", + choices=["none", "test", "module"], help="read sairedis messages") + group.addoption("--faster-init", action="store", default=1, type=int, + choices=[0, 1], help="Enable speeding up initialization -- default: 1") + group.addoption("--faster-cli", action="store", default=0, type=int, + choices=[0, 1, 2, 3], help="Enable speeding up CLI -- default: 0") + group.addoption("--port-init-wait", action="store", type=int, default=300, + help="Wait time in seconds for ports to come up -- default: 300") + group.addoption("--reboot-wait", action="store", type=int, default=0, + help="Wait time in seconds for ports to come up -- default: 0") + group.addoption("--fetch-core-files", action="store", default="session", + choices=['always', 'onfail', "none", "onerror", "session", \ + "onfail-epilog", + "module-always", "module-onfail", "module-onerror"], + help="Fetch the core files from DUT to logs location -- default: session") + group.addoption("--get-tech-support", action="store", default="onfail-epilog", + choices=['always', 'onfail', "none", "onerror", "session", \ + "onfail-epilog", + "module-always", "module-onfail", "module-onerror"], + help="Get the tech-support information from DUT to logs location -- default: session") + group.addoption("--pertest-topo-check", action="store", default=0, type=int, + choices=[0, 1], help="Topology check per testcase -- default: 0") + group.addoption("--tc-max-timeout", action="store", type=int, default=600, + help="Max time that a testcase can take to execute -- default: 600") + group.addoption("--module-init-max-timeout", action="store", type=int, default=1200, + help="Max time that a module initialization can take to execute -- default: 1200") + group.addoption("--results-prefix", action="store", default=None, + help="Prefix to be used for results") + group.addoption("--exclude-devices", action="store", default=None, + help="exclude given duts from testbed") + group.addoption("--include-devices", action="store", default=None, + help="include given duts from testbed") + group.addoption("--run-progress-report", action="store", default=0, + type=int, help="send run progress report at given frequency") + group.addoption("--env", action="append", default=[], + nargs=2, help="environment variables") + group.addoption("--random-order", action="store", default=1, type=int, + choices=[0, 1], help="Enable executing tests in random order -- default: 1") + group.addoption("--rps-reboot", action="store", default=None, + metavar="", + help="Reboot given devices using RPS") + group.addoption("--pde", action="store_true", default=False, + help="PDE image support -- default: false") + group.addoption("--community-build", action="store", default=0, type=int, + choices=[0, 1], help="Community build support -- default: 0") + group.addoption("--tryssh", action="store", default=0, type=int, + choices=[0, 1, 2, 3], help="Try executing through SSH -- default: 0") + group.addoption("--flex-dut", action="store", default=1, type=int, + choices=[0, 1], help="Rearrange DUT based on min topology - default: 1") + group.addoption("--first-test-only", action="store_true", default=False, + help="Execute only first test in each module - default: false") + group.addoption("--config-profile", action="store", default=None, + choices=['l2', 'l3', 'NA'], help="Profile to load - default: None") + group.addoption("--build-url", action="store", default=None, + help="Profile to load - default: None") + group.addoption("--clear-tech-support", action="store", default=0, type=int, + choices=[0, 1], help="Clears tech support data on the dut -- default: 0") + group.addoption("--module-epilog-tgen-cleanup", action="store", default=1, type=int, + choices=[0, 1], help="Enable TGEN cleanup in module epilog") + group.addoption("--module-epilog", action="store", default=1, type=int, + choices=[0, 1], help="Enable module epilog") + group.addoption("--graceful-exit", action="store", default=1, type=int, + choices=[0, 1], help="Graceful exit on control+c") + group.addoption("--reuse-results", action="store", default="none", + choices=['none', 'all', 'pass', 'allpass'], + help="Reuse results from previous execution") + group.addoption("--dev-prop", action="append", default=[], nargs=2, + help="override device property in the testbed") + group.addoption("--ixserver", action="append", default=[], + help="override ixnetwork server") + group.addoption("--ui-type", action="store", default="click", + choices=['click', 'klish', 'klishplus'], + help="CLI type needed in scripts execution") + +def get_tgen_utils(): + from spytest.tgen import tgen_utils + return tgen_utils + +def get_work_area(): + return gWorkArea + +def set_work_area(val): + global gWorkArea + gWorkArea = val + +def _create_work_area2(config): + cfg = SpyTestDict() + cfg.filemode = config.getoption("--file-mode") + cfg.testbed = config.getoption("--testbed-file") + cfg.logs_path = config.getoption("--logs-path") + cfg.log_lvl = config.getoption("--log-level") + cfg.tclist_file = config.getoption("--tclist-file") + cfg.tclist_map = config.getoption("--tclist-map") + cfg.tclist_bucket = config.getoption("--tclist-bucket", None) + cfg.tclist_csv = config.getoption("--tclist-csv", None) + cfg.email_csv = config.getoption("--email") + if cfg.email_csv: + cfg.email_csv = ",".join(cfg.email_csv) + cfg.email_subject = config.getoption("--email-subject", "Run Report") + cfg.email_attachments = bool(config.getoption("--email-attachments", 0)) + cfg.skip_tgen = config.getoption("--skip-tgen") + cfg.tgen_module_init = bool(config.getoption("--tgen-module-init", 1)) + cfg.load_config_method = config.getoption("--load-config-method") + cfg.topology_check = config.getoption("--topology-check", "skip") + cfg.port_defaults = config.getoption("--port-defaults") + cfg.native_port_breakout = config.getoption("--native-port-breakout") + cfg.port_breakout = bool(cfg.port_defaults in ["breakout", "both"]) + cfg.port_speed = bool(cfg.port_defaults in ["speed", "both"]) + cfg.skip_init_config = config.getoption("--skip-init-config") + cfg.skip_load_config = config.getoption("--skip-load-config") + cfg.load_image = config.getoption("--load-image", "onie") + cfg.ignore_dep_check = bool(config.getoption("--ignore-dep-check")) + cfg.memory_check = config.getoption("--memory-check") + cfg.syslog_check = config.getoption("--syslog-check") + cfg.save_sairedis = config.getoption("--save-sairedis") + cfg.faster_init = bool(config.getoption("--faster-init", 1)) + cfg.port_init_wait = config.getoption("--port-init-wait") + cfg.reboot_wait = config.getoption("--reboot-wait") + cfg.poll_for_ports = bool(config.getoption("--poll-for-ports", 1)) + cfg.fetch_core_files = config.getoption("--fetch-core-files") + cfg.get_tech_support = config.getoption("--get-tech-support") + cfg.skip_verify_config = config.getoption("--skip-verify-config") + cfg.pertest_topo_check = bool(config.getoption("--pertest-topo-check", 0)) + cfg.tc_max_timeout = config.getoption("--tc-max-timeout") + cfg.module_max_timeout = config.getoption("--module-init-max-timeout") + cfg.results_prefix = config.getoption("--results-prefix") + cfg.exclude_devices = config.getoption("--exclude-devices") + cfg.include_devices = config.getoption("--include-devices") + cfg.run_progress_report = config.getoption("--run-progress-report", 0) + cfg.rps_reboot = config.getoption("--rps-reboot", None) + cfg.pde = config.getoption("--pde", False) + cfg.community_build = config.getoption("--community-build", False) + cfg.flex_dut = bool(config.getoption("--flex-dut", 1)) + cfg.first_test_only = config.getoption("--first-test-only", False) + cfg.tryssh = config.getoption("--tryssh", 0) + cfg.env = config.getoption("--env", []) + cfg.pdb_on_error = config.getoption("--pdb", False) + x_flag = config.getoption("-x", False) + exitfirst_flag = config.getoption("--exitfirst", False) + cfg.exit_on_firstfail = True if x_flag or exitfirst_flag else False + cfg.maxfail = config.getoption("--maxfail", 0) + cfg.faster_cli = config.getoption("--faster-cli", 0) + cfg.tc_log_support = config.getoption("--tc-log", False) + cfg.config_profile = config.getoption("--config-profile") + cfg.build_url = config.getoption("--build-url") + cfg.clear_tech_support = bool(config.getoption("--clear-tech-support", 0)) + cfg.module_epilog_tgen_cleanup = config.getoption("--module-epilog-tgen-cleanup") + cfg.module_epilog = config.getoption("--module-epilog") + cfg.graceful_exit = config.getoption("--graceful-exit") + cfg.reuse_results = config.getoption("--reuse-results") + cfg.dev_prop = config.getoption("--dev-prop", []) + cfg.ixserver = config.getoption("--ixserver", []) + cfg.ui_type = config.getoption("--ui-type", "click") + + cfg.skip_load_image = bool(cfg.load_image == "none") + + if cfg.pde: + cfg.skip_init_config = True + cfg.skip_load_config = "base" + os.environ["SPYTEST_SKIP_INIT_COMMANDS"] = "1" + + if config.getoption("--quick-test", False): + cfg.skip_load_image = True + cfg.fetch_core_files = "none" + cfg.get_tech_support = "none" + cfg.syslog_check = "none" + cfg.memory_check = "none" + cfg.save_sairedis = "none" + cfg.skip_load_config = "base" + cfg.skip_init_config = True + cfg.port_breakout = False + cfg.port_speed = False + os.environ["SPYTEST_SKIP_INIT_COMMANDS"] = "1" + os.environ["SPYTEST_KDUMP_ENABLE"] = "0" + + if cfg.community_build: + cfg.port_init_wait = 240 + os.environ["SPYTEST_KDUMP_ENABLE"] = "0" + + wa = get_work_area() + if not wa: + wa = WorkArea(cfg) + set_work_area(wa) + return wa + +def _create_work_area(request): + wa = _create_work_area2(request.config) + start = get_timenow() + wa._session_init() + wa.session_init_completed = True + end = get_timenow() + wa.log("session started in {} seconds".format(end - start)) + +def _delete_work_area(): + wa = get_work_area() + if not wa: + return + wa._session_clean() + [user_root, logs_path, slave_id] = _get_logs_path() + if not batch.is_member(): + # master or standalone + consolidate_results() + if not batch.is_slave(): + # NOTE: standalone is handled somewhere else + wa._context.email() + else: + wa._context._cleanup_gracefully() + set_work_area(None) + bg_results.stop() + +def log_test_exception(excinfo, hook='test_function'): + dtrace("log_test_exception", excinfo) + wa = get_work_area() + if not wa or not wa._context.log: + return + root = os.path.join(os.path.dirname(__file__), '..') + root = os.path.abspath(root) + dicts = os.path.join(root, "dicts.py") + ex_msg = "{} {}".format(excinfo.typename, excinfo.value) + msg = "Exception: {}".format(ex_msg) + wa._context.log.error(msg) + entries = traceback.extract_tb(excinfo.tb) + index = 0 + has_exp = False + desc = "" + for item in reversed(entries): + fname, line, func, text = item + if not fname.startswith(root): + continue + if not has_exp: + if fname != dicts: + if hook == 'test_module': + exp_msg = wa._context.result.build_msg("exception_name_file_line", ex_msg, fname, line) + desc = wa._context.report("ConfigFail", "module_config_failed", exp_msg) + else: + desc = wa._context.report("ScriptError", "exception_name_file_line", ex_msg, fname, line) + has_exp = True + msg = "[{}] {}:{} {} {}".format(index, fname, line, func, text) + index = index + 1 + wa._context.log.error(msg) + for dut in wa.get_dut_names(): + wa._context.net.dut_log(dut, msg, True, logging.ERROR) + return desc + +def _build_tclist_file(config): + file_name = config.getoption("--tclist-file") + if not file_name: + file_name = os.getenv("SPYTEST_TCLIST_FILE", None) + if not file_name: + return None + + [user_root, logs_path, slave_id] = _get_logs_path() + if os.path.isfile(file_name): + file_path = file_name + else: + file_path = os.path.join(user_root, file_name) + if not os.path.isfile(file_path): + print("Failed to locate test case list file {}".format(file_name)) + os._exit(8) + + with utils.open_file(file_path) as fh: + test_names = [] + for test_name in fh: + test_name = test_name.strip() + if test_name and not test_name.startswith("#"): + test_names.append(test_name) + if len(test_names) <= 0: + msg = "no test cases are specified in test case list file" + print("{} {}".format(msg, file_name)) + os._exit(9) + + return set(test_names) + +def _add_tcmap_entry(age, cadence, comp, tcid, func): + if tcid in tcmap.cadence: + msg = "duplicate test case id {}" + tcmap.errors.append(msg.format(tcid)) + if func not in tcmap.tclist: + tcmap.tclist[func] = [] + if tcid not in tcmap.tclist[func]: + tcmap.tclist[func].append(tcid) + else: + msg = "duplicate test case id {}" + tcmap.errors.append(msg.format(tcid)) + tcmap.comp[tcid] = comp + tcmap.cadence[tcid] = cadence + tcmap.func[tcid] = func + +def _load_csv(csv_file, path): + path = os.path.join(os.path.dirname(__file__), '..', path) + csv_file = os.path.join(os.path.abspath(path), csv_file) + + if os.path.exists(csv_file): + filepath = csv_file + else: + return [] + rows = [] + with open(filepath, 'r') as fd: + for row in csv.reader(fd): + rows.append(row) + fd.close() + return rows + +def _load_tcmap(verify=True, items=None): + tcmap.tclist = OrderedDict() + tcmap.comp = OrderedDict() + tcmap.cadence = OrderedDict() + tcmap.func = OrderedDict() + tcmap.modules = OrderedDict() + tcmap.module_max_timeout = OrderedDict() + tcmap.faster_cli = OrderedDict() + tcmap.tryssh = OrderedDict() + tcmap.tc_max_timeout = OrderedDict() + tcmap.errors = [] + + for row in _load_csv("faster_cli.csv", "reporting"): + if len(row) < 2: continue + fcli, name = [str(i).strip() for i in row[:2]] + if fcli.strip().startswith("#"): continue + name = os.path.basename(name) + tcmap.faster_cli[name] = utils.integer_parse(fcli, 0) + + for row in _load_csv("tryssh.csv", "reporting"): + if len(row) < 2: continue + tryssh, name = [str(i).strip() for i in row[:2]] + if tryssh.strip().startswith("#"): continue + name = os.path.basename(name) + tcmap.tryssh[name] = utils.integer_parse(tryssh, 0) + + for row in _load_csv("module_max_time.csv", "reporting"): + if len(row) < 2: continue + time, name = [str(i).strip() for i in row[:2]] + if time.strip().startswith("#"): continue + name = os.path.basename(name) + tcmap.module_max_timeout[name] = utils.integer_parse(time, 0) + + for row in _load_csv("test_max_time.csv", "reporting"): + if len(row) < 2: continue + time, name = [str(i).strip() for i in row[:2]] + if time.strip().startswith("#"): continue + tcmap.tc_max_timeout[name] = utils.integer_parse(time, 0) + + tcmap_csv = os.getenv("SPYTEST_TCMAP_CSV_FILENAME", "tcmap.csv") + for row in _load_csv(tcmap_csv, "reporting"): + if len(row) == 4: + # TODO treat the data as module + (age, cadence, comp, name) = (row[0], row[1], row[2], row[3]) + if name in tcmap.modules: + msg = "duplicate module {}" + tcmap.errors.append(msg.format(name)) + continue + module = SpyTestDict() + module.age = age + module.cadence = cadence + module.comp = comp + module.name = name + tcmap.modules[name] = module + continue + if len(row) < 5: + print("Invalid line", row) + continue + (age, cadence, comp, tcid, func) = (row[0], row[1], row[2], row[3], row[4]) + if age.strip().startswith("#"): + continue + _add_tcmap_entry(age, cadence, comp, tcid, func) + + # verify the tcmap if required + if verify: _verify_tcmap(items) + +def _verify_tcmap(items=None): + + # expand the modules + for name, module in tcmap.modules.items(): + if not items: continue + for item in items: + if item.location[0] != name: + if item.location[0] != os.path.basename(name): + continue + tc = item.location[2] + # use function name for TC + _add_tcmap_entry(module.age, module.cadence, module.comp, tc, tc) + + # check if any function mapped in multiple cadences + for func, tcid_list in tcmap.tclist.items(): + cadences = dict() + for tcid in tcid_list: + cadences[tcmap.cadence[tcid]] = 1 + if len(cadences) > 1: + msg = "function {} is mapped to {} testcases in multiple cadences {}" + tcmap.errors.append(msg.format(func, len(tcid_list), cadences)) + + # check if any function mapped in multiple components + for func, tcid_list in tcmap.tclist.items(): + components = dict() + for tcid in tcid_list: + components[tcmap.comp[tcid]] = 1 + if len(components) > 1: + msg = "function {} is mapped to {} testcases in multiple components {}" + #TODO: enable this once the issues are fixed in tcmap.csv + #tcmap.errors.append(msg.format(func, len(tcid_list), components.keys())) + print(msg.format(func, len(tcid_list), components.keys())) + + _show_tcmap_errors() + +def _show_tcmap_errors(): + if tcmap.errors: + print("===== TCMAP Errors ======") + print("\n".join(tcmap.errors)) + print("========================") + +def _build_tclist_map(config, items): + use_cadence = config.getoption("--tclist-map", None) + if not use_cadence: return None + use_cadences = use_cadence.replace(",", ' ').split() + if not use_cadences: return None + test_names = [] + for use_cadence in use_cadences: + for name, module in tcmap.modules.items(): + if use_cadence == "all" or module.cadence == use_cadence: + for item in items: + if item.location[0] == name: + func = item.location[2] + if func not in test_names: + test_names.append(func) + for tcid, cadence in tcmap.cadence.items(): + if use_cadence == "all" or cadence == use_cadence: + func = tcmap.func[tcid] + if func not in test_names: + test_names.append(func) + + if len(test_names) <= 0: + msg = " no '{}' test cases found in test case map file" + print(msg.format(use_cadence)) + os._exit(9) + + if tcmap.errors: + _show_tcmap_errors() + if not config.getoption("--ignore-tcmap-errors", 0): + os._exit(9) + + return set(test_names) + +def _build_selected_tests(items, test_names): + global missing_test_names_msg + seen_test_names = set() + selected_items = [] + deselected_items = [] + for item in items: + alt_item_name = item.location[2][5:] + if item.location[2] in test_names or item.nodeid in test_names: + selected_items.append(item) + elif alt_item_name in test_names: + selected_items.append(item) + seen_test_names.add(alt_item_name) + else: + deselected_items.append(item) + seen_test_names.add(item.location[2]) + seen_test_names.add(item.nodeid) + + # trace missing tests + missing_test_names = test_names - seen_test_names + if missing_test_names: + message = ("Ignoring below missing test list:\n - ") + message += "\n - ".join(missing_test_names) + print(message) + missing_test_names_msg = message + return selected_items, deselected_items + +def shuffle_items(items): + files = OrderedDict() + for item in items: + module = getattr(item, "module", None) + if module.__file__ not in files: + files[module.__file__] = [] + files[module.__file__].append(item) + names = list(files.keys()) + seed = int(os.getenv("SPYTEST_RAMDOM_SEED", "100")) + from random import Random + Random(seed).shuffle(names) + new_items = [] + for name in names: + new_items.extend(files[name]) + items[:] = new_items + +def modify_tests(config, items): + test_names = [] + + # create PID file + create_pid_file() + + # load the tcmap - verify later + _load_tcmap(False) + + # get the test names from CSV if specified + tclist_method = "csv" + tclist_csv = config.getoption("--tclist-csv", None) + if tclist_csv: + test_names = tclist_csv.replace(",", ' ').split() + if not test_names: + print("no test cases are specified in test case csv {}".format(tclist_csv)) + os._exit(10) + test_names = set(test_names) + + # --tclist-csv superceeds --tclist-file + if not test_names: + tclist_method = "file" + test_names = _build_tclist_file(config) + + # --tclist-file superceeds --tclist-map + if not test_names: + tclist_method = "map" + test_names = _build_tclist_map(config, items) + + if test_names: + selected, deselected = _build_selected_tests(items, test_names) + items[:] = selected + config.hook.pytest_deselected(items=deselected) + if tclist_method == "map": + utils.banner("deselected tests cases", func=ftrace) + for item in deselected: + ftrace(item.nodeid) + utils.banner(None, func=ftrace) + + # verify the tcmap + _verify_tcmap(items) + + # ignore the test cases that are already completed + exclude_executed_tests(config, items) + + # check for known markers + if not config.getoption("--ignore-known-markers", 0): + read_known_markers(config, items) + + # add the dependency + if not config.getoption("--ignore-dep-check", 0): + build_dependency(config, items) + + # shuffile ine items for random order + if config.getoption("--random-order", 1): + shuffle_items(items) + +def get_result_files(logs_path): + (csv_files, retval) = ([], []) + if batch.is_slave(): + csv_files.extend(glob.glob("{}/../*_result_all.csv".format(logs_path))) + tc_index = 2 + else: + csv_files.extend(glob.glob("{}/*_result.csv".format(logs_path))) + tc_index = 1 + for csv_file in csv_files: + abs_path = os.path.abspath(csv_file) + if abs_path not in retval: + retval.append(abs_path) + return [tc_index, retval] + +def exclude_executed_tests(config, items): + + if batch.is_master(): + return + + reuse_results = config.getoption("--reuse-results", None) + if reuse_results in ["none"]: + return + + [user_root, logs_path, slave_id] = _get_logs_path() + [tc_index, csv_files] = get_result_files(logs_path) + + # prepare reused test case list + reused_results = OrderedDict() + for csv_file in csv_files: + for row in Result.read_report_csv(csv_file): + if reuse_results in ["all"]: + reused_results[row[tc_index]] = 1 + elif reuse_results in ["pass", "allpass"] and \ + row[tc_index+1] in ["Pass"]: + reused_results[row[tc_index]] = 1 + + # prepare reused module list + selected_modules = dict() + for item in items: + if item.location[2] not in reused_results: + selected_modules[item.location[0]] = 1 + + # prepare selected items based on reused module list + reused_test_results.clear() + new_selected = OrderedDict() + for item in items: + if item.location[0] in selected_modules: + if reuse_results in ["allpass"]: + # current test is in selected tests and + # we are excluding only all pass + new_selected[item.location[2]] = item + continue + if item.location[2] not in reused_results: + new_selected[item.location[2]] = item + else: + reused_test_results[item.location[2]] = 1 + items[:] = new_selected.values() + +def collect_test(item): + dtrace("collect_test", item) + collected_items[item.location[2]] = item + nodeid_test_names[item.nodeid] = item.location[2] + +def add_dep_item(item, items): + marker = item.get_closest_marker("depends") + if marker: + for dep_name in marker.args: + if dep_name not in items: + if dep_name not in collected_items: + # this is the case of using --count option + msg = "item {} dependency {} not found in collected".format(item, dep_name) + if not item.originalname or not item.name: + print(msg) + continue + repeat = item.name.replace(item.originalname,"") + dep_name = "{}{}".format(dep_name, repeat) + if dep_name not in collected_items: + print(msg) + continue + add_item = collected_items[dep_name] + add_dep_item(add_item, items) + if add_item not in items.values(): + items[dep_name] = add_item + if item not in items.values(): + items[item.location[2]] = item + +def build_dependency(config, items): + selected_test_items.clear() + for item in items: + add_dep_item(item, selected_test_items) + items[:] = selected_test_items.values() + for item in items: + selected_test_results[item.location[2]] = None + +def check_dependency(name): + errs = [] + if name not in selected_test_items: + errs.append("some thing is wrong - failed to find test item") + return errs + item = selected_test_items[name] + marker = item.get_closest_marker("depends") + if not marker: + return None + for dep_name in marker.args: + if selected_test_results[dep_name] != "Pass": + errs.append(dep_name) + return errs + +def read_known_markers(config, items): + must_fail_items.clear() + for item in items: + marker = item.get_closest_marker("must_fail") + if marker: + must_fail_items[item.location[2]] = None + +current_test.excinfo = None +current_test.hook = "" +current_test.name = "" +current_test.result = "" +current_test.result_desc = "" +current_module.start_time = None +current_module.end_time = None +current_module.time_taken = None +current_module.epilog_start = None +current_module.global_module_finished = True +current_module.global_module_finalized = True +current_module.user_module_finished = True +current_module.user_module_finalized = True +current_module.name = "" +current_module.result = "" +current_module.result_desc = "" +def set_current_result(res=None, desc=""): + if not desc or not res: + current_module.result_desc = "" + current_module.result = "ConfigFail" + current_test.result_desc = "" + current_test.result = "ConfigFail" + elif current_test.hook == "test_module": + if not current_module.result_desc: + current_module.result_desc = desc + current_module.result = res + elif current_test.hook == "test_function": + if not current_test.result_desc: + current_test.result_desc = desc + current_test.result = res + +def log_module_time_start(): + time_now = get_timenow() + current_module.start_time = time_now + current_module.time_taken = None + wa = get_work_area() + wa._context.net.tc_start(time_now) + +def log_module_time_finish(nodeid=None, func_name=None): + dtrace("log_module_time_finish", nodeid, func_name, + current_module.start_time, current_module.time_taken) + + if not current_module.time_taken and current_module.start_time: + current_module.end_time = get_timenow() + current_module.time_taken = get_elapsed(current_module.start_time, True, min_time) + + wa = get_work_area() + + if nodeid and not func_name and current_module.time_taken: + wa._write_stats(nodeid, "", "Module Configuration", current_module.time_taken) + + if nodeid and func_name and current_module.time_taken: + wa._context.publish2(nodeid, None, None, current_module.time_taken, None, "", "Module Prolog") + wa.log_time("Module Config Time ({} - {}) Published".format(current_module.start_time, current_module.end_time)) + current_module.time_taken = None + current_module.start_time = None + +def make_report(item, call): + dtrace("make_report", item, call, call.excinfo) + if call.excinfo and call.excinfo.typename not in [ "XFailed", "Skipped"]: + current_test.excinfo = call.excinfo + else: + current_test.excinfo = None + +def log_report(report): + wa = get_work_area() + worker_id = getattr(report, "worker_id", None) + if worker_id: + log_report_master(report, wa) + else: + log_report_slave(report, wa) + +def log_report_slave(report, wa): + [user_root, logs_path, slave_id] = _get_logs_path() + + if report.nodeid in nodeid_test_names: + func_name = nodeid_test_names[report.nodeid] + else: + func_name = None + + # record module finish time + dtrace("log_report", report, func_name, current_test) + if report.when == "setup" and func_name and wa: + log_module_time_finish(report.nodeid, func_name) + + # fail tests when the module config is failed + if report.when == "setup" and report.outcome != "passed" and wa and func_name: + if not current_test.excinfo: + wa._test_log_init(report.nodeid, func_name) + if current_test.hook == "test_function": + desc = wa._context.set_default_error("ConfigFail", "pretest_config_failed") + elif wa.abort_module_msg: + [res, desc] = ['SKIPPED', wa.abort_module_msg] + desc = wa._context.report(res, "msg", desc) + wa._test_log_finish(report.nodeid, func_name, res, desc, min_time) + else: + [res, desc] = [current_module.result, current_module.result_desc] + desc = wa._context.report(res, "module_config_failed", desc) + wa._test_log_finish(report.nodeid, func_name, res, desc, min_time) + else: + if current_test.hook != "test_function": + wa._test_log_init(report.nodeid, func_name) + desc = log_test_exception(current_test.excinfo, current_test.hook) + wa._test_log_finish(report.nodeid, func_name, "ConfigFail", desc, min_time) + + if report.when == "setup" and report.outcome == "passed" and wa: + wa.event("Test Execution:", report.nodeid) + +def log_report_master(report, wa): + if report.when == "teardown": + consolidate_results(wa.cfg.run_progress_report, True) + +def session_start(session): + if os.getenv("SPYTEST_LIVE_RESULTS", "1") == "1": + bg_results.start(consolidate_results) + +def get_rate(val, total): + if total: + return '{:.2%}'.format(val*1.0/total) + return "0.00%" + +def read_all_results(logs_path, suffix, offset): + + def csv_base_file(val): + val = os.path.basename(val) + return val + + # get all the result file paths + csv_files = glob.glob("{}/gw*/*_{}.csv".format(logs_path, suffix)) + csv_files.sort(key=csv_base_file) + + # prepare the map for module result and result file + module_result = OrderedDict() + rows_dict = dict() + for csv_file in csv_files: + rows = Result.read_report_csv(csv_file) + rows_dict[csv_file] = rows + for row in rows: + module_result[row[offset]] = csv_file + + # Results + [results, links] = ([], []) + for csv_file in csv_files: + rows = rows_dict[csv_file] + gw_name = os.path.basename(os.path.dirname(csv_file)) + for row in rows: + if module_result[row[offset]] == csv_file: + gw_logs = find_log_path(row[offset], csv_file, gw_name) + new_row = [gw_name] + new_row.extend(row) + results.append(new_row) + links.append(gw_logs) + + return [results, links] + +def consolidate_results(progress=None, thread=False, count=None): + + # generate email report + generate_email_report(count) + + [user_root, logs_path, slave_id] = _get_logs_path() + count = batch.get_member_count() if count is None else count + if slave_id or count < 1 or not batch.is_batch(): + return + + # if threaded set event + if thread and bg_results.is_valid(): + bg_results.run() + return + + # check if we really need to do this + if progress is not None and progress <= 0: + return + + # Func Results + [results, links] = read_all_results(logs_path, "result", 0) + if links: + for i,row in enumerate(results): row.append(links[i]) + consolidated = sorted(results, key=itemgetter(5)) + if links: + for i,row in enumerate(consolidated): links[i] = row.pop() + results_csv = get_results_csv(logs_path, True) + Result.write_report_csv(results_csv, consolidated, 0) + generate_module_report(results_csv, 1, links) + html_file = os.path.splitext(results_csv)[0]+'.html' + Result.write_report_html(html_file, consolidated, 0, True, 4) + wa = get_work_area() + if wa and wa._context: + wa._context.run_progress_report(len(consolidated)) + + # TC Results + [results, links] = read_all_results(logs_path, "tcresult", 7) + if links: + for i,row in enumerate(results): row.append(links[i]) + consolidated = sorted(results, key=itemgetter(5)) + if links: + for i,row in enumerate(consolidated): links[i] = row.pop() + tcresults_csv = get_tc_results_csv(logs_path, True) + Result.write_report_csv(tcresults_csv, consolidated, 1) + generate_component_report(results_csv, tcresults_csv, 1) + html_file = os.path.splitext(tcresults_csv)[0]+'.html' + Result.write_report_html(html_file, consolidated, 1, True, 4) + + # syslog Results + [results, links] = read_all_results(logs_path, "syslog", 1) + if links: + for i,row in enumerate(results): row.append(links[i]) + consolidated = sorted(results, key=itemgetter(5)) + if links: + for i,row in enumerate(consolidated): links[i] = row.pop() + syslog_csv = get_syslog_csv(logs_path, True) + Result.write_report_csv(syslog_csv, consolidated, 2) + html_file = os.path.splitext(syslog_csv)[0]+'.html' + Result.write_report_html(html_file, consolidated, 2, True) + + # Stats + [consolidated, links] = read_all_results(logs_path, "stats", 0) + csv_file = get_file_path("stats", "csv", logs_path, True) + Result.write_report_csv(csv_file, consolidated, 3) + html_file = os.path.splitext(csv_file)[0]+'.html' + Result.write_report_html(html_file, consolidated, 3, True) + +def generate_email_report_files(files, nodes, report_html): + + count = len(files) + reports_header = ["Stat"] + if not nodes: + reports_header.append("Value") + else: + for node in nodes: + reports_header.append(node) + + if count > 1: + reports_header.append("Consolidated") + + report_data = OrderedDict() + for key in report_cols: + row = [key] # stat name + for index in range(0, count): + row.append("0") # node stat + if count > 1: + row.append("0") #total + report_data[key] = row + + all_reports = [] + + # fill the stat values + for index in range(0, count): + report_file = files[index] + if os.path.exists(report_file): + lines = utils.read_lines(report_file) + rows = [i for i in lines if i] + for row in rows: + (key, val) = row.split('=') + key = key.strip() + val = val.strip() + if key == "Software Version": + set_mail_build(val) + if key in report_data: + report_data[key][index+1] = val + + # compute totals + (pass_count, tc_count) = (0, 0) + for key in report_cols: + if count <= 1: + pass + elif "Execution Started" in key: + first_started = "NA" + for ele in report_data[key][1:]: + date_time_obj = utils.date_parse(ele) + if date_time_obj is None: + continue + if first_started == "NA" or date_time_obj < first_started: + first_started = date_time_obj + report_data[key][count+1] = str(first_started) + elif "Execution Completed" in key: + last_completed = "NA" + for ele in report_data[key][1:]: + date_time_obj = utils.date_parse(ele) + if date_time_obj is None: + continue + if last_completed == "NA" or date_time_obj > last_completed: + last_completed = date_time_obj + report_data[key][count+1] = str(last_completed) + elif "Execution Time" in key: + first_started = report_data["Execution Started"][count+1] + last_completed = report_data["Execution Completed"][count+1] + try: + first_started = utils.date_parse(first_started) + last_completed = utils.date_parse(last_completed) + exec_time = utils.time_diff(first_started, last_completed, True) + report_data[key][count+1] = str(exec_time) + except: + report_data[key][count+1] = "NA" + elif "Session Init Time" in key: + max_init_time = 0 + for ele in report_data[key][1:]: + if ele != "0" and ele != "": + (h,m,s) = ele.split(':') + tmp_secs = int(h) * 3600 + int(m) * 60 + int(s) + if tmp_secs > max_init_time: + max_init_time = tmp_secs + report_data[key][count+1] = utils.time_format(max_init_time) + elif "Tests Time" in key: + total_secs = 0 + for ele in report_data[key][1:]: + if ele != "0" and ele != "": + (h,m,s) = ele.split(':') + total_secs += int(h) * 3600 + int(m) * 60 + int(s) + report_data[key][count+1] = utils.time_format(total_secs) + elif key in result_vals or key == "Module Count": + total = sum([int(i) for i in report_data[key][1:]]) + report_data[key][count+1] = total + elif key in result_vals or key == "Function Count": + total = sum([int(i) for i in report_data[key][1:]]) + report_data[key][count+1] = total + elif key in result_vals or key == "Test Count": + tc_count = sum([int(i) for i in report_data[key][1:]]) + report_data[key][count+1] = tc_count + elif key in result_vals or key == "Pass Count": + pass_count = sum([int(i) for i in report_data[key][1:]]) + report_data[key][count+1] = pass_count + elif key in result_vals or key == "Pass Rate": + report_data[key][count+1] = get_rate(pass_count, tc_count) + elif key in result_vals or key == "SysLog Count": + total = sum([int(i) for i in report_data[key][1:]]) + report_data[key][count+1] = total + else: + report_data[key][count+1] = "NA" + + for key in report_cols: + all_reports.append(report_data[key]) + + if len(all_reports) < len(reports_header): + (rows, cols) = ([], [""]) + for row in all_reports: cols.append(row[0]) + for col_index in range(1, len(reports_header)): + new_row = [reports_header[col_index]] + for row in all_reports: + new_row.append(row[col_index]) + rows.append(new_row) + report_status = utils.write_html_table2(cols, rows) + else: + report_status = utils.write_html_table2(reports_header, all_reports) + with open(report_html, "w") as ofh: + ofh.write("\n\n{}\n".format(report_status)) + +def generate_email_report(count=None): + [user_root, logs_path, slave_id] = _get_logs_path() + if slave_id: + return + + count = batch.get_member_count() if count is None else count + if count <= 1 and not batch.is_batch(): + report_txt = get_report_txt(logs_path) + report_html = os.path.splitext(report_txt)[0]+'.html' + return generate_email_report_files([report_txt], [], report_html) + + report_txt = get_report_txt(logs_path, True) + report_html = os.path.splitext(report_txt)[0]+'.html' + (files, nodes, report_txt) = ([],[], get_report_txt()) + for index in range(0, count): + node = "gw{}".format(index) + report_file = os.path.join(logs_path, node, report_txt) + files.append(report_file) + nodes.append(node) + + return generate_email_report_files(files, nodes, report_html) + +def find_log_path(name, csv_file, node): + replace = "_tests_{}".format(name.replace(".py", ".log")) + replace = replace.replace("/", "_") + suffix = os.path.basename(csv_file) + suffix = suffix.replace("_result_all.csv", replace) + suffix = suffix.replace("_result.csv", replace) + return suffix if not node else "{}/{}".format(node, suffix) + +def generate_module_report(csv_file, offset=0, links=None): + html_file = os.path.splitext(csv_file)[0]+'_modules.html' + rows = Result.read_report_csv(csv_file) + module_logs = OrderedDict() + modules = OrderedDict() + + for i, row in enumerate(rows): + name = row[offset] + if name not in modules: + if offset == 0: + module_logs[name] = find_log_path(name, csv_file, None) + elif links is not None: + module_logs[name] = links[i] + else: + module_logs[name] = find_log_path(name, csv_file, row[0]) + modules[name] = OrderedDict() + module = modules[name] + module["PassRate"] = 0 + module["SysLogs"] = 0 + module["CDT"] = 0 + module["FCLI"] = 0 + module["TSSH"] = 0 + module["DCNT"] = 0 + module["Functions"] = 0 + module["PrologTime"] = 0 + module["EpilogTime"] = 0 + module["FuncTime"] = 0 + module["ExecTime"] = 0 + for res in result_vals: + module[res] = 0 + else: + module = modules[name] + res = row[offset+2].upper() + secs = utils.time_parse(row[offset+3]) + syslogs = utils.integer_parse(row[offset+5]) + syslogs = syslogs if syslogs else 0 + fcli = utils.integer_parse(row[offset+6]) + tryssh = utils.integer_parse(row[offset+7]) + num_duts = utils.integer_parse(row[offset+8]) + desc = row[offset+9] + if res in module: + module[res] = module[res] + 1 + module["SysLogs"] = module["SysLogs"] + syslogs + module["FuncTime"] = module["FuncTime"] + secs + module["Functions"] = module["Functions"] + 1 + module["PassRate"] = get_rate(module["PASS"], module["Functions"]) + else: + if "Prolog" in desc: + module["PrologTime"] = module["PrologTime"] + secs + module["FCLI"] = fcli + module["TSSH"] = tryssh + module["DCNT"] = num_duts + else: + module["EpilogTime"] = module["EpilogTime"] + secs + module["SysLogs"] = module["SysLogs"] + syslogs + + total = OrderedDict() + for module in modules.values(): + module["ExecTime"] = module["FuncTime"] + module["PrologTime"] + module["EpilogTime"] + module["CDT"] = module["ExecTime"] * module["DCNT"] + for col in module: + try: + if col not in total: total[col] = module[col] + else: total[col] = total[col] + module[col] + except: pass + total["PassRate"] = get_rate(total["PASS"], total["Functions"]) + + if modules: + modules["==== TOTAL ===="] = total + module_logs["==== TOTAL ===="] = None + + def sort_func(y): + try: + #return y[1]['ExecTime'] + return y[1]['CDT'] + except: + return 0 + + # sort the modules on total execution time + sorted_modules = sorted(modules.items(), key=sort_func) + + (rows, cols, links) = ([],[],[]) + for name, module in sorted_modules: + for col in ["PrologTime", "EpilogTime", "FuncTime", "ExecTime", "CDT"]: + module[col] = utils.time_format(int(module[col])) + links.append(module_logs[name]) + row = [name] + row.extend(module.values()) + rows.append(row) + cols = list(module.keys()) + cols.insert(0, "") + utils.write_html_table2(cols, rows, html_file, links) + csv_file = os.path.splitext(html_file)[0]+'.csv' + utils.write_csv_file(cols, rows, csv_file) + +def generate_component_report(results_csv, tcresults_csv, offset=0): + modules = OrderedDict() + func_time = dict() + func_syslogs = dict() + tcmodmap = dict() + tc_rows = Result.read_report_csv(tcresults_csv) + func_rows = Result.read_report_csv(results_csv) + for row in func_rows: + name = row[offset] + func = row[offset+1] + res = row[offset+2] + secs = utils.time_parse(row[offset+3]) + syslogs = utils.integer_parse(row[offset+5]) + syslogs = syslogs if syslogs else 0 + num_duts = utils.integer_parse(row[offset+8]) + desc = row[offset+9] + if name not in modules: + modules[name] = OrderedDict() + module = modules[name] + module["PrologTime"] = 0 + module["EpilogTime"] = 0 + module["SysLogs"] = 0 + module["DCNT"] = num_duts + else: + module = modules[name] + + if not func: + if "Prolog" in desc: + module["PrologTime"] = module["PrologTime"] + secs + else: + module["EpilogTime"] = module["EpilogTime"] + secs + module["SysLogs"] = module["SysLogs"] + syslogs + else: + tcmodmap[func] = name + func_time[func] = secs + func_syslogs[func] = syslogs + + components = OrderedDict() + total_executed = 0 + total_pass_count = 0 + total_pass_rate = 0.00 + total_envfail_count = 0 + total_envfail_rate = 0.00 + total_skipped_count = 0 + total_skipped_rate = 0.00 + total_time_taken = 0 + total_dut_time = 0 + total_syslog_count = 0 + for row in tc_rows: + tc = row[offset+1] + res = row[offset+2].upper() + if tc not in tcmap.comp: + # use the value from tcresults + name = row[offset] + else: + name = tcmap.comp[tc] + + if tc not in tcmap.func: + # use the value from tcresults + func = row[offset+6] + else: + func = tcmap.func[tc] + + if name not in components: + components[name] = OrderedDict() + component = components[name] + component["PassRate"] = 0.00 + component["Executed"] = 0 + component["PassCount"] = 0 + component["TimeTaken"] = 0 + component["SysLogs"] = 0 + component["EnvFailCount"] = 0 + component["EnvFailRate"] = 0.00 + component["SkippedCount"] = 0 + component["SkippedRate"] = 0.00 + component["CDT"] = 0 + else: + component = components[name] + if res == "PASS": + component["PassCount"] = component["PassCount"] + 1 + total_pass_count = total_pass_count + 1 + elif res in ["SKIPPED"]: + component["SkippedCount"] = component["SkippedCount"] + 1 + total_skipped_count = total_skipped_count + 1 + elif res in ["ENVFAIL", "TOPOFAIL", "TGENFAIL"]: + component["EnvFailCount"] = component["EnvFailCount"] + 1 + total_envfail_count = total_envfail_count + 1 + try: + func_secs = func_time[func] + syslogs = func_syslogs[func] + except: + #print("=========== Failed to find function {} time -- ignore".format(func)) + func_secs = 0 + syslogs = 0 + try: + module = modules[tcmodmap[func]] + prolog_secs = module["PrologTime"] + epilog_secs = module["EpilogTime"] + module_syslogs = module["SysLogs"] + num_duts = module["DCNT"] + except: + #print("=========== Failed to find module {} time -- ignore".format(func)) + prolog_secs = 0 + epilog_secs = 0 + module_syslogs = 0 + num_duts = 1 + all_secs = func_secs + prolog_secs + epilog_secs + component["Executed"] = component["Executed"] + 1 + component["PassRate"] = get_rate(component["PassCount"], component["Executed"]) + component["TimeTaken"] = component["TimeTaken"] + all_secs + component["SysLogs"] = component["SysLogs"] + syslogs + module_syslogs + component["EnvFailRate"] = get_rate(component["EnvFailCount"], component["Executed"]) + component["SkippedRate"] = get_rate(component["SkippedCount"], component["Executed"]) + component["CDT"] = component["CDT"] + all_secs * num_duts + total_executed = total_executed + 1 + total_pass_rate = get_rate(total_pass_count, total_executed) + total_time_taken = total_time_taken + all_secs + total_dut_time = total_dut_time + all_secs * num_duts + total_syslog_count = total_syslog_count + syslogs + module_syslogs + total_envfail_rate = get_rate(total_envfail_count, total_executed) + total_skipped_rate = get_rate(total_skipped_count, total_executed) + if func_secs: + func_time[func] = 0 + if syslogs: + func_syslogs[func] = 0 + if prolog_secs: + module["PrologTime"] = 0 + if epilog_secs: + module["EpilogTime"] = 0 + module["SysLogs"] = 0 + + components = OrderedDict(sorted(components.items())) + + name = "==== TOTAL ====" + components[name] = OrderedDict() + component = components[name] + component["PassRate"] = total_pass_rate + component["Executed"] = total_executed + component["PassCount"] = total_pass_count + component["TimeTaken"] = total_time_taken + component["SysLogs"] = total_syslog_count + component["EnvFailCount"] = total_envfail_count + component["EnvFailRate"] = total_envfail_rate + component["SkippedCount"] = total_skipped_count + component["SkippedRate"] = total_skipped_rate + component["CDT"] = total_dut_time + + # remove the columns that are not needed + for name, component in components.items(): + del component["EnvFailCount"] + del component["SkippedCount"] + + (rows, cols) = ([], []) + for name, component in components.items(): + component["TimeTaken"] = utils.time_format(int(component["TimeTaken"])) + component["CDT"] = utils.time_format(int(component["CDT"])) + row = [name] + row.extend(component.values()) + rows.append(row) + cols = list(component.keys()) + cols.insert(0, "") + html_file = os.path.splitext(tcresults_csv)[0]+'_components.html' + utils.write_html_table2(cols, rows, html_file) + csv_file = os.path.splitext(html_file)[0]+'.csv' + utils.write_csv_file(cols, rows, csv_file) + +def _report_data_generation(execution_start, execution_end, + session_init_time, total_tc_time, + results_csv, tcresults_csv, syslog_csv): + + generate_component_report(results_csv, tcresults_csv) + tc_rows = Result.read_report_csv(tcresults_csv) + html_file = os.path.splitext(tcresults_csv)[0]+'.html' + Result.write_report_html(html_file, tc_rows, 1, False) + + generate_module_report(results_csv) + func_rows = Result.read_report_csv(results_csv) + html_file = os.path.splitext(results_csv)[0]+'.html' + Result.write_report_html(html_file, func_rows, 0, False) + + syslog_rows = Result.read_report_csv(syslog_csv) + html_file = os.path.splitext(syslog_csv)[0]+'.html' + Result.write_report_html(html_file, syslog_rows, 2, False) + + tc_result_dict = {} + for key in result_vals: + tc_result_dict[key] = 0 + total_tcs = 0 + for row in tc_rows: + col_result = str(row[2]) + if col_result != "" and col_result is not None: + col_result = col_result.upper() + if col_result in tc_result_dict: + tc_result_dict[col_result] += 1 + total_tcs += 1 + else: + print(col_result, " is not found in tc results ") + + func_result_dict = {} + for key in result_vals: + func_result_dict[key] = 0 + modules = dict() + total_funcs = 0 + total_syslogs = 0 + for row in func_rows: + modules[row[0]] = 1 + col_result = str(row[2]) + syslogs = utils.integer_parse(row[5]) + syslogs = syslogs if syslogs else 0 + total_syslogs = total_syslogs + syslogs + if col_result != "" and col_result is not None: + col_result = col_result.upper() + if col_result in func_result_dict: + func_result_dict[col_result] += 1 + total_funcs += 1 + else: + print(col_result, " is not found in results ") + + data = "" + start_time = execution_start.replace(microsecond=0) + end_time = execution_end.replace(microsecond=0) + data = "{}\nExecution Started = {}".format(data, start_time) + data = "{}\nExecution Completed = {}".format(data, end_time) + exec_time = utils.time_diff(execution_start, execution_end, True) + data = "{}\nExecution Time = {}".format(data, exec_time) + data = "{}\nSession Init Time = {}".format(data, session_init_time) + data = "{}\nTests Time = {}".format(data, total_tc_time) + + for item, value in func_result_dict.items(): + data = "{}\n{} = {}".format(data, item, value) + data = "{}\nFunction Count = {}".format(data, total_funcs) + data = "{}\nModule Count = {}".format(data, len(modules)) + data = "{}\nTest Count = {}".format(data, total_tcs) + data = "{}\nPass Count = {}".format(data, tc_result_dict["PASS"]) + data = "{}\nPass Rate = {}".format(data, get_rate(tc_result_dict["PASS"], total_tcs)) + data = "{}\nSysLog Count = {}".format(data, total_syslogs) + + return data + +def compare_results(dir1, dir2, show_all=True): + file1=glob.glob(dir1+"/*_result*.csv") + file2=glob.glob(dir2+"/*_result*.csv") + if not file1 or not file2: + return + rows1 = Result.read_report_csv(file1[0]) + rows2 = Result.read_report_csv(file2[0]) + results = SpyTestDict() + for row in rows1: + if not row[3]: + continue + key = "{}::{}".format(row[1], row[2]) + results[key] = [row[1], row[2], row[3], row[4]] + for row in rows2: + if not row[3]: + continue + key = "{}::{}".format(row[1], row[2]) + if key in results: + results[key].extend([row[3], row[4]]) + for res in results: + row = results[res] + if show_all or row[2] != row[4]: + print(row) + +def session_finish(session, exitstatus): + + # stop the result thread if started + if bg_results.is_valid(): + bg_results.stop() + + if not batch.finish(): + return + + consolidate_results() + +def configure(config): + + [user_root, logs_path, slave_id] = _get_logs_path() + + if batch.configure(config, logs_path): + # create psuedo workarea for the master + wa = _create_work_area2(config) + _load_tcmap() + batch.set_tcmap(tcmap) + batch.set_logger(wa._context.log) + +def unconfigure(config): + + if not batch.unconfigure(config): + return + + # delete psuedo workarea for the master + _delete_work_area() + +def parse_batch_args(numprocesses, buckets): + [user_root, logs_path, slave_id] = _get_logs_path() + return batch.parse_args(numprocesses, buckets, logs_path) + +def make_scheduler(config, log): + return batch.make_scheduler(config, log) + +def configure_nodes(config): + return batch.configure_nodes(config) + +def configure_node(node): + return batch.configure_node(node) + +def begin_node(gateway): + return batch.begin_node(gateway) + +def finish_node(node, err): + return batch.finish_node(node, err) + +def fixture_post_finalizer(fixturedef, request): + dtrace("fixture_post_finalizer", fixturedef, request, current_test) + + wa = get_work_area() + if not wa: + return None + + if fixturedef.argname == "global_module_hook": + if not current_module.global_module_finalized: + result = "Pass: {}/{}".format(wa.module_tc_executed-wa.module_tc_fails, wa.module_tc_executed) + wa.event("Framework Module Hook Finalize:", request.node.nodeid, result) + current_module.global_module_finalized = True + wa.log_time("Framework Module Hook Finilize") + wa.module_log_init(None) + used_devices = wa._get_devices_usage_list() + wa.event("Devices used:", request.node.nodeid, len(used_devices), wa._get_devices_usage_list()) + batch.verify_bucket(request.node.nodeid, len(used_devices), wa.module_tc_fails) + wa._set_device_usage_collection(False) + elif fixturedef.argname == "global_function_hook": + wa.event("Framework Function Hook Finalize:", request.node.nodeid) + wa.log_time("Framework Function Hook Finilize") + current_module.epilog_start = get_timenow() + wa.tc_log_init(None) + #dbg_msg = "Devices used in the entire module till now {} {}:".format(fixturedef.scope, fixturedef.argname) + #wa.event(dbg_msg, wa._get_devices_usage_list()) + wa._set_device_usage_collection(True) + elif fixturedef.argname == "__pytest_repeat_step_number": + pass + elif fixturedef.scope == "module": + if not current_module.user_module_finalized: + current_module.user_module_finalized = True + wa.event("Module Hook Finalize:", fixturedef.baseid, fixturedef.argname) + wa._post_module_epilog(fixturedef.baseid, True) + set_current_result() + time_taken = get_elapsed(current_module.epilog_start, True, min_time) + wa._context.publish2(fixturedef.baseid, None, None, time_taken, None, "", "Module Epilog") + current_module.epilog_start = None + wa.log_time("User Module {} Hook Finilize".format(fixturedef.baseid)) + #dbg_msg = "Devices used in the entire module till now {} {}:".format(fixturedef.scope, fixturedef.argname) + #wa.event(dbg_msg, wa._get_devices_usage_list()) + wa._set_device_usage_collection(False) + elif fixturedef.scope == "function": + wa.event("Function Hook Finalize:", fixturedef.baseid, fixturedef.argname) + wa._post_function_epilog(fixturedef.baseid) + wa.log_time("User Function {} Hook Finilize".format(fixturedef.baseid)) + #dbg_msg = "Devices used in the entire module till now {} {}:".format(fixturedef.scope, fixturedef.argname) + #wa.event(dbg_msg, wa._get_devices_usage_list()) + wa._set_device_usage_collection(False) + elif fixturedef.scope == "class": + wa.event("Class Hook Finalize:", fixturedef.baseid, fixturedef.argname) + wa.log_time("User Class {} Hook Finilize".format(fixturedef.baseid)) + else: + wa.event("Misc Hook Finalize:", fixturedef.baseid, fixturedef.argname) + wa.log_time("Misc {} Hook scope {} Finilize".format(fixturedef.baseid, fixturedef.scope)) + +def fixture_setup(fixturedef, request): + + dtrace("fixture_setup", fixturedef, request, current_test) + + wa = get_work_area() + if not wa: + return None + + if fixturedef.argname == "global_module_hook": + module_name = "tests/{}".format(request.node.nodeid.replace(".py", "")) + module_name = module_name.replace("/", "_") + wa.module_log_init(module_name) + wa.log_time("Framework Module Hook Start") + log_module_time_start() + current_test.hook = "global_module" + wa.event("Framework Module Hook:", request.node.nodeid) + current_module.global_module_finished = False + current_module.global_module_finalized = False + current_module.name = "" + current_test.name = "" + set_current_result() + wa._set_device_usage_collection(False) + #dbg_msg = "Devices used in the entire module till now {} {}:".format(fixturedef.scope, fixturedef.argname) + #wa.event(dbg_msg, wa._get_devices_usage_list()) + elif fixturedef.argname == "global_function_hook": + wa.tc_log_init(request.node.location[2]) + wa.log_time("Framework Function Hook Start") + log_module_time_finish(request.node.nodeid) + current_test.hook = "global_function" + current_test.name = "" + wa.instrument(None, "pre-infra-module") + wa.event("Framework Function Hook:", request.node.nodeid) + wa._set_device_usage_collection(False) + #dbg_msg = "Devices used in the entire module till now {} {}:".format(fixturedef.scope, fixturedef.argname) + #wa.event(dbg_msg, wa._get_devices_usage_list()) + elif fixturedef.argname == "__pytest_repeat_step_number": + pass + elif fixturedef.scope == "module": + if wa.abort_module_msg: + wa.event("SKIP Module Hook:", fixturedef.baseid, fixturedef.argname) + pytest.skip(wa.abort_module_msg) + wa.log_time("User Module {} Hook Start".format(fixturedef.baseid)) + wa._pre_module_prolog(fixturedef.baseid) + current_test.hook = "test_module" + set_current_result() + wa.event("Module Hook:", fixturedef.baseid, fixturedef.argname) + wa.instrument(None, "pre-user-module") + current_module.name = fixturedef.baseid + current_test.name = "" + current_module.user_module_finished = False + current_module.user_module_finalized = False + #dbg_msg = "Devices used in the entire module till now {} {}:".format(fixturedef.scope, fixturedef.argname) + #wa.event(dbg_msg, wa._get_devices_usage_list()) + elif fixturedef.scope == "function": + if wa.abort_module_msg: + wa.event("SKIP Function Hook:", fixturedef.baseid, fixturedef.argname) + pytest.skip(wa.abort_module_msg) + wa.log_time("User Function {} Hook Start".format(fixturedef.baseid)) + current_test.hook = "test_function" + try: current_test.name = request.function.func_name + except: current_test.name = request.function.__name__ + wa._pre_function_prolog(current_test.name) + wa.event("Function Hook:", fixturedef.baseid, fixturedef.argname) + wa.instrument(None, "pre-user-func") + #dbg_msg = "Devices used in the entire module till now {} {}:".format(fixturedef.scope, fixturedef.argname) + #wa.event(dbg_msg, wa._get_devices_usage_list()) + elif fixturedef.scope == "class": + wa.log_time("User Class {} Hook Start".format(fixturedef.baseid)) + current_test.hook = "test_class" + wa.event("Class Hook:", fixturedef.baseid, fixturedef.argname) + else: + wa.log_time("Misc {} Hook scope {} Start".format(fixturedef.baseid, fixturedef.scope)) + current_test.hook = "misc" + wa.event("Misc Hook:", fixturedef.baseid, fixturedef.argname) + + if not wa.base_config_verified: + setattr(fixturedef, "cached_result", ["SKIP", None, None]) + return "SKIP" + + return None + +def fixture_setup_finish(fixturedef, request): + dtrace("fixture_setup_finish", fixturedef, request, current_test) + + wa = get_work_area() + if not wa: + return None + + if fixturedef.argname == "global_session_request": + pass + elif fixturedef.argname == "global_module_hook": + if not current_module.global_module_finished: + current_module.global_module_finished = True + wa.event("Framework Module Hook Finish:", request.node.nodeid) + wa.log_time("Framework Module Hook end") + wa._clear_devices_usage_list() + wa._set_device_usage_collection(True) + #dbg_msg = "Devices used in the entire module till now {} {}:".format(fixturedef.scope, fixturedef.argname) + #wa.event(dbg_msg, wa._get_devices_usage_list()) + elif fixturedef.argname == "global_function_hook": + wa.instrument(None, "post-infra-module") + wa.event("Framework Function Hook Finish:", request.node.nodeid) + wa.log_time("Framework Function Hook end") + wa._set_device_usage_collection(True) + #dbg_msg = "Devices used in the entire module till now {} {}:".format(fixturedef.scope, fixturedef.argname) + #wa.event(dbg_msg, wa._get_devices_usage_list()) + elif fixturedef.argname == "__pytest_repeat_step_number": + pass + elif fixturedef.scope == "module": + wa._set_device_usage_collection(False) + #dbg_msg = "Devices used in the entire module till now {} {}:".format(fixturedef.scope, fixturedef.argname) + #wa.event(dbg_msg, wa._get_devices_usage_list()) + if not wa.cfg.module_epilog: + fixturedef._finalizers = [] + if not current_module.user_module_finished: + current_module.user_module_finished = True + # current_module.result can't be used as its value is ConfigFail + # hence using current_module.result_desc + if not current_module.result_desc: + wa._post_module_prolog(fixturedef.baseid, True) + else: + wa._post_module_prolog(fixturedef.baseid, False) + wa.instrument(None, "post-user-module") + wa.event("Module Hook Finish:", fixturedef.baseid, fixturedef.argname) + wa.log_time("User Module {} Hook end".format(fixturedef.baseid)) + elif fixturedef.scope == "function": + #dbg_msg = "Devices used in the entire module till now {} {}:".format(fixturedef.scope, fixturedef.argname) + #wa.event(dbg_msg, wa._get_devices_usage_list()) + wa.instrument(None, "post-user-func") + wa._post_function_prolog(fixturedef.baseid) + wa.event("Function Hook Finish:", fixturedef.baseid, fixturedef.argname) + wa.log_time("User Function {} Hook end".format(fixturedef.baseid)) + elif fixturedef.scope == "class": + wa.event("Class Hook Finish:", fixturedef.baseid, fixturedef.argname) + wa.log_time("User Class {} Hook end".format(fixturedef.baseid)) + else: + wa.event("Misc Hook Finish:", fixturedef.baseid, fixturedef.argname) + wa.log_time("Misc {} Hook scope {} end".format(fixturedef.baseid, fixturedef.scope)) + +def pyfunc_call(pyfuncitem, after): + wa = get_work_area() + if not wa: + return None + func_name = pyfuncitem.location[2] + if after: + wa._pre_function_epilog(func_name) + elif wa.abort_module_msg: + return wa.abort_module_msg + +def fixture_callback(request, scope, isend): + if scope == "session" and not isend: + return _create_work_area(request) + if scope == "session" and isend: + return _delete_work_area() + + wa = get_work_area() + if not wa: + return None + + filepath = request.fspath.basename + if scope == "module" and not isend: + return wa._module_init(filepath) + if scope == "module" and isend: + return wa._module_clean(filepath) + + func_name = request.node.location[2] + if scope == "function" and not isend: + return wa._function_init(request.node.nodeid, func_name) + if scope == "function" and isend: + return wa._function_clean(request.node.nodeid, func_name) + + return None + diff --git a/spytest/spytest/infra.py b/spytest/spytest/infra.py new file mode 100644 index 00000000000..3a9afc05e8d --- /dev/null +++ b/spytest/spytest/infra.py @@ -0,0 +1,468 @@ + +from spytest.framework import get_work_area as getwa + +def banner(msg, width=80, delimiter="#", wrap=True, tnl=True, lnl=True): + getwa().banner(msg, width, delimiter, wrap, tnl=tnl, lnl=lnl) + +def debug(msg): + getwa().debug(msg) + +def log(msg): + getwa().log(msg) + +def warn(msg): + getwa().warn(msg) + +def exception(msg): + getwa().exception(msg) + +def error(msg): + getwa().error(msg) + +def wait(val, msg=None): + getwa().wait(val, msg) + +def tg_wait(val, msg=None): + getwa().tg_wait(val, msg) + +def report_tc_pass(tcid, msgid, *args): + getwa().report_tc_pass(tcid, msgid, *args) + +def report_tc_fail(tcid, msgid, *args): + getwa().report_tc_fail(tcid, msgid, *args) + +def report_tc_unsupported(tcid, msgid, *args): + getwa().report_tc_unsupported(tcid, msgid, *args) + +def report_pass(msgid, *args): + """ + Infrastructure API used by test scripts to report pass + :param msgid: message identifier from /messages/*.yaml files + :param args: arguments required in message identifier specification + :return: + """ + getwa().report_pass(msgid, *args) + +def report_fail(msgid, *args): + """ + Infrastructure API used by test scripts to report fail + :param msgid: message identifier from /messages/*.yaml files + :param args: arguments required in message identifier specification + :return: + """ + getwa().report_fail(msgid, *args) + +def report_env_fail(msgid, *args): + """ + Infrastructure API generally used with in framework to report + test failure because of environment issue + :param msgid: message identifier from /messages/*.yaml files + :param args: arguments required in message identifier specification + :return: + """ + getwa().report_env_fail(msgid, *args) + +def report_tgen_fail(msgid, *args): + getwa().report_tgen_fail(msgid, *args) + +def report_unsupported(msgid, *args): + getwa().report_unsupported(msgid, *args) + +def report_config_fail(msgid, *args): + getwa().report_config_fail(msgid, *args) + +def apply_script(dut, cmdlist): + return getwa().apply_script(dut, cmdlist) + +def apply_json(dut, json): + return getwa().apply_json(dut, json) + +def apply_json2(dut, json): + return getwa().apply_json2(dut, json) + +def apply_files(dut, file_list): + return getwa().apply_files(dut, file_list) + +def run_script(dut, script_path, *args): + return getwa().run_script(dut, 600, script_path, *args) + +def run_script_with_timeout(dut, timeout, script_path, *args): + """ + todo: Update Documentation + :param dut: + :type dut: + :param timeout: in secs + :type timeout: + :param script_path: + :type script_path: + :return: + :rtype: + """ + return getwa().run_script(dut, timeout, script_path, *args) + +def enable_console_debug_msgs(dut): + """ + todo: Update Documentation + :param dut: + :type dut: + :return: + :rtype: + """ + return getwa().enable_disable_console_debug_msgs(dut, True) + +def disable_console_debug_msgs(dut): + """ + todo: Update Documentation + :param dut: + :type dut: + :return: + :rtype: + """ + return getwa().enable_disable_console_debug_msgs(dut, False) + +def clear_config(dut): + """ + todo: Update Documentation + :param dut: + :type dut: + :return: + :rtype: + """ + return getwa().clear_config(dut) + +def config_db_reload(dut, save=False): + """ + todo: Update Documentation + :param dut: + :type dut: + :param save: + :type save: + :return: + :rtype: + """ + return getwa().config_db_reload(dut, save) + +def upgrade_image(dut, url, skip_reboot=False, port_break=True, port_speed=True): + """ + Upgrade the software in the given DUT from given URL + :param dut: + :type dut: + :param url: URL string used to upgrade + :type url: String + :param skip_reboot: Flag to avoid rebooting device after upgrade + :type url: boolean (default False) + :return: + :rtype: + """ + return getwa().upgrade_image(dut, url, skip_reboot, port_break, port_speed) + +def reboot(dut, method="normal", skip_port_wait=False, skip_exception=False, skip_fallback=False): + """ + todo: Update Documentation + :param dut: + :type dut: + :return: + :rtype: + """ + return getwa().reboot(dut, method, skip_port_wait, skip_exception, skip_fallback) + +def get_dut_names(): + """ + This method is used to get all the DUT names + + :return: names of all the duts + :rtype: list + """ + return getwa().get_dut_names() + +def get_tg_names(): + """ + This method is used to get all the TG names + + :return: names of all the tg + :rtype: list + """ + return getwa().get_tg_names() + +def get_free_ports(dut): + """ + This method gets all the ports that are not connected to either + partner DUT or Traffic Generator + + :param dut: device under test + :type dut: + :return: all the free ports + :rtype: list + """ + return getwa().get_free_ports(dut) + +def get_all_ports(dut): + """ + This method gets all the ports that are not reserved + + :param dut: device under test + :type dut: + :return: all the free ports + :rtype: list + """ + return getwa().get_all_ports(dut) + +def get_tg_info(tg): + return getwa().get_tg_info(tg) + +def get_links(dut, peer=None): + return getwa().get_links(dut, peer) + +def get_dut_links_local(dut, peer=None, index=None): + return getwa().get_dut_links_local(dut, peer, index) + +def get_dut_links(dut, peer=None): + return getwa().get_dut_links(dut, peer) + +def get_tg_links(dut, peer=None): + return getwa().get_tg_links(dut, peer) + +def get_service_info(dut, name): + """ + todo: Update Documentation + :param name: + :type name: + :param dut: + :type dut: + :return: + :rtype: + """ + return getwa().get_service_info(dut, name) + +def do_rps(dut, op, on_delay=None, off_delay=None, recon=True): + """ + This method performs the RPS operations such as on/off/reset. + RPS models supported are Raritan, ServerTech, Avocent + and all are through telnet. + The RPS information is obtained from the testbed file. + :param op: operation i.e. on/off/reset + :param dut: DUT identifier + :type dut: basestring + :return: True if the operation is successful else False + """ + return getwa().do_rps(dut, op, on_delay, off_delay, recon) + +def get_testbed_vars(): + """ + returns the testbed variables in a dictionary + :return: testbed variables dictionary + :rtype: dict + """ + return getwa().get_testbed_vars() + +def lock_topology(*args): + """ + locks the topology to specified specification though + current testbed topology has more than specified + :param spec: needed topology specification + :type spec: basestring + :return: True if the operation is successful else False + :rtype: bool + """ + return getwa().lock_topology(*args) + +def ensure_min_topology(*args): + """ + verifies if the current testbed topology satifies the + minimum topology required by test script + :param spec: needed topology specification + :type spec: basestring + :return: True if current topology is good enough else False + :rtype: bool + """ + return getwa().ensure_min_topology(*args) + +def get_config(dut, scope="current"): + return getwa().get_config(dut, scope) + +def get_build(dut, scope="current"): + return getwa().get_build(dut, scope) + +def get_param(name, default): + return getwa().get_param(name, default) + +def get_device_param(dut, name, default): + return getwa().get_device_param(dut, name, default) + +def get_link_param(dut, local, name, default): + return getwa().get_link_param(dut, local, name, default) + +def get_args(arg): + return getwa().get_args(arg) + +def get_ui_type(dut=None): + return getwa().get_ui_type(dut) + +def get_run_config(): + return getwa().get_run_config() + +def get_tgen(name, port=None, tg=None): + return getwa().get_tgen(name, port, tg) + +def get_mgmt_ip(dut): + return getwa().get_mgmt_ip(dut) + +def get_datastore(dut, name, scope="default"): + return getwa().get_datastore(dut, name, scope) + +def get_device_alias(dut): + return getwa().get_device_alias(dut) + +def set_device_alias(dut, name): + return getwa().set_device_alias(dut, name) + +def exec_ssh(dut, username=None, password=None, cmdlist=[]): + return getwa().exec_ssh(dut, username, password, cmdlist) + +def change_passwd(dut, username, password): + return getwa().change_passwd(dut, username, password) + +def upload_file_to_dut(dut, src_file, dst_file): + return getwa().upload_file_to_dut(dut, src_file, dst_file) + +def download_file_from_dut(dut, src_file, dst_file): + return getwa().download_file_from_dut(dut, src_file, dst_file) + +def set_module_verifier(verifier): + return getwa().set_module_verifier(verifier) + +def ansible_dut(dut, playbook): + return getwa().ansible_dut(dut, playbook) + +def ansible_service(service, playbook): + return getwa().ansible_service(service, playbook) + +def add_addl_auth(dut, username, password): + return getwa().add_addl_auth(dut, username, password) + +def set_port_defaults(dut, breakout=True, speed=True): + return getwa().set_port_defaults(dut, breakout, speed) + +def wait_system_status(dut, max_time): + return getwa().wait_system_status(dut, max_time) + +def wait_system_reboot(dut): + return getwa().wait_system_reboot(dut) + +def add_prevent(what): + return getwa().add_prevent(what) + +def exec_remote(ipaddress, username, password, scriptpath, wait_factor=2): + return getwa().exec_remote(ipaddress, username, password, scriptpath, wait_factor) + +def add_module_vars(dut, name, value): + return getwa().add_module_vars(dut, name, value) + +def instrument(self, dut, scope): + return getwa().instrument(dut, scope) + +def change_prompt(dut, mode, **kwargs): + return getwa().change_prompt(dut, mode, **kwargs) + +def cli_config(dut, cmd, mode=None, skip_error_check=False, delay_factor=0, **kwargs): + return getwa().cli_config(dut, cmd, mode, skip_error_check, delay_factor, **kwargs) + +def cli_show(dut, cmd, mode=None, skip_tmpl=False, skip_error_check=False, **kwargs): + return getwa().cli_show(dut, cmd, mode, skip_tmpl, skip_error_check, **kwargs) + +def get_logs_path(for_file=None): + return getwa().get_logs_path(for_file) + +def is_dry_run(): + return getwa().is_dry_run() + +def profiling_start(msg, max_time): + return getwa().profiling_start(msg, max_time) + +def profiling_stop(pid): + return getwa().profiling_stop(pid) + +def get_config_profile(): + return getwa().get_config_profile() + +def get_device_type(dut): + return getwa().get_device_type(dut) + +def is_community_build(dut=None): + return getwa().is_community_build(dut) + +def is_vsonic(dut=None): + return getwa().is_vsonic(dut) + +def rest_create(dut, path, data, *args, **kwargs): + return getwa().rest_create(dut, path, data, *args, **kwargs) + +def rest_update(dut, path, data, *args, **kwargs): + return getwa().rest_update(dut, path, data, *args, **kwargs) + +def rest_modify(dut, path, data, *args, **kwargs): + return getwa().rest_modify(dut, path, data, *args, **kwargs) + +def rest_init(dut, username, password, altpassword): + return getwa().rest_init(dut, username, password, altpassword) + +def rest_read(dut, path, *args, **kwargs): + return getwa().rest_read(dut, path, *args, **kwargs) + +def rest_delete(dut, path, *args, **kwargs): + return getwa().rest_delete(dut, path, *args, **kwargs) + +def rest_parse(dut, filepath=None, all_sections=False, paths=[], **kwargs): + return getwa().rest_parse(dut, filepath, all_sections, paths, **kwargs) + +def rest_apply(dut, data): + return getwa().rest_apply(dut, data) + +def exec_ssh_remote_dut(dut, ipaddress, username, password, command=None, timeout=30): + return getwa().exec_ssh_remote_dut(dut, ipaddress, username, password, command, timeout) + +def parse_show(dut, cmd, output): + return getwa().parse_show(dut, cmd, output) + +def show(dut, cmd, **kwargs): + return getwa().show_new(dut, cmd, **kwargs) + +def config(dut, cmd, **kwargs): + return getwa().config_new(dut, cmd, **kwargs) + +def vtysh(dut, cmd): + return getwa().config_new(dut, cmd, type="vtysh", conf=False) + +def vtysh_config(dut, cmd): + return getwa().config_new(dut, cmd, type="vtysh", conf=True) + +def vtysh_show(dut, cmd, skip_tmpl=False, skip_error_check=False): + return getwa().show_new(dut, cmd, type="vtysh", skip_tmpl=skip_tmpl, skip_error_check=skip_error_check) + +def lldp_config(dut, cmd): + return getwa().config_new(dut, cmd, type="lldp", conf=True) + +def lldp_show(dut, cmd, skip_tmpl=False, skip_error_check=False): + return getwa().show_new(dut, cmd, type="lldp", skip_tmpl=skip_tmpl, skip_error_check=skip_error_check) + +def run_uicli_script(dut, scriptname): + return getwa().run_uicli_script(dut, scriptname) + +def run_uirest_script(dut, scriptname): + return getwa().run_uirest_script(dut, scriptname) + +def run_uignmi_script(dut, scriptname, **kwargs): + return getwa().run_uignmi_script(dut, scriptname, **kwargs) + +def generate_tech_support(dut, name): + return getwa().generate_tech_support(dut, name) + +def get_credentials(dut): + return getwa().get_credentials(dut) + +def is_valid_base_config(): + return getwa().is_valid_base_config() + +def dump_all_commands(dut, type='click'): + return getwa().dump_all_commands(dut, type) + diff --git a/spytest/spytest/logger.py b/spytest/spytest/logger.py new file mode 100644 index 00000000000..abe7984d829 --- /dev/null +++ b/spytest/spytest/logger.py @@ -0,0 +1,292 @@ +import logging +import sys +import traceback +import threading +import os +import time +import datetime +from spytest.st_time import get_timestamp + +def get_thread_name(): + name = threading.current_thread().name + name = name.replace("MainThread", "Thread-0") + try: + num = int(name.replace("Thread-", "")) + name = "T%04d: " % (num) + except: + pass + return name + +def get_log_lvl_name(lvl): + lvl_map = {"INFO" : "INFO ", "WARNING": "WARN "} + if lvl in lvl_map: + return lvl_map[lvl] + return lvl + +def time_delta(elapsed): + seconds = elapsed.total_seconds() + msec = elapsed.microseconds/1000 + hour = seconds // 3600 + seconds = seconds % 3600 + minutes = seconds // 60 + seconds = seconds % 60 + return "%d:%02d:%02d,%03d" % (hour, minutes, seconds, msec) + +class LogFormatter(object): + + def __init__(self, is_elapsed): + self.is_elapsed = is_elapsed + self.start_time = time.time() + + def format(self, record): + if self.is_elapsed: + elapsed_seconds = record.created - self.start_time + elapsed = datetime.timedelta(seconds=elapsed_seconds) + time_stamp = time_delta(elapsed) + else: + time_stamp = get_timestamp(True) + thid = get_thread_name() + lvl = get_log_lvl_name(record.levelname) + msg = record.getMessage() + return "{} {}{} {}".format(time_stamp, thid, lvl, msg) + +class Logger(object): + """ + todo: Update Documentation + """ + + def __init__(self, file_prefix=None, filename=None, name='', level=logging.INFO, tlog=False, mlog=True): + """ + Initialization of the logger object + :param filename: filename where the logs will be generated. spytest.log if not passed + :type filename: str + :param name: name of the instance from where the logs or written + :type name: str + :param level: logging level + """ + self.logdir = None + self.name = name + self.logger = logging.getLogger(name) + self.logger.setLevel(level) + self.dut_loggers = dict() + self.tc_log_handler = None + self.module_log_handler = None + self.file_prefix = file_prefix + self.tc_log_handler_support = tlog + self.module_log_handler_support = mlog + self.use_elapsed_time_fmt = bool(os.getenv("SPYTEST_LOGS_TIME_FMT_ELAPSED", "0") == "1") + self.tc_log_fmt = None + self.module_log_fmt = None + self.base_fmt = LogFormatter(self.use_elapsed_time_fmt) + + logfile = filename if filename else "spytest.log" + + logfile = self._add_prefix(logfile) + + self.logdir = os.path.dirname(logfile) + self.add_file_handler(self.logger, logfile) + + # Handler for Console logs + if not os.getenv("SPYTEST_NO_CONSOLE_LOG"): + console_handler = logging.StreamHandler(sys.stdout) + console_handler.setFormatter(self.base_fmt) + self.logger.addHandler(console_handler) + + def __del__(self): + pass + + def add_file_handler(self, logger, logfile): + logdir = os.path.dirname(logfile) + if logdir and not os.path.exists(logdir): + os.makedirs(logdir) + + file_handler = logging.FileHandler(logfile, 'w') + file_handler.setFormatter(self.base_fmt) + logger.addHandler(file_handler) + + def _add_prefix(self, filename): + if self.file_prefix: + return "{}_{}".format(self.file_prefix, filename) + if self.logdir: + filename = os.path.join(self.logdir, filename) + return filename + + def info(self, msg, dut=None, split_lines=False, exc_info=False): + """ + todo: Update Documentation + :param msg: + :type msg: + :param dut: + :type dut: + :return: + :rtype: + """ + + self.log(logging.INFO, msg, dut, split_lines, exc_info=exc_info) + + def error(self, msg, dut=None, split_lines=False, exc_info=True): + """ + todo: Update Documentation + :param msg: + :type msg: + :param dut: + :type dut: + :return: + :rtype: + """ + self.log(logging.ERROR, msg, dut, split_lines, exc_info=exc_info) + + def debug(self, msg, dut=None, split_lines=False): + """ + todo: Update Documentation + :param msg: + :type msg: + :param dut: + :type dut: + :return: + :rtype: + """ + self.log(logging.DEBUG, msg, dut, split_lines) + + def warning(self, msg, dut=None, split_lines=False): + """ + todo: Update Documentation + :param msg: + :type msg: + :param dut: + :type dut: + :return: + :rtype: + """ + self.log(logging.WARNING, msg, dut, split_lines) + + def exception(self, msg, dut=None, split_lines=False): + """ + todo: Update Documentation + :param msg: + :type msg: + :param dut: + :type dut: + :return: + :rtype: + """ + msg2 = "{}\n{}".format(msg, traceback.format_exc()) + self.log(logging.ERROR, msg2, dut, split_lines) + + def set_lvl(self, lvl): + """ + todo: Update Documentation + :param lvl: + :type lvl: + :return: + :rtype: + """ + if lvl == "debug": + self.logger.setLevel(logging.DEBUG) + + def log(self, lvl, msg, dut=None, split_lines=False, exc_info=False): + """ + todo: Update Documentation + :param lvl: + :type lvl: + :param msg: + :type msg: + :param dut: + :type dut: + :return: + :rtype: + """ + if dut: + self.dut_log(dut, msg, lvl, split_lines, exc_info=exc_info) + elif split_lines: + for line in msg.splitlines(): + self.logger.log(lvl, line, exc_info=exc_info) + else: + self.logger.log(lvl, msg, exc_info=exc_info) + + def dut_log(self, dut, msg, lvl=logging.INFO, skip_general=False, + split_lines=False, conn=None, exc_info=False): + + if isinstance(msg, list): + for line in msg: + self.dut_log(dut, line, lvl, skip_general, False, conn, exc_info=exc_info) + return + + if split_lines: + for line in msg.splitlines(): + self.dut_log(dut, line, lvl, skip_general, False, conn, exc_info=exc_info) + return + + if dut not in self.dut_loggers: + logfile_path = "{0}.log".format(dut) + self.dut_loggers[dut] = logging.getLogger(logfile_path) + logfile_path = self._add_prefix(logfile_path) + self.add_file_handler(self.dut_loggers[dut], logfile_path) + self.dut_loggers[dut].propagate = False + + try: + msg1 = str(msg) + except UnicodeEncodeError as exp: + msg1 = unicode(msg) + + # add main log + if not skip_general: + if conn: + msg2 = "[{}-{}] {}".format(dut, conn, msg1) + else: + msg2 = "[{}] {}".format(dut, msg1) + self.logger.log(lvl, msg2, exc_info=exc_info) + for handler in self.logger.handlers: + handler.flush() + + # add DUT log + if conn: + msg2 = "[{}] {}".format(conn, msg1) + else: + msg2 = "{}".format(msg1) + self.dut_loggers[dut].log(lvl, msg2, exc_info=exc_info) + for handler in self.dut_loggers[dut].handlers: + handler.flush() + + def tc_log_init(self, test_name): + if not self.tc_log_handler_support: + return + if self.tc_log_handler: + self.tc_log_handler.close() + self.logger.removeHandler(self.tc_log_handler) + self.tc_log_handler = None + + if test_name: + logfile_path = self._add_prefix("{}.log".format(test_name)) + self.tc_log_handler = logging.FileHandler(logfile_path, 'w') + self.tc_log_fmt = LogFormatter(self.use_elapsed_time_fmt) + self.tc_log_handler.setFormatter(self.tc_log_fmt) + self.logger.addHandler(self.tc_log_handler) + + def module_log_init(self, module_name): + if not self.module_log_handler_support: + return + if self.module_log_handler: + self.module_log_handler.close() + self.logger.removeHandler(self.module_log_handler) + self.module_log_handler = None + + if module_name: + logfile_path = self._add_prefix("{}.log".format(module_name)) + self.module_log_handler = logging.FileHandler(logfile_path, 'w') + self.module_log_fmt = LogFormatter(self.use_elapsed_time_fmt) + self.module_log_handler.setFormatter(self.module_log_fmt) + self.logger.addHandler(self.module_log_handler) + +if __name__ == "__main__": + logger = Logger("ut", "logs.log") + logger.info("generic info 1") + logger.dut_log("D1", "dut-1 info 1") + logger.dut_log("D2", "dut-2 info 1") + logger.dut_log("D1", "dut-1 info 1", conn="SSH") + logger.dut_log("D2", "dut-2 info 1", conn="SSH") + logger.error("generic error 1") + logger.warning("generic warning 1") + logger.dut_log("D1", "dut-1 info 1", lvl=logging.WARNING) + logger.dut_log("D2", "dut-2 info 1", lvl=logging.WARNING) + diff --git a/spytest/spytest/mail.py b/spytest/spytest/mail.py new file mode 100644 index 00000000000..3d1b8d27548 --- /dev/null +++ b/spytest/spytest/mail.py @@ -0,0 +1,61 @@ +import os +import smtplib + +from email.mime.multipart import MIMEMultipart +from email.mime.base import MIMEBase +from email.mime.text import MIMEText +from email import encoders + +def send(server, to, subject, body, filenames=[], html=False, preamble="Report", cc=[], bcc=[]): + + # ensure we have some address to send + if not to: + if not cc: + if not bcc: + return + to = bcc + else: + to = cc + + msg = MIMEMultipart() + msg['Subject'] = subject + msg['From'] = server["sendor"] + msg['To'] = ",".join(to) + msg['CC'] = ",".join(cc) + msg['BCC'] = ",".join(bcc) + msg.preamble = preamble + + if html: + msg.attach(MIMEText(body, 'html')) + else: + msg.attach(MIMEText(body, 'plain')) + + for filename in filenames: + fp = open(filename, 'rb') + img = MIMEBase("application", "octet-stream") + img.set_payload(fp.read()) + fp.close() + encoders.encode_base64(img) + basename = os.path.basename(filename) + img.add_header('Content-Disposition', 'attachment', filename=basename) + msg.attach(img) + + s = smtplib.SMTP(host=server["host"]) + s.sendmail(msg["From"], msg['To'].split(","), msg.as_string()) + s.quit() + + +if __name__ == '__main__': + to = ["rama.kristipati@broadcom.com"] + cc = ["rama.kristipati@broadcom.com"] + bcc = ["rama.kristipati@broadcom.com"] + server = { + "host": 'smtphost.broadcom.com', + "sendor": "TAMAlert@broadcom.com", + "pass": "tamalert" + } + filenames = [ + "results_logs.log", + "results_result.csv" + ] + send(server, to, "test mail subject", "test mail body", filenames, cc=cc, bcc=bcc) diff --git a/spytest/spytest/main.py b/spytest/spytest/main.py new file mode 100644 index 00000000000..506e2a1f1b9 --- /dev/null +++ b/spytest/spytest/main.py @@ -0,0 +1,123 @@ +import os +import sys +import time +import argparse +import random +import pytest +import pyfiglet + +from spytest.version import get_git_ver +from spytest.framework import parse_batch_args +import utilities.common as utils + +def _banner(): + result = pyfiglet.figlet_format("SPyTest") + print(result) + +def _print_git_ver(): + sha = get_git_ver() + print("\nVERSION: {}\n".format(sha)) + +def _parse_args(pre_parse=False): + # pytest hack to let it wotk with absolute paths for testbed and tclist + parser = argparse.ArgumentParser(description='Process SpyTest arguments.', + add_help=False) + if pre_parse: + parser.add_argument("--args-file", action="store", default=None, + help="spytest arguments from file path") + parser.add_argument("--testbed-file", action="store", default=None, + help="testbed file path -- default: ./testbed.yaml") + parser.add_argument("--tclist-file", action="store", + default=None, help="test case list file path") + parser.add_argument("--logs-path", action="store", + default=None, help="logs folder -- default: .") + parser.add_argument("--logs-level", action="store", + default="info", help="logs level -- default: info") + parser.add_argument("--log-level", action="store", dest="logs_level", + default="info", help="logs level -- default: info") + parser.add_argument("--results-prefix", action="store", + default=None, help="Prefix to be used for results.") + parser.add_argument("--file-mode", action="store_true", default=False, + help="Execute in file mode -- default: false") + parser.add_argument("-n", "--numprocesses", action="store", default=None, + type=int, help="number of preocessese") + parser.add_argument("--tclist-bucket", action="append", default=None, + help="use test cases from buckets") + parser.add_argument("--env", action="append", default=[], + nargs=2, help="environment variables") + + args, unknown = parser.parse_known_args() + if pre_parse and args.args_file: + # read arguments from file + user_root = os.getenv("SPYTEST_USER_ROOT") + if user_root and not os.path.isabs(args.args_file): + filepath = os.path.join(user_root, args.args_file) + else: + filepath = args.args_file + file_args = [] + for line in utils.read_lines(filepath): + file_args.extend(utils.split_with_quoted_strings(line)) + + # update sys.argv with arguments from file + index = sys.argv.index("--args-file") + new_argv = [] + new_argv.extend(sys.argv[:index]) + new_argv.extend(file_args) + new_argv.extend(sys.argv[index+2:]) + sys.argv = new_argv + + # update SPYTEST_CMDLINE_ARGS with arguments from file + app_cmdline = os.getenv("SPYTEST_CMDLINE_ARGS", "") + app_args = utils.split_with_quoted_strings(app_cmdline) + index = app_args.index("--args-file") + app_new_args = [] + app_new_args.extend(app_args[:index]) + for arg in file_args: + app_new_args.append("'{}'".format(arg) if " " in arg else arg) + app_new_args.extend(app_args[index+2:]) + os.environ["SPYTEST_CMDLINE_ARGS"] = " ".join(app_new_args) + + return _parse_args() + + sys.argv = [sys.argv[0]] + sys.argv.extend(unknown) + + for name, value in args.env: + print("setting environment {} = {}".format(name, value)) + os.environ[name] = value + + if args.testbed_file: + os.environ["SPYTEST_TESTBED_FILE"] = args.testbed_file + if args.tclist_file: + os.environ["SPYTEST_TCLIST_FILE"] = args.tclist_file + if args.logs_path: + os.environ["SPYTEST_LOGS_PATH"] = args.logs_path + os.environ["SPYTEST_LOGS_LEVEL"] = args.logs_level + + prefix="results" + if args.results_prefix: + file_prefix = args.results_prefix + os.environ["SPYTEST_RESULTS_PREFIX"] = file_prefix + else: + file_prefix = "{0}_{1}".format(prefix, time.strftime("%Y_%m_%d_%H_%M")) + os.environ["SPYTEST_FILE_PREFIX"] = file_prefix + + # filemode is needed in more places + if args.file_mode: + os.environ["SPYTEST_FILE_MODE"] = "1" + sys.argv.append("--file-mode") + + addl_args = parse_batch_args(args.numprocesses, args.tclist_bucket) + sys.argv.extend(addl_args) + + os.environ["SPYTEST_RAMDOM_SEED"] = str(random.randint(10000,20000)) + +def main(silent=False): + if not silent: + _banner() + _parse_args(True) + if not silent: + _print_git_ver() + pytest.main() + + diff --git a/spytest/spytest/net.py b/spytest/spytest/net.py new file mode 100644 index 00000000000..349ed68eaf7 --- /dev/null +++ b/spytest/spytest/net.py @@ -0,0 +1,5524 @@ +from __future__ import unicode_literals, print_function +import os +import sys +import json +import re +import tempfile +import logging +import traceback +import random +import signal +import time +import copy +import math +import shlex +import threading +import subprocess +from inspect import currentframe +from collections import OrderedDict + +import utilities.common as utils + +from spytest import profile +from spytest.dicts import SpyTestDict +from spytest.logger import Logger +from spytest.template import Template +from spytest.access.connection import DeviceConnection, DeviceConnectionTimeout +from spytest.access.connection import DeviceFileUpload, DeviceFileDownload +from spytest.access.connection import initDeviceConnectionDebug +from spytest.ansible import ansible_playbook +from spytest.prompts import Prompts +from spytest.rest import Rest +from spytest.ordyaml import OrderedYaml +from spytest.uicli import UICLI +from spytest.uirest import UIRest +from spytest.st_time import get_timenow +from spytest.st_time import get_elapsed +from spytest.uignmi import UIGnmi + + +lldp_prompt = r"\[lldpcli\]\s*#\s*$" +regex_login = r"\S+\s+login:\s*$" +regex_login_anywhere = r"\S+\s+login:\s*" +regex_password = r"[Pp]assword:\s*$" +regex_password_anywhere = r"[Pp]assword:\s*" +regex_onie = r"\s*ONIE:/ #\s*$" +regex_onie_sleep = r"\s*Info: Sleeping for [0-9]+ seconds\s*" +regex_onie_resque = r"\s+Please press Enter to activate this console.\s*$" +sonic_mgmt_hostname = "--sonic-mgmt--" + +class Net(object): + + def __init__(self, cfg, file_prefix=None, logger=None, testbed=None): + """ + initialization of Net + :param logger: + :type logger: + """ + initDeviceConnectionDebug(file_prefix) + self.cfg = cfg + self.logger = logger or Logger() + self.tb = testbed + self.topo = SpyTestDict({"duts": OrderedDict()}) + self.tmpl = dict() + self.rest = dict() + self.syslogs = dict() + self.is_vsonic_cache = dict() + self.memory_checks = dict() + self.skip_trans_helper = dict() + self.image_install_status = OrderedDict() + self.devices_used_in_tc = OrderedDict() + self.devices_used_collection = False + self.abort_without_mgmt_ip = False + self.trace_callback_support = False + self.module_start_time = None + self.module_max_timeout = self.cfg.module_max_timeout + self.tc_start_time = None + self.tc_max_timeout = self.cfg.tc_max_timeout + self.tc_get_tech_support = False + self.tc_fetch_core_files = False + self.fcli = 1 if self.cfg.faster_cli else 0 + self.tryssh = 1 if self.cfg.tryssh else 0 + self.use_last_prompt = False + self.profile_max_timeout_msg = None + self.prevent_list = [] + self.wa = None + self.prev_testcase = None + if os.getenv("SPYTEST_LIVE_TRACE_OUTPUT"): + self.trace_callback_support = True + self.use_sample_data = os.getenv("SPYTEST_USE_SAMPLE_DATA", None) + self.debug_find_prompt = bool(os.getenv("SPYTEST_DEBUG_FIND_PROMPT")) + self.dry_run_cmd_delay = os.getenv("SPYTEST_DRYRUN_CMD_DELAY", "0") + self.dry_run_cmd_delay = int(self.dry_run_cmd_delay) + self.connect_retry_delay = 5 + self.orig_time_sleep = time.sleep + #time.sleep = self.wait + self.force_console_transfer = False + self.max_cmds_once = 100 + self.kdump_supported = bool(os.getenv("SPYTEST_KDUMP_ENABLE", "1") == "1") + self.pending_downloads = dict() + self.log_dutid_fmt = os.getenv("SPYTEST_LOG_DUTID_FMT", "LABEL") + self.dut_log_lock = threading.Lock() + + def is_use_last_prompt(self): + fcli = os.getenv("SPYTEST_FASTER_CLI_OVERRIDE", None) + if fcli is not None: + self.fcli = 1 if fcli != "0" else 0 + fcli_last_prompt = os.getenv("SPYTEST_FASTER_CLI_LAST_PROMPT", "1") + self.use_last_prompt = True if fcli_last_prompt != "0" else False + return self.use_last_prompt + + def _init_dev(self, devname): + if devname not in self.topo["duts"]: + self.topo["duts"].update({devname: {}}) + self.syslogs.update({devname: []}) + + access = self._get_dev_access(devname) + access["type"] = "unknown" + access["errors"] = SpyTestDict() + access["current_handle"] = 0 + access["tryssh"] = False + access["filemode"] = False + access["current_prompt_mode"] = "unknown-prompt" + + return access + + def set_device_alias(self, devname, name): + access = self._get_dev_access(devname) + access["alias"] = name + + def _reset_device_aliases(self): + for _devname in self.topo["duts"]: + access = self._get_dev_access(_devname) + access["alias"] = access["alias0"] + + def _get_dut_label(self, devname): + access = self._get_dev_access(devname) + if access["dut_name"] == access["alias"]: + return access["dut_name"] + return "{}-{}".format(access["dut_name"], access["alias"]) + + def _get_dev_access(self, devname): + return self.topo["duts"][devname] + + def _get_handle(self, devname, index=None): + if index is None: + index = self._get_handle_index(devname) + return self._get_param(devname, "handle", index) + + def _set_handle(self, devname, handle, index=None): + if index is None: + index = self._get_handle_index(devname) + self._set_param(devname, "handle", handle, index) + + def _get_handle_index(self, devname): + access = self._get_dev_access(devname) + return access["current_handle"] + + def _get_param(self, devname, name, index=0): + name2 = "{}.{}".format(name, index) + access = self._get_dev_access(devname) + if name2 in access: + return access[name2] + if name in access: + return access[name] + return None + + def _set_param(self, devname, name, value, index=0): + access = self._get_dev_access(devname) + access["{}.{}".format(name, index)] = value + access[name] = value + + def _set_prompt(self, devname, name, value): + access = self._get_dev_access(devname) + access["prompts"].patterns[name] = value + + def _get_cli_prompt(self, devname, index=0): + return self._get_param(devname, "normal-user-cli-prompt", index) + + def _switch_connection(self, devname, index=0): + access = self._get_dev_access(devname) + old = access["current_handle"] + if old != index: + msg = "switching from handle {} to {}".format(old, index) + self.dut_log(devname, msg, lvl=logging.WARNING) + access["current_handle"] = index + self._set_last_prompt(access, None) + return True + return False + + def is_sonic_device(self, devname): + access = self._get_dev_access(devname) + if access["filemode"]: return True + connection_param = access["connection_param"] + return bool(connection_param["access_model"].startswith("sonic_")) + + def is_vsonic_device(self, devname): + return bool(self.tb.get_device_type(devname) in ["vsonic"]) + + def _is_console_connection(self, devname, connection_param=None): + if not connection_param: + access = self._get_dev_access(devname) + if access["filemode"]: return True + connection_param = access["connection_param"] + if connection_param["access_model"].endswith("_terminal"): + return True + if connection_param["access_model"].endswith("_sshcon"): + return True + return False + + def _tryssh_switch(self, devname, recover=None, reconnect=True, check=True): + + # nothing to be done if tryssh is not enabled + if not self.tryssh and check: + return False + + # nothing to be done if not console run + if not self._is_console_connection(devname): + return False + + # switch to console + if recover is None: + return self._switch_connection(devname, 0) + + # switch to ssh + if recover == False: + return self._switch_connection(devname, 1) + + # reconnect? and switch to ssh + if not reconnect: + return self._switch_connection(devname, 1) + + # reconnect and switch to ssh + hndl = self._get_handle(devname, 1) + if hndl: hndl.disconnect() + return self._tryssh_init(devname, False) + + def dut_log(self, devname, msg, skip_general=False, lvl=logging.INFO, cond=True): + if not cond: + return + if self.dut_log_lock: self.dut_log_lock.acquire() + try: + access = self._get_dev_access(devname) + conn = "SSH" if access["current_handle"] != 0 else None + log_dutid_fmt = self.log_dutid_fmt.upper() + if log_dutid_fmt == "ID": + dut_name = access["dut_name"] + elif log_dutid_fmt == "ALIAS": + dut_name = access["alias"] + else: + dut_name = self._get_dut_label(devname) + self.logger.dut_log(dut_name, msg, lvl, + skip_general, True, conn=conn) + except Exception as e: + print("dut_log", e) + finally: + if self.dut_log_lock: self.dut_log_lock.release() + + def _copy_value(self, from_dict, to_dict, names): + for name in names: + if name in from_dict: + to_dict[name] = from_dict[name] + + def register_devices(self, _topo): + for devname in _topo["duts"]: + device_model = "sonic" + dut = _topo["duts"][devname] + self._init_dev(devname) + self.set_console_only(bool(not self.tryssh), False) + access = self._get_dev_access(devname) + self._copy_value(dut, access, ["sshcon_username", "sshcon_password"]) + if "dut_name" in dut: + access.update({"dut_name": dut["dut_name"]}) + if "alias" in dut: + access.update({"alias0": dut["alias"]}) + access.update({"alias": dut["alias"]}) + if "access_model" in dut: + access.update({"access_model": dut["access_model"]}) + if "device_model" in dut: + device_model = dut["device_model"] + access.update({"device_model": device_model}) + if "username" in dut: + access.update({"username": dut["username"]}) + if "password" in dut: + access.update({"password": dut["password"]}) + if "altpassword" in dut: + access.update({"altpassword": dut["altpassword"]}) + if "onie_image" in dut: + access.update({"onie_image": dut["onie_image"]}) + if "errors" in dut: + access.update({"errors": dut["errors"]}) + if "mgmt_ipmask" in dut: + access.update({"mgmt_ipmask": dut["mgmt_ipmask"]}) + if "mgmt_gw" in dut: + access.update({"mgmt_gw": dut["mgmt_gw"]}) + if "port" in dut: + access.update({"port": dut["port"]}) + if "ip" in dut: + access.update({"ip": dut["ip"]}) + else: + msg = "'ipaddr' parameter missing in topology input" + raise ValueError(msg) + if os.getenv("SPYTEST_SWAP_PASSWORD"): + if "password" in access and "altpassword" in access: + password = access["password"] + access["password"] = access["altpassword"] + access["altpassword"] = password + self.tmpl.update({devname: Template(device_model)}) + self.rest.update({devname: Rest(logger=self.logger)}) + + def unregister_devices(self): + for _devname in self.topo["duts"]: + self._disconnect_device(_devname) + + self.topo["duts"] = {} + + def _trace_received(self, devname, cmd, hndl, msg1, msg2, line): + if msg1: + self.dut_log(devname, msg1, lvl=logging.WARNING) + if msg2: + self.dut_log(devname, "{}: {}".format(line, msg2)) + profile.prompt_nfound(cmd) + try: + self.dut_log(devname, "============={}: DATA Rcvd ============".format(line)) + for msg in "".join(hndl.get_cached_read_data()).split("\n"): + if not msg: continue + msg = msg.strip() + if not msg: continue + self.dut_log(devname, "'{}'".format(msg)) + self.dut_log(devname, "======================================") + except: + self.dut_log(devname, "{}: DATA Rcvd: {}".format(line, "UNKNOWN")) + + def _find_prompt(self, access, net_connect=None, count=15, sleep=2, recovering=False): + device = access["devname"] + hndl = self._get_handle(device) if not net_connect else net_connect + line = currentframe().f_back.f_lineno + for i in range(count): + try: + ########### Try connecting again ##################### + if not self._is_console_connection(device): + if (i > 0 and not net_connect) or not hndl: + self.dut_log(device, "Trying to read prompt after reconnecting") + if not self.reconnect(devname=device): + if sleep > 0: + time.sleep(sleep) + continue + hndl = self._get_handle(device) + ########### Try connecting again ##################### + if not hndl: + self.dut_log(device, "Failed to read prompt: Null handle") + else: + for j in range(5): + output = hndl.find_prompt() + access["last-prompt"] = output + if hndl.verify_prompt(output): + break + self.dut_log(device, "{}: invalid-prompt: {}".format(line, output)) + self._check_error(access, "\n", output) + output = utils.to_string(output) + if self.debug_find_prompt: + self.dut_log(device, "find-prompt({}): {}".format(line, output)) + try: + prompts = access["prompts"] + access["current_prompt_mode"] = prompts.get_mode_for_prompt(output) + except: + access["current_prompt_mode"] = "unknown-prompt" + return output + except: + msg1 = utils.stack_trace(traceback.format_exc()) + msg2 = "Failed to read prompt .. handle: '{}', try: {}".format(hndl, i) + self._trace_received(device, "find_prompt", hndl, msg1, msg2, line) + if not hndl: break + if not hndl.is_alive(): + hndl = None + if sleep > 0: time.sleep(sleep) + + # dump sysrq traces + if hndl and os.getenv("SPYTEST_SYSRQ_ENABLE"): + try: + output = hndl.sysrq_trace() + if output: + self.dut_log(device, output, lvl=logging.WARNING) + except Exception as exp: + print(exp) + + # recover using RPS and report result + if self.wa: + try: + if self._get_handle_index(device) == 0: + msg = "Console hang proceeding with RPS Reboot" + self.dut_log(device, msg, lvl=logging.WARNING) + self.wa.do_rps(device, "reset") + if not self.wa.session_init_completed: + if not recovering: + # try finding the prompt once again + # as the session init is not completed + return self._find_prompt(access, recovering=True) + msg = "Failed to recover the DUT even after RPS reboot" + self.dut_log(device, msg, lvl=logging.ERROR) + os._exit(15) + else: + # Disconnect the handle + if hndl: hndl.disconnect() + # Set the ssh handle in access as None + self._set_handle(device, None, 1) + # tryssh as False + access["tryssh"] = False + # Switch connection to console handle + self._switch_connection(device, 0) + # Find Prompt on console handle + self._find_prompt(access) + except Exception as exp: + print(exp) + self.wa.report_env_fail("console_hang_observed") + sys.exit(0) + + def _connect_to_device2(self, device, retry, msgs): + connected = False + net_connect = None + count = 0 + while True: + try: + if count > 0: + if self.connect_retry_delay > 0: + time.sleep(self.connect_retry_delay) + msgs.append("Re-Trying %d.." % (count + 1)) + net_connect = DeviceConnection(logger=self.logger, **device) + connected = True + break + except DeviceConnectionTimeout: + msgs.append("Timed-out..") + count += 1 + if count > retry: + break + except Exception as e: + self.logger.warning(e) + count += 1 + if count > retry: + break + + if connected: + net_connect.set_logger(self.logger) + return net_connect + + msgs.append("Cannot connect: {}:{}".format(device["ip"], device["port"])) + + return None + + def _connect_to_device(self, device, access, retry=0): + msgs = [] + net_connect = self._connect_to_device2(device, retry, msgs) + devname = access["devname"] + for msg in msgs: + self.dut_log(devname, msg) + if net_connect: + self.dut_log(devname, "Connected ...", lvl=logging.DEBUG) + prompt = self._find_prompt(access, net_connect) + self._set_param(devname, "prompt", prompt) + return net_connect + + def _disconnect_device(self, devname): + if devname in self.topo["duts"]: + hndl = self._get_handle(devname) + if hndl: + try: + hndl.send_command_timing("\x03") + except: + pass + hndl.disconnect() + self._set_handle(devname, None) + + def trace_callback_set(self, devname, val): + def trace_callback(self, msg): + try: + print(msg) + except: + pass + if not self.trace_callback_support: + return + access = self._get_dev_access(devname) + if access["filemode"]: + return + if not self._is_console_connection(devname): + return + func = getattr(self._get_handle(devname), "trace_callback_set") + if func: + if val: + func(trace_callback, self) + else: + func(None, None) + + def disable_ztp(self, devname): + if self.cfg.pde or self.cfg.community_build: + pass # nothing to be done + elif "ztp" not in self.prevent_list: + self._exec(devname, "sudo ztp disable -y", None, + "normal-user", delay_factor=6) + + def show_dut_time(self, devname): + date=self._exec(devname, "date -u +'%Y-%m-%d %H:%M:%S'", None, "normal-user", trace_dut_log=2) + self.dut_log(devname, "=== UTC Date on the device {}".format(date)) + + def is_vsonic(self, dut): + if dut in self.is_vsonic_cache: + return self.is_vsonic_cache[dut] + output = self.show_new(dut,'ls /etc/sonic/bcmsim.cfg',skip_tmpl=True) + val = not bool(re.search(r'No such file or directory',output)) + self.is_vsonic_cache[dut] = val + return val + + def set_login_timeout(self, devname): + if self.is_vsonic_device(devname): + self._exec(devname, "echo 3 | sudo tee /proc/sys/kernel/printk", None, "normal-user") + if self.is_sonic_device(devname): + self._exec(devname, "export TMOUT=0", None, "normal-user") + self._exec(devname, "stty cols 5000", None, "normal-user") + + def read_vtysh_hostname(self, devname, retry=10): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + prompts = access["prompts"] + if self.cfg.pde: + access["hostname"] = "sonic" + return access["hostname"] + + hostname_cmd = "sudo vtysh -c 'show running-config | include hostname'" + cli_prompt = self._get_param(devname, "normal-user-cli-prompt") + output = self._send_command(access, hostname_cmd, cli_prompt, + skip_error_check=True) + if "Error response from daemon" in output or \ + "failed to connect to any daemons" in output: + if retry <= 0: + msg = "Failed to read hostname from vtysh - assuming sonic" + access["hostname"] = "sonic" + self.dut_log(devname, msg, lvl=logging.WARNING) + return access["hostname"] + msg = "Failed to read hostname from vtysh retry again in 5 sec" + self.dut_log(devname, msg, lvl=logging.WARNING) + self.wait(5) + retry = retry - 1 + return self.read_vtysh_hostname(devname, retry) + + access["hostname"] = "sonic" + for line in [_f for _f in str(output).split("\n") if _f]: + if line.startswith("hostname"): + access["hostname"] = line.replace("hostname", "").strip() + msg = "Hostname in vtysh: {}".format(access["hostname"]) + self.dut_log(devname, msg) + prompts.update_with_hostname(access["hostname"]) + return access["hostname"] + + # phase 0: init 1: upgrade 2: reboot + def do_post_reboot(self, devname, phase=2, ifa=True, kdump=True, max_ready_wait=0): + self.set_login_timeout(devname) + if self.is_sonic_device(devname): + self.disable_ztp(devname) + self.read_vtysh_hostname(devname) + self.wa.wait_system_status(devname, max_time=max_ready_wait) + self._set_mgmt_ip(devname) + if phase in [0, 1]: + self.reset_restinit(devname) + self._fetch_mgmt_ip(devname, 5, 2) + self.wa.instrument(devname, "post-reboot") + + if os.getenv("SPYTEST_DATE_SYNC", "0") != "0": + cmd_date = "sudo date --set='{}'".format(get_timenow().strftime("%c")) + self._exec(devname, cmd_date, None, "normal-user") + + ################################################################## + # tasks after every upgrade + if phase in [0, 1]: + self.wa.hooks.ensure_upgrade(devname) + ################################################################## + + # show date on the device + self.show_dut_time(devname) + + (exec_reboot, reboot_ifa, reboot_kdump) = (False, ifa, kdump) + if ifa and os.getenv("SPYTEST_IFA_ENABLE"): + try: + self.config_new(devname, "ifa -config -enable -y", expect_reboot=True) + exec_reboot = True + reboot_ifa = False + except Exception as exp: + msg = "Failed enable IFA ({})".format(exp) + self.dut_log(devname, msg, lvl=logging.WARNING) + if kdump and self.kdump_supported and not self.is_vsonic_device(devname): + try: + cmd = "sudo show kdump status" + output = self.show_new(devname, cmd, skip_tmpl=True, skip_error_check=True) + if (("Kdump Administrative Mode: Enabled" not in output) or + ("Kdump Operational State: Ready" not in output)): + self.config_new(devname, "config kdump enable") + self.config_new(devname, "config save -y") + exec_reboot = True + reboot_kdump = False + except Exception as exp: + msg = "Failed to enable KDUMP ({})".format(exp) + self.dut_log(devname, msg, lvl=logging.WARNING) + if exec_reboot: + self.do_post_reboot(devname, phase, reboot_ifa, reboot_kdump, max_ready_wait) + + def connect_to_device_current(self, devname, retry=0): + access = self._get_dev_access(devname) + if access["current_handle"] == 0: + return self.connect_to_device(devname, retry, True) + return self._tryssh_init(devname, False) + + def init_normal_prompt(self, devname, index=0): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + prompt = self._find_prompt(access) + prompt2 = prompt.replace("\\", "") + self._set_param(devname, "prompt", prompt, index) + self._set_param(devname, "normal-user-cli-prompt", prompt, index) + self._set_prompt(devname, "normal-user", prompt) + return prompt2 + + def connect_to_device(self, devname, retry=0, recon=False): + connection_param = { + 'access_model': 'sonic_ssh', + 'username': 'admin', + 'password': 'YourPaSsWoRd', + 'blocking_timeout': 30, + 'keepalive': 1, + 'port': 22 + } + access = self._get_dev_access(devname) + + if "access_model" in access: + connection_param.update({"access_model": access["access_model"]}) + if "username" in access: + connection_param.update({"username": access["username"]}) + if "password" in access: + connection_param.update({"password": access["password"]}) + if "altpassword" in access: + connection_param.update({"altpassword": access["altpassword"]}) + if "verbose" in access: + connection_param.update({"verbose": access["verbose"]}) + if "port" in access: + connection_param.update({"port": access["port"]}) + if "mgmt_ipmask" in access: + connection_param.update({"mgmt_ipmask": access["mgmt_ipmask"]}) + if "mgmt_gw" in access: + connection_param.update({"mgmt_gw": access["mgmt_gw"]}) + if "addl_auth" in access: + connection_param.update({"addl_auth": access["addl_auth"]}) + + self._copy_value(access, connection_param, ["sshcon_username", "sshcon_password"]) + connection_param['net_devname'] = devname + connection_param['net_login'] = self._net_login + + connection_param['ip'] = access["ip"] + + access["filemode"] = self.cfg.filemode + access["devname"] = devname + access["last-prompt"] = None + if "device_model" in access: + access["prompts"] = Prompts(access["device_model"]) + else: + access["prompts"] = Prompts() + + self.dut_log(devname, "Connecting to device (%s): %s: %s:%s .." % + (access["alias"], connection_param["access_model"], + connection_param['ip'], connection_param['port'])) + + if not self.cfg.filemode: + access["connection_param"] = connection_param + net_connect = self._connect_to_device(connection_param, + access, retry=retry) + if not net_connect: + dut_label = self._get_dut_label(devname) + msg = "Failed to connect to device {}".format(dut_label) + self.dut_log(devname, msg, lvl=logging.ERROR) + + if os.getenv("SPYTEST_RECOVERY_MECHANISMS", "0") != "0": + if not self.wa.session_init_completed and \ + self._is_console_connection(devname, connection_param) and \ + self.is_sonic_device(devname): + msg = "Trying RPS Reboot to recover the device {}".format(dut_label) + self.dut_log(devname, msg, lvl=logging.WARNING) + self.wa.do_rps(devname, "reset", recon=False) + net_connect = self._connect_to_device(connection_param, + access, retry=retry) + if not net_connect: + msg = "Failed to connect to device {} even after RPS reboot".format(dut_label) + self.dut_log(devname, msg, lvl=logging.ERROR) + return False + else: + return False + else: + return False + + self._set_handle(devname, net_connect) + prompt2 = self.init_normal_prompt(devname) + msg = "Prompt at the connection start: '{}' ".format(prompt2) + self.dut_log(devname, msg) + + if not self.wa.session_init_completed: + self.image_install_status[devname] = False + + ################################################################## + # detect if we are in ONIE discovery prompt + if self._is_console_connection(devname, connection_param): + if "ONIE" in self._get_param(devname, "prompt"): + if not self.recover_from_onie(devname, True): + if not self.recover_from_onie(devname, False): + return False + self.init_normal_prompt(devname) + ################################################################## + # Ensure that we are at normal user mode to be able to + # continue after reboot from test scripts + if self._is_console_connection(devname, connection_param) and \ + self.is_sonic_device(devname): + try: + prompt = self._get_param(devname, "normal-user-cli-prompt") + output = self._send_command(access, "?", ufcli=False, + trace_dut_log=0, skip_error_check=True) + if "command not found" not in output: + prompt = self._exit_docker(devname, prompt) + + prompt2 = prompt.replace("\\", "") + if re.compile(lldp_prompt).match(prompt2) or sonic_mgmt_hostname in prompt2: + prompt = self._exit_docker(devname, prompt) + + self._enter_linux(devname, prompt) + prompt = self.init_normal_prompt(devname) + output = self._send_command(access, "whoami", skip_error_check=True, ufcli=False) + if "Unknown command" in output: + self._exit_vtysh(devname, onconnect=1) + prompt = self.init_normal_prompt(devname) + output = self._send_command(access, "whoami") + whoami = output.split("\n")[0].strip() + if connection_param["username"] != whoami: + msg = "current user {} is not same in testbed {}" + msg = msg.format(whoami, connection_param["username"]) + self.dut_log(devname, msg, lvl=logging.WARNING) + prompt_terminator = r"([#|\$]\s*$|{})".format(regex_login) + self._send_command(access, "exit", prompt_terminator) + self._enter_linux(devname) + prompt = self.init_normal_prompt(devname) + except Exception as exp: + msg = "Please report this issue ({})".format(exp) + self.logger.error(msg) + msg = utils.stack_trace(traceback.format_exc()) + self.dut_log(devname, msg, lvl=logging.WARNING) + + # perform post reboot operations + connection_param["mgmt-ip"] = None + + # wait for short time when we are going to upgrade + max_ready_wait = 0 if self.cfg.skip_load_image else 1 + if not recon: + if not self.cfg.community_build: + self.config_new(devname, "sonic-clear logging", skip_error_check=True) + self.dut_log(devname, "reading initial version") + self.show_new(devname, "show version", skip_tmpl=True, skip_error_check=True) + self.do_post_reboot(devname, phase=0, max_ready_wait=max_ready_wait) + else: + self.set_login_timeout(devname) + + # open ssh connection to switch execution to + self._tryssh_init(devname) + + else: + self._set_param(devname, "normal-user-cli-prompt", "dummy") + + prompt = self._get_param(devname, "normal-user-cli-prompt") + msg = "Prompt at the connection finish: '{}' ".format(prompt.replace("\\", "")) + self.dut_log(devname, msg) + self.show_dut_time(devname) + + return True + + def _tryssh_init(self, devname, init=True): + access = self._get_dev_access(devname) + if not access["tryssh"] and not access.get("static-mgmt-ip"): + return True + + if access["filemode"]: + return True + + old = access["connection_param"].get("mgmt-ip") + if access["tryssh"] and not init: + self._fetch_mgmt_ip(devname, 5, 2) + new = access["connection_param"].get("mgmt-ip") + if old != new: + self.dut_log(devname, "IP address changed from {} to {}".format(old, new)) + + if init and not old: + self.dut_log(devname, "Trying to get the device mgmt-ip..") + self._fetch_mgmt_ip(devname, 5, 2) + + [hndl, ipaddr] = self._connect_to_device_ssh(devname) + if hndl: + self._set_handle(devname, hndl, 1) + self.init_normal_prompt(devname, 1) + self._switch_connection(devname, 1) + self.set_login_timeout(devname) + return True + self.dut_log(devname, "Failed to ssh connect {}".format(ipaddr)) + access["tryssh"] = False + self._set_handle(devname, None, 1) + return False + + def _connect_to_device_ssh(self, devname): + access = self._get_dev_access(devname) + device = copy.copy(access["connection_param"]) + if not device["mgmt-ip"]: + return [None, None] + device["ip"] = device["mgmt-ip"] + device["port"] = 22 + device["blocking_timeout"] = 30 + device["access_model"] = "sonic_ssh" + del device["mgmt-ip"] + msgs = [] + self.dut_log(devname, "initiate ssh to {}".format(device["ip"])) + hndl = self._connect_to_device2(device, 0, msgs) + return [hndl, device["ip"]] + + def _set_mgmt_ip(self, devname): + access = self._get_dev_access(devname) + access["static-mgmt-ip"] = False + if access["filemode"]: + return + + # no need to set static mgmt ip if not run from terminal + connection_param = access["connection_param"] + if not self._is_console_connection(devname): + return + + # check if mgmt option is specified in testbed + mgmt_ipmask = connection_param.get("mgmt_ipmask", None) + mgmt_gw = connection_param.get("mgmt_gw", None) + if not mgmt_ipmask or not mgmt_gw: + return + + # TODO: check if mgmt is already used in network + #utils.ipcheck(mgmt) + + prompt = self._enter_linux_exit_vtysh(devname) + if prompt is None: + prompt = self._find_prompt(access) + self._set_param(devname, "prompt", prompt) + cmd = "sudo /sbin/ifconfig eth0 {}".format(mgmt_ipmask) + cmd = "{};sudo /sbin/route add default gw {}".format(cmd, mgmt_gw) + self._send_command(access, cmd) + access["static-mgmt-ip"] = True + self.rest_init(devname, True, access.get("username"), access.get("password"), access.get("altpassword")) + + #self._apply_remote(devname, "set-mgmt-ip", ["static", mgmt_ipmask, mgmt_gw]) + + def _fetch_mgmt_ip(self, devname, try_again=3, wait_for_ip=0): + if not self.is_sonic_device(devname): return + access = self._get_dev_access(devname) + if access["filemode"]: return + + (switched, reconnect) = (False, True) + try: + switched = self._tryssh_switch(devname) + old = access["connection_param"].get("mgmt-ip") + self._fetch_mgmt_ip2(devname, try_again, wait_for_ip) + new = access["connection_param"].get("mgmt-ip") + if old and new and old == new: + # no need to reconnect to SSH + reconnect = False + if switched: self._tryssh_switch(devname, True, reconnect) + except Exception as e: + msg = utils.stack_trace(traceback.format_exc()) + self.dut_log(devname, msg, lvl=logging.WARNING) + if switched: self._tryssh_switch(devname, False) + raise e + + def _fetch_mgmt_ip2(self, devname, try_again=3, wait_for_ip=0): + access = self._get_dev_access(devname) + + # if the device type is terminal issue ifconfig to get eth0 ipaddress + connection_param = access["connection_param"] + if not self._is_console_connection(devname): + connection_param["mgmt-ip"] = None + self.rest_init(devname, access.get("username"), access.get("password"), access.get("altpassword"), True) + return + + prompt = self._enter_linux_exit_vtysh(devname) + if prompt is None: + prompt = self._find_prompt(access) + self._set_param(devname, "prompt", prompt) + no_ifconfig = True + try: + if not no_ifconfig: + output = self._send_command(access, "/sbin/ifconfig eth0") + data = self._textfsm_apply(devname, "unix_ifcfg.tmpl", output)[0] + access["connection_param"]["mgmt-ip"] = data[4][0].encode('ascii') + self._send_command(access, "/sbin/ip route list dev eth0", skip_error_check=True) + else: + try: + output = self._send_command(access, "/sbin/ip route list dev eth0", skip_error_check=True) + data = self._textfsm_apply(devname, "linux/ip_route_list_dev.tmpl", output)[0] + access["connection_param"]["mgmt-ip"] = data[1].encode('ascii') + except: + msg = "Unable to get the ip address of eth0 from '/sbin/ip route list'. Falling back to 'ifconfig'.." + self.dut_log(devname, msg, lvl=logging.WARNING) + output = self._send_command(access, "/sbin/ifconfig eth0") + data = self._textfsm_apply(devname, "unix_ifcfg.tmpl", output)[0] + access["connection_param"]["mgmt-ip"] = data[4][0].encode('ascii') + msg = "eth0: {}".format(access["connection_param"]["mgmt-ip"]) + self.dut_log(devname, msg) + self.rest_init(devname, access.get("username"), access.get("password"), access.get("altpassword"), True) + self.check_pending_downloads(devname) + except: + msg1 = "Failed to read ip address of eth0" + msg2 = "Failed to read ip address of eth0..Retrying" + msg = msg2 if try_again > 0 else msg1 + self.dut_log(devname, msg, lvl=logging.WARNING) + self.dut_log(devname, "{}: Rcvd: '{}'".format(try_again, output)) + if try_again > 0: + if wait_for_ip > 0 or len(output) == 0: + # output is present but there is no IP address + self.wait(wait_for_ip) + try_again = try_again - 1 + self._fetch_mgmt_ip2(devname, try_again, wait_for_ip) + return + if self.abort_without_mgmt_ip: + msg = "Cannot proceed without management IP address" + self.dut_log(devname, msg, lvl=logging.ERROR) + os._exit(15) + connection_param["mgmt-ip"] = None + + def connect_all_devices(self, faster_init=False): + [retvals, exceptions] = utils.exec_foreach(faster_init, self.topo["duts"], + self.connect_to_device, 10) + + for devname in self.topo["duts"]: + connected = retvals.pop(0) + exception = exceptions.pop(0) + if not connected: + msg = "Failed to connect to device" + elif exception: + msg = "Error connecting to device" + else: + continue + self.dut_log(devname, msg) + return False + self.logger.info("Connected to All devices") + return True + + def _report_error(self, matched_result, cmd): + if not matched_result: + return False + if isinstance(matched_result, list): + result = matched_result[0] + msgid = matched_result[1] + else: + result = "Fail" + msgid = matched_result + if result == "DUTFail": + self.wa.report_dut_fail(msgid, cmd) + elif result == "EnvFail": + self.wa.report_env_fail(msgid, cmd) + elif result == "TGenFail": + self.wa.report_tgen_fail(msgid, cmd) + else: + self.wa.report_fail(msgid, cmd) + return True + + def _check_error(self, access, cmd, output, skip_raise=False): + devname = access["devname"] + actions = [] + matched_result = None + matched_err = "" + for err, errinfo in list(access["errors"].items()): + #self.logger.debug("COMPARE-CMD: {} vs {}".format(errinfo.command, cmd)) + if re.compile(errinfo.command).match(cmd): + #self.logger.debug("COMPARE-OUT: {} vs {}".format(errinfo.search, output)) + if re.search(errinfo.search, output): + #self.logger.debug("MATCHED-OUT: {} ACTION: {} {}/{}".format( + #output, errinfo.action, skip_raise, self.wa.session_init_completed)) + actions = utils.make_list(errinfo.action) + matched_err = err + matched_result = errinfo.get("result", None) + break + if not matched_err: + return output + new_output = [] + # check if coredump and techsupport are needed + for action in actions: + if action == "core-dump": + self.tc_fetch_core_files = True + if action == "tech-support": + self.tc_get_tech_support = True + self.wa.set_module_lvl_action_flags(action) + for action in actions: + if action == "reboot": + self.dut_log(devname, output, lvl=logging.WARNING) + out = self.recover(devname, "Rebooting match {} action".format(matched_err)) + new_output.append(out) + self._report_error(matched_result, cmd) + elif action == "raise": + if not self.wa.session_init_completed: + return output + if skip_raise: + msg = "Skipped error checking. Even though detected pattern: '{}'".format(matched_err) + self.dut_log(devname, msg, lvl=logging.WARNING) + return output + msg = "Error: failed to execute '{}'".format(cmd) + self.dut_log(devname, msg, lvl=logging.WARNING) + self.dut_log(devname, output, lvl=logging.WARNING) + if not self._report_error(matched_result, cmd): + self.dut_log(devname, "detected pattern: {}".format(matched_err)) + raise ValueError("Command '{}' returned error".format(cmd)) + return '\n'.join(new_output) + + def _trace_cli(self, access, cmd): + # trace the CLI commands in CSV file, to be used to measure coverage + # module,function,cli-mode,command + #pass + try: self.wa._trace_cli(access["devname"], access["current_prompt_mode"], cmd) + except: pass + + def _try(self, access, line, new_line, fcli, cmd, expect_string, delay_factor, **kwargs): + output = "" + attempt = 0 + devname = access["devname"] + + if self.devices_used_collection: + self.devices_used_in_tc[devname] = True + + ctrl_c_used = False + while attempt < 3: + try: + if not self._get_handle(devname): + self.connect_to_device_current(devname) + if attempt != 0: + msg = "cmd: {} attempt {}..".format(cmd, attempt) + self.dut_log(devname, msg, lvl=logging.WARNING) + msg = "Disconnecting the device {} connection ..".format(devname) + self.dut_log(devname, msg, lvl=logging.WARNING) + self._disconnect_device(devname) + msg = "Reconnecting to the device {} ..".format(devname) + self.dut_log(devname, msg, lvl=logging.WARNING) + self.connect_to_device_current(devname) + hndl = self._get_handle(devname) + if not hndl: + msg = "Device not connected: attempt {}..".format(attempt) + self.dut_log(devname, msg, lvl=logging.WARNING) + reconnect_wait = 5 + msg = "Waiting for {} secs before re-attempting connection to Device {}..".format(reconnect_wait, devname) + self.dut_log(devname, msg, lvl=logging.WARNING) + self.wait(reconnect_wait) + attempt += 1 + continue + if attempt != 0: + msg = "Trying CR: attempt {}.. cmd '{}' ..".format(attempt, cmd) + self.dut_log(devname, msg, lvl=logging.WARNING) + hndl.send_command_timing("") + elif new_line: + output = hndl.send_command_new(fcli, cmd, expect_string, delay_factor, **kwargs) + self._trace_cli(access, cmd) + else: + output = hndl.send_command_timing(cmd, normalize=False) + hndl.clear_buffer() + break + except Exception as ex: + if self.wa.is_shutting_down(): + return "" + msg = utils.stack_trace(traceback.format_exc()) + self.dut_log(devname, msg, lvl=logging.WARNING) + t = "Exception {} occurred.. (attempt {}) line: {} cmd: {}" + msg = t.format(type(ex).__name__, attempt, line, cmd) + self.dut_log(devname, msg) + if self._get_handle(devname): + hndl = self._get_handle(devname) + line = currentframe().f_back.f_lineno + self._trace_received(devname, cmd, hndl, None, None, line) + try: + if attempt == 0: + msg = "Trying CR: attempt {}.. cmd '{}' ..".format(attempt, cmd) + self.dut_log(devname, msg, lvl=logging.WARNING) + #output = hndl.send_command(fcli, "", expect_string) + hndl.send_command_timing("") + output = hndl.find_prompt() + hndl.clear_buffer() + if hndl.verify_prompt(output): + break + except Exception as cr_ex: + msg = "Unable to find prompt even after trying CR .." + self.dut_log(devname, msg, lvl=logging.WARNING) + t = "Exception {} occurred while trying CR.. (attempt {}) line: {} cmd: {} exception: {}" + msg = t.format(type(cr_ex).__name__, attempt, line, cmd, cr_ex) + self.dut_log(devname, msg) + line = currentframe().f_back.f_lineno + self._trace_received(devname, "", hndl, None, None, line) + try: + msg = "Trying CTRL+C: attempt {}..".format(attempt) + self.dut_log(devname, msg, lvl=logging.WARNING) + hndl.send_command_timing("\x03") + hndl.clear_buffer() + ctrl_c_used = True + # TODO: Need to check for both testcase and module on result setting. + #if self.tc_start_time: + # self.wa.report_scripterror("command_failed_recovered_using_ctrlc", cmd) + except Exception as ex2: + if self.wa.is_shutting_down(): + self.dut_log(devname, "run shutting down", lvl=logging.WARNING) + return "" + msg = utils.stack_trace(traceback.format_exc()) + self.dut_log(devname, msg, lvl=logging.WARNING) + t = "Exception occurred even after CTRL+C.. (attempt {0}) type {1} args:\n{2!r}" + msg = t.format(attempt, type(ex2).__name__, ex2.args) + self.dut_log(devname, msg, lvl=logging.WARNING) + attempt += 1 + + if ctrl_c_used: + if self.tc_start_time: + self.wa.report_fail("command_failed_recovered_using_ctrlc", cmd) + elif self.module_start_time: + msg = "Command '{}' failed to give prompt during module config, recovered using CTRL+C".format(cmd) + self.wa.report_config_fail("module_config_failed", msg) + + return output + + def _send_command(self, access, cmd, expect=None, skip_error_check=False, + delay_factor=0, trace_dut_log=3, new_line=True, + ufcli=True, **kwargs): + output = "" + + # use default delay factor if not specified + delay_factor = 2 if delay_factor == 0 else delay_factor + fcli = self.fcli if ufcli else 0 + + devname = access["devname"] + + if trace_dut_log in [1, 3]: + cmd_log = cmd.replace("\r", "") + cmd_log = cmd_log.replace("\n", "\\n") + + # disable faster-cli if there are new lines + if not fcli or delay_factor > 2: + self.dut_log(devname, "SCMD: {}".format(cmd_log)) + elif cmd.count("\n") > 0: + self.dut_log(devname, "SCMD: {}".format(cmd_log)) + fcli = 0 + else: + self.dut_log(devname, "FCMD: {}".format(cmd_log)) + + if not access["filemode"]: + if not expect: + expect_string = self._get_param(devname, "prompt") + else: + expect_string = expect + + pid = profile.start(cmd, access["dut_name"]) + line = currentframe().f_back.f_lineno + #self.dut_log(devname, "EXPECT: {}".format(expect_string)) + output = self._try(access, line, new_line, fcli, + cmd, expect_string, delay_factor, **kwargs) + profile.stop(pid) + + if trace_dut_log in [2, 3]: + self.dut_log(devname, output) + + self._check_tc_timeout(access) + + elif self.dry_run_cmd_delay > 0: + time.sleep(self.dry_run_cmd_delay) + + output = self._check_error(access, cmd, output, skip_error_check) + + return output + + def do_pre_rps(self, devname, op): + self._tryssh_switch(devname) + + def do_post_rps(self, devname, op): + if op not in ["off"]: + self.dut_log(devname, "Reconnecting after RPS reboot", lvl=logging.WARNING) + rps_flag = False + index = 0 + rps_reboot_static_wait = 30 + while index < 3: + if self.reconnect(devname): + rps_flag = True + break + msg = "Waiting for '{}' secs after RPS reboot.".format(rps_reboot_static_wait) + self.dut_log(devname, msg, lvl=logging.WARNING) + self.wait(rps_reboot_static_wait) + index = index + 1 + if not rps_flag: + return rps_flag + self._enter_linux(devname) + self.do_post_reboot(devname) + self._tryssh_switch(devname, True) + return True + + def reconnect(self, devname=None): + if not devname or isinstance(devname, list): + if not devname: + devlist = self.topo["duts"] + else: + devlist = devname + + for _devname in devlist: + connected = self.reconnect(devname=_devname) + if not connected: + msg = "Error reconnecting to device" + self.dut_log(_devname, msg) + return False + elif devname in self.topo["duts"]: + hndl = self._get_handle(devname) + if hndl: + hndl.disconnect() + self._set_handle(devname, None) + connected = self.connect_to_device_current(devname, retry=3) + if not connected: + msg = "Error, reconnecting to device" + self.dut_log(devname, msg) + return False + return True + + ######################### SPYTEST additions ######################### + def _exit_docker(self, devname, prompt=None): + access = self._get_dev_access(devname) + if access["filemode"]: + return True + + dbg = self.debug_find_prompt + + if self.is_use_last_prompt(): + if not prompt: + prompt = access["last-prompt"] + elif prompt != access["last-prompt"]: + prompt = None + + if not prompt: + prompt = self._find_prompt(access) + prompt2 = prompt.replace("\\", "") + self.dut_log(devname, "prompt = '{}'".format(prompt), cond=dbg) + self.dut_log(devname, "prompt2 = '{}'".format(prompt2), cond=dbg) + hndl = self._get_handle(devname) + + msg = "trying to change from prompt({})".format(prompt2) + self.dut_log(devname, msg, lvl=logging.DEBUG) + + new_prompt = None + if re.compile(lldp_prompt).match(prompt2): + try: + hndl.send_command_timing("exit") + new_prompt = self._find_prompt(access) + except: + pass + else: + try: + hndl.send_command_timing("\x03") + hndl.send_command_timing("end") + hndl.send_command_timing("\x03") + hndl.send_command_timing("exit") + new_prompt = self._find_prompt(access) + except: + pass + + msg = "prompt after recovery({})".format(new_prompt.replace("\\", "")) + self.dut_log(devname, msg, lvl=logging.DEBUG) + self._set_last_prompt(access, new_prompt) + return new_prompt + + def _net_login(self, devname, hndl): + self._set_handle(devname, hndl) + #self.debug_find_prompt = True + self._enter_linux(devname) + + def _enter_linux(self, devname, prompt=None): + for i in range(10): + (rv, known_prompt) = self._enter_linux_once(devname, prompt) + if rv: + return known_prompt + prompt = known_prompt + return None + + def _enter_linux_once(self, devname, prompt=None): + access = self._get_dev_access(devname) + if access["filemode"]: + return (True, None) + + dbg = self.debug_find_prompt + + if self.is_use_last_prompt(): + if not prompt: + prompt = access["last-prompt"] + elif prompt != access["last-prompt"]: + prompt = None + + if not prompt: + prompt = self._find_prompt(access) + prompt2 = prompt.replace("\\", "") + msg = "prompt = '{}' prompt2 = '{}'".format(prompt, prompt2) + self.dut_log(devname, msg, cond=dbg) + + if re.compile(lldp_prompt).match(prompt2): + try: + cli_prompt = self._get_param(devname, "normal-user-cli-prompt") + output = self._send_command(access, "exit", cli_prompt) + self._set_last_prompt(access, cli_prompt) + except: + output = "" + self._set_last_prompt(access, None) + return (True, None) + + if sonic_mgmt_hostname in prompt2: + try: + cli_prompt = self._get_param(devname, "normal-user-cli-prompt") + prompt = self._exit_docker(devname, prompt) + self._set_last_prompt(access, cli_prompt) + except: + output = "" + self._set_last_prompt(access, None) + return (True, None) + + (rv, known_prompt) = (True, None) + prompt_terminator = r"(\S+\s+login:\s*$|[Pp]assword:\s*$|\(current\) UNIX password:\s*|[#|\$]\s*$)" + #prompt_terminators = [regex_login, regex_password, "\(current\) UNIX password:\s*", "[#|\$]\s*$"] + #prompt_terminator = "|".join(prompt_terminators) + if re.compile(regex_login).match(prompt2): + self.dut_log(devname, "enter username", cond=dbg) + output = self._send_command(access, access["username"], prompt_terminator, strip_prompt=False) + if prompt2 in output: + return (False, known_prompt) + self.dut_log(devname, "enter password", cond=dbg) + output = self._send_command(access, access["password"], prompt_terminator, + ufcli=False, skip_error_check=True, strip_prompt=False) + self.dut_log(devname, "login output:='{}'".format(output), cond=dbg) + if "bash: {}: command not found".format(access["password"]) in output: + self.dut_log(devname, "Logged into system without password", lvl=logging.WARNING) + self._set_last_prompt(access, None) + self.init_normal_prompt(devname) + elif prompt2 not in output: + (rv, output) = self._change_default_pwd(devname, access["password"], access["altpassword"], output) + self._set_last_prompt(access, None) + if prompt2 not in output and not re.search(regex_password_anywhere, output): + self.init_normal_prompt(devname) + self.dut_log(devname, "login output='{}'".format(output), cond=dbg) + elif re.compile(regex_password).match(prompt2): + self.dut_log(devname, "enter password", cond=dbg) + output = self._send_command(access, access["password"], + prompt_terminator, ufcli=False, strip_prompt=False) + self.dut_log(devname, "password output:='{}'".format(output), cond=dbg) + (rv, output) = self._change_default_pwd(devname, access["password"], access["altpassword"], output) + self._set_last_prompt(access, None) + if prompt2 not in output and not re.search(regex_password_anywhere, output): + self.init_normal_prompt(devname) + self.dut_log(devname, "password output='{}'".format(output), cond=dbg) + else: + self.dut_log(devname, "neither username nor password", cond=dbg) + output = "" + known_prompt = prompt + + if prompt2 in output: + output = self._send_command(access, access["username"], prompt_terminator, strip_prompt=False) + if prompt2 in output: + return (False, known_prompt) + output = self._send_command(access, access["altpassword"], + prompt_terminator, ufcli=False, strip_prompt=False) + self.dut_log(devname, "login2 output:='{}'".format(output), cond=dbg) + (rv, output) = self._change_default_pwd(devname, access["altpassword"], access["password"], output) + self._set_last_prompt(access, None) + if prompt2 not in output and not re.search(regex_password_anywhere, output): + self.init_normal_prompt(devname) + self.dut_log(devname, "login2 output='{}'".format(output), cond=dbg) + known_prompt = None + return (False, known_prompt) + else: + msg = "prompt2 is not seen in output '{}'" + self.dut_log(devname, msg.format(output), cond=dbg) + + return (rv, known_prompt) + + def _change_default_pwd(self, devname, pwd, altpwd, output): + access = self._get_dev_access(devname) + device = access["devname"] + line = currentframe().f_back.f_lineno + + try: + hndl = self._get_handle(device) + hndl.password = pwd + hndl.altpassword = altpwd + output = hndl.extended_login(output) + return (True, output) + except: + msg1 = utils.stack_trace(traceback.format_exc()) + msg2 = "Failed to change default password" + msg2 = "Unexpected messages on console - trying to recover" + self._trace_received(device, "change_default_pwd", hndl, msg1, msg2, line) + return (False, "") + + def _exit_vtysh(self, devname, onconnect=0, prompt=None): + access = self._get_dev_access(devname) + if access["filemode"]: + return + + hostname = "sonic" + if "hostname" in access and access["hostname"]: + hostname = access["hostname"] + + prompt_terminator = r"([#|\$]\s*$)" + if onconnect: + self._send_command(access, "end\r\nexit", prompt_terminator) + self._set_last_prompt(access, None) + return + + if self.is_use_last_prompt(): + if not prompt: + prompt = access["last-prompt"] + elif prompt != access["last-prompt"]: + prompt = None + + if not prompt: + prompt = self._find_prompt(access) + prompt2 = prompt.replace("\\", "") + if re.compile(regex_password).match(prompt2): + self._send_command(access, access["password"], prompt_terminator, ufcli=False) + if prompt.startswith(hostname): + self._send_command(access, "end\r\nexit", prompt_terminator) + self._set_last_prompt(access, None) + + def _enter_linux_exit_vtysh(self, devname, prompt=None): + prompt = self._enter_linux(devname, prompt) + return self._exit_vtysh(devname, onconnect=0, prompt=prompt) + + def _set_last_prompt(self, access, prompt, mode=None): + access["last-prompt"] = prompt + if mode and "vtysh" in mode: + access["last-prompt"] = None #TEMP# + + def _exec_mode_change(self, devname, l_cmd, to_prompt, from_prompt): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + expect = "|".join([from_prompt, to_prompt]) + self._send_command(access, l_cmd, expect, True, ufcli=False) + prompt = self._find_prompt(access) + if prompt == from_prompt: + raise Exception("Failed to change mode") + + def _exec(self, devname, cmd, expect=None, mode=None, + skip_error_check=False, delay_factor=0, + expect_reboot=False, trace_dut_log=3, ufcli=True): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + if access["filemode"]: + pid = profile.start(cmd, access["dut_name"]) + profile.stop(pid) + self.dut_log(devname, cmd) + if self.dry_run_cmd_delay > 0: + time.sleep(self.dry_run_cmd_delay) + return "" + + if not mode: + return self._send_command(access, cmd, expect, skip_error_check, + trace_dut_log=trace_dut_log, + delay_factor=delay_factor, ufcli=ufcli) + + # get current prompt or reuse the prompt + last_prompt = access["last-prompt"] + if self.is_use_last_prompt(): + prompt = last_prompt + else: + prompt = None + if prompt is None: + prompt = self._find_prompt(access) + prompt2 = prompt.replace("\\", "") + prompt = self._enter_linux(devname, prompt) + if prompt is None: + prompt = self._find_prompt(access) + prompt2 = prompt.replace("\\", "") + + # arrive at vtysh prompt based on hostname + hostname = "sonic" + if "hostname" in access and access["hostname"]: + hostname = access["hostname"] + vtysh_prompt = "{}#".format(hostname) + vtysh_config_prompt = "{}(config)#".format(hostname) + vtysh_maybe_config_prompt = r"{}#|{}\(config.*\)#".format(hostname, hostname) + + # lldp shell commands + if mode == "lldp-user": + if prompt2.startswith(sonic_mgmt_hostname): + msg = "trying to change from sonic-mgmt({}) while executing {}" + msg = msg.format(prompt, cmd) + self.dut_log(devname, msg) + try: + prompt = self._exit_docker(devname, prompt) + prompt2 = prompt.replace("\\", "") + except: + pass + self._set_last_prompt(access, prompt) + elif prompt.startswith(hostname): + msg = "trying to change from vtysh({}) while executing {}" + msg = msg.format(prompt, cmd) + self.dut_log(devname, msg) + try: + cli_prompt = self._get_param(devname, "normal-user-cli-prompt") + self._send_command(access, "end\r\nexit", cli_prompt) + self._set_last_prompt(access, cli_prompt) + prompt = self._find_prompt(access) + prompt2 = prompt.replace("\\", "") + except: + self._set_last_prompt(access, None) + + if prompt == self._get_param(devname, "normal-user-cli-prompt"): + self.dut_log(devname, "trying to enter into lldpcli({}) while executing {}".format(prompt, cmd)) + self._send_command(access, "docker exec -it lldp lldpcli", lldp_prompt) + self._set_last_prompt(access, lldp_prompt, "lldp") + prompt2 = prompt = lldp_prompt + + if prompt2 == lldp_prompt and cmd == "exit": + try: + cli_prompt = self._get_param(devname, "normal-user-cli-prompt") + output = self._send_command(access, "exit", cli_prompt) + self._set_last_prompt(access, cli_prompt, "lldp") + except: + output = "" + self._set_last_prompt(access, None) + return output + + if prompt == lldp_prompt or prompt2 == lldp_prompt: + output = self._send_command(access, cmd, lldp_prompt, + skip_error_check, ufcli=ufcli) + self._set_last_prompt(access, lldp_prompt, "lldp") + return output + + # import pdb; pdb.set_trace() + print("unhandled - 4", cmd, hostname, prompt2) + return "" + + # sonic shell commands + if mode == "normal-user": + if prompt == lldp_prompt or prompt2 == lldp_prompt: + msg = "trying to change from lldp({}) while executing {}" + msg = msg.format(prompt, cmd) + self.dut_log(devname, msg) + try: + cli_prompt = self._get_param(devname, "normal-user-cli-prompt") + self._send_command(access, "exit", cli_prompt) + self._set_last_prompt(access, cli_prompt) + except: + self._set_last_prompt(access, None) + elif prompt2.startswith(sonic_mgmt_hostname): + msg = "trying to change from sonic-mgmt({}) while executing {}" + msg = msg.format(prompt, cmd) + self.dut_log(devname, msg) + try: + prompt = self._exit_docker(devname, prompt) + prompt2 = prompt.replace("\\", "") + except: + pass + self._set_last_prompt(access, prompt) + elif prompt.startswith(hostname): + msg = "trying to change from vtysh({}) while executing {}" + msg = msg.format(prompt, cmd) + self.dut_log(devname, msg) + try: + cli_prompt = self._get_param(devname, "normal-user-cli-prompt") + self._send_command(access, "end\r\nexit", cli_prompt) + self._set_last_prompt(access, cli_prompt) + except: + self._set_last_prompt(access, None) + if not expect_reboot: + return self._send_command(access, cmd, expect, skip_error_check, + trace_dut_log=trace_dut_log, + delay_factor=delay_factor, ufcli=ufcli) + # special case where the show may result into reboot + expect = "|".join([self._get_param(devname, "prompt"), regex_login]) + delay_factor = 6 if delay_factor < 6 else delay_factor + + try: + self._tryssh_switch(devname) + output = self._send_command(access, cmd, expect, True, + delay_factor=delay_factor, ufcli=ufcli) + prompt = self._find_prompt(access) + if prompt != self._get_param(devname, "prompt"): + self._enter_linux(devname, prompt) + self.do_post_reboot(devname) + self._tryssh_switch(devname, True) + else: + self._tryssh_switch(devname, False) + except Exception as exp: + self._tryssh_switch(devname, False) + raise exp + return output + + if prompt2.startswith(sonic_mgmt_hostname): + msg = "trying to change from sonic-mgmt({}) while executing {}" + msg = msg.format(prompt, cmd) + self.dut_log(devname, msg) + try: + prompt = self._exit_docker(devname, prompt) + prompt2 = prompt.replace("\\", "") + except: + pass + self._set_last_prompt(access, prompt) + + # we need to go to vtysh for below cases + if prompt == self._get_param(devname, "normal-user-cli-prompt"): + self.dut_log(devname, "trying to enter into vtysh({}) while executing {}".format(prompt, cmd)) + self._exec_mode_change(devname, "sudo vtysh\r\nterminal length 0", vtysh_prompt, prompt) + self._set_last_prompt(access, vtysh_prompt) + prompt2 = prompt = vtysh_prompt + + # vty shell commands + if mode == "vtysh-user": + if cmd == "end": + output = self._exec(devname, cmd, vtysh_prompt) + self._set_last_prompt(access, vtysh_prompt) + return output + if prompt2 == vtysh_prompt and cmd == "exit": + try: + cli_prompt = self._get_param(devname, "normal-user-cli-prompt") + output = self._send_command(access, "exit", cli_prompt) + self._set_last_prompt(access, cli_prompt) + except: + output = "" + self._set_last_prompt(access, None) + return output + + if prompt == vtysh_prompt or prompt2 == vtysh_prompt: + output = self._send_command(access, cmd, vtysh_prompt, + skip_error_check, ufcli=ufcli) + self._set_last_prompt(access, vtysh_prompt, "vtysh-user") + return output + if prompt.startswith(r"{}\(".format(hostname)): + output = self._send_command(access, "do " + cmd, + r"{}\(.*\)#".format(hostname), + skip_error_check, ufcli=ufcli) + print("see if this can be removed-1", prompt2, cmd, prompt, re.escape(prompt2)) + self._set_last_prompt(access, None) + return output + # import pdb; pdb.set_trace() + print("unhandled - 1", cmd, hostname, prompt2) + return "" + + # we need to go to vtysh config for below cases + if prompt == vtysh_prompt or prompt2 == vtysh_prompt: + self._exec(devname, "configure terminal", r"{}\(config\)#".format(hostname)) + prompt2 = prompt = vtysh_config_prompt + self._set_last_prompt(access, prompt) + + # vty shell config commands + if mode == "vtysh-config": + if cmd == "end": + output = self._exec(devname, cmd, vtysh_prompt) + self._set_last_prompt(access, vtysh_prompt) + return output + if cmd == "exit": + output = self._exec(devname, cmd, vtysh_maybe_config_prompt) + self._set_last_prompt(access, None) + return output + if re.compile(r"{}\(.*\)#".format(hostname)).match(prompt2): + output = self._send_command(access, cmd, r"{}\(.*\)#".format(hostname), + skip_error_check, ufcli=ufcli) + self._set_last_prompt(access, re.escape(prompt2), "vtysh-config") + return output + + # import pdb; pdb.set_trace() + print("unhandled - 2", cmd, hostname, prompt2) + return "" + + # what should we do here + # import pdb; pdb.set_trace() + print("unhandled - 3", mode, cmd, prompt2) + return "" + + def change_prompt(self, devname, tomode=None, **kwargs): + return self._change_prompt(devname, tomode, None, **kwargs) + + def _change_prompt(self, devname, tomode=None, startmode=None, **kwargs): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + prompts = access["prompts"] + + dbg = self.debug_find_prompt + + if access["filemode"]: + self.dut_log(devname, tomode) + return None + + # Identify the current prompt + if startmode: + prompt = prompts.get_prompt_for_mode(startmode) + else: + prompt = None + + # Identify the current mode + if not prompt or prompt == "unknown-mode": + for i in range(3): + prompt = self._find_prompt(access) + startmode = prompts.get_mode_for_prompt(prompt) + if startmode != "unknown-prompt": + break + + if startmode == "unknown-prompt": + msg = "Current prompt pattern not found in patterns dict." + self.dut_log(devname, msg, lvl=logging.ERROR) + return "unknown-prompt" + + # Return current mode when no prompt is given for change. + if not tomode: + msg = "Returning current mode {} as provided tomode is None.".format(startmode) + self.dut_log(devname, msg, lvl=logging.DEBUG, cond=dbg) + return startmode + + # Return invalid if given prompt is not present. + if tomode not in prompts.patterns: + msg = "Prompt pattern not found." + self.dut_log(devname, msg, lvl=logging.ERROR) + return "unknown-mode" + + # Check whether the arguments given for prompt change are valid or not? + prompts.check_args_for_req_mode(tomode, **kwargs) + + # Check whether do we need to move previous level to come back to same prompt with different values. + if startmode == "login_prompt": + start_prompt = prompts.get_prompt_for_mode(startmode) + msg = "DUT enterted into '{}({})'. Recovering to normal mode.".format(startmode, start_prompt) + self.dut_log(devname, msg, lvl=logging.ERROR) + self._enter_linux(devname) + prompt = self._find_prompt(access) + startmode = prompts.get_mode_for_prompt(prompt) + if startmode == "unknown-prompt": + return "unknown-prompt" + self.set_login_timeout(devname) + + # Check whether do we need to move previous level to come back to same prompt with different values. + if startmode == tomode: + change_required = prompts.check_move_for_parent_of_frommode(prompt, startmode, **kwargs) + if change_required: + [cmd, expected_prompt] = prompts.get_backward_command_and_prompt(startmode) + self._send_command(access, cmd, expected_prompt) + startmode = prompts.modes[startmode][0] + else: + msg = "Returning as current mode is equal to required mode." + self.dut_log(devname, msg, lvl=logging.DEBUG, cond=dbg) + return tomode + else: + # Check whether do we need to go back to parent for both the modes. + change_required = prompts.check_move_for_parent_of_tomode(prompt, tomode, **kwargs) + if change_required: + if startmode != prompts.modes[tomode][0]: + required_mode = prompts.modes[tomode][0] + else: + required_mode = prompts.modes[startmode][0] + while startmode != required_mode and prompts.modes[startmode][0] != "": + [cmd, expected_prompt] = prompts.get_backward_command_and_prompt(startmode) + self._send_command(access, cmd, expected_prompt) + startmode = prompts.modes[startmode][0] + + # Identify the list of backward and forward modes we need to move. + modeslist_1 = [] + srcMode = startmode + while srcMode != "": + modeslist_1.append(srcMode) + if srcMode in prompts.modes: + srcMode = prompts.modes[srcMode][0] + continue + srcMode = "" + + modeslist_2 = [] + dstMode = tomode + while dstMode != "": + modeslist_2.insert(0, dstMode) + if dstMode in prompts.modes: + dstMode = prompts.modes[dstMode][0] + continue + dstMode = "" + + backward_modes = [] + forward_modes = copy.copy(modeslist_2) + for mode in modeslist_1: + if mode in forward_modes: + forward_modes.remove(mode) + continue + backward_modes.append(mode) + + #self.dut_log(devname, "Modes_1: {}".format(modeslist_1)) + #self.dut_log(devname, "Modes_2: {}".format(modeslist_2)) + #self.dut_log(devname, "backward_modes: {}".format(backward_modes)) + #self.dut_log(devname, "forward_modes: {}".format(forward_modes)) + + # Move back for each backward mode. + for mode in backward_modes: + [cmd, expected_prompt] = prompts.get_backward_command_and_prompt(mode) + if cmd.strip() == "" or expected_prompt.strip() == "": + continue + msg = "Backward command to execute: {} ; Expected Prompt: {}".format(cmd, expected_prompt) + #self.dut_log(devname, msg) + self._send_command(access, cmd, expected_prompt) + + # Move ahead for each forward mode. + # Get the command by substituting with required values from the given arguments. + for mode in forward_modes: + [cmd, expected_prompt] = prompts.get_forward_command_and_prompt_with_values(mode, **kwargs) + if cmd.strip() == "" or expected_prompt.strip() == "": + continue + msg = "Forward command to execute: {} ; Expected Prompt: {}".format(cmd, expected_prompt) + #self.dut_log(devname, msg) + self._send_command(access, cmd, expected_prompt) + + # Identify the current prompt, check and return appropriately. + for i in range(3): + prompt = self._find_prompt(access) + endmode = prompts.get_mode_for_prompt(prompt) + if endmode != "unknown-prompt": + break + + if endmode == tomode: + msg = "Successfully changed the prompt from {} to {}.".format(startmode, endmode) + self.dut_log(devname, msg, lvl=logging.DEBUG) + return tomode + return "unknown-mode" + + def cli_config(self, devname, cmd, mode=None, skip_error_check=False, delay_factor=0, **kwargs): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + prompts = access["prompts"] + + if access["filemode"]: + self.dut_log(devname, cmd) + return "" + + frommode = self.change_prompt(devname, mode, **kwargs) + if frommode not in ["unknown-mode", "unknown-prompt"]: + if frommode in prompts.sudo_include_prompts: + if not cmd.startswith("sudo "): + cmd = "sudo " + cmd + expected_prompt = prompts.get_prompt_for_mode(frommode) + output = self._send_command(access, cmd, expected_prompt, skip_error_check, delay_factor=delay_factor) + return output + msg = "Unable to change the prompt mode to {}.".format(mode) + self.dut_log(devname, msg, lvl=logging.ERROR) + raise ValueError(msg) + + def cli_show(self, devname, cmd, mode=None, skip_tmpl=False, skip_error_check=False, delay_factor=0, **kwargs): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + prompts = access["prompts"] + + if access["filemode"]: + self.dut_log(devname, cmd) + return "" + + frommode = self.change_prompt(devname, mode, **kwargs) + if frommode not in ["unknown-mode", "unknown-prompt"]: + actual_cmd = cmd + if not re.search(r"\| no-more$", cmd.strip()) and frommode.startswith("mgmt"): + cmd = cmd + " | no-more" + if frommode not in prompts.do_exclude_prompts: + if not cmd.startswith("do "): + cmd = "do " + cmd + expected_prompt = prompts.get_prompt_for_mode(frommode) + output = self._send_command(access, cmd, expected_prompt, skip_error_check, delay_factor=delay_factor) + if skip_tmpl: + return output + return self._tmpl_apply(devname, actual_cmd, output) + msg = "Unable to change the prompt mode to {}.".format(mode) + self.dut_log(devname, msg, lvl=logging.ERROR) + raise ValueError(msg) + + def _check_devname(self, devname): + if devname != "": + # todo verify + return devname + for d in self.topo["duts"]: + return d + return None + + def _tmpl_apply(self, devname, cmd, output): + try: + parsed = self.tmpl[devname].apply(output, cmd) + self.logger.debug(parsed) + return parsed + except Exception as e: + self.logger.exception(e) + return output + + def _textfsm_apply(self, devname, tmpl_file, output): + return self.tmpl[devname].apply_textfsm(tmpl_file, output) + + def _fill_sample_data(self, devname, cmd, skip_error_check, output): + if self.cfg.filemode and not output and self.use_sample_data: + output = self.tmpl[devname].read_sample(cmd) + self.dut_log(devname, output) + access = self._get_dev_access(devname) + output = self._check_error(access, cmd, output, skip_error_check) + return output + + def clear_config(self, devname, method="reload"): + """ + This API method executes the clear config on the device + + :param devname: name of the device under test (DUT) + :type devname: + :return: + """ + largs = [method] + if devname: + return self._apply_remote(devname, "apply-base-config", largs) + else: + for dev_name in self.topo["duts"]: + self._apply_remote(dev_name, "apply-base-config", largs) + return True + + def config_db_reload(self, devname, save=False): + """ + todo: Update Documentation + :param dut: + :type dut: + :param save: + :type save: + :return: + :rtype: + """ + + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + + save_cmd = 'sudo config save -y' + reload_cmd = 'sudo config reload -y' + + if os.getenv("SPYTEST_HELPER_CONFIG_DB_RELOAD", "yes") != "no": + largs = ["yes" if save else "no"] + output = self._apply_remote(devname, "config-reload", largs) + return output + + if access["filemode"]: + if save: + self.dut_log(devname, save_cmd) + self.dut_log(devname, reload_cmd) + return True + + # ensure we are in sonic mode + self._enter_linux_exit_vtysh(devname) + + prompt = self._get_param(devname, "normal-user-cli-prompt") + if save: + self._send_command(access, save_cmd, prompt, False, 1) + + output = self._send_command(access, reload_cmd, prompt, True, 3) + + return output + + def apply_script(self, devname, cmdlist): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + if access["filemode"]: + for cmd in cmdlist: + self.dut_log(devname, cmd) + return + + vtysh_mode_flag = False + vtysh_config_mode_flag = False + + hostname = "sonic" + if "hostname" in access and access["hostname"]: + hostname = access["hostname"] + vtysh_prompt = "{}#".format(hostname) + + # ensure we are in sonic mode + self._enter_linux_exit_vtysh(devname) + + for cmd in cmdlist: + if not cmd.strip(): + #self.logger.warning("skipping empty line") + continue + + if cmd == "vtysh" or cmd == "sudo vtysh": + vtysh_mode_flag = True + continue + + if cmd == "configure terminal" and vtysh_mode_flag: + vtysh_config_mode_flag = True + continue + + if vtysh_config_mode_flag: + self.config_new(devname, cmd, type="vtysh", conf=True) + elif vtysh_mode_flag: + self.config_new(devname, cmd, type="vtysh", conf=False) + else: + self.config_new(devname, cmd) + + prompt = self._find_prompt(access) + prompt2 = prompt.replace("\\", "") + + if prompt == self._get_param(devname, "normal-user-cli-prompt"): + vtysh_mode_flag = False + vtysh_config_mode_flag = False + + if prompt == vtysh_prompt or prompt2 == vtysh_prompt: + vtysh_mode_flag = True + vtysh_config_mode_flag = False + + if re.compile(r"{}\(.*\)#".format(hostname)).match(prompt2): + vtysh_mode_flag = True + vtysh_config_mode_flag = True + + # ensure we are in sonic mode after we exit + self._exit_vtysh(devname) + + def apply_json(self, devname, data): + """ + todo: Update Documentation + :param devname: + :type devname: + :param data: + :type data: + :return: + :rtype: + """ + devname = self._check_devname(devname) + try: + obj = json.loads(data) + indented = json.dumps(obj, indent=4) + except: + self.logger.warning("invalid json - trying to fix") + # remove trailing object comma + regex = re.compile( + r'(,)\s*}(?=([^"\\]*(\\.|"([^"\\]*\\.)*[^"\\]*"))*[^"]*$)') + data = regex.sub("}", data) + # remove trailing array comma + regex = re.compile( + r'(,)\s*\](?=([^"\\]*(\\.|"([^"\\]*\\.)*[^"\\]*"))*[^"]*$)') + data = regex.sub("]", data) + try: + obj = json.loads(data) + indented = json.dumps(obj, indent=4) + except: + raise ValueError("invalid json data") + + # write json content into file + for retry in range(3): + src_file = tempfile.mktemp() + src_fp = open(src_file, "w") + src_fp.write(indented) + src_fp.close() + if os.path.exists(src_file) and os.path.getsize(src_file) != 0: + msg = "Created a tmp file {}. Size of the file {} ..".format(src_file, os.path.getsize(src_file)) + self.dut_log(devname, msg, lvl=logging.WARNING) + break + else: + msg = "Failed to create a tmp file {}.. Retrying again..".format(src_file) + self.dut_log(devname, msg, lvl=logging.WARNING) + + applied = False + for retry in range(3): + # transfer the file + access = self._get_dev_access(devname) + dst_file = self._upload_file(access, src_file) + + # issue config load + if access["filemode"]: + applied = True + break + + # ensure we are in sonic mode + self._enter_linux_exit_vtysh(devname) + + check_file_cmd = "ls -lrt {}".format(dst_file) + output = self.config_new(devname, check_file_cmd, skip_error_check=True) + + # execute the command. + config_cmd = "config load -y {}".format(dst_file) + output = self.config_new(devname, config_cmd, skip_error_check=True) + if 'Path "{}" does not exist.'.format(dst_file) not in output: + applied = True + break + msg = "Failed to find the transfered destination file retry again in 3 sec" + self.dut_log(devname, msg, lvl=logging.WARNING) + time.sleep(3) + + # remove temp file + os.remove(src_file) + + # try apply_json2 as last resort + if not applied: + msg = "Failed to find the transfered destination file even after retries - try using echo" + self.dut_log(devname, msg, lvl=logging.WARNING) + self.apply_json2(devname, data) + + def apply_json2(self, devname, data): + access = self._get_dev_access(devname) + if not access["filemode"]: + dst_file = "/tmp/apply_json2.json" + self._save_json_to_remote_file(devname, data, dst_file) + config_cmd = "config load -y {}".format(dst_file) + self.config_new(devname, config_cmd) + + def recover_from_onie(self, devname, install): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + self._send_command(access, "onie-discovery-stop") + self._send_command(access, "onie-stop") + + if install and self.cfg.skip_load_image: + msg = "Skip install from ONIE" + self.dut_log(devname, msg, lvl=logging.WARNING) + return False + + if not install: + try: + msg = "Trying to recover from ONIE with reboot" + self.dut_log(devname, msg, lvl=logging.WARNING) + expect = "|".join([regex_login, regex_onie, regex_onie_sleep]) + self._send_command(access, "reboot", expect, True, 3) + if self.wait_onie_or_login(devname) == 2: + # reboot took the device into login prompt + return True + msg = "Failed to take device into login - try loading image" + self.dut_log(devname, msg, lvl=logging.ERROR) + # pass through installation + except: + msg = "Failed to recover from ONIE with reboot" + self.dut_log(devname, msg, lvl=logging.ERROR) + os._exit(15) + return False + + # installing + if self.cfg.build_url: + onie_image = self.cfg.build_url + else: + onie_image = access["onie_image"] + if not onie_image: + msg = "No image is specified to load from ONIE" + self.dut_log(devname, msg, lvl=logging.ERROR) + return False + if not self.onie_nos_install(devname, onie_image): + if os.getenv("SPYTEST_RECOVERY_MECHANISMS", "0") != "0": + expect = "|".join([regex_login, regex_onie, regex_onie_sleep]) + msg = "Trying to recover from ONIE with reboot as it failed to download the image" + self.dut_log(devname, msg, lvl=logging.WARNING) + self._send_command(access, "reboot", expect, True, 3) + if self.wait_onie_or_login(devname) == 1: + if not self.onie_nos_install(devname, onie_image): + return False + else: + return False + else: + return False + if not self.wa.session_init_completed: + self.image_install_status[devname] = True + return True + + def onie_nos_install(self, devname, url): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + + cmd = "onie-nos-install {}".format(url) + self.dut_log(devname, "trying {}".format(cmd)) + self.trace_callback_set(devname, True) + expect = "|".join([regex_login, regex_onie]) + self._send_command(access, cmd, expect, True, 18) + self.trace_callback_set(devname, False) + + ptype = self.wait_onie_or_login(devname) + if ptype == 1: + msg = "Device Onie Install Failed" + self.dut_log(devname, msg, lvl=logging.WARNING) + return False + elif ptype == 2: + msg = "Device Onie Install Completed" + self.dut_log(devname, msg) + self.init_normal_prompt(devname) + return True + + msg = "Failed to get login prompt after ONIE upgrade" + self.dut_log(devname, msg, lvl=logging.ERROR) + return False + + def wait_onie_or_login(self, devname): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + for attempt in range(10): + prompt = self._find_prompt(access) + prompt2 = prompt.replace("\\", "") + if re.compile(regex_onie).match(prompt2): + if attempt == 0: + # try again as ONIE some times just throws the prompt + time.sleep(5) + continue + return 1 + if re.compile(regex_login).match(prompt2): + self._enter_linux(devname, prompt) + return 2 + msg = "Unexpected Prompt {}".format(prompt2) + self.dut_log(devname, msg, lvl=logging.WARNING) + self.wait(1) + return 0 + + def upgrade_onie_image1(self, devname, url, max_ready_wait=0): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + + if not self.wa.session_init_completed: + if devname in self.image_install_status and self.image_install_status[devname]: + self.dut_log(devname, "Image already upgraded during the time of DUT connect using ONIE process.") + return True + + upgrade_image_cmd = ";".join(""" + sudo apt-get -f install -y grub-common + sudo mkdir -p /mnt/onie-boot/ + sudo mount /dev/sda2 /mnt/onie-boot/ + sudo /mnt/onie-boot/onie/tools/bin/onie-boot-mode -o rescue + sudo grub-editenv /mnt/onie-boot/grub/grubenv set diag_mode=none + sudo grub-editenv /mnt/onie-boot/grub/grubenv set onie_mode=rescue + sudo grub-editenv /host/grub/grubenv set next_entry=ONIE + sudo grub-reboot --boot-directory=/host/ ONIE + sudo umount /mnt/onie-boot/ + """.strip().splitlines()) + + self.dut_log(devname, "Upgrading image from onie '{}'.".format(url)) + + if access["filemode"]: + return + + # ensure we are in sonic mode + self._enter_linux_exit_vtysh(devname) + + # Issue sonic installer command. + skip_error_check = False if self.wa.session_init_completed else True + self.trace_callback_set(devname, True) + cli_prompt = self._get_param(devname, "normal-user-cli-prompt") + self._send_command(access, upgrade_image_cmd, cli_prompt, skip_error_check, 18) + self.trace_callback_set(devname, False) + + # we need to download the helper files again + self.skip_trans_helper[devname] = dict() + + # Issue reboot command and look for ONIE rescue mode. + if not self.reboot(devname, onie=True): + msg = "Reboot failed as unable to get the onie rescue mode." + self.dut_log(devname, msg, False, logging.ERROR) + raise ValueError(msg) + + self._send_command(access, "\r\n", regex_onie) + if not self.onie_nos_install(devname, url): + msg = "Image download failed using onie-nos-install." + self.dut_log(devname, msg, False, logging.ERROR) + raise ValueError(msg) + + self.dut_log(devname, "reading version after upgrade") + self.show_new(devname, "show version", skip_tmpl=True, skip_error_check=True) + self.do_post_reboot(devname, max_ready_wait=max_ready_wait, phase=1) + return True + + def upgrade_onie_image2(self, devname, url, max_ready_wait=0): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + + if not self.wa.session_init_completed: + if devname in self.image_install_status and self.image_install_status[devname]: + self.dut_log(devname, "Image already upgraded during the time of DUT connect using ONIE process.") + return True + + self.dut_log(devname, "Upgrading image from '{}'.".format(url)) + dut_image_location = "/host/onie-installer-x86_64" + + if access["filemode"]: + return + + # ensure we are in sonic mode + self._enter_linux_exit_vtysh(devname) + + # Download the image from url to /host/onie-installer-x86_64 location. + download_image_cmd = "sudo curl --retry 15 -o {} {}".format(dut_image_location, url) + + # Issue the download_image_cmd command. + for count in range(3): + skip_error_check = False if self.wa.session_init_completed else True + self.trace_callback_set(devname, True) + cli_prompt = self._get_param(devname, "normal-user-cli-prompt") + self.dut_log(devname, "Trying image download using curl command, iteration {}".format(count+1)) + output = self._send_command(access, download_image_cmd, cli_prompt, + skip_error_check, delay_factor=18, + trace_dut_log=1) + self.trace_callback_set(devname, False) + + if re.search(r"curl:\s+\(\d+\)", output): + errorline = [m for m in output.split("\n") if re.search(r"curl:\s+\(\d+\)", m)] + errorline = str("".join(errorline)) + msg = "Image download to host location failed using curl command. Error: '{}'" + msg = msg.format(errorline) + self.dut_log(devname, msg, False, logging.ERROR) + if count >=2: + raise ValueError(msg) + continue + + # Check for the downloaded file type. + filetype_cmd = "file {}".format(dut_image_location) + file_output = self._send_command(access, filetype_cmd, cli_prompt, + skip_error_check, delay_factor=1) + if not re.search(r"binary\s+data", file_output): + errorline = file_output.split("\n")[0] + msg = "Image downloaded to host location is not a proper image type. File type: '{}'" + msg = msg.format(errorline) + self.dut_log(devname, msg, False, logging.ERROR) + raise ValueError(msg) + + self.dut_log(devname, "Image downloaded to host location successfully.") + break + + # Grub commands for image download. + upgrade_image_cmd = ";".join(""" + sudo apt-get -f install -y grub-common + sudo mkdir -p /mnt/onie-boot/ + sudo mount /dev/sda2 /mnt/onie-boot/ + sudo /mnt/onie-boot/onie/tools/bin/onie-boot-mode -o install + sudo grub-editenv /mnt/onie-boot/grub/grubenv set diag_mode=none + sudo grub-editenv /mnt/onie-boot/grub/grubenv set onie_mode=install + sudo grub-editenv /host/grub/grubenv set next_entry=ONIE + sudo grub-reboot --boot-directory=/host/ ONIE + sudo umount /mnt/onie-boot/ + """.strip().splitlines()) + + # Issue the grub commands. + skip_error_check = False if self.wa.session_init_completed else True + self.trace_callback_set(devname, True) + cli_prompt = self._get_param(devname, "normal-user-cli-prompt") + output = self._send_command(access, upgrade_image_cmd, cli_prompt, + skip_error_check, 18) + self.trace_callback_set(devname, False) + + # we need to download the helper files again + self.skip_trans_helper[devname] = dict() + + # Issue reboot command. + reboot_flag = False + if not self._is_console_connection(devname): + reboot_flag = self.reboot(devname, onie=True) + else: + reboot_flag = self.reboot(devname) + if not reboot_flag: + msg = "Reboot failed after the image download using onie install." + self.dut_log(devname, msg, False, logging.ERROR) + raise ValueError(msg) + + self.dut_log(devname, "reading version after upgrade") + self.show_new(devname, "show version", skip_tmpl=True, skip_error_check=True) + self.do_post_reboot(devname, max_ready_wait=max_ready_wait, phase=1) + return True + + def upgrade_image(self, devname, url, skip_reboot=False, migartion=True, max_ready_wait=0): + """ + Upgrade the software in the given DUT from given URL + :param devname: + :type devname: + :param url: URL string used to upgrade + :type url: String + :param skip_reboot: Flag to avoid rebooting device after upgrade + :type url: boolean (default False) + :return: + :rtype: + """ + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + + if not self.wa.session_init_completed: + if devname in self.image_install_status and self.image_install_status[devname]: + self.dut_log(devname, "Image already upgraded during the time of DUT connect using ONIE process.") + return True + + if migartion: + upgrade_image_cmd = "sudo sonic_installer install {} -y".format(url) + else: + upgrade_image_cmd = "sudo sonic_installer install --skip_migration {} -y".format(url) + self.dut_log(devname, "Upgrading image from '{}'.".format(url)) + + if access["filemode"]: + return + + # ensure we are in sonic mode + self._enter_linux_exit_vtysh(devname) + + # Issue sonic installer command. + skip_error_check = False if self.wa.session_init_completed else True + self.trace_callback_set(devname, True) + cli_prompt = self._get_param(devname, "normal-user-cli-prompt") + output = self._send_command(access, upgrade_image_cmd, cli_prompt, + skip_error_check, 18) + self.trace_callback_set(devname, False) + + if re.search("Installed SONiC base image SONiC-OS successfully", output): + prompt = self._find_prompt(access) + if prompt == self._get_param(devname, "normal-user-cli-prompt"): + if skip_reboot: + msg = "Image upgraded successfully." + self.dut_log(devname, msg) + elif self.reboot(devname, max_ready_wait=max_ready_wait): + self._enter_linux(devname, prompt) + msg = "Image upgraded and rebooted successfully." + self.dut_log(devname, msg) + else: + msg = "Reboot failed after the image download." + self.dut_log(devname, msg, False, logging.ERROR) + raise ValueError(msg) + elif re.search("Not installing SONiC version", output) and \ + re.search("as current running SONiC has the same version", output): + msg = "No need to upgrade as the image is already of same version." + self.dut_log(devname, msg) + else: + msg = "Image not loaded on to the device using URL: {}".format(url) + self.dut_log(devname, msg, False, logging.ERROR) + raise ValueError(msg) + return True + + def recover(self, devname, msg): + self.dut_log(devname, msg) + if self.reboot(devname): + self._enter_linux(devname) + self.dut_log(devname, "{} - Successful".format(msg)) + else: + self.logger.error("{} - Failed".format(msg)) + raise ValueError(msg) + + def reboot(self, devname, method="normal", skip_port_wait=False, + onie=False, skip_exception=False, skip_fallback=False, + max_ready_wait=0): + + try: + self._tryssh_switch(devname) + rv = self._reboot(devname, method, skip_port_wait, onie, + skip_exception, skip_fallback, max_ready_wait) + self._tryssh_switch(devname, True) + except Exception as e: + msg = utils.stack_trace(traceback.format_exc()) + self.dut_log(devname, msg, lvl=logging.WARNING) + self._tryssh_switch(devname, False) + if not skip_exception: + raise e + rv = False + + return rv + + def _reboot(self, devname, method="normal", skip_port_wait=False, + onie=False, skip_exception=False, skip_fallback=False, + max_ready_wait=0): + + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + + flag_fast_warm_reboot = 0 + if method in ["normal", "reboot"]: + reboot_cmd = "sudo reboot" + elif method in ["fast", "fast-reboot"]: + reboot_cmd = "sudo fast-reboot" + flag_fast_warm_reboot = 1 + elif method in ["warm", "warm-reboot"]: + reboot_cmd = "sudo warm-reboot" + flag_fast_warm_reboot = 1 + else: + reboot_cmd = "sudo reboot" + + if access["filemode"]: + self.dut_log(devname, "Reboot command '{}'.".format(reboot_cmd)) + return True + + # ensure we are in sonic mode + self._enter_linux_exit_vtysh(devname) + + # just incase if we are in SSH mode + self._switch_connection(devname, 0) + # just incase if we are in SSH mode + + user_mode = self._get_param(devname, "normal-user-cli-prompt") + + # check if the reboot needs confirmation and handle accordingly + output = self._send_command(access, "fast-reboot -h", user_mode, + skip_error_check=True) + if "skip the user confirmation" in output: + reboot_cmd = "{} -y".format(reboot_cmd) + + # negative test for API + if method in ["fast-reboot-fail"]: + flag_fast_warm_reboot = 1 + reboot_cmd = "echo sudo fast-reboot" + + # Issue reboot command. + self.wa.instrument(devname, "pre-reboot") + self.trace_callback_set(devname, True) + if not self._is_console_connection(devname): + reboot_static_wait = 120 + if onie: + reboot_static_wait = 600 + ssh_patterns = r"(systemctl daemon-reload|requested COLD shutdown|[#|\$]\s*$)" + output = self._send_command(access, reboot_cmd, ssh_patterns, True, 1) + msg = "Waiting for '{}' secs after reboot on SSH.".format(reboot_static_wait) + self.dut_log(devname, msg, False) + self.wait(reboot_static_wait) + if not onie: + reboot_polling_time = 120 + time_left = reboot_polling_time + dut_mgmt_ip = str(access["connection_param"]["ip"]) + while time_left > 0: + retval = utils.ipcheck(dut_mgmt_ip) + msg = "Pinging IP : '{}' : {}".format(dut_mgmt_ip, retval) + self.dut_log(devname, msg, False) + if retval: + break + time_left = time_left - 2 + if time_left == 0: + msg = "Dut IP '{}' is not reachable even after pinging for '{}' secs after reboot on SSH" + msg = msg.format(dut_mgmt_ip, reboot_polling_time) + self.dut_log(devname, msg, False, logging.ERROR) + return False + self._disconnect_device(devname) + wait_after_ping = 30 + msg = "Waiting for '{}' secs before attempting connection via SSH.".format(wait_after_ping) + self.dut_log(devname, msg, False) + self.wait(wait_after_ping) + retry_count = 0 + while retry_count < 10: + retval = self.connect_to_device(devname) + msg = "Connection attempt : '{}', Status: '{}'".format(retry_count, retval) + self.dut_log(devname, msg, False) + if retval: + break + retry_count = retry_count + 1 + self.wait(10) + elif onie: + output = self._send_command(access, reboot_cmd, regex_onie_resque, True, 6) + return True + else: + expect = "|".join([user_mode, regex_login, regex_login_anywhere]) + output = self._send_command(access, reboot_cmd, expect, True, 6) + self.trace_callback_set(devname, False) + + reboot_status = False + result_set = ["DUTFail", "reboot_failed"] + try_count = 3 + while try_count > 0: + prompt = self._find_prompt(access) + prompt2 = prompt.replace("\\", "") + if re.compile(regex_login).match(prompt2): + msg = "Device Reboot ({}) Completed.".format(reboot_cmd) + self.dut_log(devname, msg) + self.wait(5) # wait for any kernel messages to show up + self._enter_linux(devname, prompt) + self.do_post_reboot(devname, max_ready_wait=max_ready_wait) + reboot_status = True + break + + if re.compile(regex_login_anywhere).match(prompt2): + msg = "Device Reboot ({}) May Be Completed - confirming".format(reboot_cmd) + self.dut_log(devname, msg) + continue + + if prompt == user_mode: + if not self._is_console_connection(devname): + msg = "Device Reboot ({}) Completed..".format(reboot_cmd) + self.dut_log(devname, msg) + self.do_post_reboot(devname, max_ready_wait=max_ready_wait) + reboot_status = True + break + msg = "Device Reboot ({}) Failed.".format(reboot_cmd) + self.dut_log(devname, msg, False, logging.ERROR) + if flag_fast_warm_reboot and not skip_fallback: + msg = "Performing normal Reboot as ({}) failed.".format(reboot_cmd) + self.dut_log(devname, msg) + self.reboot(devname, skip_exception=True) + result_set = ["DUTFail", "fast_or_warm_reboot_failed"] + break + if not skip_exception: + raise ValueError(msg) + + msg = "Prompt '{}' is neither login nor usermode." + msg = msg.format(prompt) + self.dut_log(devname, msg, False, logging.ERROR) + try_count = try_count - 1 + + if not reboot_status: + self._report_error(result_set, reboot_cmd) + elif self.cfg.reboot_wait: + msg = "Waiting for '{}' secs after reboot.".format(self.cfg.reboot_wait) + self.dut_log(devname, msg, False) + self.wait(self.cfg.reboot_wait) + + return reboot_status + + def wait_system_reboot(self, devname): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + self._send_command(access, "\n", regex_login, True, 6) + try_count = 3 + while try_count > 0: + prompt = self._find_prompt(access) + prompt2 = prompt.replace("\\", "") + if re.compile(regex_login).match(prompt2): + msg = "Device Reboot Completed." + self.dut_log(devname, msg) + self._enter_linux(devname, prompt) + self.do_post_reboot(devname) + return True + try_count = try_count - 1 + + return False + + def _transfer_base64(self, access, src_file, dst_file): + devname = access["devname"] + prompt = self._get_param(devname, "normal-user-cli-prompt") + script_cmd = "rm -f {}.tmp {}".format(dst_file, dst_file) + self._exec(devname, script_cmd, prompt) + redir = ">" + lines = utils.b64encode(src_file) + (count, split) = (len(lines), self.max_cmds_once) + for i in range(0, count, split): + script_cmds = [] + for j in range(i, i+split): + if j >= count: + break + script_cmds.append(lines[j]) + if script_cmds: + line = "".join(script_cmds) + script_cmd = "echo {} {} {}.tmp".format(line, redir, dst_file) + self._send_command(access, script_cmd, prompt, True) + redir = ">>" + script_cmd = "base64 -d {}.tmp > {}".format(dst_file, dst_file) + self._exec(devname, script_cmd, prompt) + + def _transfer_base64_small(self, access, src_file, dst_file): + script_cmds = [] + script_cmd = "rm -f {}.tmp {}".format(dst_file, dst_file) + script_cmds.append(script_cmd) + redir = ">" + lines = utils.b64encode(src_file) + for line in lines: + script_cmd = "echo {} {} {}.tmp".format(line, redir, dst_file) + script_cmds.append(script_cmd) + redir = ">>" + script_cmd = "base64 -d {}.tmp > {}".format(dst_file, dst_file) + script_cmds.append(script_cmd) + + script_cmd = ";".join(script_cmds) + devname = access["devname"] + cli_prompt = self._get_param(devname, "normal-user-cli-prompt") + self._exec(devname, script_cmd, cli_prompt) + + def _save_json_to_remote_file(self, devname, data, dst_file): + devname = self._check_devname(devname) + do_indent = False + try: + if do_indent: + obj = json.loads(data) + indented = json.dumps(obj, indent=4) + else: + indented = data + except: + self.logger.warning("invalid json - trying to fix") + # remove trailing object comma + regex = re.compile( + r'(,)\s*}(?=([^"\\]*(\\.|"([^"\\]*\\.)*[^"\\]*"))*[^"]*$)') + data = regex.sub("}", data) + # remove trailing array comma + regex = re.compile( + r'(,)\s*\](?=([^"\\]*(\\.|"([^"\\]*\\.)*[^"\\]*"))*[^"]*$)') + data = regex.sub("]", data) + try: + obj = json.loads(data) + indented = json.dumps(obj, indent=4) + except: + raise ValueError("invalid json data") + + access = self._get_dev_access(devname) + if not access["filemode"]: + # ensure we are in sonic mode + self._enter_linux_exit_vtysh(devname) + # echo data to remote file + self._echo_text_to_file(access, indented, dst_file) + + def _echo_text_to_file(self, access, content, dst_file, prefix=""): + str_list = content.split("\n") + if prefix: + str_list.insert(0, prefix) + return self._echo_list_to_file(access, str_list, dst_file) + + def _echo_list_to_file(self, access, str_list, dst_file, split=None): + l_split = self.max_cmds_once if not split else split + devname = access["devname"] + msg = "Creating: DST: {}".format(dst_file) + self.dut_log(devname, msg) + redir = ">" + for clist in utils.split_list(str_list, l_split): + content = "\n".join(clist) + script_cmd = "printf '{}\n' {} {}\n".format(content, redir, dst_file) + cli_prompt = self._get_param(devname, "normal-user-cli-prompt") + self._exec(devname, script_cmd, cli_prompt, ufcli=False, trace_dut_log=1) + redir = ">>" + + return dst_file + + def _upload_file(self, access, src_file, dst_file=None): + if not dst_file: + dst_file = "/tmp/{}".format(os.path.basename(src_file)) + msg = "Transfer: SRC: {} DST: {}".format(src_file, dst_file) + devname = access["devname"] + self.dut_log(devname, msg) + if access["filemode"]: + return dst_file + if self.force_console_transfer: + self._transfer_base64(access, src_file, dst_file) + return dst_file + try: + connection_param = access["connection_param"] + msg = "Doing SFTP transfer {}".format(connection_param["mgmt-ip"]) + self.dut_log(devname, msg) + self._fetch_mgmt_ip(devname) + DeviceFileUpload(self._get_handle(devname), src_file, + dst_file, connection_param) + except Exception as e: + print(e) + self.dut_log(devname, "SFTP Failed - Doing Console transfer") + self._transfer_base64(access, src_file, dst_file) + return dst_file + + def _upload_file2(self, devname, access, src_file, md5check=False): + remote_dir = "/etc/spytest" + + if devname not in self.skip_trans_helper: + self.skip_trans_helper[devname] = dict() + + if src_file not in self.skip_trans_helper[devname]: + prompt = self._get_param(devname, "normal-user-cli-prompt") + src_file2 = "%s/%s" % (os.path.basename(os.path.dirname(src_file)), + os.path.basename(src_file)) + remote_file = os.path.join(remote_dir, src_file2) + skip_transfer = False + if md5check: + script_cmd = "sudo md5sum {}".format(remote_file) + output = self._send_command(access, script_cmd, prompt, False) + try: + md5sum2 = output.split("\n")[0].strip() + if utils.md5(src_file) == md5sum2.split(" ")[0].strip(): + skip_transfer = True + except: + pass + if not skip_transfer: + dst_file = self._upload_file(access, src_file) + script_cmd = "sudo mkdir -p {} && sudo cp -f {} {}".format( + os.path.dirname(remote_file), dst_file, remote_file) + output = self._send_command(access, script_cmd, prompt, False, 6) + self.skip_trans_helper[devname][src_file] = remote_file + + return self.skip_trans_helper[devname][src_file] + + def _upload_file3(self, access, src_file, dst_file): + remote_dir = os.path.dirname(dst_file) + + tmp_file = self._upload_file(access, src_file) + if remote_dir: + script_cmd = "sudo mkdir -p {} && sudo cp -f {} {}".format( + remote_dir, tmp_file, dst_file) + devname = access["devname"] + cli_prompt = self._get_param(devname, "normal-user-cli-prompt") + self._send_command(access, script_cmd, cli_prompt, False, 6) + + def _download_file(self, access, src_file, dst_file): + devname = access["devname"] + msg = "Download: SRC: {} DST: {}".format(src_file, dst_file) + self.dut_log(devname, msg) + if not access["filemode"]: + connection_param = access["connection_param"] + try: + msg = "Doing SFTP download {}".format( + connection_param["mgmt-ip"]) + self.dut_log(devname, msg) + DeviceFileDownload(self._get_handle(devname), src_file, + dst_file, connection_param) + except Exception as e: + try: + self.dut_log(devname, "SFTP Failed - Doing transfer using filedata on console") + cmd = "file {}".format(src_file) + output = self.show_new(devname, cmd, skip_tmpl=True, skip_error_check=True) + if "ASCII" in output: + cmd = "cat {}".format(src_file) + output = self.show_new(devname, cmd, skip_tmpl=True, skip_error_check=True) + content =output[:output.rfind('\n')] + dst_fp = open(dst_file, "w") + dst_fp.write(content) + dst_fp.close() + else: + if "No such file or directory" in output: + self.dut_log(devname, "File {} not found".format(src_file)) + else : + self.dut_log(devname, "Only text based files can be transferred using console") + self.logger.info(e) + return "FAIL" + except Exception as e1: + self.logger.info(e1) + return "FAIL" + return "SUCCESS" + + def add_pending_download(self, devname, remote_file_path, local_file_path): + if devname not in self.pending_downloads: + self.pending_downloads[devname] = [] + self.pending_downloads[devname].append([remote_file_path, local_file_path]) + + def check_pending_downloads(self, devname): + # TODO: download the pending files + self.pending_downloads[devname] = [] + + def _add_port_wait(self, args_str, wait, poll, is_community): + args_str = args_str + " --port-init-wait {}".format(wait) + if poll: + args_str = args_str + " --poll-for-ports yes" + else: + args_str = args_str + " --poll-for-ports no" + if is_community: + args_str = args_str + " --community-build" + return args_str + + def _add_core_dump_flags(self, args_str, value_list): + core_flag = value_list.pop(0) + dump_flag = value_list.pop(0) + if core_flag != "none": + core_flag = "YES" + else: + core_flag = "NO" + if dump_flag != "none": + dump_flag = "YES" + else: + dump_flag = "NO" + if value_list: + clear_flag = value_list.pop(0) + else: + clear_flag = False + if clear_flag: + clear_flag = "YES" + else: + clear_flag = "NO" + args_str = ",".join([core_flag, dump_flag, clear_flag]) + return args_str + + def _port_breakout_options(self, devname): + breakout = self.tb.get_device_param(devname, "breakout", None) + + # indicate port breakout native type + retval = "" + if breakout and "native" in breakout: + native = breakout["native"] + if "{}".format(native) == "1": + retval = " --breakout-native" + + # handle case when breakout is not specified + if not retval and self.cfg.native_port_breakout: + retval = " --breakout-native" + + # custom port breakout + if breakout and \ + "name" in breakout and \ + "ports" in breakout and \ + "options" in breakout: + name = breakout["name"] + ports = breakout["ports"] + options = breakout["options"] + cust_platform_dict = SpyTestDict() + cust_platform_dict[name] = SpyTestDict() + cust_platform_dict[name]["breakout"] = SpyTestDict() + cust_platform_dict[name]["breakout"][",".join(map(str, ports))] = options + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + cust_platform_json = json.dumps(cust_platform_dict, indent=4) + dst_file = "/tmp/custom_breakout.json" + self._echo_text_to_file(access, cust_platform_json, dst_file) + retval = retval + " --breakout-file {}".format(dst_file) + + return retval + + def make_local_file_path(self, devname, filepath, suffix, ts=None): + access = self._get_dev_access(devname) + dut_label = self._get_dut_label(devname) + if ts is None: ts = time.strftime("%Y%m%d%H%M") + if filepath: + name = filepath.replace(".py", "").replace("/", "_") + file_name = "{0}_{1}_{2}_{3}".format(ts, dut_label, name, suffix) + else: + file_name = "{0}_{1}_{2}".format(ts, dut_label, suffix) + return str(os.path.join(self.logger.logdir, file_name)) + + def _apply_remote(self, devname, option_type, value_list=[]): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + + # ensure we are in sonic mode + self._enter_linux_exit_vtysh(devname) + + if option_type == "port-defaults" and value_list[0]: + helper = os.path.join(os.path.dirname(__file__), + "remote", "port_breakout.py") + helper = os.path.abspath(helper) + helper = self._upload_file2(devname, access, helper, md5check=True) + + if option_type == "dump-click-cmds": + helper = os.path.join(os.path.dirname(__file__), + "remote", "click-helper.py") + helper = os.path.abspath(helper) + helper = self._upload_file2(devname, access, helper, md5check=True) + + # transfer the python file, which is used to apply the files remotely. + helper = os.path.join(os.path.dirname(__file__), + "remote", "spytest-helper.py") + helper = os.path.abspath(helper) + helper = self._upload_file2(devname, access, helper, md5check=True) + + args_str = "" + script_cmd = None + skip_error_check = False + execute_in_console = False + delay_factor = 6 + + # Depending on the option value, do the pre tasks. + if option_type == "apply-configs": + # transfer the cfg files + dst_file_list = [] + method = value_list.pop(0) + for name in value_list: + for src_file in utils.make_list(name): + dst_file = self._upload_file2(devname, access, src_file) + dst_file_list.append(dst_file) + args_str = '"' + '" "'.join(dst_file_list) + '"' + args_str = args_str + " --apply-file-method " + method + self.dut_log(devname, "Applying config files remotely '{}'".format(args_str)) + skip_error_check = True + elif option_type == "run-test": + timeout = value_list.pop(0) + args_str = " ".join(value_list) + delay_factor = int(math.ceil((timeout * 1.0) / 100)) + elif option_type == "init-ta-config": + profile_name = value_list.pop(-1).lower() + args_str = self._add_core_dump_flags(args_str, value_list) + args_str = args_str + " --config-profile {}".format(profile_name) + if os.getenv("SPYTEST_NTP_CONFIG_INIT", "0") != "0": + args_str = args_str + " --env SPYTEST_NTP_CONFIG_INIT 1" + if os.getenv("SPYTEST_CLEAR_MGMT_INTERFACE", "0") != "0": + args_str = args_str + " --env SPYTEST_CLEAR_MGMT_INTERFACE 1" + if os.getenv("SPYTEST_CLEAR_DEVICE_METADATA_HOSTNAME", "0") != "0": + args_str = args_str + " --env SPYTEST_CLEAR_DEVICE_METADATA_HOSTNAME 1" + elif option_type in ["save-base-config", "save-module-config"]: + # no arguments are required to create ta config + args_str = "" + elif option_type in ["apply-base-config", "apply-module-config"]: + execute_in_console = True + apply_ta_config_method = value_list[0] + args_str = " {}".format(apply_ta_config_method) + skip_error_check = True + elif option_type == "disable-debug": + # no arguments are required to disabling debug messages on to console + args_str = "" + elif option_type == "enable-debug": + # no arguments are required to enabling debug messages on to console + args_str = "" + elif option_type == "syslog-check": + args_str = value_list[0] + args_str = args_str + " --phase '{} {}'".format(value_list[1], value_list[2]) + skip_error_check = True + delay_factor = 3 + elif option_type == "sairedis": + args_str = value_list[0] + skip_error_check = True + delay_factor = 3 + elif option_type == "set-mgmt-ip": + args_str = " {} ".format(value_list[0]) + args_str = args_str + " --ip-addr-mask {}".format(value_list[1]) + args_str = args_str + " --gw-addr {}".format(value_list[2]) + elif option_type == "fetch-core-files": + if self.kdump_supported: + args_str = "collect_kdump" + else: + args_str = "none" + skip_error_check = True + delay_factor = 12 + elif option_type == "get-tech-support": + # no arguments are required to fetching tech support data from dut to logs dir + args_str = "" + skip_error_check = True + delay_factor = 12 + elif option_type == "init-clean": + args_str = self._add_core_dump_flags(args_str, value_list) + elif option_type == "update-reserved-ports": + port_list = value_list.pop(0) + args_str = ' '.join(port_list) + elif option_type == "port-defaults": + execute_in_console = True + args_str = "" + if value_list[0]: + args_str = args_str + " --breakout {}".format(' '.join(value_list[0])) + args_str = args_str + self._port_breakout_options(devname) + if value_list[1]: + args_str = args_str + " --speed {}".format(' '.join(map(str,value_list[1]))) + skip_error_check = True + elif option_type == "config-reload": + execute_in_console = True + args_str = value_list[0] + elif option_type == "wait-for-ports": + args_str = self._add_port_wait(args_str, value_list[0], value_list[1], value_list[2]) + elif option_type == "config-profile": + args_str = value_list.pop(0).lower() + execute_in_console = bool(args_str != "na") + elif option_type == "dump-click-cmds": + pass + else: + msg = "Unknown option {} for remote operation".format(option_type) + self.dut_log(devname, msg, lvl=logging.ERROR) + raise ValueError(msg) + + # Construct the command that need to be executed on the DUT. + if self.cfg.community_build and "--community-build" not in args_str: + args_str = args_str + " --community-build" + if self.cfg.load_config_method == "replace": args_str = args_str + " --use-config-replace" + script_cmd = "sudo python {} --{} {} ".format(helper, option_type, args_str) + #self.dut_log(devname, "Using command: {}".format(script_cmd)) + + try: + if execute_in_console: + self._tryssh_switch(devname) + self.trace_callback_set(devname, True) + cli_prompt = self._get_param(devname, "normal-user-cli-prompt") + output = self._send_command(access, script_cmd, cli_prompt, + skip_error_check, delay_factor, + trace_dut_log=1) + self.trace_callback_set(devname, False) + self.dut_log(devname, output) + if execute_in_console: + self._tryssh_switch(devname, True) + except Exception as exp: + msg = utils.stack_trace(traceback.format_exc()) + self.dut_log(devname, msg, lvl=logging.WARNING) + if execute_in_console: + self._tryssh_switch(devname, False) + raise exp + + if option_type in ["run-test", "syslog-check"]: + return output + + process_apply_config = True + fetch_mgmt_ip = True + if re.search("Error", output) or re.search("No such file or directory", output): + msg = "Failed to execute the command {}".format(script_cmd) + self.dut_log(devname, msg, lvl=logging.ERROR) + if option_type not in ["apply-base-config", "apply-module-config", "port-defaults"]: + raise ValueError(msg) + msg = "Recovering the devices by rebooting" + self.dut_log(devname, msg, lvl=logging.ERROR) + process_apply_config = False + self.recover(devname, "Recovering the devices") + + if option_type in ["apply-base-config", "apply-module-config"] and process_apply_config: + if not re.search("Config, FRR, COPP are same as TA files", output): + fetch_mgmt_ip = True + if option_type in ["apply-module-config"] and self.prev_testcase: + pc_msg = "***** TESTCASE '{}' CONFIG CLEANUP NOT DONE *****".format(self.prev_testcase) + self.logger.warning(pc_msg) + if re.search("REBOOT REQUIRED", output) or \ + apply_ta_config_method in ["reboot", "force_reboot"]: + self.recover(devname, "Reboot after applying TA configuration") + + if option_type == "dump-click-cmds": + file_name = "results_{0}_{1}_build_cmds.txt".format( + time.strftime("%Y_%m_%d_%H_%M"), devname) + local_file_path = str(os.path.join(self.logger.logdir, file_name)) + utils.write_file(local_file_path, "") + for line in output.strip().split("\n")[:-1]: + utils.write_file(local_file_path, "{}\n".format(line), "a") + + if option_type == "fetch-core-files": + if re.search("NO-CORE-FILES", output): + msg = "No Core files found on the DUT." + self.dut_log(devname, msg) + else: + # Get the remote file name from the output data. + #remote_file_path = "/tmp/allcorefiles.tar.gz" + remote_file_path = "" + for line in output.strip().split("\n"): + match = re.match(r'CORE-FILES:\s+(\S+.tar.gz)', str(line).strip()) + if match: + remote_file_path = match.group(1) + break + if remote_file_path: + # Construct the local file name. + local_file_path = self.make_local_file_path(devname, + value_list[0], "corefiles.tar.gz") + # Perform the file download if any files found. + retval = self._download_file(access, remote_file_path, local_file_path) + if re.search("FAIL", retval): + self.add_pending_download(devname, remote_file_path, local_file_path) + msg = "Downloading core files - Failed." + self.dut_log(devname, msg, lvl=logging.ERROR) + raise ValueError(msg) + if self.kdump_supported: + if re.search("NO-KDUMP-FILES", output): + msg = "No kdump files found on the DUT." + self.dut_log(devname, msg) + else: + # Get the remote file name from the output data. + #remote_file_path = "/tmp/allcorefiles.tar.gz" + remote_file_path = "" + for line in output.strip().split("\n"): + match = re.match(r'KDUMP-FILES:\s+(\S+.tar.gz)', str(line).strip()) + if match: + remote_file_path = match.group(1) + break + if remote_file_path: + # Construct the local file name. + local_file_path = self.make_local_file_path(devname, + value_list[0], "kdumpfiles.tar.gz") + # Perform the file download if any files found. + retval = self._download_file(access, remote_file_path, local_file_path) + if re.search("FAIL", retval): + self.add_pending_download(devname, remote_file_path, local_file_path) + msg = "Downloading kdump files - Failed." + self.dut_log(devname, msg, lvl=logging.ERROR) + raise ValueError(msg) + + if option_type == "get-tech-support": + if re.search("NO-DUMP-FILES", output): + self.dut_log(devname, output) + output = self.show_new(devname, "cat /tmp/show_tech_support.log", + skip_error_check=True, skip_tmpl=True) + self.dut_log(devname, output) + raise ValueError("Failed to fetch tech support") + else: + # Get the remote file name from the output data. + remote_file_path = "" + for line in output.strip().split("\n"): + match = re.match(r'DUMP-FILES:\s+(\S+.tar.gz)', str(line).strip()) + if match: + remote_file_path = match.group(1) + break + if remote_file_path: + # Construct the local file name. + local_file_path = self.make_local_file_path(devname, value_list[0], + os.path.basename(remote_file_path)) + # Perform the file download if any files found. + retval = self._download_file(access, remote_file_path, local_file_path) + if re.search("FAIL", retval): + self.add_pending_download(devname, remote_file_path, local_file_path) + msg = "Downloading tech-support files - Failed." + self.dut_log(devname, msg, lvl=logging.ERROR) + raise ValueError(msg) + else: + msg = "Failed to read DUMP File" + self.dut_log(devname, msg, lvl=logging.ERROR) + + if option_type == "sairedis" and value_list[0] == "read": + if re.search("NO-SAIREDIS-FILE", output): + self.dut_log(devname, output) + else: + # Get the remote file name from the output data. + remote_file_path = "" + for line in output.strip().split("\n"): + match = re.match(r'SAI-REDIS-FILE:\s+(/etc/spytest/sairedis.txt)', str(line).strip()) + if match: + remote_file_path = match.group(1) + break + if remote_file_path: + # Construct the local file name. + local_file_path = self.make_local_file_path(devname, + value_list[1], "sai.log", "tests_") + # Perform the file download if any files found. + retval = self._download_file(access, remote_file_path, local_file_path) + if re.search("FAIL", retval): + self.add_pending_download(devname, remote_file_path, local_file_path) + msg = "Downloading sai radis files - Failed." + self.dut_log(devname, msg, lvl=logging.ERROR) + raise ValueError(msg) + else: + msg = "Failed to read sai radis File" + self.dut_log(devname, msg, lvl=logging.ERROR) + + # fetch the management IP again + if fetch_mgmt_ip: + self._fetch_mgmt_ip(devname) + + return True + + def generate_tech_support(self, devname, name): + for retry in range(2): + try: + self._apply_remote(devname, "get-tech-support", [name]) + break + except: + continue + + def save_sairedis(self, devname, phase, name): + if self.cfg.save_sairedis in ["none"]: + return "" + + msg = "save-sairedis {} {}".format(phase, name) + self.dut_log(devname, msg, lvl=logging.DEBUG) + + if phase == "pre-module-prolog": + if self.cfg.save_sairedis in ["module"]: + self._apply_remote(devname, "sairedis", ["clean", name]) + elif phase == "post-module-epilog": + if self.cfg.save_sairedis in ["module"]: + self._apply_remote(devname, "sairedis", ["read", name]) + elif phase == "pre-test": + if self.cfg.save_sairedis in ["test"]: + self._apply_remote(devname, "sairedis", ["clear", name]) + elif phase == "post-test": + if self.cfg.save_sairedis in ["test"]: + self._apply_remote(devname, "sairedis", ["read", name]) + + def do_memory_checks(self, devname, phase, name): + if self.cfg.memory_check in ["none"]: + return + + if phase == "pre-module-prolog": + show = True + elif phase == "post-module-prolog": + show = True + elif phase == "post-module-epilog": + show = True + elif phase == "pre-test": + show = bool(self.cfg.memory_check in ["test"]) + elif phase == "post-test": + show = bool(self.cfg.memory_check in ["test"]) + else: + show = False + + if not show: + msg = "memory check {}".format(phase) + self.dut_log(devname, msg, lvl=logging.DEBUG) + else: + if devname in self.memory_checks: + file_path = self.memory_checks[devname] + else: + file_path = self.make_local_file_path(devname, "", "all.log", + "memory_utilization") + self.memory_checks[devname] = file_path + utils.write_file(file_path, "") + utils.write_file(file_path, "\n================ {} {} =================\n".format(phase, name), "a") + output = self._exec(devname, "cat /proc/meminfo", mode="normal-user", skip_error_check=True, trace_dut_log=1) + utils.write_file(file_path, output, "a") + utils.write_file(file_path, "\n --------------------------------\n", "a") + output = self._exec(devname, "docker stats -a --no-stream", mode="normal-user", skip_error_check=True, trace_dut_log=1) + utils.write_file(file_path, output, "a") + utils.write_file(file_path, "\n --------------------------------\n", "a") + output = self._exec(devname, "top -b -n 1", mode="normal-user", skip_error_check=True, trace_dut_log=1) + utils.write_file(file_path, output, "a") + utils.write_file(file_path, "\n --------------------------------\n", "a") + output = self._exec(devname, "pstree -p", mode="normal-user", skip_error_check=True, trace_dut_log=1) + utils.write_file(file_path, output, "a") + utils.write_file(file_path, "\n --------------------------------\n", "a") + output = self._exec(devname, "free -mlh", mode="normal-user", skip_error_check=True, trace_dut_log=1) + utils.write_file(file_path, output, "a") + + def do_syslog_checks(self, devname, phase, name): + if self.cfg.syslog_check in ["none"]: + return "" + + msg = "syslog check {}".format(phase) + self.dut_log(devname, msg, lvl=logging.DEBUG) + + (msgtype) = ("") + if phase == "pre-module-prolog": + lvl = "none" + elif phase == "post-module-prolog": + lvl = self.cfg.syslog_check + msgtype = "Module Prolog" + elif phase == "post-module-epilog": + lvl = self.cfg.syslog_check + msgtype = "Module Epilog" + elif phase == "pre-test": + lvl = "none" + elif phase == "post-test": + lvl = self.cfg.syslog_check + else: + lvl = "none" + + output = self._apply_remote(devname, "syslog-check", [lvl, phase, name]) + syslog_levels = self.wa.syslog_levels + if lvl in syslog_levels: + index = syslog_levels.index(lvl) + needed = "|".join(syslog_levels[:index+1]) + regex = r"^\S+\s+\d+\s+\d+:\d+:\d+(\.\d+){{0,1}}\s+\S+\s+({})\s+" + cre = re.compile(regex.format(needed.upper())) + for line in output.split("\n"): + if cre.search(line): + self.syslogs[devname].append([devname, msgtype, line]) + + access = self._get_dev_access(devname) + if access["filemode"]: + if lvl != "none": + val = random.randint(1, 1000) + val = len(self.syslogs[devname]) + self.syslogs[devname].append([devname, msgtype, "test syslog {}".format(val)]) + + return output + + def get_fcli(self): + return self.fcli + + def get_tryssh(self): + return self.tryssh + + def get_syslogs(self, clear=True): + retval = [] + for devname in self.topo["duts"]: + retval.extend(self.syslogs[devname]) + if clear: + self.syslogs[devname] = [] + return retval + + def do_audit(self, phase, dut, func_name, res): + if phase != "post-test": return + if self.tc_get_tech_support and self.cfg.get_tech_support in ["onerror"]: + self.generate_tech_support(dut, func_name) + self._apply_remote(dut, "init-clean", ["none", self.cfg.get_tech_support, self.cfg.clear_tech_support]) + elif self.cfg.get_tech_support in ["always"]: + self.generate_tech_support(dut, func_name) + self._apply_remote(dut, "init-clean", ["none", self.cfg.get_tech_support, self.cfg.clear_tech_support]) + elif self.cfg.get_tech_support in ["onfail"] and \ + res.lower() in ["fail", "xfail", "dutfail"]: + self.generate_tech_support(dut, func_name) + self._apply_remote(dut, "init-clean", ["none", self.cfg.get_tech_support, self.cfg.clear_tech_support]) + + if self.tc_fetch_core_files and self.cfg.fetch_core_files in ["onerror"]: + self._apply_remote(dut, "fetch-core-files", [func_name]) + self._apply_remote(dut, "init-clean", [self.cfg.fetch_core_files, "none", self.cfg.clear_tech_support]) + elif self.cfg.fetch_core_files in ["always"]: + self._apply_remote(dut, "fetch-core-files", [func_name]) + self._apply_remote(dut, "init-clean", [self.cfg.fetch_core_files, "none", self.cfg.clear_tech_support]) + elif self.cfg.fetch_core_files in ["onfail"] and \ + res.lower() in ["fail", "xfail", "dutfail"]: + self._apply_remote(dut, "fetch-core-files", [func_name]) + self._apply_remote(dut, "init-clean", [self.cfg.fetch_core_files, "none", self.cfg.clear_tech_support]) + + def apply_files(self, devname, file_list, method="incremental"): + """ + todo: Update Documentation + :param devname: + :param file_list: list of the files + :return: + """ + if not file_list: + return + devname = self._check_devname(devname) + for filepath in file_list: + if isinstance(filepath, list): + val_list = [method] + val_list.extend(filepath) + self._apply_remote(devname, "apply-configs", val_list) + elif filepath.endswith('.cmds'): + msg = "Applying commands from {}".format(filepath) + self.dut_log(devname, msg) + cmdlist = utils.read_lines(filepath) + self.apply_script(devname, cmdlist) + else: + self._apply_remote(devname, "apply-configs", [method, filepath]) + + # Get the vtysh hostname from DUT as the config may have changed + self.read_vtysh_hostname(devname) + # set required environment variables as the config may have reset + self.set_login_timeout(devname) + + def run_script(self, devname, timeout, script_path, *args): + """ + todo: Update Documentation + :param dut: + :type dut: + :param timeout: in secs + :type timeout: + :param script_path: + :type script_path: + :return: + :rtype: + """ + val_list = [timeout, script_path] + for arg in args: + val_list.append(arg) + return self._apply_remote(devname, "run-test", val_list) + + def enable_disable_console_debug_msgs(self, devname, flag): + """ + todo: Update Documentation + :param devname: + :type devname: + :param flag: + :type flag: + :return: + :rtype: + """ + if flag: + self._apply_remote(devname, "enable-debug") + else: + self._apply_remote(devname, "disable-debug") + + def _get_mgmt_ip(self, devname): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + if access["filemode"]: + return "" + + connection_param = access["connection_param"] + if self._is_console_connection(devname): + return connection_param["mgmt-ip"] + return connection_param['ip'] + + def get_mgmt_ip(self, devname): + addr = self._get_mgmt_ip(devname) + if not addr: + # eth0 IP is not available, try to read it now + try: + self._fetch_mgmt_ip(devname) + addr = self._get_mgmt_ip(devname) + except: + addr = "" + return addr + + def exec_ssh(self, devname, username=None, password=None, cmdlist=[]): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + + if access["filemode"]: + return "" + + ip = self.get_mgmt_ip(devname) + device = dict() + if not username and not password: + device = copy.copy(access["connection_param"]) + if "mgmt-ip" in device: + del device["mgmt-ip"] + elif not password: + device["username"] = username + device["password"] = access["password"] + device["altpassword"] = access["altpassword"] + elif not username: + device["username"] = access["username"] + device["password"] = password + else: + device["username"] = username + device["password"] = password + device["ip"] = ip + device["port"] = 22 + device["blocking_timeout"] = 30 + device["access_model"] = "sonic_ssh" + + msgs = [] + net_connect = self._connect_to_device2(device, 0, msgs) + if not net_connect: + return None + + output = [] + nc_prompt = self._find_prompt(access, net_connect) + for cmd in cmdlist: + output.append(cmd) + try: + output.append(net_connect.send_command(cmd,nc_prompt)) + except Exception as e: + output.append("Exception: {}".format(e)) + + net_connect.disconnect() + return "\n".join(output) + + def exec_remote(self, ipaddress, username, password, scriptpath, wait_factor=2): + # Check the reachability + if not utils.ipcheck(ipaddress): + msg = "Unable to reach the remote machine '{}'".format(ipaddress) + self.logger.error(msg) + raise ValueError(msg) + + # Construct the dict for connection + device = dict() + device["ip"] = ipaddress + device["username"] = username + device["password"] = password + device["port"] = 22 + device["blocking_timeout"] = 30 + device["access_model"] = "sonic_ssh" + + # Connect to linux server + msgs = [] + net_connect = self._connect_to_device2(device, 0, msgs) + if not net_connect: + msg = "Unable to connect to the server '{}' using the given credentials.".format(ipaddress) + self.logger.error(msg) + raise ValueError(msg) + + # Construct the tmp location filename. + dst_file = "/tmp/{}".format(os.path.basename(scriptpath)) + + # mgmt-ip shpuld not be assigned during the connection initiation. + device["mgmt-ip"] = None + + # Upload the script to linux server. + try: + self.logger.info("Doing SCP transfer of file '{}' to '{}'".format(scriptpath, ipaddress)) + DeviceFileUpload(net_connect, scriptpath, dst_file, device) + except Exception as e: + print(e) + msg = "SCP transfer of file '{}' failed to the server '{}'".format(scriptpath, ipaddress) + self.logger.error(msg) + raise ValueError(msg) + + linux_prompt = net_connect.find_prompt() + self.logger.debug("LINUX_PROMPT: {}".format(linux_prompt)) + + # Change the permissions of the uploaded file + self.logger.info("Changing permissions for '{}'".format(dst_file)) + cmd = "chmod 755 {} ".format(dst_file) + net_connect.send_command(cmd, expect_string=linux_prompt, delay_factor=wait_factor) + net_connect.clear_buffer() + + # Execute the script + self.logger.info("Executing script '{}'".format(dst_file)) + output = net_connect.send_command(dst_file, expect_string=linux_prompt, delay_factor=wait_factor) + net_connect.clear_buffer() + + # Disconnect the connection + net_connect.disconnect() + + # Check for error + if re.search("Error", output) or re.search("No such file or directory", output): + msg = "Failed to execute the script: '{}' on remote machine '{}'".format(scriptpath, ipaddress) + self.logger.error(msg) + raise ValueError(msg) + + # Return the output + return output + + def change_passwd(self, devname, username, password): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + cli_prompt = self._get_param(devname, "normal-user-cli-prompt") + + if access["filemode"]: + return "" + + err_flag = 0 + self._enter_linux_exit_vtysh(devname) + + delay_factor = 3 # so that --faster-cli is not used + prompt_terminator = r"Enter new UNIX password:\s*$|{}\s*$".format(cli_prompt) + output = self._send_command(access, "sudo passwd {}".format(username), prompt_terminator, delay_factor=delay_factor) + self.logger.debug("OUTPUT: {}".format(output)) + if re.search("Enter new UNIX password:", output): + output = self._send_command(access, password, r"Retype new UNIX password:\s*$", delay_factor=delay_factor) + self.logger.debug("OUTPUT: {}".format(output)) + if re.search(".*UNIX password:", output): + output = self._send_command(access, password, cli_prompt, delay_factor=delay_factor) + self.logger.debug("OUTPUT: {}".format(output)) + if not re.search("password updated successfully", output): + err_flag = 1 + else: + err_flag = 1 + elif re.search("does not exist", output): + err_flag = 2 + else: + err_flag = 1 + + # Handling for worst case when required prompts didn't match. + self._send_command(access, "\n\n\n", cli_prompt) + + if err_flag == 2: + return "user not found" + + if err_flag == 1: + return "Password updation failed" + + return "Password updated successfully" + + def upload_file_to_dut(self, devname, src_file, dst_file): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + + if access["filemode"]: + return "" + + # ensure we are in sonic mode + self._enter_linux_exit_vtysh(devname) + + return self._upload_file3(access, src_file, dst_file) + + def download_file_from_dut(self, devname, src_file, dst_file): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + + if access["filemode"]: + return "" + + # ensure we have management IP address + self._fetch_mgmt_ip(devname) + + return self._download_file(access, src_file, dst_file) + + def _run_ansible_script(self, playbook, hosts, username, password, filemode=False): + + hosts = utils.make_list(hosts) + + msg = "Using call: ansible_playbook({}, {}, {}, {})" + msg = msg.format(playbook, hosts, username, password) + self.logger.info(msg) + if filemode: + return "" + + try: logs_path = self.wa.get_logs_path() + except: logs_path = None + + output = "" + try: + output = ansible_playbook(playbook, hosts, username, password, logs_path) + except Exception as e: + self.logger.error(e) + raise ValueError(e) + + if re.search("Error", output) or re.search("No such file or directory", output): + self.logger.error(output) + raise ValueError(output) + self.logger.info(output) + return output + + def ansible_dut(self, devname, playbook): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + host = None + if access["filemode"] or not self._is_console_connection(devname): + host = access["ip"] + else: + host = access["connection_param"]["mgmt-ip"] + if not host: + msg = "No management ip for device: '{}'".format(devname) + self.dut_log(devname, msg, lvl=logging.ERROR) + raise ValueError(msg) + username = access["username"] + password = access["password"] + + output = "" + try: + output = self._run_ansible_script(playbook, host, username, password, access["filemode"]) + except: + password = access["altpassword"] + output = self._run_ansible_script(playbook, host, username, password, access["filemode"]) + return output + + def ansible_service(self, service_data, playbook): + host = service_data["ip"] + username = service_data["username"] + password = service_data["password"] + + return self._run_ansible_script(playbook, host, username, password, service_data["filemode"]) + + def _check_dut_state(self, devname): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + + cmd = "show uptime" + if access["filemode"]: + self.dut_log(devname, "Command '{}'.".format(cmd)) + return True + + # ensure we are in sonic mode + self._enter_linux_exit_vtysh(devname) + + # Issue command. + try: + cli_prompt = self._get_param(devname, "normal-user-cli-prompt") + output = self._send_command(access, cmd, cli_prompt, True, 1) + + prompt = self._find_prompt(access) + if prompt == cli_prompt and re.search("^up", output): + return True + except: + return False + return False + + def add_addl_auth(self, devname, username, password): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + if "addl_auth" not in access: + access["addl_auth"] = [] + access["addl_auth"].append([username, password]) + if "connection_param" in access: + access["connection_param"]["addl_auth"] = access["addl_auth"] + + def module_init_start(self, module_max_timeout, fcli, tryssh): + self.module_start_time = get_timenow() + self.module_max_timeout = module_max_timeout + self.fcli = fcli + self.tryssh = tryssh + self.tc_start_time = None + self.clear_prevent() + self.set_console_only(bool(not self.tryssh)) + self._reset_device_aliases() + + def clear_devices_usage_list(self): + self.devices_used_in_tc.clear() + + def get_devices_usage_list(self): + return list(self.devices_used_in_tc.keys()) + + def set_device_usage_collection(self, collect_flag): + self.devices_used_collection = collect_flag + + def function_init_start(self, tc_max_timeout): + self.module_start_time = None + self.tc_max_timeout = tc_max_timeout + self.tc_get_tech_support = False + self.tc_fetch_core_files = False + for devname in self.topo["duts"]: + self.init_per_test(devname) + + def _session_close_dut(self, devname): + self._exec(devname, "stty cols 80", None, "normal-user") + + def session_close(self): + utils.exec_foreach(self.cfg.faster_init, self.topo["duts"], + self._session_close_dut) + + def init_per_test(self, devname): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + if "addl_auth" in access: + del access["addl_auth"] + if "connection_param" in access: + if "addl_auth" in access["connection_param"]: + del access["connection_param"]["addl_auth"] + + def set_workarea(self, waobj=None): + self.wa = waobj + + def _set_tryssh(self, devname, tryssh_val, switch): + access = self._get_dev_access(devname) + old = access["tryssh"] + access["tryssh"] = tryssh_val + if not switch or old == access["tryssh"]: + return + if access["tryssh"]: + self._tryssh_switch(devname, True, check=False) + else: + self._tryssh_switch(devname, check=False) + + def set_console_only(self, val, switch=True): + tryssh_val = bool(self.tryssh and not val) + utils.exec_foreach(self.cfg.faster_init, self.topo["duts"], + self._set_tryssh, tryssh_val, switch) + + def tc_start(self, start_time=None): + self.tc_start_time = start_time + profile.init() + + # ensure devices are in sonic mode at test start + if start_time: + for devname in self.topo["duts"]: + access = self._get_dev_access(devname) + if access["filemode"]: + continue + self._set_last_prompt(access, None) + self._enter_linux_exit_vtysh(devname) + + def clear_prevent(self): + self.prevent_list = [] + + def add_prevent(self, what): + self.prevent_list.append(what) + + def get_stats(self): + return profile.get_stats() + + def set_prev_tc(self, prev_tc=None): + self.prev_testcase = prev_tc + + def tg_wait(self, val): + self.wait(val, True) + + def wait(self, val, is_tg=False): + profile.wait(val, is_tg) + if self.cfg.filemode: + return + self._check_tc_timeout(None, val) + left = val + while left > 0: + self._check_tc_timeout(None) + if left <= 5: + self.orig_time_sleep(left) + break + self.orig_time_sleep(5) + left = left - 5 + + def _check_tc_timeout(self, access, add_time=0): + retval = None + if self.module_max_timeout and self.module_start_time: + time_taken = get_elapsed(self.module_start_time, False) + time_taken = time_taken + add_time + if time_taken > self.module_max_timeout: + msg = "Max time '{}' reached. Exiting the module init" + msg = msg.format(self.module_max_timeout) + if access: + self.dut_log(access["devname"], msg) + else: + self.logger.error(msg) + if self.wa: + self.wa.report_timeout("module_init_max_timeout") + sys.exit(0) + retval = self.module_max_timeout - time_taken + elif self.tc_max_timeout and self.tc_start_time: + time_taken = get_elapsed(self.tc_start_time, False) + time_taken = time_taken + add_time + if time_taken > self.tc_max_timeout: + msg = "Max time '{}' reached. Exiting the testcase" + msg = msg.format(self.tc_max_timeout) + if access: + self.dut_log(access["devname"], msg) + else: + self.logger.error(msg) + if self.wa: + self.wa.report_timeout("test_case_max_timeout") + sys.exit(0) + retval = self.tc_max_timeout - time_taken + return retval + + def _timeout_handler(self, signum, frame): + self.logger.debug("Timeout Handler signal={}".format(signum)) + if signum != signal.SIGALRM: # do we need this check? + return + if self.profile_max_timeout_msg: + if self.wa: + self.wa.report_timeout("operation_max_timeout", self.profile_max_timeout_msg) + sys.exit(0) + if self.module_max_timeout and self.module_start_time: + if self.wa: + self.wa.report_timeout("module_init_max_timeout") + sys.exit(0) + elif self.tc_max_timeout and self.tc_start_time: + if self.wa: + self.wa.report_timeout("test_case_max_timeout") + sys.exit(0) + + def _timeout_cancel(self, left): + if left is not None: + self.logger.debug("Cancelling timer LEFT={}".format(left)) + signal.setitimer(signal.ITIMER_REAL, 0) + signal.signal(signal.SIGALRM, signal.SIG_DFL) + + def profiling_start(self, msg, max_time): + self.profile_max_timeout_msg = None + left = self._check_tc_timeout(None) + if left is None: + return profile.start(msg, data=left) + if max_time == 0: + timeout = left + elif left > max_time: + timeout = max_time + self.profile_max_timeout_msg = "{}-{}".format(msg, max_time) + else: + timeout = left + self.logger.debug("Start timer MAX={} LEFT={} TIMEOUT={}".format(max_time, left, timeout)) + signal.signal(signal.SIGALRM, self._timeout_handler) + signal.setitimer(signal.ITIMER_REAL, timeout) + return profile.start(msg, data=left) + + def profiling_stop(self, pid): + left = profile.stop(pid) + self._timeout_cancel(left) + + def rest_init(self, dut, username, password, altpassword, cached=False): + access = self._get_dev_access(dut) + access["curr_pwd"] = None + if not cached: + self._fetch_mgmt_ip(dut) + ip = self._get_mgmt_ip(dut) + self.rest[dut].reinit(ip, username, password, altpassword) + access["curr_pwd"] = self.rest[dut]._get_credentials()[1] + + def reset_restinit(self, dut): + access = self._get_dev_access(dut) + access["curr_pwd"] = None + self.rest[dut].reset_curr_pwd() + + def rest_create(self, dut, path, data, *args, **kwargs): + return self.rest[dut].post(path, data, *args, **kwargs) + + def rest_update(self, dut, path, data, *args, **kwargs): + return self.rest[dut].put(path, data, *args, **kwargs) + + def rest_modify(self, dut, path, data, *args, **kwargs): + return self.rest[dut].patch(path, data, *args, **kwargs) + + def rest_read(self, dut, path, *args, **kwargs): + return self.rest[dut].get(path, *args, **kwargs) + + def rest_delete(self, dut, path, *args, **kwargs): + return self.rest[dut].delete(path, *args, **kwargs) + + def rest_parse(self, dut, filepath=None, all_sections=False, paths=[], **kwargs): + return self.rest[dut].parse(filepath, all_sections, paths, **kwargs) + + def rest_apply(self, dut, data): + return self.rest[dut].apply(data) + + def get_credentials(self, dut): + access = self._get_dev_access(dut) + retList = [access.get("username")] + pwdlist = [access.get("password"), access.get("altpassword"), access.get("curr_pwd")] + retList.extend(pwdlist) + return retList + + def _parse_cli_opts(self, **kwargs): + opts = SpyTestDict() + opts.ctype = kwargs.get("type", "click") + opts.skip_tmpl = kwargs.get("skip_tmpl", False) + opts.skip_error_check = kwargs.get("skip_error_check", False) + opts.expect_reboot = kwargs.get("expect_reboot", False) + opts.max_time = kwargs.get("max_time", 0) + opts.sudo = kwargs.get("sudo", None) + if opts.sudo is None: + opts.sudo = True if opts.ctype == "click" else False + opts.sep = ";" if opts.ctype == "click" else "\n" + opts.conf = kwargs.get("conf", True) + opts.confirm = kwargs.get("confirm", None) + opts.faster_cli = bool(kwargs.get("faster_cli", True)) + opts.delay_factor = int(math.ceil((opts.max_time * 1.0) / 100)) + opts.cmds_delay_factor = 3 if opts.delay_factor <=3 else opts.delay_factor + return opts + + # prepare list of commands + def _build_cmd_list(self, cmd, opts): + cmd_list = [] + for l_cmd in utils.string_list(cmd): + if l_cmd == "su": + continue + if opts.sudo and not l_cmd.startswith("sudo "): + l_cmd = "sudo " + l_cmd + cmd_list.append(l_cmd) + return cmd_list + + def _change_mode(self, devname, is_show, cmd, opts): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + if access["filemode"]: + return ("", "", "") + + (prefix, op) = ("", "") + + # check if the current prompt satisfies show/config command + current_mode = self._change_prompt(devname) + if current_mode.startswith("mgmt"): + if opts.ctype == "klish": + if is_show: + if current_mode == "mgmt-user": + return ("", op, current_mode) + return ("do ", op, "mgmt-any-config") + else: + if current_mode == "mgmt-user": + if cmd in ["exit"]: + return ("", op, "normal-user") + if not opts.conf: + return ("", op, "mgmt-user") + elif current_mode == "mgmt-config": + if cmd in ["end", "exit"]: + return ("", op, "mgmt-user") + if opts.conf: + return ("", op, "mgmt-any-config") + else: + if cmd in ["end"]: + return ("", op, "mgmt-user") + if opts.conf: + return ("", op, "mgmt-any-config") + elif current_mode.startswith("vtysh"): + if opts.ctype == "vtysh": + if is_show: + if current_mode == "vtysh-user": + return ("", op, current_mode) + return ("do ", op, "vtysh-any-config") + else: + if current_mode == "vtysh-user": + if cmd in ["exit"]: + return ("", op, "normal-user") + if not opts.conf: + return ("", op, "vtysh-user") + elif current_mode == "vtysh-config": + if cmd in ["end", "exit"]: + return ("", op, "vtysh-user") + if opts.conf: + return ("", op, "vtysh-any-config") + else: + if cmd in ["end"]: + return ("", op, "vtysh-user") + if opts.conf: + return ("", op, "vtysh-any-config") + + # change the mode + if opts.ctype == "click": + op = self._change_prompt(devname, "normal-user", startmode=current_mode) + return ("", op, "normal-user") + elif opts.ctype == "vtysh" and (is_show or not opts.conf): + op = self._change_prompt(devname, "vtysh-user", startmode=current_mode) + return ("", op, "vtysh-user") + elif opts.ctype == "vtysh": + op = self._change_prompt(devname, "vtysh-config", startmode=current_mode) + return ("", op, "vtysh-any-config") + elif opts.ctype == "klish" and (is_show or not opts.conf): + op = self._change_prompt(devname, "mgmt-user", startmode=current_mode) + return ("", op, "mgmt-user") + elif opts.ctype == "klish": + op = self._change_prompt(devname, "mgmt-config", startmode=current_mode) + return ("", op, "mgmt-any-config") + elif opts.ctype == "lldp": + op = self._change_prompt(devname, "lldp-user", startmode=current_mode) + return ("", op, "lldp-user") + + return (prefix, op, "unknown-mode") + + def parse_show(self, devname, cmd, output): + return self._tmpl_apply(devname, cmd, output) + + def show_new(self, devname, cmd, **kwargs): + opts = self._parse_cli_opts(**kwargs) + (prefix, op, expect_mode) = self._change_mode(devname, True, cmd, opts) + + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + prompts = access["prompts"] + if access["filemode"]: + self.dut_log(devname, cmd) + return "" + + if expect_mode in ["unknown-mode", "unknown-prompt"]: + msg = "Unknown prompt/mode." + self.dut_log(devname, msg, lvl=logging.ERROR) + raise ValueError(msg) + + actual_cmd = cmd + if opts.ctype == "klish" and not re.search(r"\| no-more$", cmd.strip()): + cmd = cmd + " | no-more" + + cmd = prefix + cmd + + expected_prompt = prompts.get_prompt_for_mode(expect_mode) + + if opts.expect_reboot: + # adjust delay and prompt when expecting reboot + expect = "|".join([expected_prompt, regex_login]) + opts.delay_factor = utils.max(6, opts.delay_factor) + try: + self._tryssh_switch(devname) + output = self._send_command(access, cmd, expect, True, + ufcli=opts.faster_cli, + delay_factor=opts.delay_factor) + prompt = self._find_prompt(access) + if prompt != expected_prompt: + self._enter_linux(devname, prompt) + self.do_post_reboot(devname) + self._tryssh_switch(devname, True) + else: + self._tryssh_switch(devname, False) + except Exception as exp: + self._tryssh_switch(devname, False) + raise exp + else: + output = self._send_command(access, cmd, expected_prompt, + opts.skip_error_check, ufcli=opts.faster_cli, + delay_factor=opts.delay_factor) + + if opts.skip_tmpl: + return output + + return self._tmpl_apply(devname, actual_cmd, output) + + def config_new(self, devname, cmd, **kwargs): + opts = self._parse_cli_opts(**kwargs) + cmd_list = self._build_cmd_list(cmd, opts) + if not cmd_list: return "" + + (prefix, op, expect_mode) = self._change_mode(devname, False, cmd, opts) + + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + prompts = access["prompts"] + + if expect_mode in ["unknown-mode", "unknown-prompt"]: + msg = "Unknown prompt/mode." + self.dut_log(devname, msg, lvl=logging.ERROR) + raise ValueError(msg) + + if len(cmd_list) > 10 and opts.ctype == "click": + self._enter_linux_exit_vtysh(devname) + # execute the command. + cmd_list.insert(0, "#!/bin/bash\n") + self._echo_list_to_file(access, cmd_list, "/tmp/config.sh") + max_run_time = len(cmd_list) * 2 + return self.run_script(devname, max_run_time, "/tmp/config.sh") + + # need to revisit if klish support multiple commands + expected_prompt = prompts.get_prompt_for_mode(expect_mode) + if opts.ctype != "klish": cmd_list = [opts.sep.join(cmd_list)] + + # add confirmation prompts if specified + confirm_prompts = [] + all_prompts = [expected_prompt] + if opts.confirm: + confirm_prompts.append(r"(.*y\/n\](\:)*\s*)$") + confirm_prompts.append(r"(.*y\/N\](\:)*\s*)$") + confirm_prompts.append(r"(.*Y\/n\](\:)*\s*)$") + confirm_prompts.append(r"(.*Y\/N\](\:)*\s*)$") + all_prompts.extend(confirm_prompts) + expected_prompt_with_confirm = "|".join(all_prompts) + expected_prompt_re = re.compile(expected_prompt) + + # execute individual commands + op_lines = [] + for l_cmd in cmd_list: + if opts.confirm: + op = self._send_command(access, l_cmd, expected_prompt_with_confirm, + opts.skip_error_check, ufcli=opts.faster_cli, + delay_factor=opts.delay_factor) + + # do we really get list? + if isinstance(op, list): + op_lines.extend(op) + continue + + # store the output + op_lines.append(op) + + # can't do any thing in case of errors + if re.search("Syntax error:", op): continue + + ## handle the case of no confirmation + #if expected_prompt_re.match(op): continue + #op2 = op.replace("\\", "") + #if expected_prompt_re.match(op2): continue + + # matched with the confirmation prompt + op = self._send_command(access, str(opts.confirm), expected_prompt, + opts.skip_error_check, new_line=False, + ufcli=opts.faster_cli, + delay_factor=opts.delay_factor) + op_lines.append(op) + op = self._send_command(access, "", expected_prompt, + opts.skip_error_check, new_line=True, + ufcli=opts.faster_cli, + delay_factor=opts.delay_factor) + op_lines.append(op) + else: + op = self._send_command(access, l_cmd, expected_prompt, + opts.skip_error_check, ufcli=opts.faster_cli, + delay_factor=opts.delay_factor) + op_lines.append(op) + return "\n".join(op_lines) + + def exec_ssh_remote_dut(self, devname, ipaddress, username, password, command=None, timeout=30): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + + if access["filemode"]: + return "" + + if not command: + command = "uptime" + + check_cmd = "which sshpass" + update_install_cmd = "sudo apt-get update;sudo apt-get -f install -y sshpass" + cli_prompt = self._get_param(devname, "normal-user-cli-prompt") + + # Check if sshpass exists, if not update and install + output = self._send_command(access, check_cmd, cli_prompt, + skip_error_check=True) + self.dut_log(devname, "Command '{}' Output: '{}'.".format(check_cmd, output)) + if "sshpass" not in output: + output = self._send_command(access, update_install_cmd, cli_prompt, ufcli=False, + skip_error_check=True) + self.dut_log(devname, "Command '{}' Output: '{}'.".format(check_cmd, update_install_cmd)) + + # Construct the sshpass command. + exec_command = "sshpass -p '{}' ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o ConnectTimeout={} {}@{} {}" + exec_command = exec_command.format(password, timeout, username, ipaddress, command) + + # Execute the command + output = self._send_command(access, exec_command, cli_prompt) + + # Return the output + return output + + def run_uicli_script(self, devname, scriptname): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + + if access["filemode"]: + return [False, 0, 0, [], []] + + msg = "Using script: script({})".format(scriptname) + self.logger.info(msg) + + output = "" + data = None + script_module = os.path.splitext(os.path.basename(scriptname))[0] + try: + with open(scriptname) as json_file: + data = json.load(json_file) + except Exception as e: + self.logger.error(e) + raise ValueError(e) + + pass_count = 0 + fail_count = 0 + invalid_count = 0 + + mappings_root = os.path.join(os.path.dirname(__file__), '..', "datastore", "ui_cli", "mappings") + uicli_scripts_root = os.path.join(os.path.dirname(__file__), '..', "datastore", "ui_cli", "json_scripts") + + autogen_params_file = os.path.join(os.path.abspath(uicli_scripts_root), "all_params.json") + manual_params_file = os.path.join(os.path.abspath(mappings_root), "param_mappings.yaml") + + commands_mapfile = os.path.join(os.path.abspath(mappings_root), "command_mappings.yaml") + cmds_oyaml = OrderedYaml(commands_mapfile, []) + cmd_mapfile_data = cmds_oyaml.get_data() or dict() + confirmation_mappings = cmd_mapfile_data.confirmation_mappings if "confirmation_mappings" in cmd_mapfile_data else SpyTestDict() + non_config_mode_mappings = cmd_mapfile_data.non_config_mode_mappings if "non_config_mode_mappings" in cmd_mapfile_data else SpyTestDict() + ignore_commands = cmd_mapfile_data.ignore_commands if "ignore_commands" in cmd_mapfile_data else SpyTestDict() + + msg = "Using autogen params file: params_file({})".format(autogen_params_file) + self.logger.info(msg) + msg = "Using manual params file: params_file({})".format(manual_params_file) + self.logger.info(msg) + msg = "Using command mappings file: commands_mappings_file({})".format(commands_mapfile) + self.logger.info(msg) + + try: + with open(autogen_params_file) as json_file: + all_params = json.load(json_file) + except Exception as e: + self.logger.error(e) + raise ValueError(e) + + try: + params_oyaml = OrderedYaml(manual_params_file, []) + params_mapfile_data = params_oyaml.get_data() or dict() + except Exception as e: + self.logger.error(e) + raise ValueError(e) + + if params_mapfile_data: + all_params.update(params_mapfile_data) + + error_patterns = [] + for err, errinfo in list(access["errors"].items()): + error_patterns.append(errinfo.search) + error_patterns.extend([".*No such file or directory.*", ".*can't open file .*"]) + + tb_vars = SpyTestDict() + tb_vars["connected_ports"] = [] + for each_link in self.wa.get_links(devname): + tb_vars["connected_ports"].append(each_link[0]) + tb_vars["free_ports"] = self.wa.get_free_ports(devname) + tb_vars["all_ports"] = self.wa.get_all_ports(devname) + + uicli = UICLI(self.logger, tb_vars, script_module) + + missing_actions_cfg_cmds = [] + commands_and_results = [] + passed_list = [] + failed_list = [] + index = 0 + for step_entry in data["Steps"]: + replaced_mode_values = SpyTestDict() + replaced_cmd_params = SpyTestDict() + + index += 1 + + # Get updated pre-configs section mode args and command args + uicli._uicli_get_preconfig_mode_arg_values_dict(all_params, step_entry, replaced_mode_values) + uicli._uicli_get_preconfig_cmd_param_values_list(all_params, step_entry, replaced_cmd_params) + + # Get updated configs section mode args and command args + uicli._uicli_get_config_mode_arg_values_dict(all_params, step_entry, replaced_mode_values) + uicli._uicli_get_config_cmd_param_values_list(all_params, step_entry, replaced_cmd_params) + + # Get updated actions section mode args and command args + uicli._uicli_get_action_mode_arg_values_dict(all_params, step_entry, replaced_mode_values) + uicli._uicli_get_action_cmd_param_values_list(all_params, step_entry, replaced_cmd_params) + + # Get list of command params to check + changed_steps = uicli._uicli_substitute_args_params(all_params, step_entry, replaced_mode_values, replaced_cmd_params) + + tmp_configs_section = step_entry.get("configs", None) + tmp_config_step = tmp_configs_section[0] + tmp_config_step_cfg = tmp_config_step.get("config", None) + tmp_config_step_cmd = tmp_config_step_cfg.get("command", None) + + try: + all_step_results = [] + for changed_step in changed_steps: + single_steps_results = self._execute_uicli_step(uicli, devname, changed_step, error_patterns, + ignore_commands, confirmation_mappings, non_config_mode_mappings, missing_actions_cfg_cmds) + #uicli.uicli_log(",".join(single_steps_results)) + all_step_results.extend(single_steps_results) + + if "FAIL" in all_step_results: + fail_count += 1 + failed_list.append([index, tmp_config_step_cmd]) + commands_and_results.append(",".join([script_module, tmp_config_step_cmd, "FAIL"])) + continue + + pass_count += 1 + passed_list.append([index, tmp_config_step_cmd]) + commands_and_results.append(",".join([script_module, tmp_config_step_cmd, "PASS"])) + + except Exception as e: + self.logger.error(e) + fail_count += 1 + failed_list.append([index, tmp_config_step_cmd]) + commands_and_results.append(",".join([script_module, tmp_config_step_cmd, "FAIL"])) + #raise ValueError(e) + + if missing_actions_cfg_cmds: + uicli.uicli_log("Missing 'actions' section for config command(s)", footer=False) + uicli.uicli_log(missing_actions_cfg_cmds, header=False) + + if commands_and_results: + uicli.uicli_log("Commands and Results", footer=False) + uicli.uicli_log(commands_and_results, header=False) + + res_list = [["PASS", pass_count], ["FAIL", fail_count], ["INVALID", invalid_count]] + uicli.uicli_log(res_list) + + if fail_count > 0: + return [False, pass_count, fail_count, passed_list, failed_list] + + return [True, pass_count, fail_count, passed_list, failed_list] + + def _execute_uicli_step(self, uicli, devname, stepentry, error_patterns, ignore_commands, + confirmation_mappings, non_config_mode_mappings, missing_actions_cfg_cmds): + steps_results = [] + cfg_cmds = [] + each_step_cmds_results = [] + + preconfigs_section = stepentry.get("pre-configs", None) + configs_section = stepentry.get("configs", None) + actions_section = stepentry.get("actions", None) + + # Execute pre-configs section + if preconfigs_section: + for config_step in preconfigs_section: + config_step_type = config_step.get("type", None) + config_step_mode = config_step.get("mode", None) + config_step_cfg = config_step.get("pre-config", None) + + current_cmd = None + current_res = "PASS" + if config_step_type == "klish": + if config_step_mode: + command_mode = config_step_mode[0] + + if len(config_step_mode) == 2: + command_mode_args = config_step_mode[1] + prompt = self.change_prompt(devname, command_mode, **command_mode_args) + elif len(config_step_mode) == 1: + prompt = self.change_prompt(devname, command_mode) + + config_step_cmd = config_step_cfg.get("command", None) + config_step_match = config_step_cfg.get("match", None) + config_step_isvalid = config_step_cfg.get("valid", 1) + + if not config_step_cmd: continue + + if any(re.match(key, config_step_cmd) for key in ignore_commands.keys()): continue + + if config_step_match: + cmd_matches = config_step_match + else: + cmd_matches = [] + + is_step_conf = True + if any(re.match(key, config_step_cmd) for key in non_config_mode_mappings.keys()): + is_step_conf = False + + is_show_cmd = False + if config_step_cmd.startswith("show"): + is_show_cmd = True + if not re.search(r"\| no-more$", config_step_cmd.strip()): + config_step_cmd = config_step_cmd + " | no-more" + + cfg_cmds.append(config_step_cmd) + current_cmd = config_step_cmd + + if prompt in ["unknown-mode", "unknown-prompt"]: + steps_results.append("FAIL") + current_res = "FAIL" + self.logger.error("Step Pre-Config Section Failed. Unable to change to the required mode") + each_step_cmds_results.append([current_cmd, current_res]) + continue + + confirm_command = False + if any(re.match(key, current_cmd) for key in confirmation_mappings.keys()): + confirm_command = next((confirmation_mappings.get(key) \ + for key in confirmation_mappings.keys() if re.match(key, current_cmd)), 'N') + + #import pdb; pdb.set_trace() + #try: + # output = self.config_new(devname, config_step_cmd, type="klish", + # skip_error_check=True, conf=is_step_conf) + #except: + # pass + hndl_before_cmd = self._get_handle(devname) + output = self.config_new(devname, current_cmd, type="klish", + skip_error_check=True, conf=is_step_conf, confirm=confirm_command) + + if not isinstance(output, list) and re.search("Syntax error:", output): + hndl = self._get_handle(devname) + hndl.send_command_timing("\x03") + + hndl_after_cmd = self._get_handle(devname) + + if hndl_before_cmd != hndl_after_cmd: + steps_results.append("FAIL") + current_res = "FAIL" + self.logger.debug("Handles- Before: {}, After: {}".format(hndl_before_cmd, hndl_after_cmd)) + self.logger.error("Step Config section Failed. Got console hang or prompt not found pattern") + elif cmd_matches: + for match in cmd_matches: + found_match = False + if isinstance(match, dict): + for entry in output: + keys_matched = 0 + for key in match.keys(): + if key in entry.keys() and match[key] == entry[key]: + keys_matched += 1 + if len(match.keys()) == keys_matched: + found_match = True + break + if found_match: break + else: + if match.strip() != "" and re.search(match, output): + found_match = True + + if not config_step_isvalid: + found_match = not found_match + + if not found_match: + steps_results.append("FAIL") + current_res = "FAIL" + self.logger.error("Step Pre-Config Section Failed. Match not found") + self.logger.error("Output: {}".format(output)) + self.logger.error("Dict Pattern: {}".format(match)) + break + else: + found_error = False + #skip_patterns = [".*Error: Entry not found.*", ".*Error: OID info not found in.*", ".*Error: Exceeds.*"] + skip_patterns = [".*Error: Entry not found.*", ".*Error: OID info not found in.*", ".*Error: Exceeds.*", + ".*Error: Invalid VLAN.*", ".*Error: mclag not configured.*", + ".*Error: L3 Configuration exists for Interface.*", ".*Error:.*It's not a L2 interface.*", + ".*Error: Not supported prefix length.*", ".*Error: Untagged VLAN.*configuration.*exist for Interface:.*", + ".*Error: Priority value should be multiple of.*", ".*Error: PortChannel does not exist:.*", ".*Error: Invalid PortChannel:.*", + ".*Error: This object is not supported in this build.*", ".*Error: This object is not supported in this platform.*", + ".*Error: Retrieving data from VLAN table for VLAN:.*", ".*Error: Neighbor.*not found.*", + ".*Error: Retrieving data from LOOPBACK_INTERFACE table for Loopback:.*", + ".*Error: Fallback option cannot be configured for an already existing PortChannel:.*", + ".*Error: Cannot configure Mode for an existing PortChannel.*"] + for err_patt in error_patterns: + if (is_show_cmd and re.search(".*Error:.*", err_patt)): + continue + is_skippable = False + for skip_patt in skip_patterns: + if re.search(skip_patt, output): + is_skippable = True + if is_skippable: continue + if re.search(err_patt, output): + found_error = True + break + + if not config_step_isvalid: + found_error = not found_error + + if found_error: + steps_results.append("FAIL") + current_res = "FAIL" + self.logger.error("Step Pre-Config section Failed. Got error pattern") + self.logger.error("Output: {}".format(output)) + each_step_cmds_results.append([current_cmd, current_res]) + + # Execute configs section + if configs_section: + for config_step in configs_section: + config_step_type = config_step.get("type", None) + config_step_mode = config_step.get("mode", None) + config_step_cfg = config_step.get("config", None) + + current_cmd = None + current_res = "PASS" + if config_step_type == "klish": + if config_step_mode: + command_mode = config_step_mode[0] + + if len(config_step_mode) == 2: + command_mode_args = config_step_mode[1] + prompt = self.change_prompt(devname, command_mode, **command_mode_args) + elif len(config_step_mode) == 1: + prompt = self.change_prompt(devname, command_mode) + + config_step_cmd = config_step_cfg.get("command", None) + config_step_match = config_step_cfg.get("match", None) + config_step_isvalid = config_step_cfg.get("valid", 1) + + if not config_step_cmd: continue + + if any(re.match(key, config_step_cmd) for key in ignore_commands.keys()): continue + + if config_step_match: + cmd_matches = config_step_match + else: + cmd_matches = [] + + is_step_conf = True + if any(re.match(key, config_step_cmd) for key in non_config_mode_mappings.keys()): + is_step_conf = False + + is_show_cmd = False + if config_step_cmd.startswith("show"): + is_show_cmd = True + if not re.search(r"\| no-more$", config_step_cmd.strip()): + config_step_cmd = config_step_cmd + " | no-more" + + cfg_cmds.append(config_step_cmd) + current_cmd = config_step_cmd + + if prompt in ["unknown-mode", "unknown-prompt"]: + steps_results.append("FAIL") + current_res = "FAIL" + self.logger.error("Step Config Section Failed. Unable to change to the required mode") + each_step_cmds_results.append([current_cmd, current_res]) + continue + + confirm_command = False + if any(re.match(key, current_cmd) for key in confirmation_mappings.keys()): + confirm_command = next((confirmation_mappings.get(key) \ + for key in confirmation_mappings.keys() if re.match(key, current_cmd)), 'N') + + #import pdb; pdb.set_trace() + #try: + # output = self.config_new(devname, config_step_cmd, type="klish", + # skip_error_check=True, conf=is_step_conf) + #except: + # pass + hndl_before_cmd = self._get_handle(devname) + output = self.config_new(devname, current_cmd, type="klish", + skip_error_check=True, conf=is_step_conf, confirm=confirm_command) + + if not isinstance(output, list) and re.search("Syntax error:", output): + hndl = self._get_handle(devname) + hndl.send_command_timing("\x03") + + hndl_after_cmd = self._get_handle(devname) + + if hndl_before_cmd != hndl_after_cmd: + steps_results.append("FAIL") + current_res = "FAIL" + self.logger.debug("Handles- Before: {}, After: {}".format(hndl_before_cmd, hndl_after_cmd)) + self.logger.error("Step Config section Failed. Got console hang or prompt not found pattern") + elif cmd_matches: + for match in cmd_matches: + found_match = False + if isinstance(match, dict): + for entry in output: + keys_matched = 0 + for key in match.keys(): + if key in entry.keys() and match[key] == entry[key]: + keys_matched += 1 + if len(match.keys()) == keys_matched: + found_match = True + break + if found_match: break + else: + if match.strip() != "" and re.search(match, output): + found_match = True + + if not config_step_isvalid: + found_match = not found_match + + if not found_match: + steps_results.append("FAIL") + current_res = "FAIL" + self.logger.error("Step Config Section Failed. Match not found") + self.logger.error("Output: {}".format(output)) + self.logger.error("Dict Pattern: {}".format(match)) + break + else: + found_error = False + #skip_patterns = [".*Error: Entry not found.*", ".*Error: OID info not found in.*", ".*Error: Exceeds.*"] + skip_patterns = [".*Error: Entry not found.*", ".*Error: OID info not found in.*", ".*Error: Exceeds.*", + ".*Error: Invalid VLAN.*", ".*Error: mclag not configured.*", + ".*Error: L3 Configuration exists for Interface.*", ".*Error:.*It's not a L2 interface.*", + ".*Error: Not supported prefix length.*", ".*Error: Untagged VLAN.*configuration.*exist for Interface:.*", + ".*Error: Priority value should be multiple of.*", ".*Error: PortChannel does not exist:.*", ".*Error: Invalid PortChannel:.*", + ".*Error: This object is not supported in this build.*", ".*Error: This object is not supported in this platform.*", + ".*Error: Retrieving data from VLAN table for VLAN:.*", ".*Error: Neighbor.*not found.*", + ".*Error: Retrieving data from LOOPBACK_INTERFACE table for Loopback:.*", + ".*Error: Fallback option cannot be configured for an already existing PortChannel:.*", + ".*Error: Cannot configure Mode for an existing PortChannel.*"] + for err_patt in error_patterns: + if (is_show_cmd and re.search(".*Error:.*", err_patt)): + continue + is_skippable = False + for skip_patt in skip_patterns: + if re.search(skip_patt, output): + is_skippable = True + if is_skippable: continue + if re.search(err_patt, output): + found_error = True + break + + if not config_step_isvalid: + found_error = not found_error + + if found_error: + steps_results.append("FAIL") + current_res = "FAIL" + self.logger.error("Step Config section Failed. Got error pattern") + self.logger.error("Output: {}".format(output)) + each_step_cmds_results.append([current_cmd, current_res]) + + # Execute actions section + if actions_section: + for action_step in actions_section: + action_step_type = action_step.get("type", None) + action_step_mode = action_step.get("mode", None) + action_step_act = action_step.get("action", None) + + current_cmd = None + current_res = "PASS" + if action_step_type == "klish": + if action_step_mode: + action_mode = action_step_mode[0] + + if len(action_step_mode) == 2: + action_mode_args = action_step_mode[1] + prompt = self.change_prompt(devname, action_mode, **action_mode_args) + elif len(action_step_mode) == 1: + prompt = self.change_prompt(devname, action_mode) + + action_step_cmd = action_step_act.get("command", None) + action_step_match = action_step_act.get("match", None) + action_step_isvalid = action_step_act.get("valid", 1) + + if not action_step_cmd: continue + + if action_step_match: + action_matches = action_step_match + else: + action_matches = [] + + skip_tmpl_value = False + for match in action_matches: + if not isinstance(match, dict): + skip_tmpl_value = True + break + + current_cmd = action_step_cmd + + if prompt in ["unknown-mode", "unknown-prompt"]: + steps_results.append("FAIL") + current_res = "FAIL" + self.logger.error("Step Action Section Failed. Unable to change to the required mode") + each_step_cmds_results.append([current_cmd, current_res]) + continue + + #import pdb; pdb.set_trace() + #try: + # output = self.show_new(devname, action_step_cmd, type="klish", + # skip_error_check=True, skip_tmpl=skip_tmpl_value) + #except: + # pass + hndl_before_cmd = self._get_handle(devname) + output = self.show_new(devname, action_step_cmd, type="klish", + skip_error_check=True, skip_tmpl=True) + + if not isinstance(output, list) and re.search("Syntax error:", output): + hndl = self._get_handle(devname) + hndl.send_command_timing("\x03") + + if not skip_tmpl_value: + output = self._tmpl_apply(devname, action_step_cmd, output) + + hndl_after_cmd = self._get_handle(devname) + + if hndl_before_cmd != hndl_after_cmd: + steps_results.append("FAIL") + current_res = "FAIL" + self.logger.debug("Handles- Before: {}, After: {}".format(hndl_before_cmd, hndl_after_cmd)) + self.logger.error("Step Config section Failed. Got console hang or prompt not found pattern") + elif action_matches: + for match in action_matches: + found_match = False + if not skip_tmpl_value: + for entry in output: + keys_matched = 0 + for key in match.keys(): + if key in entry.keys() and match[key] == entry[key]: + keys_matched += 1 + if len(match.keys()) == keys_matched: + found_match = True + break + if found_match: break + else: + if match.strip() != "" and re.search(match, output): + found_match = True + + if not action_step_isvalid: + found_match = not found_match + + if not found_match: + steps_results.append("FAIL") + current_res = "FAIL" + self.logger.error("Step Action Section Failed. Match not found") + self.logger.error("Output: {}".format(output)) + self.logger.error("Dict Pattern: {}".format(match)) + break + else: + found_error = False + for err_patt in error_patterns: + if re.search(err_patt, output): + found_error = True + break + + if not action_step_isvalid: + found_error = not found_error + + if found_error: + steps_results.append("FAIL") + current_res = "FAIL" + self.logger.error("Step Action section Failed. Got error pattern") + self.logger.error("Output: {}".format(output)) + each_step_cmds_results.append([current_cmd, current_res]) + else: + missing_actions_cfg_cmds.append(",".join(cfg_cmds)) + + if each_step_cmds_results: + uicli.uicli_log(each_step_cmds_results) + + return steps_results + + def run_uirest_script(self, devname, scriptname): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + + if access["filemode"]: + return [False, 0, 0] + + msg = "Using script: script({})".format(scriptname) + self.logger.info(msg) + + json_name = os.path.basename(scriptname) + script_module = os.path.splitext(os.path.basename(scriptname))[0] + + try: + with open(scriptname) as json_file: + data = json.load(json_file) + except Exception as e: + self.logger.error(e) + raise ValueError(e) + + pass_count = 0 + fail_count = 0 + invalid_count = 0 + + uicli_scripts_root = os.path.join(os.path.dirname(__file__), '..', "datastore", "ui_cli", "json_scripts") + autogen_params_file = os.path.join(os.path.abspath(uicli_scripts_root), "all_params.json") + + mappings_root = os.path.join(os.path.dirname(__file__), '..', "datastore", "ui_rest", "mappings") + uirest_scripts_root = os.path.join(os.path.dirname(__file__), '..', "datastore", "ui_rest", "json_scripts") + #uirest_scripts_root = os.path.join(os.path.dirname(__file__), '..', "datastore", "ui_rest", "json_scripts_test") + + path_args_file = os.path.join(os.path.abspath(uirest_scripts_root), "path_args.json") + data_args_file = os.path.join(os.path.abspath(uirest_scripts_root), "data_args.json") + + msg = "Using path args file: params_file({})".format(path_args_file) + self.logger.info(msg) + msg = "Using data args file: params_file({})".format(data_args_file) + self.logger.info(msg) + msg = "Using autogen params file: params_file({})".format(autogen_params_file) + self.logger.info(msg) + + try: + with open(path_args_file) as json_file: + all_path_args = json.load(json_file) + except Exception as e: + self.logger.error(e) + raise ValueError(e) + + try: + with open(data_args_file) as json_file: + all_data_args = json.load(json_file) + except Exception as e: + self.logger.error(e) + raise ValueError(e) + + try: + with open(autogen_params_file) as json_file: + all_params = json.load(json_file) + except Exception as e: + self.logger.error(e) + raise ValueError(e) + + error_patterns = [] + for err, errinfo in list(access["errors"].items()): + error_patterns.append(errinfo.search) + + tb_vars = SpyTestDict() + tb_vars["connected_ports"] = [] + for each_link in self.wa.get_links(devname): + tb_vars["connected_ports"].append(each_link[0]) + tb_vars["free_ports"] = self.wa.get_free_ports(devname) + tb_vars["all_ports"] = self.wa.get_all_ports(devname) + + uirest = UIRest(self.logger, tb_vars) + + commands_and_results = [] + passed_list = [] + failed_list = [] + index = 0 + + for step_entry in data["Steps"]: + replaced_mode_values = SpyTestDict() + replaced_cmd_params = SpyTestDict() + + index += 1 + + # Get updated pre-configs section path args and data values + uirest._uirest_get_preconfig_mode_path_values(all_path_args, all_params, step_entry, replaced_mode_values) + uirest._uirest_get_preconfig_mode_data_values(all_data_args, all_params, step_entry, replaced_mode_values, replaced_cmd_params) + + # Get updated configs section path args and data values + uirest._uirest_get_config_mode_path_values(all_path_args, all_params, step_entry, replaced_mode_values) + uirest._uirest_get_config_mode_data_values(all_data_args, all_params, step_entry, replaced_mode_values, replaced_cmd_params) + + # Get updated actions section path args and data values + uirest._uirest_get_action_mode_arg_values(all_path_args, all_params, step_entry, replaced_mode_values) + uirest._uirest_get_action_cmd_param_values(all_data_args, all_params, step_entry, replaced_mode_values, replaced_cmd_params) + + # Get list of command params to check + changed_steps = uirest._uirest_substitute_path_data_params(step_entry, all_data_args, all_params, replaced_mode_values, replaced_cmd_params) + + tmp_configs_section = step_entry.get("configs", None) + tmp_config_step = tmp_configs_section[0] + tmp_config_step_cmd = tmp_config_step.get("path", None) + tmp_config_step_opt = tmp_config_step.get("operation", None) + tmp_cmd_call = "{};{}".format(tmp_config_step_cmd, tmp_config_step_opt) + + try: + all_step_results = [] + for changed_step in changed_steps: + single_steps_results = self._execute_uirest_step(uirest, devname, json_name, changed_step) + all_step_results.extend(single_steps_results) + + if "FAIL" in all_step_results: + fail_count += 1 + failed_list.append([index, tmp_cmd_call]) + commands_and_results.append(",".join([script_module, tmp_cmd_call, "FAIL"])) + continue + + pass_count += 1 + passed_list.append([index, tmp_cmd_call]) + commands_and_results.append(",".join([script_module, tmp_cmd_call, "PASS"])) + + except Exception as e: + self.logger.error(e) + fail_count += 1 + failed_list.append([index, tmp_cmd_call]) + commands_and_results.append(",".join([script_module, tmp_cmd_call, "FAIL"])) + + if commands_and_results: + uirest.uirest_log("Commands and Results", footer=False) + uirest.uirest_log(commands_and_results, header=False) + + res_list = [["PASS", pass_count], ["FAIL", fail_count], ["INVALID", invalid_count]] + uirest.uirest_log(res_list) + + if fail_count > 0: + return [False, pass_count, fail_count, passed_list, failed_list] + + return [True, pass_count, fail_count, passed_list, failed_list] + + def _execute_uirest_step(self, uirest, devname, filename, stepentry): + steps_results = [] + each_step_cmds_results = [] + + preconfigs_section = stepentry.get("pre-configs", None) + configs_section = stepentry.get("configs", None) + actions_section = stepentry.get("actions", None) + + # Execute pre-configs section + if preconfigs_section: + for config_step in preconfigs_section: + step_name = config_step["name"] + step_op = config_step["operation"] + msg = "{}::{} {}".format(filename, step_name, step_op) + utils.banner(msg, tnl=False) + + retval = self.rest_apply(devname, config_step) + + if not isinstance(retval, dict): + current_res = "FAIL" + steps_results.append(current_res) + each_step_cmds_results.append([msg, current_res]) + elif step_name not in retval: + current_res = "FAIL" + steps_results.append(current_res) + each_step_cmds_results.append([msg, current_res]) + else: + result_data = retval[step_name] + is_failed = False + if isinstance(result_data, list): + if not result_data[0]: + is_failed = True + elif isinstance(result_data, dict): + if "operation" not in result_data or "status" not in result_data or "output" not in result_data: + is_failed = True + elif "status" in result_data and result_data["status"] not in [200, 201, 202, 203, 204, 205, 404, 405]: + is_failed = True + else: + is_failed = True + if is_failed: + current_res = "FAIL" + steps_results.append(current_res) + each_step_cmds_results.append([msg, current_res]) + + # Execute configs section + if configs_section: + for config_step in configs_section: + step_name = config_step["name"] + step_op = config_step["operation"] + msg = "{}::{} {}".format(filename, step_name, step_op) + utils.banner(msg, tnl=False) + + retval = self.rest_apply(devname, config_step) + + if not isinstance(retval, dict): + current_res = "FAIL" + steps_results.append(current_res) + each_step_cmds_results.append([msg, current_res]) + elif step_name not in retval: + current_res = "FAIL" + steps_results.append(current_res) + each_step_cmds_results.append([msg, current_res]) + else: + result_data = retval[step_name] + is_failed = False + if isinstance(result_data, list): + if not result_data[0]: + is_failed = True + elif isinstance(result_data, dict): + if "operation" not in result_data or "status" not in result_data or "output" not in result_data: + is_failed = True + elif "status" in result_data and result_data["status"] not in [200, 201, 202, 203, 204, 205, 404, 405]: + is_failed = True + else: + is_failed = True + if is_failed: + current_res = "FAIL" + steps_results.append(current_res) + each_step_cmds_results.append([msg, current_res]) + + # Execute actions section + if actions_section: + for action_step in actions_section: + step_name = action_step["name"] + step_op = action_step["operation"] + msg = "{}::{} {}".format(filename, step_name, step_op) + utils.banner(msg, tnl=False) + + retval = self.rest_apply(devname, action_step) + + if not isinstance(retval, dict): + current_res = "FAIL" + steps_results.append(current_res) + each_step_cmds_results.append([msg, current_res]) + elif step_name not in retval: + current_res = "FAIL" + steps_results.append(current_res) + each_step_cmds_results.append([msg, current_res]) + else: + result_data = retval[step_name] + is_failed = False + if isinstance(result_data, list): + if not result_data[0]: + is_failed = True + elif isinstance(result_data, dict): + if "operation" not in result_data or "status" not in result_data or "output" not in result_data: + is_failed = True + elif "status" in result_data and result_data["status"] not in [200, 201, 202, 203, 204, 205, 404, 405]: + is_failed = True + else: + is_failed = True + if is_failed: + current_res = "FAIL" + steps_results.append(current_res) + each_step_cmds_results.append([msg, current_res]) + + if each_step_cmds_results: + uirest.uirest_log(each_step_cmds_results) + + return steps_results + + def run_uignmi_script(self, devname, scriptname, **kwargs): + devname = self._check_devname(devname) + access = self._get_dev_access(devname) + + if access["filemode"]: + return "" + + msg = "Using script: script({})".format(scriptname) + self.logger.info(msg) + + json_name = os.path.basename(scriptname) + script_module = os.path.splitext(os.path.basename(scriptname))[0] + + try: + with open(scriptname) as json_file: + data = json.load(json_file) + except Exception as e: + self.logger.error(e) + raise ValueError(e) + + pass_count = 0 + fail_count = 0 + invalid_count = 0 + + uicli_scripts_root = os.path.join(os.path.dirname(__file__), '..', "datastore", "ui_cli", "json_scripts") + autogen_params_file = os.path.join(os.path.abspath(uicli_scripts_root), "all_params.json") + + mappings_root = os.path.join(os.path.dirname(__file__), '..', "datastore", "ui_gnmi", "mappings") + uignmi_scripts_root = os.path.join(os.path.dirname(__file__), '..', "datastore", "ui_rest", "json_scripts") + + path_args_file = os.path.join(os.path.abspath(uignmi_scripts_root), "path_args.json") + data_args_file = os.path.join(os.path.abspath(uignmi_scripts_root), "data_args.json") + + msg = "Using path args file: params_file({})".format(path_args_file) + self.logger.info(msg) + msg = "Using data args file: params_file({})".format(data_args_file) + self.logger.info(msg) + msg = "Using autogen params file: params_file({})".format(autogen_params_file) + self.logger.info(msg) + + try: + with open(path_args_file) as json_file: + all_path_args = json.load(json_file) + except Exception as e: + self.logger.error(e) + raise ValueError(e) + + try: + with open(data_args_file) as json_file: + all_data_args = json.load(json_file) + except Exception as e: + self.logger.error(e) + raise ValueError(e) + + try: + with open(autogen_params_file) as json_file: + all_params = json.load(json_file) + except Exception as e: + self.logger.error(e) + raise ValueError(e) + + error_patterns = [] + for err, errinfo in list(access["errors"].items()): + error_patterns.append(errinfo.search) + + tb_vars = SpyTestDict() + tb_vars["connected_ports"] = [] + for each_link in self.wa.get_links(devname): + tb_vars["connected_ports"].append(each_link[0]) + tb_vars["free_ports"] = self.wa.get_free_ports(devname) + tb_vars["all_ports"] = self.wa.get_all_ports(devname) + + uignmi = UIGnmi(self.logger, tb_vars) + + commands_and_results = [] + passed_list = [] + failed_list = [] + index = 0 + for step_entry in data["Steps"]: + replaced_mode_values = SpyTestDict() + replaced_cmd_params = SpyTestDict() + + index += 1 + + # Get updated pre-configs section path args and data values + uignmi._uignmi_get_preconfig_mode_path_values(all_path_args, all_params, step_entry, + replaced_mode_values) + uignmi._uignmi_get_preconfig_mode_data_values(all_data_args, all_params, step_entry, + replaced_mode_values, replaced_cmd_params) + + # Get updated configs section path args and data values + uignmi._uignmi_get_config_mode_path_values(all_path_args, all_params, step_entry, replaced_mode_values) + uignmi._uignmi_get_config_mode_data_values(all_data_args, all_params, step_entry, replaced_mode_values, + replaced_cmd_params) + + # Get updated actions section path args and data values + uignmi._uignmi_get_action_mode_arg_values(all_path_args, all_params, step_entry, replaced_mode_values) + uignmi._uignmi_get_action_cmd_param_values(all_data_args, all_params, step_entry, replaced_mode_values, + replaced_cmd_params) + + # Get list of command params to check + changed_steps = uignmi._uignmi_substitute_path_data_params(step_entry, all_data_args, all_params, + replaced_mode_values, replaced_cmd_params) + + tmp_configs_section = step_entry.get("configs", None) + tmp_config_step = tmp_configs_section[0] + tmp_config_step_cmd = tmp_config_step.get("path", None) + tmp_config_step_opt = tmp_config_step.get("operation", None) + tmp_cmd_call = "{};{}".format(tmp_config_step_cmd, tmp_config_step_opt) + if tmp_config_step_opt not in ["get", "patch", "delete"]: continue + try: + all_step_results = [] + kwargs.update({"mgmt_ip":access["connection_param"].get("mgmt-ip")}) + for changed_step in changed_steps: + single_steps_results = self._execute_uignmi_step(uignmi, devname, json_name, changed_step, + **kwargs) + all_step_results.extend(single_steps_results) + + if "FAIL" in all_step_results: + fail_count += 1 + failed_list.append([index, tmp_cmd_call]) + commands_and_results.append(",".join([script_module, tmp_cmd_call, "FAIL"])) + continue + + pass_count += 1 + passed_list.append([index, tmp_cmd_call]) + commands_and_results.append(",".join([script_module, tmp_cmd_call, "PASS"])) + + except Exception as e: + self.logger.error(e) + fail_count += 1 + failed_list.append([index, tmp_cmd_call]) + commands_and_results.append(",".join([script_module, tmp_cmd_call, "FAIL"])) + + if commands_and_results: + uignmi.uignmi_log("Commands and Results", footer=False) + uignmi.uignmi_log(commands_and_results, header=False) + + res_list = [["PASS", pass_count], ["FAIL", fail_count], ["INVALID", invalid_count]] + uignmi.uignmi_log(res_list) + + if fail_count > 0: + return [False, pass_count, fail_count, passed_list, failed_list] + + return [True, pass_count, fail_count, passed_list, failed_list] + + def _execute_uignmi_step(self, uignmi, devname, filename, stepentry, **kwargs): + steps_results = [] + each_step_cmds_results = [] + + preconfigs_section = stepentry.get("pre-configs", None) + configs_section = stepentry.get("configs", None) + actions_section = stepentry.get("actions", None) + + # Execute pre-configs section + if preconfigs_section: + for config_step in preconfigs_section: + step_name = config_step["name"] + step_op = config_step["operation"] + if step_op not in ["get", "patch", "delete"]: continue + msg = "{}::{} {}".format(filename, step_name, step_op) + utils.banner(msg, tnl=False) + + retval = self.gnmi_apply(devname, config_step, **kwargs) + + if (step_op != "get" and not retval) or (step_op == "get" and not isinstance(retval, dict)): + current_res = "FAIL" + steps_results.append(current_res) + each_step_cmds_results.append([msg, current_res]) + + # Execute configs section + if configs_section: + for config_step in configs_section: + step_name = config_step["name"] + step_op = config_step["operation"] + if step_op not in ["get", "patch", "delete"]: continue + msg = "{}::{} {}".format(filename, step_name, step_op) + utils.banner(msg, tnl=False) + + retval = self.gnmi_apply(devname, config_step, **kwargs) + + if (step_op != "get" and not retval) or (step_op == "get" and not isinstance(retval, dict)): + current_res = "FAIL" + steps_results.append(current_res) + each_step_cmds_results.append([msg, current_res]) + + # Execute actions section + if actions_section: + for action_step in actions_section: + step_name = action_step["name"] + step_op = action_step["operation"] + if step_op not in ["get", "patch", "delete"]: continue + msg = "{}::{} {}".format(filename, step_name, step_op) + utils.banner(msg, tnl=False) + + retval = self.gnmi_apply(devname, config_step, **kwargs) + + if (step_op != "get" and not retval) or (step_op == "get" and not isinstance(retval, dict)): + current_res = "FAIL" + steps_results.append(current_res) + each_step_cmds_results.append([msg, current_res]) + + if each_step_cmds_results: + uignmi.uignmi_log(each_step_cmds_results) + return steps_results + + def _gnmi_set(self, devname, xpath, **kwargs): + """ + API to set GNMI configuration + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param xpath: + :param json_content: + :param kwargs: + :return: + """ + self.logger.info("Performing GNMI SET OPERATION ...") + json_content = kwargs.get("json_content") + + if json_content: + temp_dir = tempfile.gettempdir() + current_datetime = utils.get_current_datetime() + file_name = "sonic_uignmi_{}.json".format(current_datetime) + tmp_path = "{}/{}".format(temp_dir, file_name) + rm_cmds = ['rm {}'.format(tmp_path)] + kwargs.update({"devname": devname}) + kwargs.update({"json_content": json_content}) + kwargs.update({"data_file_path": tmp_path}) + command = self._prepare_gnmi_command(xpath, **kwargs) + file_operation = utils.write_to_json_file(json_content, tmp_path) + if not file_operation: + self.logger.error("File operation failed.") + return False + container_crash_err_strngs = ["transport is closing", "connection refused", "Error response from daemon:"] + docker_crash = False + docker_status_cmd = "docker inspect -f '{{.State.Running}}' telemetry" + # output = self.config_new(devname, command, skip_error_check=True) + output = self._run_gnmi_command(command) + self.logger.debug("OUTPUT : {}".format(output)) + for rm_cmd in rm_cmds: + self._run_gnmi_command(rm_cmd) + if output.get("error"): + for err_code_str in container_crash_err_strngs: + if err_code_str in output.get("error"): + self.logger.info("Observed {} error, may be telemetry docker got crashed".format(err_code_str)) + docker_crash = True + break + if not docker_crash: + self.logger.info(output.get("error")) + return False + if docker_crash: + timeout = 300 + curr_time = 0 + wait_time = 10 + iteration = 1 + check_flag = True + itr_msg = "ITERATION {} : Observed that telemetry docker is not running or crashed, hence waiting for {} secs" + while curr_time < timeout: + status_output = self.config_new(devname, docker_status_cmd, skip_error_check=True) + self.logger.info(itr_msg.format(iteration, wait_time)) + if ("true" not in status_output) or ("transport is closing" in output.get("error") and "true" in status_output): + self.wait(wait_time) + curr_time += wait_time + check_flag = False + iteration +=1 + if iteration > 3 and "true" in status_output: + return False + continue + else: + check_flag = True + break + if not check_flag: + self.logger.info("ERROR code observed with telemetry docker...") + return False + if curr_time >= timeout: + self.logger.info("Max retries reached") + return False + else: + self.logger.info(output.get("error")) + return False + return output + else: + self.logger.info("Could not find JSON CONTENT for SET operation") + return False + + def _gnmi_get(self, devname, xpath, **kwargs): + """ + API to do GNMI get operations + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param xpath: + :param kwargs: + :return: + """ + self.logger.info("Performing GNMI GET OPERATION ...") + skip_tmpl = kwargs.get('skip_tmpl', False) + + result = dict() + try: + kwargs.update({"devname": devname}) + command = self._prepare_gnmi_command(xpath, **kwargs) + docker_status_cmd = "docker inspect -f '{{.State.Running}}' telemetry" + container_crash_err_strngs = ["transport is closing", "connection refused", "Error response from daemon:"] + docker_crash = False + output = self._run_gnmi_command(command) + # output = self.show(devname, command, skip_tmpl=skip_tmpl, skip_error_check=True) + if output.get("error"): + for err_code_str in container_crash_err_strngs: + if err_code_str in output.get("error"): + self.logger.info("Observed {} error, may be telemetry docker got crashed".format(err_code_str)) + docker_crash = True + break + if not docker_crash: + self.logger.info(output.get("error")) + return False + if docker_crash: + timeout = 300 + curr_time = 0 + wait_time = 10 + iteration = 1 + check_flag = True + itr_msg = "ITERATION {} : Observed that telemetry docker is not running or crashed, hence waiting for {} secs" + while curr_time < timeout: + status_output = self.config_new(devname, docker_status_cmd, skip_error_check=True) + self.logger.info(itr_msg.format(iteration, wait_time)) + if ("true" not in status_output) or ( + "transport is closing" in output.get("error") and "true" in status_output): + self.wait(wait_time) + curr_time += wait_time + check_flag = False + iteration += 1 + if iteration > 3 and "true" in status_output: + return False + continue + else: + check_flag = True + break + if not check_flag: + self.logger.info("ERROR code observed with telemetry docker...") + return False + if curr_time >= timeout: + self.logger.info("Max retries reached") + return False + else: + self.logger.info(output.get("error")) + return False + else: + return output + except Exception as e: + self.logger.error(e) + return False + + def _gnmi_delete(self, devname, xpath, **kwargs): + """ + API to do GNMI get operations + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + :param dut: + :param xpath: + :param kwargs: + :return: + """ + self.logger.info("Performing GNMI DELETE OPERATION ...") + try: + kwargs.update({"devname":devname}) + command = self._prepare_gnmi_command(xpath, **kwargs) + container_crash_err_strngs = ["transport is closing", "connection refused", "Error response from daemon:"] + docker_crash = False + docker_status_cmd = "docker inspect -f '{{.State.Running}}' telemetry" + output = self._run_gnmi_command(command) + # output = self.config_new(devname, command, skip_error_check=True) + self.logger.debug("OUTPUT : {}".format(output)) + if output.get("error"): + for err_code_str in container_crash_err_strngs: + if err_code_str in output.get("error"): + self.logger.info("Observed {} error, may be telemetry docker got crashed".format(err_code_str)) + docker_crash = True + break + if not docker_crash: + self.logger.info(output.get("error")) + return False + if docker_crash: + timeout = 300 + curr_time = 0 + wait_time = 10 + iteration = 1 + check_flag = True + itr_msg = "ITERATION {} : Observed that telemetry docker is not running or crashed, hence waiting for {} secs" + while curr_time < timeout: + status_output = self.config_new(devname, docker_status_cmd, skip_error_check=True) + self.logger.info(itr_msg.format(iteration, wait_time)) + if ("true" not in status_output) or ("transport is closing" in output.get("error") and "true" in status_output): + self.wait(wait_time) + curr_time += wait_time + check_flag = False + iteration +=1 + if iteration > 3 and "true" in status_output: + return False + continue + else: + check_flag = True + break + if not check_flag: + self.logger.info("ERROR code observed with telemetry docker...") + return False + if curr_time >= timeout: + self.logger.info("Max retries reached") + return False + else: + self.logger.info(output.get("error")) + return False + return output + except Exception as e: + self.logger.error(e) + return False + + def _prepare_gnmi_command(self, xpath, **kwargs): + credentials = self.get_credentials(kwargs.get("devname")) + ip_address = kwargs.get('mgmt_ip', '127.0.0.1') + port = kwargs.get('port', '8080') + insecure = kwargs.get('insecure', '') + username = kwargs.get('username', credentials[0]) + password = kwargs.get('password', credentials[3]) + # gnmi_utils_path = kwargs.get("gnmi_utils_path", ".") + gnmi_utils_path = "/tmp" + cert = kwargs.get('cert') + action = kwargs.get("action", "get") + pretty = kwargs.get('pretty') + logstostderr = kwargs.get('logstostderr') + mode = kwargs.get('mode', '--update') + docker_path = kwargs.get("docker_path") + docker_command = "docker exec -it telemetry bash" + if action == "get": + gnmi_command = 'gnmi_get -xpath {} -target_addr {}:{}'.format(xpath, ip_address, port) + """ + if username: + gnmi_command += " --username {}".format(username) + if password: + gnmi_command += " --password {}".format(password) + if cert: + gnmi_command += " --cert {}".format(cert) + gnmi_command += " --insecure {}".format(insecure) + gnmi_command += " --logtostderr" + command = '{}/{}'.format(gnmi_utils_path, gnmi_command) + return command + """ + elif action == "set": + gnmi_command = 'gnmi_set {} {}:@{} --target_addr {}:{}'.format(mode, xpath, kwargs.get("data_file_path"), ip_address, port) + if pretty: + gnmi_command += " --pretty" + """ + if username: + gnmi_command += " --username {}".format(username) + if password: + gnmi_command += " --password {}".format(password) + if cert: + gnmi_command += " --cert {}".format(cert) + if pretty: + gnmi_command += " --pretty" + gnmi_command += " --insecure {}".format(insecure) + gnmi_command += " --logtostderr" + command = '{}/{}'.format(gnmi_utils_path, gnmi_command) + return command + """ + elif action == "delete": + gnmi_command = 'gnmi_set --delete {} --target_addr {}:{}'.format(xpath, ip_address, port) + """ + if username: + gnmi_command += " --username {}".format(username) + if password: + gnmi_command += " --password {}".format(password) + if cert: + gnmi_command += " --cert {}".format(cert) + gnmi_command += " --insecure {}".format(insecure) + gnmi_command += " --logtostderr" + command = '{}/{}'.format(gnmi_utils_path, gnmi_command) + return command + """ + if username: + gnmi_command += " --username {}".format(username) + if password: + gnmi_command += " --password {}".format(password) + if cert: + gnmi_command += " --cert {}".format(cert) + gnmi_command += " --insecure {}".format(insecure) + gnmi_command += " --logtostderr" + command = '{}/{}'.format(gnmi_utils_path, gnmi_command) + return command + + def gnmi_apply(self, devname, config_step, **kwargs): + operation = config_step["operation"] + if operation == "patch": + action = "set" + elif operation == "delete": + action = "delete" + else: + action = "get" + xpath = config_step["path"] + gnmi_utils_path = os.path.join(os.path.dirname(__file__), "..","tests", "ui_gnmi","utilities") + gnmi_utils_abs_path = os.path.abspath(gnmi_utils_path) + self.logger.info("GNMI UTILS PATH - {}".format(gnmi_utils_abs_path)) + kwargs.update({"gnmi_utils_path":gnmi_utils_abs_path}) + kwargs.update({"json_content":config_step["data"]}) + kwargs.update({"action":action}) + kwargs.update({"skip_tmpl":True}) + if action == "get": + return self._gnmi_get(devname, xpath, **kwargs) + elif action == "set": + return self._gnmi_set(devname, xpath, **kwargs) + elif action == "delete": + return self._gnmi_delete(devname, xpath, **kwargs) + else: + self.logger.info("Invalid operation for GNMI -- {}".format(action)) + return False + + def _run_gnmi_command(self, command): + result = dict() + self.logger.info("CMD: {}".format(command)) + process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + data, error = process.communicate() + rc = process.poll() + result.update({"output": data}) + result.update({"rc": rc}) + result.update({"error": error}) + self.logger.info("RESULT {}".format(result)) + return result + + def dump_all_commands(self, devname, type='click'): + self._apply_remote(devname, "dump-click-cmds") + diff --git a/spytest/spytest/ordyaml.py b/spytest/spytest/ordyaml.py new file mode 100644 index 00000000000..5fe8a5e43a0 --- /dev/null +++ b/spytest/spytest/ordyaml.py @@ -0,0 +1,124 @@ +import os +import sys +import yaml + +from spytest.dicts import SpyTestDict +import utilities.common as utils + +class OrderedYaml(object): + + def _locate(self, filename): + for path in self._paths: + filename1 = os.path.join(path, filename) + if os.path.isfile(filename1): + return filename1 + if os.path.isfile(filename): + return filename + return None + + def _load(self, stream, file_dict=dict(), Loader=yaml.Loader, + object_pairs_hook=SpyTestDict): + def _yaml_include(loader, node): + filename = self._locate(node.value) + if not filename: + msg = "Failed to locate included file '{}'".format(node.value) + self.errs.append(msg) + return None + file_dict[filename] = 1 + with utils.open_file(filename) as inputfile: + return yaml.load(inputfile, Loader) + + def _construct_mapping(loader, node): + loader.flatten_mapping(node) + return object_pairs_hook(loader.construct_pairs(node)) + + Loader.add_constructor( + yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, + _construct_mapping) + + Loader.add_constructor("!include", _yaml_include) + return yaml.load(stream, Loader) + + def _dump(self, data, stream=None, Dumper=yaml.Dumper, **kwds): + def _dict_representer(dumper, data): + return dumper.represent_mapping( + yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, + data.items()) + + Dumper.add_representer(SpyTestDict, _dict_representer) + return yaml.dump(data, stream, Dumper, **kwds) + + def _init_paths(self, filename, paths): + self._paths = [] + self._paths.append(os.path.dirname(filename)) + self._paths.extend(paths) + + def __init__(self, filename, paths=[], content=""): + self._paths = [] + self.errs = [] + self.valid = False + self.obj = None + self.all_files = dict() + self.file_path = None + if filename: + self.file_path = self.init_file(filename, paths) + else: + self.init_content(content) + + def init_content(self, content): + all_files = dict() + try: + self.text0 = content + self.text1 = self._load(self.text0, all_files, yaml.SafeLoader) + self.text1 = self._dump(self.text1) + self.obj = self._load(self.text1, all_files, yaml.SafeLoader) + self.valid = True + return all_files + except Exception as e: + self.errs.append(e) + raise(e) + + def init_file(self, filename, paths=[]): + self._init_paths(filename, paths) + file_path = self._locate(filename) + if not file_path: + self.errs.append("File {} not found".format(filename)) + return None + fh = utils.open_file(file_path) + if not fh: + self.errs.append("Failed to open {}".format(filename)) + return None + try: + text0 = fh.read() + fh.close() + self.all_files = self.init_content(text0) + self.all_files[file_path] = 1 + return file_path + except Exception as e: + self.errs.append(e) + raise(e) + + def get_raw(self, expanded=False): + return self.text1 if expanded else self.text0 + + def get_file_path(self): + return self.file_path + + def get_data(self): + return self.obj + + def is_valid(self): + return self.valid + + def get_files(self): + return self.all_files + + def get_errors(self): + return self.errs + +if __name__ == "__main__": + def_filename = "../testbeds/lvn_regression.yaml" + file_name = sys.argv[1] if len(sys.argv) >= 2 else def_filename + dmap = OrderedYaml(file_name) + utils.print_yaml(dmap.get_data(), "") + diff --git a/spytest/spytest/profile.py b/spytest/spytest/profile.py new file mode 100644 index 00000000000..457dd947094 --- /dev/null +++ b/spytest/spytest/profile.py @@ -0,0 +1,106 @@ + +from spytest.st_time import get_timenow +from spytest.dicts import SpyTestDict +import spytest.logger as logger + +class Profile(object): + + def __init__(self): + self.pnfound = 0 + self.tg_total_wait = 0 + self.tc_total_wait = 0 + self.tg_total_wait = 0 + self.tc_cmd_time = 0 + self.tc_cmds = [] + self.tg_cmd_time = 0 + self.tg_cmds = [] + self.infra_cmd_time = 0 + self.infra_cmds = [] + self.cmds = [] + self.profile_ids = dict() + self.canbe_parallel = [] + + def init(self): + self.__init__() + + def start(self, msg, dut=None, data=None): + msg = msg.replace("\r", "") + msg = msg.replace("\n", "\\n") + count = len(self.profile_ids) + self.profile_ids[count] = [get_timenow(), dut, msg, data] + return count + + def stop(self, pid): + [start_time, dut, msg, data] = self.profile_ids[pid] + delta = get_timenow() - start_time + cmd_time = int(delta.total_seconds() * 1000) + thid = logger.get_thread_name() + if dut: + if pid > 0 and thid == "T0000: ": + [pstart_time, pdut, pmsg, pdata] = self.profile_ids[pid-1] + if pmsg == msg and dut != pdut: + self.canbe_parallel.append([start_time, msg, dut, pdut]) + if "spytest-helper.py" in msg: + self.infra_cmds.append([start_time, thid, dut, msg, cmd_time]) + self.infra_cmd_time = self.infra_cmd_time + cmd_time + self.cmds.append([start_time, thid, "INFRA", dut, msg, cmd_time]) + else: + self.tc_cmds.append([start_time, thid, dut, msg, cmd_time]) + self.tc_cmd_time = self.tc_cmd_time + cmd_time + self.cmds.append([start_time, thid, "CMD", dut, msg, cmd_time]) + else: + self.tg_cmds.append([start_time, thid, dut, msg, cmd_time]) + self.tg_cmd_time = self.tg_cmd_time + cmd_time + self.cmds.append([start_time, thid, "TG", dut, msg, cmd_time]) + return data + + def wait(self, val, is_tg=False): + start_time = get_timenow() + thid = logger.get_thread_name() + if is_tg: + self.tg_total_wait = self.tg_total_wait + val + self.cmds.append([start_time, thid, "TGWAIT", None, "TG sleep", val]) + else: + self.tc_total_wait = self.tc_total_wait + val + self.cmds.append([start_time, thid, "WAIT", None, "static delay", val]) + + def prompt_nfound(self, cmd): + start_time = get_timenow() + thid = logger.get_thread_name() + self.pnfound = self.pnfound + 1 + self.cmds.append([start_time, thid, "PROMPT_NFOUND", None, cmd, ""]) + + def get_stats(self): + stats = SpyTestDict() + stats.tg_total_wait = self.tg_total_wait + stats.tc_total_wait = self.tc_total_wait + stats.tc_cmd_time = self.tc_cmd_time + stats.tc_cmds = self.tc_cmds + stats.tg_cmd_time = self.tg_cmd_time + stats.tg_cmds = self.tg_cmds + stats.infra_cmd_time = self.infra_cmd_time + stats.infra_cmds = self.infra_cmds + stats.cmds = self.cmds + stats.canbe_parallel = self.canbe_parallel + stats.pnfound = self.pnfound + return stats + +obj = Profile() +def init(): + return obj.init() + +def start(msg, dut=None, data=None): + return obj.start(msg, dut, data) + +def stop(pid): + return obj.stop(pid) + +def wait(val, is_tg=False): + return obj.wait(val, is_tg) + +def get_stats(): + return obj.get_stats() + +def prompt_nfound(cmd): + return obj.prompt_nfound(cmd) + diff --git a/spytest/spytest/prompts.py b/spytest/spytest/prompts.py new file mode 100644 index 00000000000..00ae9c03e4d --- /dev/null +++ b/spytest/spytest/prompts.py @@ -0,0 +1,475 @@ +import os +import re +import logging +from spytest.dicts import SpyTestDict +from spytest.ordyaml import OrderedYaml + +prompts_root = os.path.join(os.path.dirname(__file__), '..', "datastore", "prompts") + +class Prompts(object): + """ + todo: Update Documentation + """ + + def __init__(self, model=None, logger=None): + """ + Construction of Prompts object + :param logger: + :type logger: + """ + self.logger = logger or logging.getLogger() + self.oyaml = None + model = "sonic" if not model else re.sub("_(ssh|terminal)$", "", model) + filename = "{}_prompts.yaml".format(model) + filename = os.path.join(os.path.abspath(prompts_root), filename) + + self.oyaml = OrderedYaml(filename,[]) + prompts_file_data = self.oyaml.get_data() or dict() + + self.patterns = prompts_file_data.patterns if "patterns" in prompts_file_data else SpyTestDict() + self.modes = prompts_file_data.modes if "modes" in prompts_file_data else SpyTestDict() + self.required_args = prompts_file_data.required_args if "required_args" in prompts_file_data else SpyTestDict() + self.sudo_include_prompts = prompts_file_data.sudo_include_prompts if "sudo_include_prompts" in prompts_file_data else [] + self.do_exclude_prompts = prompts_file_data.do_exclude_prompts if "do_exclude_prompts" in prompts_file_data else [] + + self.stored_values = SpyTestDict() + + def __del__(self): + pass + + def update_with_hostname(self, hostname): + for pattern in self.patterns: + if re.search(r"{}", self.patterns[pattern]): + #print("Matched Pattern: '{}' : '{}' : '{}'".format(pattern, self.patterns[pattern], self.patterns[pattern].format(hostname))) + self.patterns[pattern] = re.sub(r"{}", hostname, self.patterns[pattern]) + + def get_mode_for_prompt(self, prompt): + prompt2 = prompt.replace("\\", "") + for mode in self.patterns: + lpattern = self.patterns[mode] + if re.search(lpattern, prompt2): + return mode + return "unknown-prompt" + + def get_prompt_for_mode(self, mode): + if mode in self.patterns: + return self.patterns[mode] + return "unknown-mode" + + def check_args_for_req_mode(self, mode, **kwargs): + missing_args_flag = 0 + args_str = "" + if mode in self.required_args: + if mode == "vtysh-router-config": + if "router" not in kwargs.keys(): + missing_args_flag = 1 + args_str = ", ".join(self.required_args[mode]) + elif kwargs["router"] in ["bgp", "eigrp", "isis", "openfabric", "ospf"]: + if "instance" not in kwargs.keys(): + missing_args_flag = 1 + args_str = ", ".join(self.required_args[mode]) + elif mode == "vtysh-router-af-config" and "addr_family" not in kwargs.keys(): + missing_args_flag = 1 + args_str = ", ".join(self.required_args[mode]) + else: + for arg in self.required_args[mode]: + if arg not in kwargs.keys(): + missing_args_flag = 1 + args_str = ", ".join(self.required_args[mode]) + break + + if missing_args_flag: + msg = "{} option(s) must be provided for {}.".format(args_str, mode) + raise ValueError(msg) + return + + def check_move_for_parent_of_frommode(self, prompt, mode, **kwargs): + if mode == "vtysh-intf-config": + return True + + if mode == "vtysh-router-config": + if "router" not in self.stored_values: + self.stored_values["router"] = kwargs["router"] + return False + else: + if self.stored_values["router"] != kwargs["router"]: + self.stored_values["router"] = kwargs["router"] + return True + + if mode == "mgmt-ipv4-acl-config": + if "aclname" not in self.stored_values: + self.stored_values["aclname"] = kwargs["aclname"] + return False + else: + if self.stored_values["aclname"] != kwargs["aclname"]: + self.stored_values["aclname"] = kwargs["aclname"] + return True + + if mode == "mgmt-evpn-view": + if "evpnname" not in self.stored_values: + self.stored_values["evpnname"] = kwargs["evpnname"] + return False + else: + if self.stored_values["evpnname"] != kwargs["evpnname"]: + self.stored_values["evpnname"] = kwargs["evpnname"] + return True + + if mode == "mgmt-bfd-peer-view": + if "peer_ip" not in self.stored_values: + self.stored_values["peer_ip"] = kwargs["peer_ip"] + return False + else: + if self.stored_values["peer_ip"] != kwargs["peer_ip"]: + self.stored_values["peer_ip"] = kwargs["peer_ip"] + return True + + if mode == "mgmt-route-map-view": + if "map_name" not in self.stored_values: + self.stored_values["map_name"] = kwargs["map_name"] + self.stored_values["action"] = kwargs["action"] + self.stored_values["seq_num"] = kwargs["seq_num"] + return False + else: + if self.stored_values["map_name"] != kwargs["map_name"] or \ + self.stored_values["action"] != kwargs["action"] or \ + self.stored_values["seq_num"] != kwargs["seq_num"]: + self.stored_values["map_name"] = kwargs["map_name"] + self.stored_values["action"] = kwargs["action"] + self.stored_values["seq_num"] = kwargs["seq_num"] + return True + + if mode == "mgmt-link-state-track-view": + if "track_name" not in self.stored_values: + self.stored_values["track_name"] = kwargs["track_name"] + return False + else: + if self.stored_values["track_name"] != kwargs["track_name"]: + self.stored_values["track_name"] = kwargs["track_name"] + return True + + if mode == "mgmt-router-bgp-view": + if "bgp_instance" not in self.stored_values: + self.stored_values["bgp_instance"] = kwargs["bgp_instance"] + self.stored_values["bgp_vrf_name"] = kwargs["bgp_vrf_name"] + return False + else: + if self.stored_values["bgp_instance"] != kwargs["bgp_instance"] or \ + self.stored_values["bgp_vrf_name"] != kwargs["bgp_vrf_name"]: + self.stored_values["bgp_instance"] = kwargs["bgp_instance"] + self.stored_values["bgp_vrf_name"] = kwargs["bgp_vrf_name"] + return True + + if mode == "mgmt-router-bgp-af-view": + if "af_type" not in self.stored_values: + self.stored_values["af_type"] = kwargs["af_type"] + self.stored_values["af_family"] = kwargs["af_family"] + return False + else: + if self.stored_values["af_type"] != kwargs["af_type"] or \ + self.stored_values["af_family"] != kwargs["af_family"]: + self.stored_values["af_type"] = kwargs["af_type"] + self.stored_values["af_family"] = kwargs["af_family"] + return True + + if mode == "mgmt-router-bgp-nbr-view": + if "ip_address" not in self.stored_values: + self.stored_values["ip_address"] = kwargs["ip_address"] + return False + else: + if self.stored_values["ip_address"] != kwargs["ip_address"]: + self.stored_values["ip_address"] = kwargs["ip_address"] + return True + + if mode == "mgmt-router-bgp-nbr-af-view": + if "nbr_af_type" not in self.stored_values: + self.stored_values["nbr_af_type"] = kwargs["nbr_af_type"] + self.stored_values["nbr_af_family"] = kwargs["nbr_af_family"] + return False + else: + if self.stored_values["nbr_af_type"] != kwargs["nbr_af_type"] or \ + self.stored_values["nbr_af_family"] != kwargs["nbr_af_family"]: + self.stored_values["nbr_af_type"] = kwargs["nbr_af_type"] + self.stored_values["nbr_af_family"] = kwargs["nbr_af_family"] + return True + + if mode == "mgmt-router-bgp-template-view": + if "group_name" not in self.stored_values: + self.stored_values["group_name"] = kwargs["group_name"] + return False + else: + if self.stored_values["group_name"] != kwargs["group_name"]: + self.stored_values["group_name"] = kwargs["group_name"] + return True + + if mode == "mgmt-router-bgp-template-af-view": + if "tpl_af_type" not in self.stored_values: + self.stored_values["tpl_af_type"] = kwargs["tpl_af_type"] + self.stored_values["tpl_af_family"] = kwargs["tpl_af_family"] + return False + else: + if self.stored_values["tpl_af_type"] != kwargs["tpl_af_type"] or \ + self.stored_values["tpl_af_family"] != kwargs["tpl_af_family"]: + self.stored_values["tpl_af_type"] = kwargs["tpl_af_type"] + self.stored_values["tpl_af_family"] = kwargs["tpl_af_family"] + return True + + if mode == "mgmt-router-bgp-l2vpn-vni-view": + if "vxlan_id" not in self.stored_values: + self.stored_values["vxlan_id"] = kwargs["vxlan_id"] + return False + else: + if self.stored_values["vxlan_id"] != kwargs["vxlan_id"]: + self.stored_values["vxlan_id"] = kwargs["vxlan_id"] + return True + + if mode == "mgmt-intf-config": + prompt2 = prompt.replace("\\", "") + intfNum = "-{})".format(kwargs["interface"]) + if intfNum in prompt2: + return False + else: + return True + + if mode == "mgmt-vlan-config": + prompt2 = prompt.replace("\\", "") + intfNum = "-Vlan{})".format(kwargs["vlan"]) + if intfNum in prompt2: + return False + else: + return True + + if mode == "mgmt-lag-config": + prompt2 = prompt.replace("\\", "") + intfNum = "-po{})".format(kwargs["portchannel"]) + if intfNum in prompt2: + return False + else: + return True + + if mode == "mgmt-management-config": + prompt2 = prompt.replace("\\", "") + intfNum = "-eth{})".format(kwargs["management"]) + if intfNum in prompt2: + return False + else: + return True + + if mode == "mgmt-vxlan-view": + prompt2 = prompt.replace("\\", "") + intfNum = "-Vxlan-{})".format(kwargs["vxlan"]) + if intfNum in prompt2: + return False + else: + return True + + if mode == "mgmt-mirror-session-config": + prompt2 = prompt.replace("\\", "") + intfNum = "-mirror-{})".format(kwargs["session_name"]) + if intfNum in prompt2: + return False + else: + return True + + if mode == "mgmt-mclag-view": + prompt2 = prompt.replace("\\", "") + intfNum = "mclag-domain-{})".format(kwargs["domain_id"]) + if intfNum in prompt2: + return False + else: + return True + + if mode == "mgmt-lo-view": + prompt2 = prompt.replace("\\", "") + intfNum = "-lo{})".format(kwargs["loopback_id"]) + if intfNum in prompt2: + return False + else: + return True + + return False + + def check_move_for_parent_of_tomode(self, prompt, mode, **kwargs): + check_for_parents = False + if mode == "vtysh-router-config": + if "router" not in self.stored_values: + self.stored_values["router"] = kwargs["router"] + return False + else: + if self.stored_values["router"] != kwargs["router"]: + self.stored_values["router"] = kwargs["router"] + check_for_parents = True + + if mode == "vtysh-router-af-config": + if "router" in kwargs: + if "router" not in self.stored_values: + self.stored_values["router"] = kwargs["router"] + return False + else: + if self.stored_values["router"] != kwargs["router"]: + self.stored_values["router"] = kwargs["router"] + check_for_parents = True + + if mode == "mgmt-ipv4-acl-config": + if "aclname" not in self.stored_values: + self.stored_values["aclname"] = kwargs["aclname"] + return False + else: + if self.stored_values["aclname"] != kwargs["aclname"]: + self.stored_values["aclname"] = kwargs["aclname"] + + if mode == "mgmt-evpn-view": + if "evpnname" not in self.stored_values: + self.stored_values["evpnname"] = kwargs["evpnname"] + return False + else: + if self.stored_values["evpnname"] != kwargs["evpnname"]: + self.stored_values["evpnname"] = kwargs["evpnname"] + return True + + if mode == "mgmt-bfd-peer-view": + if "peer_ip" not in self.stored_values: + self.stored_values["peer_ip"] = kwargs["peer_ip"] + return False + else: + if self.stored_values["peer_ip"] != kwargs["peer_ip"]: + self.stored_values["peer_ip"] = kwargs["peer_ip"] + return True + + if mode == "mgmt-route-map-view": + if "map_name" not in self.stored_values: + self.stored_values["map_name"] = kwargs["map_name"] + self.stored_values["action"] = kwargs["action"] + self.stored_values["seq_num"] = kwargs["seq_num"] + return False + else: + if self.stored_values["map_name"] != kwargs["map_name"] or \ + self.stored_values["action"] != kwargs["action"] or \ + self.stored_values["seq_num"] != kwargs["seq_num"]: + self.stored_values["map_name"] = kwargs["map_name"] + self.stored_values["action"] = kwargs["action"] + self.stored_values["seq_num"] = kwargs["seq_num"] + return True + + if mode == "mgmt-link-state-track-view": + if "track_name" not in self.stored_values: + self.stored_values["track_name"] = kwargs["track_name"] + return False + else: + if self.stored_values["track_name"] != kwargs["track_name"]: + self.stored_values["track_name"] = kwargs["track_name"] + return True + + if mode == "mgmt-router-bgp-view": + if "bgp_instance" not in self.stored_values: + self.stored_values["bgp_instance"] = kwargs["bgp_instance"] + self.stored_values["bgp_vrf_name"] = kwargs["bgp_vrf_name"] + return False + else: + if self.stored_values["bgp_instance"] != kwargs["bgp_instance"] or \ + self.stored_values["bgp_vrf_name"] != kwargs["bgp_vrf_name"]: + self.stored_values["bgp_instance"] = kwargs["bgp_instance"] + self.stored_values["bgp_vrf_name"] = kwargs["bgp_vrf_name"] + return True + + if mode == "mgmt-router-bgp-af-view": + if "af_type" not in self.stored_values: + self.stored_values["af_type"] = kwargs["af_type"] + self.stored_values["af_family"] = kwargs["af_family"] + return False + else: + if self.stored_values["af_type"] != kwargs["af_type"] or \ + self.stored_values["af_family"] != kwargs["af_family"]: + self.stored_values["af_type"] = kwargs["af_type"] + self.stored_values["af_family"] = kwargs["af_family"] + return True + + if mode == "mgmt-router-bgp-nbr-view": + if "ip_address" not in self.stored_values: + self.stored_values["ip_address"] = kwargs["ip_address"] + return False + else: + if self.stored_values["ip_address"] != kwargs["ip_address"]: + self.stored_values["ip_address"] = kwargs["ip_address"] + return True + + if mode == "mgmt-router-bgp-nbr-af-view": + if "nbr_af_type" not in self.stored_values: + self.stored_values["nbr_af_type"] = kwargs["nbr_af_type"] + self.stored_values["nbr_af_family"] = kwargs["nbr_af_family"] + return False + else: + if self.stored_values["nbr_af_type"] != kwargs["nbr_af_type"] or \ + self.stored_values["nbr_af_family"] != kwargs["nbr_af_family"]: + self.stored_values["nbr_af_type"] = kwargs["nbr_af_type"] + self.stored_values["nbr_af_family"] = kwargs["nbr_af_family"] + return True + + if mode == "mgmt-router-bgp-template-view": + if "group_name" not in self.stored_values: + self.stored_values["group_name"] = kwargs["group_name"] + return False + else: + if self.stored_values["group_name"] != kwargs["group_name"]: + self.stored_values["group_name"] = kwargs["group_name"] + return True + + if mode == "mgmt-router-bgp-template-af-view": + if "tpl_af_type" not in self.stored_values: + self.stored_values["tpl_af_type"] = kwargs["tpl_af_type"] + self.stored_values["tpl_af_family"] = kwargs["tpl_af_family"] + return False + else: + if self.stored_values["tpl_af_type"] != kwargs["tpl_af_type"] or \ + self.stored_values["tpl_af_family"] != kwargs["tpl_af_family"]: + self.stored_values["tpl_af_type"] = kwargs["tpl_af_type"] + self.stored_values["tpl_af_family"] = kwargs["tpl_af_family"] + return True + + if mode == "mgmt-router-bgp-l2vpn-vni-view": + if "vxlan_id" not in self.stored_values: + self.stored_values["vxlan_id"] = kwargs["vxlan_id"] + return False + else: + if self.stored_values["vxlan_id"] != kwargs["vxlan_id"]: + self.stored_values["vxlan_id"] = kwargs["vxlan_id"] + return True + + if check_for_parents: + parent_modes_list = [] + curr_mode = self.get_mode_for_prompt(prompt) + while True: + parent_modes_list.append(self.modes[curr_mode][0]) + curr_mode = self.modes[curr_mode][0] + if curr_mode == "": + break + if mode in parent_modes_list: + return True + + return False + + def get_backward_command_and_prompt(self, mode): + if mode not in self.modes: + return ["", ""] + cmd = self.modes[mode][2] + expected_prompt = self.get_prompt_for_mode(self.modes[mode][0]) + return [cmd, expected_prompt] + + def get_forward_command_and_prompt_with_values(self, mode, **kwargs): + if mode not in self.modes: + return ["", ""] + cmd = self.modes[mode][1] + expected_prompt = self.get_prompt_for_mode(mode) + if mode in self.required_args: + values = [] + for arg in self.required_args[mode]: + if arg in kwargs.keys(): + if mode == "mgmt-intf-config" and arg == "interface": + intf_value = re.sub("Ethernet", "Ethernet ", kwargs[arg]) + values.append(intf_value) + else: + values.append(kwargs[arg]) + else: + values.append("") + cmd = cmd.format(*values) + return [cmd, expected_prompt] + diff --git a/spytest/spytest/pytest.ini b/spytest/spytest/pytest.ini new file mode 100644 index 00000000000..bc4362c0b7b --- /dev/null +++ b/spytest/spytest/pytest.ini @@ -0,0 +1,12 @@ +[pytest] +addopts = --color=no -v -p no:logging -s --capture=no +log_format = %(asctime)s %(levelname)s %(message)s +log_date_format = %Y-%m-%d %H:%M:%S +log_cli=true +log_cli_level=DEBUG +log_file=a.log +log_file_level = DEBUG +color=no +filterwarnings = + error + ignore::UserWarning diff --git a/spytest/spytest/remote/click-helper.py b/spytest/spytest/remote/click-helper.py new file mode 100644 index 00000000000..e33d5e65b14 --- /dev/null +++ b/spytest/spytest/remote/click-helper.py @@ -0,0 +1,74 @@ +import sys +import argparse +from config.main import config +from show.main import cli as show + +parser = argparse.ArgumentParser() +parser.add_argument("--format", help="output format", default="txt") +c_args = parser.parse_args() + +class ListCommands: + sources = [config, show] + config_commands = {} + show_commands = {} + def __init__(self): + pass + + @staticmethod + def walk_sources(): + for source in ListCommands.sources: + if source.name == 'cli': + source.name = "show" + ListCommands.walk_source(source) + + @staticmethod + def get_param_string(params): + param_string = "" + prefix = '< ' + suffix = ' >' + argName = 'argName: ' + for param in params: + paramName=param.name + if param.__class__.__name__ == "Option": + prefix = '[ ' + suffix = ' ]' + argName = 'opts: ' + paramName = str(param.opts) + param_string += ' ' + prefix + argName + paramName + ', type: ' + str(param.type) + ', required: ' + str(param.required) + suffix + return param_string + + @staticmethod + def walk_source(source, cmd_string=""): + param_string = ListCommands.get_param_string(source.params) + cmd_string = cmd_string + ' ' + source.name + ' ' + param_string + if hasattr(source, 'commands'): + for command in source.commands: + ListCommands.walk_source(source.commands[command], cmd_string) + else: + cmd_string = " ".join(list(filter(None, cmd_string.split(' ')))) + cmd_entry = {'help': ' ' if source.help is None else source.help, 'cmd': cmd_string} + if cmd_string.startswith('config'): + ListCommands.config_commands[cmd_string] = cmd_entry + elif cmd_string.startswith('show'): + ListCommands.show_commands[cmd_string] = cmd_entry + else: + pass + + +if __name__ == '__main__': + ListCommands.walk_sources() + cmd_sources = [ListCommands.config_commands, ListCommands.show_commands] + + if c_args.format == "txt": + for source in cmd_sources: + for cmd in sorted(source): + command = source[cmd] + print(command["cmd"]) + print("\tDescription: " + command["help"]) + elif c_args.format == "xml": + print("Not implemented yet") + else: + print("Not supported format") + sys.exit(2) + + diff --git a/spytest/spytest/remote/port_breakout.py b/spytest/spytest/remote/port_breakout.py new file mode 100644 index 00000000000..9cd411e4750 --- /dev/null +++ b/spytest/spytest/remote/port_breakout.py @@ -0,0 +1,877 @@ +#! /usr/bin/python -u + +import os +import sys +import re +import getopt +import copy +import json +import shutil +import os.path +import subprocess + +SIM_HOST = False + +SAI_PROFILE_DELIMITER = '=' +INTERFACE_KEY="Ethernet" +if not SIM_HOST: + NEW_FILE_EXT="" +else: + NEW_FILE_EXT="" + +sonic_platforms = { + "x86_64-accton_as9716_32d-r0": { + "breakout": { + "0,4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64,68,72,76,80,84,88,92,96,100,104,108,112,116,120,124": [ "1x400", "4x100", "4x50", "2x100", "2x200", "4x25", "4x10" ] + } + }, + "x86_64-accton_as7326_56x-r0": { + "breakout": { + "48,52,56,60,64,68,72": [ "4x10", "4x25", "1x100", "1x40" ] + } + }, + "x86_64-accton_as7712_32x-r0": { + "breakout": { + "0,4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64,68,72,76,80,84,88,92,96,100,104,108,112,116,120,124": [ "4x10", "4x25", "1x100", "1x40" ] + } + }, + "x86_64-accton_as7816_64x-r0": { + "breakout": { + "0,8,16,24,32,40,48,56,64,72,80,88,96,104,112,120": [ "4x10", "4x25", "1x100", "1x40" ] + } + }, + "x86_64-accton_as7726_32x-r0": { + "breakout": { + "0,4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64,68,72,76,80,84,88,92,96,100,104,108,112,116,120": [ "4x25", "4x10", "1x100", "1x40" ] + } + }, + "x86_64-delta_ag9032v1-r0": { + "breakout": { + "0,4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64,68,72,76,80,84,88,92,96,100,104,108,112,116,120,124": [ "4x10", "4x25", "1x100", "1x40" ] + } + }, + "x86_64-quanta_ix4_bwde-r0": { + "breakout": { + "128,132,136,140,144,148,152,156,192,196,200,204,208,212,216,220": [ + "4x10", "4x25", "1x100", "1x40" ] + } + }, + "x86_64-quanta_ix8_rglbmc-r0": { + "breakout": { + "48,52,56,60,64,68,72": [ "4x10", "4x25", "1x100", "1x40" ] + } + }, + "x86_64-quanta_ix9_bwde-r0": { + "breakout": { + "0,8,16,24,32,40,48,56,64,72,80,88,96,104,112,120,128,136,144,152,160,168,176,184,192,200,208,216,224,232,240,248": [ "1x400", "4x100", "4x25", "4x10", "4x50", "2x200", "2x100" ] + } + } +} + +##################################################################################################### +### Platform related code + +if not SIM_HOST: + def get_platform(): + cmd = "cat /host/machine.conf | grep onie_platform | cut -d '=' -f 2" + pin = subprocess.Popen(cmd, + shell=True, + close_fds=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + id = pin.communicate()[0] + id = id.strip() + return id + + def get_platform_path(): + path = "/usr/share/sonic/platform" + if os.path.exists(path): + return path + path = "/usr/share/sonic/device/" + get_platform() + return path + + def get_hwsku(): + dir = get_platform_path() + pin = subprocess.Popen("cat " + dir + "/default_sku | cut -d ' ' -f 1", + shell=True, + close_fds=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + id = pin.communicate()[0] + id = id.strip() + return id + + + # run command + def run_command(command, display_cmd=False, ignore_error=False, print_to_console=True): + ### + ### Run bash command and print output to stdout + ### + if display_cmd == True: + print("Running command: " + command) + + proc = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE) + (out, err) = proc.communicate() + + if len(out) > 0 and print_to_console: + print(out) + + if proc.returncode != 0 and not ignore_error: + sys.exit(proc.returncode) + + return out, err + + def get_bcm_file(platform, hwsku): + sai_profile_kvs = {} + + sai_file = get_platform_path() + "/" + hwsku + "/" + "sai.profile" + if os.path.exists(sai_file): + command = "grep SAI_INIT_CONFIG_FILE "+ sai_file + else: + command = "sonic-cfggen -d -t " + sai_file + ".j2 | grep SAI_INIT_CONFIG_FILE" + sai_profile_content, _ = run_command(command, print_to_console=False) + + for line in sai_profile_content.split('\n'): + if not SAI_PROFILE_DELIMITER in line: + continue + key, value = line.split(SAI_PROFILE_DELIMITER) + sai_profile_kvs[key] = value.strip() + + try: + sai_xml_path = sai_profile_kvs['SAI_INIT_CONFIG_FILE'] + except KeyError: + print >> sys.stderr, "Failed to get SAI XML from sai profile" + sys.exit(1) + + bcm_file = "config.bcm" + if not SIM_HOST: + bcm_file = get_platform_path() + "/" + hwsku + "/" + os.path.basename(sai_xml_path) + + return bcm_file + +##################################################################################################### + + +def get_ini_file(platform, hwsku): + if not SIM_HOST: + ini_file = get_platform_path() + '/' + hwsku + '/' + "port_config.ini" + else: + ini_file = "port_config.ini" + return ini_file + +def get_cfg_file(platform, hwsku): + if not SIM_HOST: + cfg_file = "/etc/sonic/config_db.json" + else: + cfg_file = "config_db.json" + return cfg_file + +def get_led_file(platform, hwsku): + if not SIM_HOST: + led_file = get_platform_path() + "/led_proc_init.soc" + else: + led_file = "led_proc_init.soc" + return led_file + +def display_files(platform, hwsku): + print("BCM File:[%s]" % (get_bcm_file(platform, hwsku))) + print("INI File:[%s]" % (get_ini_file(platform, hwsku))) + print("CFG File:[%s]" % (get_cfg_file(platform, hwsku))) + +bko_dict_4 = { + "1x100": { "lanes":4, "speed":100, "step":4, "bko":0, "name": "hundredGigE" }, + "1x40": { "lanes":4, "speed":40, "step":4, "bko":0, "name": "fourtyGigE" }, + "4x10": { "lanes":4, "speed":10, "step":1, "bko":1, "name": "tenGigE" }, + "4x25": { "lanes":4, "speed":25, "step":1, "bko":1, "name": "twentyfiveGigE" }, + "2x10": { "lanes":2, "speed":10, "step":1, "bko":1, "name": "tenGigE" }, + "2x25": { "lanes":2, "speed":25, "step":1, "bko":1, "name": "twentyfiveGigE" }, +} + +bko_dict_8 = { + "1x400": { "lanes":8, "speed":400, "step":8, "bko":0, "name": "fourhundredGigE" }, + "2x200": { "lanes":8, "speed":200, "step":4, "bko":1, "name": "twohundredGigE" }, + "2x100": { "lanes":8, "speed":100, "step":4, "bko":1, "name": "hundredGigE" }, + "4x100": { "lanes":8, "speed":100, "step":2, "bko":1, "name": "hundredGigE" }, + "4x50": { "lanes":8, "speed":50, "step":2, "bko":1, "name": "fiftyGigE" }, + "4x25": { "lanes":4, "speed":25, "step":1, "bko":1, "name": "twentyfiveGigE" }, + "4x10": { "lanes":4, "speed":10, "step":1, "bko":1, "name": "tenGigE" }, +} + +bko_dict = bko_dict_4 + +# +# Get breakout step: +# +def bko_opt_valid(opt): + if opt in bko_dict: + return True + else: + return False + +def get_bkout_step(opt): + return bko_dict[opt]["step"] + +def get_bkout_subport_name(opt): + return bko_dict[opt]["name"] + +def get_bkout_subport_speed(opt): + return bko_dict[opt]["speed"] + +def get_is_bkout(opt): + return bko_dict[opt]["bko"] + +def get_bkout_lanes(opt): + return bko_dict[opt]["lanes"] + +def get_bkout_ports(port, opt): + lanes = 4 + step = 1 + + if not port.startswith(INTERFACE_KEY): + return None + + idx = port.split()[0].split(INTERFACE_KEY,1)[1] + if not idx.isdigit(): + return None + + ports = [] + for i in range(0, lanes, step): + portx = INTERFACE_KEY + str(int(idx) + (i/step)) + ports.append(portx) + return ports + + +# +# Breakout a port in INI file: +# Ethernet48 81,82,83,84 hundredGigE13 + +# Change to +# Ethernet48 81 twentyfiveGigE13:1 +# Ethernet49 82 twentyfiveGigE13:2 +# Ethernet50 83 twentyfiveGigE13:3 +# Ethernet51 84 twentyfiveGigE13:4 + +# +# Ethernet48 81,82,83,84 hundredGigE13 +# return: +# "48" +# ["81", "82", "83", "84"] +# "hundredGigE" +# "13" + + +def get_info_in_ini(line, title): + idx = line.split()[0].split(INTERFACE_KEY,1)[1] + lanes = line.split()[1].split(",") + name = line.split()[2] + temp = name.split(":")[0] + porti = re.sub('.*?([0-9]*)$',r'\1', temp) + + if "index" in title: + fp_idx = int(line.split()[title.index("index")]) + else: + fp_idx = None + return idx, lanes, name, porti, fp_idx + +def break_in_ini(port, ini_file, opt): + print("Breaking port %s to %s in ini ..." % (port, opt)) + + bak_file = ini_file + ".bak" + shutil.copy(ini_file, bak_file) + + new_file = ini_file + NEW_FILE_EXT + step = get_bkout_step(opt) + + f_in = open(bak_file, 'r') + f_out = open(new_file, 'w') + + first_port = True + title = [] + + done = False + for line in f_in.readlines(): + line.strip() + if len(line.rstrip()) == 0: + continue + + if re.search("^#", line) is not None: + # The current format is: # name lanes alias index speed + # Where the ordering of the columns can vary + if len(title) == 0: + title = line.split()[1:] + print title + f_out.write(line) + continue + + line = line.lstrip() + line_port = line.split()[0] + + if line_port in get_bkout_ports(port, opt): + if done: + f_out.write(line) + continue + done = True + oidx, olanes, name, oporti, fp_idx = get_info_in_ini(line, title) + + if get_is_bkout(opt) and len(olanes) < get_bkout_lanes(opt): + print("Port %s Already breakout ..." % (port)) + print("Existing ...") + f_in.close() + f_out.close() + shutil.copy(bak_file, new_file) + + sys.exit() + + # + # Non-Breakout case + # + if not get_is_bkout(opt) and not first_port: + print("--- {} removed".format(line_port)) + continue + + if not get_is_bkout(opt) and first_port: + idx = oidx + lanes = [] + for i in range(0, get_bkout_lanes(opt), 1): + lanes.append(str(int(olanes[0])+i)) + porti = oporti + + if get_is_bkout(opt): + idx = oidx + lanes = olanes + porti = oporti + + # + # original string: + # Ethernet20 69,70,71,72 hundredGigE6 + # + print(" %s" % line.rstrip()) + + # Generate new interface line + for i in range(0, min(len(lanes), get_bkout_lanes(opt)), step): + # + # Ethernet20 + # + temp_str = "Ethernet%d" % (int(idx) + (i/step)) + new_intf = "%-15s " % temp_str + + temp_str = lanes[i+0] + # + # generate 69 + # + for j in range(1, step): + temp_str += ",%s" % (lanes[i+j]) + new_intf += "%-21s " % temp_str + + # + # Generate twentyfiveGigE6:i + # + if get_is_bkout(opt): + temp_str = "%s%s:%d" % (get_bkout_subport_name(opt), porti, (i/step + 1)) + else: + temp_str = "%s%s" % (get_bkout_subport_name(opt), porti) + + new_intf += "%-19s " % temp_str + + # + # index + # + if fp_idx is not None: + temp_str = "%d" % (fp_idx) + new_intf += "%-6s " % temp_str + + + # + # speed + # + temp_str = "%d000" % get_bkout_subport_speed(opt) + new_intf += "%-10s " % temp_str + + # + # valid_speeds + # + if 'valid_speeds' in title: + temp_str = str(get_bkout_subport_speed(opt) * 1000) + if get_bkout_subport_speed(opt) == 100: + if get_bkout_step(opt) == 4: + # NRZ mode + temp_str = "100000,40000" + else: + # PAM4 mode + temp_str = "100000" + elif get_bkout_subport_speed(opt) == 40: + temp_str = "100000,40000" + + new_intf += "%s" % temp_str + + + if not get_is_bkout(opt) and first_port: + print "===>" + new_intf + new_intf += "\n" + f_out.write(new_intf) + first_port = False + if get_is_bkout(opt): + print "===>" + new_intf + new_intf += "\n" + f_out.write(new_intf) + + else: + f_out.write(line) + + print "--------------------------------------------------------" + f_in.close() + f_out.close() + + print lanes + return lanes + +# +# Parse logic port, phyical port, speed from bcm +# +def parse_port_bcm(bcm_str): + lp = bcm_str.split("=")[0].split("_")[1] + pp = bcm_str.split("=")[1].split(":")[0] + sp = bcm_str.split("=")[1].split(":")[1] + + return lp, pp, sp + +# +# portmap_84=81:100 +# +# portmap_84=81:25 +# portmap_85=82:25 +# portmap_86=83:25 +# portmap_87=84:25 +# +# +def break_in_bcm(port, lanes, bcm_file, opt, platform): + print("Breaking %s to %s in bcm ..." % (port, opt)) + + bak_file = bcm_file + ".bak" + shutil.copy(bcm_file, bak_file) + + new_file = bcm_file + NEW_FILE_EXT + step = get_bkout_step(opt) + + f_in = open(bak_file, 'r') + f_out = open(new_file, 'w') + + first_port = True + fec_removed = False + print lanes + for oline in f_in.readlines(): + line = oline.lstrip() + + if line.startswith('#'): + f_out.write(oline) + continue + + ### when running in unbreakout mode, the FEC setting per breakout should be removed + if not get_is_bkout(opt) and line.startswith("port_fec") and fec_removed: + continue + if not line.startswith("portmap"): + f_out.write(oline) + continue + + ### logic port, phyical port, speed + lp, pp, sp = parse_port_bcm(line) + if pp not in lanes: + f_out.write(oline) + fec_removed = False + continue + + if not get_is_bkout(opt): + fec_removed = True + + if not get_is_bkout(opt) and not first_port: + print("--- portmap_{} removed".format(lp)) + continue + + #### generate new port map + print(" %s" % line.rstrip()) + for i in range(0, min(len(lanes), get_bkout_lanes(opt)), step): + if '.' in lp: + nlp = lp.split('.')[0] + unit = lp.split('.')[1] + new_intf = "portmap_%d.%s=%d:%d:%d" % ((int(nlp) + (i / step)), unit, (int(pp)+i), get_bkout_subport_speed(opt), get_bkout_step(opt)) + else: + new_intf = "portmap_%d=%d:%d:%d" % ((int(lp) + (i / step)), (int(pp)+i), get_bkout_subport_speed(opt), get_bkout_step(opt)) + fec_intf = "port_fec_"+ str(int(lp) + (i/step)) +"=3" + + if not get_is_bkout(opt) and first_port: + f_out.write(new_intf) + f_out.write("\n") + print "===>" + new_intf + first_port = False + if get_is_bkout(opt): + f_out.write(new_intf) + f_out.write("\n") + ### generate default FEC only for IX9 platform + if opt == "4x100" and platform == "x86_64-quanta_ix9_bwde-r0": + f_out.write(fec_intf) + f_out.write("\n") + print "===>" + new_intf + + print "--------------------------------------------------------" + f_in.close() + f_out.close() + +# +# breakout ports in json file +# +def break_in_cfg(port, cfg_file, lanes, opt): + if not os.access(os.path.dirname(cfg_file), os.W_OK): + print("Skipping config_db.json updates for a write permission issue") + return + + step = get_bkout_step(opt) + print("Breaking %s to %s in cfg ... " % (port, opt)) + + bak_file = cfg_file + ".bak" + shutil.copy(cfg_file, bak_file) + + new_file = cfg_file + NEW_FILE_EXT + + with open(bak_file) as f: + data = json.load(f) + + with open(cfg_file, 'w') as outfile: + json.dump(data, outfile, indent=4, sort_keys=True) + + ### + ### Process in 'INTERFACE' + ### + if 'INTERFACE' in data: + for key, value in sorted(data['INTERFACE'].iteritems()): + pkey = key.split('|')[0] + if port == pkey: + data['INTERFACE'].pop(key) + + ### + ### Process in 'PORT' + + + ### remove port instance in data + ### + idx = 0 + ports = get_bkout_ports(port, opt) + for x in ports: + if idx >= get_bkout_lanes(opt): + break + idx += 1 + if data['PORT'].get(x) != None: + port_instance = data['PORT'].get(x) + data['PORT'].pop(x) + print " ", x, port_instance + + idx = port.split()[0].split(INTERFACE_KEY,1)[1] + porti = re.sub('.*?([0-9]*)$',r'\1', port_instance['alias'].split(":")[0]) + + for i in range(0, min(len(lanes), get_bkout_lanes(opt)), step): + + if get_is_bkout(opt): + temp_str = lanes[i] + for j in range(1, step): + temp_str += ",%s" % (lanes[i+j]) + port_instance['lanes'] = temp_str + port_instance['alias'] = get_bkout_subport_name(opt) + porti + ':' + str(i/step + 1) + else: + port_instance['alias'] = get_bkout_subport_name(opt) + porti + port_instance['lanes'] = ','.join(str(e) for e in lanes) + + port_instance['speed'] = str(get_bkout_subport_speed(opt)) + "000" + port_instance['valid_speeds'] = str(get_bkout_subport_speed(opt)) + "000" + + new_port = INTERFACE_KEY + str(int(idx) + (i/step)) + xxx = copy.deepcopy(port_instance) + data['PORT'][new_port] = xxx + ### print data['PORT'][new_port] + + for i in range(0, min(len(lanes), get_bkout_lanes(opt)), step): + new_port = INTERFACE_KEY + str(int(idx) + (i/step)) + print "===>", new_port, data['PORT'][new_port] + + with open(new_file, 'w') as outfile: + json.dump(data, outfile, indent=4, sort_keys=True) + + print "--------------------------------------------------------" + +def break_a_port(port, opt, platform, hwsku): + ini_file = get_ini_file(platform, hwsku) + bcm_file = get_bcm_file(platform, hwsku) + cfg_file = get_cfg_file(platform, hwsku) + + lanes = break_in_ini(port, ini_file, opt) + break_in_bcm(port, lanes, bcm_file, opt, platform) + break_in_cfg(port, cfg_file, lanes, opt) + +def usage(): + print "Usage: " + sys.argv[0] + " interface 4x100|4x25" + print "Breakout None-breaokout a port" + print "Options:" + print " -p port" + print " -o breakout option" + for k in bko_dict: + print " %s" % k + print " " + print "Example:" + print " Breakout port Ethernet4 to 4x10G" + print " %s -p Ethernet4 -o 4x10" % (sys.argv[0]) + print " None-Breakout port Ethernet4 to 40G" + print " %s -p Ethernet4 -o 1x40" % (sys.argv[0]) + print " " + print "Note:" + print " Make sure understand which ports are able to breakout before execute command." + print " Make backup below config files" + print " - /usr/share/sonic/device/[platform]/[hwsku]/[config.bcm]" + print " - /usr/share/sonic/device/[platform]/[hwsku]/port_config.ini" + print " - /etc/sonic/config_db.json" + + sys.exit(1) + +def platform_checking(platform, hwsku, port, opt): + # + # default allow breakout ports on any platforms and ports + # + rc = True + + if not port.startswith(INTERFACE_KEY): + print "Wrong port name %s ..." % (port) + return False + + + if platform in sonic_platforms and 'breakout' in sonic_platforms[platform]: + idx = port.split()[0].split(INTERFACE_KEY,1)[1] + for keys in sonic_platforms[platform]['breakout']: + if idx in keys.split(',') and opt in sonic_platforms[platform]['breakout'][keys]: + print "Breakout port %s to %s in platform %s is allowed." % (port, opt, platform) + return True + else: + print "Error: Breakout port %s to %s in platform %s is NOT allowed !!!" % (port, opt, platform) + rc = False + + + # + # Platforms not in sonic_platforms, or not defined 'breakout' + # + if rc is True: + print "Warnning:" + print "Breakout port on platform %s is dangerous !!!" % (platform) + print "Please double-check make sure port %s can be configured to %s" % (port, opt) + + return rc + +# +# check breakout option valid +# configure files existing +# +def check_vaildation(platform, hwsku, port, opt): + + ini_file = get_ini_file(platform, hwsku) + + ports = get_bkout_ports(port, opt) + if ports == None: + print("Wrong interface name:%s" % (port)) + return False + + ### need re-visit + idx = port.split()[0].split(INTERFACE_KEY,1)[1] + + if int(idx) % (get_bkout_lanes(opt) / get_bkout_step(opt)) != 0: + print("Can not work on port:%s" % (port)) + return False + + f_in = open(ini_file, 'r') + + ini_ports = [] + ini_lanes = [] + port_found = 0 + title = [] + + for line in f_in.readlines(): + line = line.lstrip() + line = line.strip() + if len(line) == 0: + continue + + if re.search("^#", line) is not None: + # The current format is: # name lanes alias index speed + # Where the ordering of the columns can vary + title = line.split()[1:] + continue + + + line_port = line.split()[0] + + ### Check breakout case + if get_is_bkout(opt): + if line_port == port: + port_found += 1 + oidx, olanes, name, oporti, fp_idx = get_info_in_ini(line, title) + if len(olanes) < get_bkout_lanes(opt): + print("port %s can not breakout to %s." % (port, opt)) + f_in.close() + return False + else: + if line_port in ports: + port_found += 1 + oidx, olanes, name, oporti, fp_idx = get_info_in_ini(line, title) + ini_ports.append(line_port) + ini_lanes += olanes + + f_in.close() + + if get_is_bkout(opt) and port_found != 1: + if port_found == 0: + print("port %s does not exist." % (port)) + if port_found > 1: + print("Duplicate(%d) port %s found in INI file." % (port_found, port)) + return False + + if not get_is_bkout(opt): + if len(ini_lanes) == 0: + print("port %s does not exist." % (port)) + return False + + return True + + +def process_args(argv): + verbose = 0 + cust = "./cust_platform.json" + list = False + port = None + opt = None + + try: + opts, args = getopt.getopt(argv, "hlvc:p:o:", \ + ["help", "list", "verbose", "cust=", "port=", "opt="]) + + for opt,arg in opts: + if opt in ('-h','--help'): + usage() + return + if opt in ('-l', '--list'): + list = True + if opt in ('-v', '--verbose'): + verbose = 1 + if opt in ('-c', '--cust'): + cust = arg + if opt in ('-p', '--port'): + port = arg + if opt in ('-o', '--option'): + opt = arg + except getopt.GetoptError: + print("Error: Invalid option") + sys.exit(1) + + #print("# Custom Platform JSON: {}".format(cust)) + if os.path.isfile(cust): + print("# Custom Platform JSON detected, merging the platform info...") + try: + with open(cust) as fp: + sonic_platforms.update(json.load(fp)) + except: + pass + else: + print("# Custom Platform JSON not found") + + if list == True: + print("Supported platform list:") + for plat in sonic_platforms: + print("* {}".format(plat)) + sys.exit(0) + + if port == None or opt == None: + print "Error: must give -p [port] and -o [option]" + + usage() + sys.exit(1) + + return verbose, port, opt + +### Breakout interface +def main(argv): + global bko_dict + + if len(argv) > 0 and argv[0] == "-h": + usage() + return + + verbose, port, opt = process_args(argv) + """ + print verbose, port, opt + """ + + if not SIM_HOST: + platform = get_platform() + hwsku = get_hwsku() + else: + platform = 'xxx' + hwsku = 'yyy' + + bcm_file = get_bcm_file(platform, hwsku) + if "th3" in bcm_file: + bko_dict = bko_dict_8 + else: + bko_dict = bko_dict_4 + + if not bko_opt_valid(opt): + print("Invalid breakout option :%s" % (opt)) + print("Supported breakout option :%s" % (bko_dict.keys())) + return + + """ + print("Platform=[%s]" % (platform)) + print("hwsku=[%s]" % (hwsku)) + display_files(platform, hwsku) + """ + + if platform_checking(platform, hwsku, port, opt) is False: + return + + ####################### SPYTEST ######################## + spytest = bool(__file__ == "/etc/spytest/remote-port_breakout.py") + (already, toggle) = (False, spytest) + if already: + f_in = open(get_ini_file(platform, hwsku), 'r') + bkout_name = get_bkout_subport_name(opt) + for line in f_in.readlines(): + [line_port,line_lanes,line_name] = (line+" . . . ").split(None, 2) + if line_port == port and line_name.startswith(bkout_name): + print("Port %s already breakout %s ..." % (port, opt)) + f_in.close() + return + f_in.close() + if toggle: + (done, is_bko) = (False, get_is_bkout(opt)) + for key, value in bko_dict.items(): + if done or is_bko == get_is_bkout(key): + continue # check with next entry + elif platform_checking(platform, hwsku, port, key) == False: + msg = "Toggle: breakout option ({}) invalid for this platform." + print(msg.format(key)) + elif check_vaildation(platform, hwsku, port, key) == False: + msg = "Toggle: breakout option ({}) checking failed." + print(msg.format(key)) + else: + break_a_port(port, key, platform, hwsku) + done = True + if not done: + return + ####################### SPYTEST ######################## + + if check_vaildation(platform, hwsku, port, opt) == False: + print("breakout options checking failed.") + return + + break_a_port(port, opt, platform, hwsku) + + ### disable pre-emphasis workaround in 'led_proc_init.soc' + #file = get_led_file(platform, hwsku) + #if os.path.exists(file): + # run_command("sed -i 's/^rcload/#rcload/g' " + file) + +if __name__ == "__main__": + main(sys.argv[1:]) + diff --git a/spytest/spytest/remote/spytest-helper.py b/spytest/spytest/remote/spytest-helper.py new file mode 100644 index 00000000000..1027df441c2 --- /dev/null +++ b/spytest/spytest/remote/spytest-helper.py @@ -0,0 +1,1051 @@ +#!/usr/bin/python + +""" +This file is used to apply the configs on DUT. +This fiel will be uploaded to DUT and executed there. +Please be sure before changing the file. +""" + +import os +import re +import glob +import json +import socket +import filecmp +import argparse +import subprocess + +g_use_config_replace = False +g_community_build = False +g_breakout_native = False +g_breakout_file = None +g_debug = False +syslog_levels=['emerg', 'alert', 'crit', 'err', 'warning', 'notice', 'info', 'debug', 'none'] + +minigraph_file = "/etc/sonic/minigraph.xml" +config_file = "/etc/sonic/config_db.json" +tmp_config_file = "/tmp/config_db.json" + +copp_config_file = "/etc/swss/config.d/00-copp.config.json" +tmp_copp_file = "/tmp/copp.json" +frr_config_file = "/etc/sonic/frr/frr.conf" +tmp_frr_file = "/tmp/frr.conf" +syslog_file = "/etc/rsyslog.d/99-default.conf" +tmp_syslog_file = "/tmp/rsyslog-default.conf" + +spytest_dir = "/etc/spytest" +init_config_file = spytest_dir + "/init_config_db.json" +base_config_file = spytest_dir + "/base_config_db.json" +module_config_file = spytest_dir + "/module_config_db.json" +init_frr_config_file = spytest_dir + "/init_frr.conf" +base_frr_config_file = spytest_dir + "/base_frr.conf" +module_frr_config_file = spytest_dir + "/module_frr.conf" +init_copp_config_file = spytest_dir + "/init_copp.json" +base_copp_config_file = spytest_dir + "/base_copp.json" +module_copp_config_file = spytest_dir + "/module_copp.json" +init_minigraph_file = spytest_dir + "/init_minigraph.xml" +base_minigraph_file = spytest_dir + "/base_minigraph.xml" +module_minigraph_file = spytest_dir + "/module_minigraph.xml" +tech_support_timestamp = spytest_dir + "/tech_support_timestamp.txt" + +port_config_file = "/usr/share/sonic/device" + +cores_tar_file_name = "/tmp/allcorefiles.tar.gz" +kdump_tar_file_name = "/tmp/allkdumpfiles.tar.gz" + +def trace(msg): + if g_debug: + print(msg) + +def read_port_inifile(): + """ + This proc is to get the last port number in the file port_config.ini + :return: + """ + (Platform, HwSKU) = get_hw_values() + int_file = port_config_file + '/' + Platform + '/' + HwSKU + '/' + 'port_config.ini' + int_file = 'cat ' + int_file + ' ' + '| ' + 'tail -1' + output = execute_check_cmd(int_file) + port = output.split(" ")[0] + return port + +def get_port_status(port): + """ + This proc is used to get the given port status. + :param port: + :return: + """ + output = execute_check_cmd("show interfaces status {}".format(port)) + if output != "": + return output + return + +def iterdict(d): + new_dict = {} + for k, v in d.items(): + if isinstance(v,dict): + v = iterdict(v) + try: + new_dict[k] = int(v) + except: + new_dict[k] = v + return new_dict + +def read_lines(file_path, default=None): + try: + with open(file_path, "r") as infile: + return infile.readlines() + except Exception as exp: + if default is None: + raise exp + return default + +def read_offset(file_path): + lines = read_lines(file_path, []) + offset = int(lines[0].split()[0]) if lines else 0 + return (file_path, offset) + +def write_offset(file_path, retval, add=0): + try: + lines = retval.split() + offset = add + int(lines[0].split()[0]) + with open(file_path, "w") as infile: + infile.write("{} unused".format(offset)) + except: pass + +def execute_from_file(file_path): + execute_cmds(read_lines(file_path)) + +def execute_cmds(cmds): + retval = [] + for cmd in cmds: + retval.append(execute_check_cmd(cmd)) + return "\n".join(retval) + +def execute_check_cmd(cmd, show=True, skip_error=False): + retval = "" + try: + if show: + print("Remote CMD: '{}'".format(cmd)) + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + out, err = proc.communicate() + proc.wait() + if not skip_error and proc.returncode != 0: + retval = "Error: Failed to execute '{}' ('{}')\n".format(cmd, err.strip()) + if out.strip() != "": + retval = retval + out.strip() + except: + retval = "Error: Exception occurred while executing the command '{}'".format(cmd) + if retval.strip() != "": + print(retval) + return retval + +def run_as_system_cmd(cmd, show=True): + retcode = None + try: + if show: + print("Remote CMD: '{}'".format(cmd)) + retcode = os.system(cmd) + if retcode != 0: + print("Error: Failed to execute '{}'. Return code is '{}'".format(cmd, retcode)) + except: + print("Error: Exception occurred while executing the command '{}'".format(cmd)) + return retcode + +def get_mac_address(): + syseeprom = execute_check_cmd("show platform syseeprom").split("\n") + for line in syseeprom: + match = re.match(r"^Base MAC Address\s+0x\d+\s+6\s+(\S+)", line) + if match: + return match.group(1) + return None + +def get_hw_values(): + platform = None + hwsku = None + platform_summ = execute_check_cmd("show platform summary").split("\n") + for line in platform_summ: + if not platform: + match = re.match(r"^Platform:\s+(\S+)", line) + if match: + platform = match.group(1) + if not hwsku: + match = re.match(r"^HwSKU:\s+(\S+)", line) + if match: + hwsku = match.group(1) + return (platform, hwsku) + +def read_json(filepath): + return eval(open(filepath, 'rU').read()) + +def get_file_diff(file1, file2, show_diff=False): + if filecmp.cmp(file1, file2): + # files have same content + return True + + # compare the dictionaries + file1_dict = read_json(file1) + file2_dict = read_json(file2) + f1_dict = iterdict(dict((k, v) for k, v in file1_dict.items() if v)) + f2_dict = iterdict(dict((k, v) for k, v in file2_dict.items() if v)) + + if f1_dict == f2_dict: + # dictionaries are same + return True + + # the files have different content + if show_diff: + print("Content in the files '{}' '{}' is different".format(file1, file2)) + + return False + +def json_fix(filepath): + data = open(filepath, 'rU').read() + try: + obj = json.loads(data) + except: + print("invalid json - trying to fix") + # remove trailing object comma + regex = re.compile(r'(,)\s*}(?=([^"\\]*(\\.|"([^"\\]*\\.)*[^"\\]*"))*[^"]*$)') + data = regex.sub("}", data) + # remove trailing array comma + regex = re.compile(r'(,)\s*\](?=([^"\\]*(\\.|"([^"\\]*\\.)*[^"\\]*"))*[^"]*$)') + data = regex.sub("]", data) + try: + obj = json.loads(data) + except: + raise ValueError("invalid json data") + + dst_file = "{}.new".format(filepath) + with open(dst_file, 'w') as outfile: + json.dump(obj, outfile, indent=4) + + return dst_file + +def backup_file(file_path): + file_name = os.path.basename(file_path) + golden_file = spytest_dir + "/{}.golden".format(file_name) + backup_filepath = spytest_dir + "/{}.backup".format(file_name) + if not os.path.exists(golden_file): + execute_check_cmd("cp {} {}".format(file_path, golden_file)) + execute_check_cmd("cp {} {}".format(file_path, backup_filepath)) + +def backup_swss_docker_file(file_path): + file_name = os.path.basename(file_path) + golden_file = spytest_dir + "/{}.golden".format(file_name) + backup_filepath = spytest_dir + "/{}.backup".format(file_name) + if not os.path.exists(golden_file): + execute_check_cmd("docker cp swss:{} {}".format(file_path, golden_file)) + execute_check_cmd("docker cp swss:{} {}".format(file_path, backup_filepath)) + return backup_filepath + +def apply_file(filepath, method): + commands_to_execute = [] + + if filepath.endswith('.json'): + filepath = json_fix(filepath) + if method == "full": + commands_to_execute.append("cp {} {}".format(filepath, init_config_file)) + else: + commands_to_execute.append("config load -y {}".format(filepath)) + commands_to_execute.append("config save -y") + elif filepath.endswith('.copp'): + filepath = json_fix(filepath) + if method == "full": + commands_to_execute.append("cp {} {}".format(filepath, init_copp_config_file)) + else: + backup_swss_docker_file(copp_config_file) + commands_to_execute.append("docker cp {} swss:{}".format(filepath, copp_config_file)) + elif filepath.endswith('.xml'): + if method == "full": + commands_to_execute.append("cp {} {}".format(filepath, init_minigraph_file)) + else: + backup_file(minigraph_file) + commands_to_execute.append("cp {} {}".format(filepath, minigraph_file)) + commands_to_execute.append("config load_minigraph -y") + commands_to_execute.append("config save -y") + elif filepath.endswith('.frr'): + if method == "full": + commands_to_execute.append("cp {} {}".format(filepath, init_frr_config_file)) + else: + backup_file(frr_config_file) + commands_to_execute.append("cp {} {}".format(filepath, frr_config_file)) + elif filepath.endswith('.sh'): + commands_to_execute.append("bash {}".format(filepath)) + elif filepath.endswith('.py'): + commands_to_execute.append("python {}".format(filepath)) + elif filepath.endswith('.bcm') or filepath.endswith('.ini') or filepath.endswith('.j2'): + # Execute the command "show platform summary" and get the Platform and HwSKU values. + (Platform, HwSKU) = get_hw_values() + + # Construct the path where we can found the Platform and HwSKU details. + device_files_loc = "/usr/share/sonic/device" + dut_file_location = "{}/{}/{}/".format(device_files_loc,Platform,HwSKU) + basename = os.path.basename(filepath) + old_file = os.path.join(dut_file_location, basename) + if not os.path.exists(old_file + ".orig"): + commands_to_execute.append("cp {} {}.orig".format(old_file, old_file)) + commands_to_execute.append("cp {} {}".format(filepath, old_file)) + + if commands_to_execute: + execute_cmds(commands_to_execute) + else: + print("Error: Invalid file format {}.".format(filepath)) + +def parse_and_apply_files(names, method): + ensure_mac_address(config_file) + if type(names) is str: + apply_file(names, method) + elif type(names) is list: + for filename in names: + parse_and_apply_files(filename, method) + +def clean_core_files(flag): + if flag == "YES": + print("remove core files files") + execute_check_cmd("rm -f /var/core/*.core.gz") + +def clean_dump_files(flag): + if flag == "YES": + print("remove techsupport dump files") + execute_check_cmd("rm -f /var/dump/*.tar.gz") + +def clear_techsupport(flag): + if flag == "YES": + print("remove core dumps and techsupport till now using CLI command.") + execute_check_cmd("sonic-clear techsupport till 'now' -y") + +def init_clean(flags): + [core_flag, dump_flag, clear_flag] = flags.split(",") + # remove core files + clean_core_files(core_flag) + # remove techsupport dump files + clean_dump_files(dump_flag) + # remove core dumps and techsupport till now using CLI command. + clear_techsupport(clear_flag) + + # disable syslog messages + print("disable syslog messages") + enable_disable_debug(False) + + # clear syslog messages + execute_check_cmd("rm -f {}/syslog.*".format(spytest_dir)) + execute_check_cmd("rm -f {}/sairedis.*".format(spytest_dir)) + execute_check_cmd("logrotate -f /etc/logrotate.conf", skip_error=True) + +def init_ta_config(flags, profile): + init_clean(flags) + + if profile == "na": + create_default_base_config() + elif profile == "l2" or profile == "l3": + create_profile_base_config(profile) + + # save current timestamp + run_as_system_cmd("date > {}".format(tech_support_timestamp)) + + # remove syslogs and sairedis files + print("clear syslog and sairedis files") + execute_check_cmd("logrotate -f /etc/logrotate.conf", skip_error=True) + execute_check_cmd("rm -f /var/log/syslog.*") + execute_check_cmd("rm -f /var/log/swss/sairedis.rec.*") + + print("DONE") + +def create_default_base_config(): + print("default base config") + # Clean the spytest directory - copp files are also saved as json + for extn in ["json", "conf", "xml"]: + execute_check_cmd("rm -f {}/*.{}".format(spytest_dir, extn)) + + # remove init configs + for filename in [init_copp_config_file, init_minigraph_file, \ + init_frr_config_file]: + execute_check_cmd("rm -f {}".format(filename)) + + # save the config to init file. + execute_check_cmd("config save -y {}".format(init_config_file)) + + file_dict = read_json(init_config_file) + + # remove all the unnecessary sections from init file + print("remove all the unnecessary sections") + retain = ['DEVICE_METADATA', 'PORT', 'FLEX_COUNTER_TABLE', "MGMT_PORT"] + if os.getenv("SPYTEST_NTP_CONFIG_INIT", "0") != "0": + retain.append("NTP_SERVER") + if os.getenv("SPYTEST_CLEAR_MGMT_INTERFACE", "0") == "0": + retain.append("MGMT_INTERFACE") + for key in file_dict.keys(): + if key not in retain: + del file_dict[key] + + # enable docker_routing_config_mode + print("enable docker_routing_config_mode") + if "DEVICE_METADATA" in file_dict: + if "localhost" in file_dict["DEVICE_METADATA"]: + file_dict["DEVICE_METADATA"]["localhost"]["docker_routing_config_mode"] = "split" + if os.getenv("SPYTEST_CLEAR_DEVICE_METADATA_HOSTNAME", "0") == "0": + file_dict["DEVICE_METADATA"]["localhost"]["hostname"] = "sonic" + + # enable all ports + print("enable all ports") + if "PORT" in file_dict: + port_dict = file_dict['PORT'] + for k, v in port_dict.items(): + v["admin_status"] = "up" + + # save the configuration to init file + with open(init_config_file, 'w') as outfile: + json.dump(file_dict, outfile, indent=4) + +def create_profile_base_config(profile): + print("{} base config".format(profile)) + # save the config to init file. + execute_check_cmd("config save -y {}".format(init_config_file)) + + print("DONE") + +def apply_config_profile(profile): + if profile == "na": + print("Skipping the profile config as it is not required for 'NA'.") + else: + output = execute_check_cmd("show config profiles") + match = re.match(r"Factory Default:\s+(\S+)", output) + if match and profile == match.group(1): + execute_check_cmd("rm -rf {}".format(config_file)) + execute_check_cmd("config profile factory {} -y".format(profile)) + print("DONE") + +def update_reserved_ports(port_list): + # If no init config_db.json return back. + if not os.path.exists(init_config_file): + print("==============================================================================") + print("===================== DEFAULT INIT CONFIG FILE IS MISSING ====================") + print("==============================================================================") + print("NOFILE") + return + + file_dict = read_json(init_config_file) + + # Change reserved port state + print("Change the reserved port state to down in config-db") + if "PORT" in file_dict: + port_dict = file_dict['PORT'] + for k, v in port_dict.items(): + if k not in port_list: + continue + v["admin_status"] = "down" + + # save the configuration to init file + with open(init_config_file, 'w') as outfile: + json.dump(file_dict, outfile, indent=4) + + print("DONE") + +def wait_for_ports_2(port_init_wait, poll_for_ports): + # Wait for last port to be available + port_num = read_port_inifile() + output = [] + if poll_for_ports == "yes": + for iter in range(0, port_init_wait/2): + retval = execute_check_cmd("grep -r PortInitDone /var/log/syslog", skip_error=True) + port_info = get_port_status(port_num) + if port_info: + output.append(port_info) + if "PortInitDone" in retval: + output.append(retval) + break + if port_info and port_num in port_info: + break + execute_check_cmd("sleep 2", False) + else: + execute_check_cmd("sleep {}".format(port_init_wait)) + return "\n".join(output) + +def wait_for_ports(port_init_wait, poll_for_ports): + if port_init_wait == 0: + return + + # use older mechanism for community build + if g_community_build: + return wait_for_ports_2(port_init_wait, poll_for_ports) + + if poll_for_ports == "yes": + # Wait for last port to be available + port_num = read_port_inifile() + for iter in range(0, port_init_wait/2): + port_info = get_port_status(port_num) + retval = execute_check_cmd("show system status") + if "System is ready" in retval: + break + if port_info and port_num in port_info: + break + execute_check_cmd("sleep 2", False) + else: + execute_check_cmd("sleep {}".format(port_init_wait)) + +# check if the MAC address is present in config_db.json +def ensure_mac_address(filepath): + file_dict = read_json(filepath) + if "DEVICE_METADATA" in file_dict: + if "localhost" in file_dict["DEVICE_METADATA"]: + if "mac" not in file_dict["DEVICE_METADATA"]["localhost"]: + print("============ Recovering MAC address =======") + mac = get_mac_address() + file_dict["DEVICE_METADATA"]["localhost"]["dmac"] = mac + with open(filepath, 'w') as outfile: + json.dump(file_dict, outfile, indent=4) + print("===========================================") + +def do_config_reload(filename=""): + if g_use_config_replace: + if filename: + execute_check_cmd("config_replace -f {}".format(filename)) + else: + execute_check_cmd("config_replace") + else: + execute_check_cmd("config reload -y {}".format(filename)) + +def dump_click_cmds(): + script = "/etc/spytest/remote/click-helper.py" + execute_check_cmd("python {}".format(script), show=False) + +def set_port_defaults(breakout, speed, port_init_wait, poll_for_ports): + if g_breakout_native: + script = "/usr/local/bin/port_breakout.py" + else: + script = None + + if not script or not os.path.exists(script): + script = "/etc/spytest/remote/port_breakout.py" + + if g_breakout_file: + script = script + " -c " + g_breakout_file + index = 0 + while index < len(breakout): + opt = breakout[index+1] + execute_check_cmd("python {} -p {} -o {}".format(script, breakout[index], opt)) + index = index + 2 + if breakout: + ensure_mac_address(config_file) + do_config_reload() + + index = 0 + while index < len(speed): + opt = speed[index+1] + retval = execute_check_cmd("portconfig -p {} -s {}".format(speed[index], opt)) + for line in retval.split("\n"): + match = re.match(r"^Port Ethernet\d+ belongs to port group (\d+)", line) + if match: + execute_check_cmd("config portgroup speed {} {}".format(match.group(1), opt)) + break + index = index + 2 + if speed: + execute_check_cmd("config save -y") + + wait_for_ports(port_init_wait, poll_for_ports) + +def config_reload(save, port_init_wait, poll_for_ports): + if save == "yes": + execute_check_cmd("config save -y") + + ensure_mac_address(config_file) + + do_config_reload() + + wait_for_ports(port_init_wait, poll_for_ports) + +def copy_or_delete(from_file, to_file): + if os.path.exists(from_file): + execute_check_cmd("cp {} {}".format(from_file, to_file)) + else: + execute_check_cmd("rm -f {}".format(to_file)) + +def show_file_content(filename, msg=""): + if g_debug: + print("======================== {} ====================".format(msg)) + if os.path.exists(filename): + execute_check_cmd("cat {}".format(filename)) + else: + print("File {} does not exist".format(filename)) + print("================================================") + +def save_base_config(): + # Save init_config_db.json and copy to base_config_db.json + execute_check_cmd("cp {} {}".format(init_config_file, base_config_file)) + + # Copy all init files as base files + copy_or_delete(init_frr_config_file, base_frr_config_file) + copy_or_delete(init_copp_config_file, base_copp_config_file) + copy_or_delete(init_minigraph_file, base_minigraph_file) + + print("DONE") + +def save_module_config(): + # Save current DB configuration to config_db.json and copy it as module config file. + execute_check_cmd("config save -y") + execute_check_cmd("cp {} {}".format(config_file, module_config_file)) + + # save the FRR configuration applied in module init + execute_check_cmd("touch {}".format(frr_config_file)) + execute_check_cmd("vtysh -c write file") + show_file_content(frr_config_file, "save_module_config FRR") + + # Copy all the actual files as module files. + copy_or_delete(frr_config_file, module_frr_config_file) + copy_or_delete(minigraph_file, module_minigraph_file) + + # Copy copp config file to ta location. + execute_check_cmd("docker cp swss:{} {}".format(copp_config_file, module_copp_config_file)) + + print("DONE") + +def apply_ta_config(method, port_init_wait, poll_for_ports, is_module_cfg): + + ta_config_file = module_config_file if is_module_cfg else base_config_file + ta_frr_config_file = module_frr_config_file if is_module_cfg else base_frr_config_file + ta_copp_config_file = module_copp_config_file if is_module_cfg else base_copp_config_file + ta_minigraph_file = module_minigraph_file if is_module_cfg else base_minigraph_file + + # If no base/module config_db.json return back. No need to check for other file formats. + if not os.path.exists(ta_config_file): + print("==============================================================================") + print("======================= TA DEFAULT CONFIG FILE IS MISSING ====================") + print("==============================================================================") + print("NOFILE") + return + + changed_files = [] + + # Save current config in DB to temp file to and compare it with base/module config_db.json file + # If there is a change, add config to list. + execute_check_cmd("config save -y {}".format(tmp_config_file)) + if not get_file_diff(tmp_config_file, ta_config_file, g_debug): + trace("TA Config File Differs") + changed_files.append("config") + + # Compare the minigraph.xml file with base/module minigraph.xml + # If there is a change, add xml to list. + if os.path.exists(minigraph_file) and os.path.exists(ta_minigraph_file): + if not filecmp.cmp(minigraph_file, ta_minigraph_file): + trace("TA Minigraph File Differs") + changed_files.append("minigraph") + + # When frr.conf file is not present, write file creates 3 different config files. + # Touch the frr.conf, this allows to create a empty file is it is not present. + # Write the current running configuration to frr.conf file + # Compare the generated frr.conf with base/module frr.conf file. + # If there is a change or no base/module/actual frr.conf file exists, add frr to list. + show_file_content(frr_config_file, "existing FRR") + execute_check_cmd("touch {}".format(frr_config_file)) + execute_check_cmd("vtysh -c write file") + show_file_content(frr_config_file, "generated FRR") + show_file_content(ta_frr_config_file, "TA FRR") + if not os.path.exists(ta_frr_config_file) and not os.path.exists(frr_config_file): + pass + elif not os.path.exists(ta_frr_config_file): + trace("TA FRR File Missing") + changed_files.append("frr") + elif not filecmp.cmp(frr_config_file, ta_frr_config_file): + trace("FRR File Differs") + changed_files.append("frr") + + # Save and compare the copp.json file + execute_check_cmd("docker cp swss:{} {}".format(copp_config_file, tmp_copp_file)) + if not os.path.exists(tmp_copp_file) and os.path.exists(ta_copp_config_file): + trace("SWSS COPP File Missing") + changed_files.append("copp") + elif os.path.exists(tmp_copp_file) and not os.path.exists(ta_copp_config_file): + trace("TA COPP File Missing") + changed_files.append("copp") + elif os.path.exists(tmp_copp_file) and os.path.exists(ta_copp_config_file): + if not get_file_diff(tmp_copp_file, ta_copp_config_file, g_debug): + trace("COPP File Differs") + changed_files.append("copp") + + # If a force method is *NOT* used, check for any entries in changed list + # If no entries are present(Means no change in configs), Return back. + if method not in ["force_reload", "force_reboot"]: + if not changed_files: + print("Config, FRR, COPP are same as TA files") + print("DONE") + return + + print("The current config differs from TA config, {}".format(changed_files)) + + # Check for each entry in changed list and copy the base/module files to the actual files. + # Copy base/module config file to actual file if config entry exists. + # If base/module frr file not exists, remove the actual file. + # Copy base/module frr file to actual file if frr entry exists. + # COPP + if "config" in changed_files: + execute_check_cmd("cp {} {}".format(ta_config_file, config_file)) + if os.path.exists(ta_minigraph_file) and "minigraph" in changed_files: + execute_check_cmd("cp -f {} {}".format(ta_minigraph_file, minigraph_file)) + if not os.path.exists(ta_frr_config_file) and "frr" in changed_files: + execute_check_cmd("rm -f {}".format(frr_config_file)) + if os.path.exists(ta_frr_config_file) and "frr" in changed_files: + execute_check_cmd("cp -f {} {}".format(ta_frr_config_file, frr_config_file)) + if os.path.exists(ta_copp_config_file) and "copp" in changed_files: + execute_check_cmd("docker cp {} swss:{}".format(ta_copp_config_file, copp_config_file)) + method = "force_reboot" + + # We copied the changed files to actual files. + # If reboot related method is used, return back asking for reboot required. + if method in ["force_reboot", "reboot"]: + print("REBOOT REQUIRED") + return + + # Following code is required for reload related methods. + # Create an empty frr.conf file. This will allow to write the running config to single file. + execute_check_cmd("touch {}".format(frr_config_file)) + + # Depending on the entries in the changed list, perform the operations. + if "minigraph" in changed_files: + execute_check_cmd("config load_minigraph -y") + execute_check_cmd("config save -y") + + # If config entry is present, perform config reload, this will take care of frr too. + # If frr enry is present, perform bgp docker restart. + if "config" in changed_files or method in ["force_reload"]: + ensure_mac_address(ta_config_file) + #execute_check_cmd("echo before reload;date") + do_config_reload(ta_config_file) + #execute_check_cmd("echo after reload;date") + if "frr" in changed_files or method in ["force_reload"]: + execute_cmds(["systemctl restart bgp"]) + execute_cmds(["sleep 10"]) + + # Re-Write the base/module frr.conf, this is to allow the hook level code to get saved in frr.conf. + execute_check_cmd("vtysh -c write file") + if os.path.exists(frr_config_file): + execute_check_cmd("cp -f {} {}".format(frr_config_file, ta_frr_config_file)) + show_file_content(ta_frr_config_file, "rewrite TA FRR") + + # Wait for last port to be available + wait_for_ports(port_init_wait, poll_for_ports) + +def run_test(script_fullpath, proc_args): + if os.path.exists(script_fullname): + execute_check_cmd("chmod 755 {}".format(script_fullpath)) + args_to_script = " ".join(proc_args) + cmd = "{} {}".format(script_fullpath, args_to_script) + execute_check_cmd(cmd) + return + print("Script '{}' not exists".format(script_fullpath)) + +def enable_disable_debug(flag): + if not os.path.exists(syslog_file): + print("==============================================================================") + print("============================= SYSLOG FILE IS MISSING =========================") + print("==============================================================================") + print("NOFILE") + return + + backup_file(syslog_file) + execute_check_cmd("cp {} {}".format(syslog_file, tmp_syslog_file)) + + cmd = "" + if flag: + cmd = '''sed -i '$ a :msg, contains, "(core dumped)" /dev/console' {}'''.format(syslog_file) + else: + cmd = '''sed '/core dumped/d' -i {}'''.format(syslog_file) + execute_check_cmd(cmd, False) + + if not filecmp.cmp(syslog_file, tmp_syslog_file): + # files have different content + execute_check_cmd("systemctl restart rsyslog") + print("DONE") + return + + print("NOCHANGE") + +def read_messages(file_path, all_file, var_file, our_file): + (offset_file, offset) = read_offset(file_path) + execute_check_cmd("tail --lines=+{} {} > {}".format(offset, all_file, our_file)) + execute_check_cmd("ls -l {}*".format(var_file)) + retval = execute_check_cmd("wc -l {}".format(our_file)) + write_offset(file_path, retval, offset) + +def syslog_read_msgs(lvl, phase): + if phase: execute_check_cmd("sudo echo {}".format(phase)) + file_path = "{}/syslog.offset".format(spytest_dir) + var_file = "/var/log/syslog" + our_file = "{}/syslog.txt".format(spytest_dir) + read_messages(file_path, var_file, var_file, our_file) + if lvl != "none" and lvl in syslog_levels: + index = syslog_levels.index(lvl) + needed = "|".join(syslog_levels[:index+1]) + cmd = r"""grep -E "^\S+\s+[0-9]+\s+[0-9]+:[0-9]+:[0-9]+(\.[0-9]+){{0,1}}\s+\S+\s+({})" {}""" + execute_check_cmd(cmd.format(needed.upper(), our_file), False, True) + +def do_sairedis(op): + if op == "clean": + execute_check_cmd("rm -f {}/sairedis.*".format(spytest_dir)) + execute_check_cmd("rm -f /var/log/swss/sairedis.rec.*") + file_path = "{}/sairedis.offset".format(spytest_dir) + var_file = "/var/log/swss/sairedis.rec" + our_file = "{}/sairedis.txt".format(spytest_dir) + all_file = "{}/sairedis.all".format(spytest_dir) + execute_check_cmd("rm -f {0};ls -1tr {1}* | xargs zcat -f >> {0}".format(all_file, var_file)) + read_messages(file_path, all_file, var_file, our_file) + if op == "read": + print("SAI-REDIS-FILE: /etc/spytest/sairedis.txt") + +def invalid_ip(addr): + try: + socket.inet_aton(addr) + except: + return True + return False + +def mgmt_ip_setting(mgmt_type, ip_addr_mask, gw_addr): + # Validate the ip/gw for static + if mgmt_type == "static": + if ip_addr_mask and gw_addr: + try: + (ipaddr, mask_str) = ip_addr_mask.split("/") + except: + print("IP and Mask should be provided with '/' delimited.") + return + try: + mask = int(mask_str) + if mask < 0 or mask > 32: + print("Invalid MASK provided.") + return + except: + print("Invalid MASK provided.") + return + if invalid_ip(ipaddr) or invalid_ip(gw_addr): + print("Invalid IP/Gateway provided.") + return + else: + print("IP or Gateway details not provided.") + return + + file_dict = read_json(config_file) + + if mgmt_type == "dhcp" and 'MGMT_INTERFACE' not in file_dict.keys(): + print("DONE-NOCHANGE-DHCP") + return + + print("Remove the required ip setting sections") + if 'MGMT_INTERFACE' in file_dict.keys(): + del file_dict['MGMT_INTERFACE'] + + if mgmt_type == "static": + print("Adding new data") + mgmt_key = "eth0|{}".format(ip_addr_mask) + mgmt_dict = {mgmt_key: {"gwaddr": gw_addr}} + file_dict['MGMT_INTERFACE'] = mgmt_dict + + # save the configuration + with open(config_file, 'w') as outfile: + json.dump(file_dict, outfile, indent=4) + + print("DONE") + +def fetch_core_files(): + # Create a tar file for using the files /var/core/*.core.gz + core_files_list = glob.glob("/var/core/*.core.gz") + if len(core_files_list) == 0: + print("NO-CORE-FILES") + return + if os.path.exists(cores_tar_file_name): + execute_check_cmd("rm -f {}".format(cores_tar_file_name)) + tar_cmd = "cd /var/core/ && tar -cf {} *.core.gz && cd -".format(cores_tar_file_name) + execute_check_cmd(tar_cmd) + if os.path.exists(cores_tar_file_name): + execute_check_cmd("rm -f /var/core/*.core.gz") + print("CORE-FILES: {}".format(cores_tar_file_name)) + return + print("NO-CORE-FILES: No tar file is generated for core.gz files") + +def get_tech_support(): + # read last time stamp + lines = read_lines(tech_support_timestamp, []) + since = "--since='{}'".format(lines[0].strip()) if lines else "" + + # Create a tar file for using the the command show techsupport + retcode = run_as_system_cmd("show techsupport {} > /tmp/show_tech_support.log 2>&1".format(since)) + if retcode != 0: + print("NO-DUMP-FILES: 'show techsupport' command failed") + return + tech_support_tarlist = sorted(glob.glob("/var/dump/*.tar.gz")) + if len(tech_support_tarlist) == 0: + print("NO-DUMP-FILES: No techsupport tar file is generated in /var/dump/") + return + retval = "DUMP-FILES: {}".format(tech_support_tarlist[-1]) + print(retval) + + # save current time stamp + run_as_system_cmd("date > {}".format(tech_support_timestamp)) + +def fetch_kdump_files(): + # Create a tar file for using the files /var/crash/datestamp & and the kexec_dump + kdump_files_type1 = execute_check_cmd("find /var/crash -name dmesg* | wc -l") + kdump_files_type2 = execute_check_cmd("find /var/crash -name kdump* | wc -l") + if kdump_files_type1 == '0' and kdump_files_type2 == '0': + print("NO-KDUMP-FILES") + return + if os.path.exists(kdump_tar_file_name): + execute_check_cmd("rm -f {}".format(kdump_tar_file_name)) + tar_cmd = "cd /var/crash/ && tar -cf {} * && cd -".format(kdump_tar_file_name) + execute_check_cmd(tar_cmd) + if os.path.exists(kdump_tar_file_name): + execute_check_cmd("sudo rm -rf /var/crash/*") + print("KDUMP-FILES: {}".format(kdump_tar_file_name)) + return + print("NO-KDUMP-FILES: No tar file is generated for kdump files") + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='SpyTest Helper script.') + + parser.add_argument("--env", action="append", default=[], + nargs=2, help="environment variables") + parser.add_argument("--apply-configs", action="store", default=None, + nargs="+", help="list of files that need to apply on dut.") + parser.add_argument("--apply-file-method", action="store", + choices=['full', 'incremental'], + help="method to apply files.") + parser.add_argument("--run-test", action="store", default=None, nargs="+", + help="execute the given script with given arguments.") + parser.add_argument("--save-base-config", action="store_true", default=False, + help="save the current config as base config.") + parser.add_argument("--save-module-config", action="store_true", default=False, + help="save the current config as module config.") + parser.add_argument("--init-ta-config", action="store", default=None, + help="save the current config as ta default config.") + parser.add_argument("--port-init-wait", action="store", type=int, default=0, + help="Wait time in seconds for ports to come up -- default: 0") + parser.add_argument("--poll-for-ports", action="store", default="no", + choices=['yes', 'no'], + help="Poll for the ports status after the DUT reload") + parser.add_argument("--apply-base-config", action="store", + choices=['reload', 'replace', 'reboot', 'force_reload', 'force_reboot'], + help="apply base config as current config.") + parser.add_argument("--apply-module-config", action="store", + choices=['reload', 'replace', 'reboot', 'force_reload', 'force_reboot'], + help="apply module config as current config.") + parser.add_argument("--json-diff", action="store", nargs=2, default=None, + help="dump the difference between json files.") + parser.add_argument("--enable-debug", action="store_true", default=False, + help="enable debug messages onto the console.") + parser.add_argument("--disable-debug", action="store_true", default=False, + help="disable debug messages onto the console.") + parser.add_argument("--syslog-check", action="store", default=None, + choices=syslog_levels, + help="read syslog messages of given level and clear all syslog messages.") + parser.add_argument("--phase", action="store", default=None, + help="phase for checks.") + parser.add_argument("--sairedis", action="store", default="none", + choices=['clear', 'read', 'none', 'clean'], help="read sairedis messages.") + parser.add_argument("--execute-from-file", action="store", default=None, + help="execute commands from file.") + parser.add_argument("--set-mgmt-ip", action="store", default=None, + choices=['dhcp', 'static', None], help="Management(eth0) address type.") + parser.add_argument("--ip-addr-mask", action="store", default=None, + help="IP address to set for management port(eth0).") + parser.add_argument("--gw-addr", action="store", default=None, + help="Gateway address to set for management port(eth0).") + parser.add_argument("--fetch-core-files", action="store", default=None, + choices=['collect_kdump', 'none'], + help="Fetch the core files from DUT to logs location.") + parser.add_argument("--get-tech-support", action="store_true", default=False, + help="Get the tech-support information from DUT to logs location.") + parser.add_argument("--init-clean", action="store", default=None, + help="Clear the core files, dump files, syslog data etc.") + parser.add_argument("--update-reserved-ports", action="store", default=None, + nargs="+", help="list of reserved ports that need to be shutdown on dut.") + parser.add_argument("--breakout", action="store", default=[], + nargs="+", help="breakout operations to be performed.") + parser.add_argument("--speed", action="store", default=[], + nargs="+", help="speed operations to be performed.") + parser.add_argument("--port-defaults", action="store_true", default=None, + help="apply breakout/speed defaults.") + parser.add_argument("--dump-click-cmds", action="store_true", default=None, + help="dump all click commnds.") + parser.add_argument("--config-reload", action="store", default=None, + choices=['yes', 'no'], + help="perform config reload operation: yes=save+reload no=reload") + parser.add_argument("--wait-for-ports", action="store_true", default=None, + help="wait for ports to comeup.") + parser.add_argument("--config-profile", action="store", default="na", + choices=['l2', 'l3', 'na'], help="Profile name to load.") + parser.add_argument("--community-build", action="store_true", default=False, + help="use community build options.") + parser.add_argument("--breakout-native", action="store_true", default=False, + help="Use port breakout script from device.") + parser.add_argument("--breakout-file", action="store", default=None, + help="Use port breakout options from file.") + parser.add_argument("--use-config-replace", action="store_true", default=False, + help="use config replace where ever config reload is needed.") + parser.add_argument("--debug", action="store_true", default=False) + + args, unknown = parser.parse_known_args() + + if unknown: + print("IGNORING unknown arguments", unknown) + + #g_debug = args.debug + g_community_build = args.community_build + g_breakout_native = args.breakout_native + g_breakout_file = args.breakout_file + g_use_config_replace = args.use_config_replace + + for name, value in args.env: + os.environ[name] = value + + if args.apply_configs: + parse_and_apply_files(args.apply_configs, args.apply_file_method) + elif args.run_test: + script_fullname = args.run_test[0] + script_arguments = args.run_test[1:] + run_test(script_fullname, script_arguments) + elif args.init_ta_config: + init_ta_config(args.init_ta_config, args.config_profile) + elif args.save_base_config: + save_base_config() + elif args.save_module_config: + save_module_config() + elif args.apply_base_config: + apply_ta_config(args.apply_base_config, args.port_init_wait, args.poll_for_ports, False) + elif args.apply_module_config: + apply_ta_config(args.apply_module_config, args.port_init_wait, args.poll_for_ports, True) + elif args.json_diff: + retval = get_file_diff(args.json_diff[0], args.json_diff[1], True) + print(retval) + elif args.enable_debug: + enable_disable_debug(True) + elif args.disable_debug: + enable_disable_debug(False) + elif args.syslog_check: + syslog_read_msgs(args.syslog_check, args.phase) + elif args.sairedis != "none": + do_sairedis(args.sairedis) + elif args.execute_from_file: + execute_from_file(args.execute_from_file) + elif args.set_mgmt_ip: + mgmt_ip_setting(args.set_mgmt_ip, args.ip_addr_mask, args.gw_addr) + elif args.fetch_core_files: + fetch_core_files() + if args.fetch_core_files == "collect_kdump": + fetch_kdump_files() + elif args.get_tech_support: + get_tech_support() + elif args.init_clean: + init_clean(args.init_clean) + elif args.update_reserved_ports: + update_reserved_ports(args.update_reserved_ports) + elif args.port_defaults: + set_port_defaults(args.breakout, args.speed, args.port_init_wait, args.poll_for_ports) + elif args.dump_click_cmds: + dump_click_cmds() + elif args.config_reload: + config_reload(args.config_reload, args.port_init_wait, args.poll_for_ports) + elif args.wait_for_ports: + wait_for_ports(args.port_init_wait, args.poll_for_ports) + elif args.config_profile: + apply_config_profile(args.config_profile) + else: + print("Error: Invalid/Unknown arguments provided for the script.") diff --git a/spytest/spytest/rest.py b/spytest/spytest/rest.py new file mode 100644 index 00000000000..82a6c5c97e6 --- /dev/null +++ b/spytest/spytest/rest.py @@ -0,0 +1,299 @@ +import os +import glob +import requests +import warnings +import jsonpatch + +from jinja2 import Environment + +from spytest.dicts import SpyTestDict +from utilities import common as utils +from utilities import json_helpers as json + +class Rest(object): + + def __init__(self, logger=None): + self.base_url = os.getenv("SPYTEST_REST_TEST_URL") + self.session = None + self.logger = logger + self.timeout = 1 + self.protocol = "https" + self.ip = None + self.username = None + self.password = None + self.altpassword = None + self.curr_pwd = None + self.cli_data = SpyTestDict() + + def reinit(self, ip, username, password, altpassword): + self.ip = ip + self.base_url = "{}://{}".format(self.protocol, ip) + try: + self._set_auth(username, password, altpassword) + except: + pass + return self + + def reset_curr_pwd(self): + self.curr_pwd = None + + def _set_auth(self, username, password, altpassword): + self.username = username + if not self.curr_pwd: + if password and altpassword: + self.password = password + self.altpassword = altpassword + for pwd in [password, altpassword]: + tmp_session = self._get_session() + tmp_session.auth = (self.username, pwd) + tmp_session.verify = False + tmp_url = self._get_url("/restconf/data/openconfig-system:system") + iter_count = 3 + while iter_count > 0: + iter_count -= 1 + try: + retval = tmp_session.get(tmp_url, verify=False, timeout=self.timeout) + self._log("Using '{}' '{}' : '{}'".format(username, pwd, retval.status_code)) + if retval.status_code == 200: + self.curr_pwd = pwd + break + except Exception as e: + self._log("Exception '{}' '{}' : '{}'".format(username, pwd, e)) + if self.curr_pwd: + break + elif password: + self.password = password + self.curr_pwd = password + elif altpassword: + self.altpassword = altpassword + self.curr_pwd = altpassword + msg = "Rest details '{}' '{}' '{}' '{}' '{}'".format( + self.ip, self.username, self.password, self.altpassword, self.curr_pwd) + self._log(msg) + + def _create_session(self): + self.session = requests.session() + self.headers = {"Accept": "application/yang-data+json", + "Content-type": "application/yang-data+json"} + self.session.headers.update(self.headers) + if self.curr_pwd: + self.session.auth = (self.username, self.curr_pwd) + self.session.verify = False + warnings.filterwarnings('ignore', message='Unverified HTTPS request') + + def _get_credentials(self): + return [self.username, self.curr_pwd] + + def _get_session(self): + self._create_session() + if not self.session: + self._create_session() + return self.session + + def _log(self, msg): + if self.logger: + self.logger.debug(msg) + else: + print(msg) + + def _dump(self, data): + self._log(json.dumps(data)) + + def _get_url(self, path, *args, **kwargs): + params = [] + for key, value in kwargs.items(): + if value: + value = value.replace(" ", "%20") + params.append('{}={}'.format(key, value)) + else: + params.append(key) + + if path.startswith("/"): + path = path[1:] + + if params: + url = "{}/{}/{}".format(self.base_url, path, ",".join(params)) + else: + url = "{}/{}".format(self.base_url, path) + + for entry in args: + url = "{}/{}".format(url, entry) + + return url + + def _json(self, retval, default={}): + try: + return retval.json() + except Exception as exp: + print(utils.stack_trace(exp)) + return default + + def _result(self, operation, retval, inp): + resp = SpyTestDict() + resp.url = retval.url + resp.operation = operation + resp.status = retval.status_code + resp.input = inp + resp.output = self._json(retval) + self._log(json.dumps(resp)) + return resp + + def post(self, path, data, *args, **kwargs): + session = self._get_session() + try: + url = self._get_url(path, *args, **kwargs) + retval = session.post(url, json.dumps(data), verify=False, timeout=self.timeout) + return self._result("POST", retval, data) + except Exception as e: + print(e) + raise e + + def put(self, path, data, *args, **kwargs): + session = self._get_session() + try: + url = self._get_url(path, *args, **kwargs) + retval = session.put(url, json.dumps(data), verify=False, timeout=self.timeout) + return self._result("PUT", retval, data) + except Exception as e: + print(e) + raise e + + def patch(self, path, data, *args, **kwargs): + session = self._get_session() + try: + url = self._get_url(path, *args, **kwargs) + retval = session.patch(url, json.dumps(data), verify=False, timeout=self.timeout) + return self._result("PATCH", retval, data) + except Exception as e: + print(e) + raise e + + def delete(self, path, *args, **kwargs): + session = self._get_session() + try: + url = self._get_url(path, *args, **kwargs) + retval = session.delete(url, verify=False, timeout=self.timeout) + return self._result("DELETE", retval, None) + except Exception as e: + print(e) + raise e + + def get(self, path, *args, **kwargs): + session = self._get_session() + try: + url = self._get_url(path, *args, **kwargs) + retval = session.get(url, verify=False, timeout=self.timeout) + return self._result("GET", retval, None) + except Exception as e: + print(utils.stack_trace(e)) + raise e + + def parse(self, filepath=None, all_sections=False, paths=[], **kwargs): + assert filepath, "File Path must be provided" + root = None + if "::" in filepath: [filepath, root] = filepath.split("::", 2) + if not isinstance(paths, list) and isinstance(paths, str): + paths = [paths] + filepath = utils.find_file(filepath, paths) + text = "\n".join(utils.read_lines(filepath)) + tmpl = Environment().from_string(text) + if root: + block = tmpl.blocks[root] + text = "\n".join(block(tmpl.new_context(kwargs))) + return json.fix(text, "Invalid json file supplied", True, object_pairs_hook=SpyTestDict) + if not all_sections or not tmpl.blocks: + text = Environment().from_string(text).render(**kwargs) + return json.fix(text, "Invalid json file supplied", True, object_pairs_hook=SpyTestDict) + retval = SpyTestDict() + for root in tmpl.blocks: + block = tmpl.blocks[root] + text = "\n".join(block(tmpl.new_context(**kwargs))) + retval[root] = json.fix(text, "Invalid json file supplied", True, object_pairs_hook=SpyTestDict) + return retval + + def load_cli(self, filepath): + self.cli_data = self.parse(filepath) + for req_list in self.cli_data.values(): + for req in req_list: + for key, cli in req.items(): + print("{} -- {} -- {}".format(cli.view, cli.cmd, key)) + + def search_cli(self, key): + for req_list in self.cli_data.values(): + for req in req_list: + if key in req: + return req[key] + return None + + def search_cli_data(self, data): + print(json.dumps(data)) + + def cli(self, request, sections=None, operations=None): + retval = SpyTestDict() + map_operations={"create":"post", "read":"get", "update":"put", "modify":"patch", "delete":"delete"} + if operations: operations = utils.make_list(operations) + for index, ent in enumerate(utils.make_list(request)): + key = ent.path.replace("/restconf/data", map_operations[ent.operation]) + key = key.replace("-", "_").replace(":", "_").replace("/", "_") + cli = self.search_cli(key) + if not cli: + print("Rest2CLI Fail: {} {}".format(key, ent.path)) + self.search_cli_data(request.data) + continue + print("Rest2CLI PASS: {} {}".format(key, cli.cmd)) + return retval + + def apply(self, request, sections=None, operations=None, ui="rest"): + if ui == "cli": return self.cli(request, sections, operations) + retval = SpyTestDict() + if operations: operations = utils.make_list(operations) + for index, ent in enumerate(utils.make_list(request)): + enable = ent.get("enable", 1) + if not enable: continue + operation = ent["operation"] + if operations and operation not in operations: continue + instance = ent.get("instance", dict()) + data = ent.get("data", dict()) + path = ent.get("path", "") + name = ent.get("name", "{}".format(index)) + if operation == "read" or operation == "get": + retval[name] = self.get(path, **instance) + elif operation == "configure" or operation == "patch": + retval[name] = self.patch(path, data, **instance) + elif operation == "unconfigure" or operation == "delete": + retval[name] = self.delete(path, **instance) + elif operation == "post": + retval[name] = self.post(path, data, **instance) + elif operation == "put": + retval[name] = self.put(path, data, **instance) + elif operation == "verify": + resp = self.get(path, **instance) + result = [True, []] + for pe in jsonpatch.make_patch(data, resp.output): + result[1].append(pe) + if pe["op"] != "add": + result[0] = False + retval[name] = result + return retval + +if __name__ == "__main__": + def _main(): + r = Rest().reinit("10.52.129.47", "admin", "broadcom", "broadcom2") + r.load_cli('../datastore/samples/all.cli') + for filepath in glob.glob('../datastore/samples/*.j2'): + #if "openconfig-system.j2" not in filepath: continue + msg = "Rest {}".format(filepath) + utils.banner(msg, tnl=False) + d = r.parse(filepath, all_sections=True) + for name, section in d.items(): + if section.operation == "read": continue + msg = "{}::{} {}".format(filepath, name, section.operation) + utils.banner(msg, tnl=False) + r.apply(section, ui="rest") + break + def _main2(): + r = Rest().reinit("10.59.143.100", "admin", "broadcom", "broadcom2") + url = "/restconf/data/openconfig-interfaces:interfaces" + print(r.get(url, "openconfig-vxlan:vxlan-if", interface="vtep1")) + _main2() + diff --git a/spytest/spytest/result.py b/spytest/spytest/result.py new file mode 100644 index 00000000000..95089c9156f --- /dev/null +++ b/spytest/spytest/result.py @@ -0,0 +1,318 @@ +import sys +import csv +import os + +import utilities.common as utils + +from spytest.st_time import get_timestamp +from spytest.datamap import DataMap + +from .mail import send as email + +slave_cols0 = ['#', 'Module', 'TestFunction', 'Result', 'TimeTaken', + 'ExecutedOn', 'Syslogs', 'FCLI', "TSSH", "DCNT", 'Description', "Devices"] +slave_cols1 = ['#', 'Feature', 'TestCase', 'Result', + 'ResultType', 'ExecutedOn', 'Description', 'Function', 'Module', "Devices"] +slave_cols2 = ['#', "Device", 'Module', 'TestFunction', 'LogMessage'] +slave_cols3 = ["#", "Module", "Result", "Test Time", "INFRA Time (ms)", "CMD Time (ms)", + "TG Time (ms)", "Wait(sec)", "TGWait(sec)", "PROMPT NFOUND", "Description"] + +slave_cols = [slave_cols0, slave_cols1, slave_cols2, slave_cols3] +merge_cols0 = ['#', "Node", 'Module', 'TestFunction', 'Result', + 'TimeTaken', 'ExecutedOn', 'Syslogs', 'FCLI', "TSSH", "DCNT", 'Description', "Devices"] +merge_cols1 = ['#', "Node", 'Feature', 'TestCase', 'Result', + 'ResultType', 'ExecutedOn', 'Description', 'Function', 'Module', "Devices"] +merge_cols2 = ['#', "Node", "Device", 'Module', 'TestFunction', 'LogMessage'] +merge_cols3 = ["#", "Node", "Module", "Result", "Test Time", "INFRA Time (ms)", "CMD Time (ms)", + "TG Time (ms)", "Wait(sec)", "TGWait(sec)", "PROMPT NFOUND", "Description"] +merge_cols = [merge_cols0, merge_cols1, merge_cols2, merge_cols3] + +class Result(object): + + def __init__(self, prefix, is_slave=True): + self.csv_fd = [None, None, None] + self.writer = [None, None, None] + self.count = [0, 0, 0] + self.result = None + self.desc = None + self.default_result = None + self.default_desc = None + dmap = DataMap("messages") + self.msgs = dmap.get() + self.prefix = prefix + self.report_csv = [None, None, None] + self.report_csv[0] = "{}_result.csv".format(prefix) + self.report_csv[1] = "{}_tcresult.csv".format(prefix) + self.report_csv[2] = "{}_syslog.csv".format(prefix) + if is_slave: + self.open_csv(prefix, 0) + self.open_csv(prefix, 1) + self.open_csv(prefix, 2) + + def __del__(self): + self.close_csv(0) + + def clear(self): + self.result = None + self.desc = None + self.default_result = None + self.default_desc = None + + def get(self): + if not self.result: + if not self.default_result: + return ["ScriptError", "test case exited without setting any result"] + return [self.default_result, self.default_desc] + return [self.result, self.desc] + + def build_msg(self, name, *args): + if name not in self.msgs: + raise ValueError("unknown message identifier '{}'".format(name)) + + s = self.msgs[name] + try: + rv = s.format(*args) + except: + raise ValueError("expected arguments not provided") + + return rv + + def set_default_error(self, res, code, *args): + self.default_result = res + try: + if code: + self.default_desc = self.build_msg(code, *args) + else: + self.default_desc = None + except Exception as e: + print(e) + self.default_desc = "Invalid error code {} : {}".format(code, e) + self.default_result = "Fail" + return self.default_desc + + def set(self, res, code, *args): + if self.result and self.result != "Pass": + msg = "result already set to {} -- ignoring".format(self.result) + print(msg) + return msg + self.result = res + try: + self.desc = self.build_msg(code, *args) + except Exception as e: + print(e) + self.desc = "Invalid error code {} : {}".format(code, e) + self.result = "Fail" + return self.desc + + def _build_record(self, nodeid, func, tcid, time_taken, comp, result=None, + desc=None, rtype="Executed", index=0, syslog_count=0, + fcli=0, tryssh=0, dut_list=[]): + self.count[index] = self.count[index] + 1 + result_def, desc_def = self.get() + result = result_def if result is None else result + desc = desc_def if desc is None else desc + + executedOn = get_timestamp(False) + + if comp: + rcdict = { + "#": self.count[index], + "Feature": comp, + "TestCase": tcid, + "Result": result, + "ExecutedOn": executedOn, + "ResultType": rtype, + "Description": desc, + "Function": func, + "Module": nodeid.split(':')[0], + "Devices": " ".join(dut_list), + } + else: + rcdict = { + "#": self.count[index], + "Module": nodeid.split(':')[0], + "TestFunction": func, + "Result": result, + "ExecutedOn": executedOn, + "TimeTaken": time_taken, + "Syslogs": syslog_count, + "FCLI": fcli, + "TSSH": tryssh, + "DCNT": len(dut_list), + "Description": desc, + "Devices": " ".join(dut_list), + } + return rcdict + + def publish(self, nodeid, func, tcid, time_taken, comp, result=None, + desc=None, rtype="Executed", syslogs=None, + fcli=0, tryssh=0, dut_list=[]): + syslog_count = 0 if not syslogs else len(syslogs) + index = 0 if rtype == "Executed" else 1 + rcdict = self._build_record(nodeid, func, tcid, time_taken, comp, + result, desc, rtype, index, syslog_count, + fcli, tryssh, dut_list) + self.write_csv(rcdict, index) + + if not comp: + index = 2 + for devname, msgtype, syslog in syslogs: + func = func if func else msgtype + self.count[index] = self.count[index] + 1 + d = { + "#": self.count[index], + "Device": devname, + "Module": nodeid.split(':')[0], + "TestFunction": func, + "LogMessage": syslog, + } + self.write_csv(d, index) + + return rcdict + + def email(self, mailcfg, attachments=None, is_html=False): + body = """ + Note: This is an automated mail sent by the SpyTest application. Please do not reply. + """ + if is_html: + body = mailcfg.body + else: + body = mailcfg.body + body + subject = mailcfg.subject + recipients = utils.split_byall(mailcfg.recipients) + server = mailcfg.server + if attachments is None: + attachments = [] + attachments.append(self.report_csv[0]) + attachments.append(self.report_csv[1]) + attachments.append(self.report_csv[2]) + email(server, recipients, subject, body, attachments, is_html) + + def open_csv(self, prefix, index): + report_csv = self.report_csv[index] + if os.getenv("SPYTEST_RESULTS_PREFIX"): + rows = Result.read_report_csv(report_csv) + else: + rows = [] + if sys.version_info.major < 3: + csv_fd = open(report_csv, "wb") + else: + csv_fd = open(report_csv, "w", newline='') + writer = csv.DictWriter(csv_fd, fieldnames=slave_cols[index], + dialect="excel") + self.csv_fd[index] = csv_fd + if not rows: + writer.writeheader() + else: + l_rows = Result.prepend_row_index(rows) + utils.write_csv_writer(slave_cols[index], l_rows, writer) + self.count[index] = len(rows) + self.writer[index] = writer + + def close_csv(self, index): + if self.csv_fd[index]: + self.csv_fd[index].close() + self.csv_fd[index] = None + self.writer[index] = None + + def write_csv(self, rcdict, index): + if self.writer[index]: + self.writer[index].writerow(rcdict) + self.csv_fd[index].flush() + + @staticmethod + def read_report_csv(filepath, rmindex=True): + rows = [] + try: + with utils.open_file(filepath) as fd: + for row in csv.reader(fd): + if row[0] != '#': + if rmindex: + row.pop(0) + rows.append(row) + except: + #print("failed to open {} to read".format(filepath)) + pass + + return rows + + @staticmethod + def write_report_csv(filepath, rows, rtype, is_batch=True, append=False): + if append: + l_rows = rows + else: + l_rows = Result.prepend_row_index(rows) + if is_batch: + utils.write_csv_file(merge_cols[rtype], l_rows, filepath, append) + else: + utils.write_csv_file(slave_cols[rtype], l_rows, filepath, append) + + @staticmethod + def prepend_row_index(rows): + l_rows=[] + for i,row in enumerate(rows): + l_row = [i+1] + l_row.extend(row) + l_rows.append(l_row) + return l_rows + + @staticmethod + def write_report_png(filepath, rows, index): + try: + import matplotlib + matplotlib.use('Agg') + import matplotlib.pyplot as plt + buckets = dict() + all_colors = { + "green":0, "red":0, "orange":0, "blue":0, + "cyan":0, "olive":0, "sienna":0, "peru":0, + "indigo":0, "magenta":0, "lightblue":0, + "yellow":0, "salmon":0, "palegreen":0, + "pink":0, "crimson":0, "lightpink":0 + } + res_colors = { + "Pass":"green", "DUTFail":"red", "Fail":"orange", + "ScriptError":"blue", "EnvFail":"cyan", "DepFail":"olive", + "ConfigFail":"sienna", "TopoFail":"peru", "TGenFail":"indigo", + "Timeout":"magenta", "Skipped":"lightblue", "Unsupported": "yellow" + } + for row in rows: + res = row[index] + if not res: + continue + if res not in buckets: + buckets[res] = 0 + buckets[res] = buckets[res] + 1 + labels = [] + colors = [] + for label in buckets: + if label in res_colors: + color = res_colors[label] + all_colors[color]=1 + else: + for c,used in all_colors.items(): + if not used: + color=c + all_colors[color]=1 + break + colors.append(color) + labels.append("{} [{}]".format(label, buckets[label])) + sizes = buckets.values() + plt.pie(sizes, colors=colors, labels=labels, autopct='%1.1f%%', startangle=140) + plt.axis('equal') + plt.savefig(filepath) + plt.clf() + except Exception as e: + print(e) + + @staticmethod + def write_report_html(filepath, rows, rtype=0, is_batch=True, index=3): + l_rows = Result.prepend_row_index(rows) + if is_batch: + utils.write_html_table(merge_cols[rtype], l_rows, filepath) + else: + utils.write_html_table(slave_cols[rtype], l_rows, filepath) + if rtype in [0, 1]: + png_file = os.path.splitext(filepath)[0]+'.png' + Result.write_report_png(png_file, l_rows, index) + diff --git a/spytest/spytest/rps.py b/spytest/spytest/rps.py new file mode 100644 index 00000000000..e0659cbe02e --- /dev/null +++ b/spytest/spytest/rps.py @@ -0,0 +1,326 @@ +import time +import logging +import telnetlib + +class RPS(object): + + def __init__(self, model, ip, port, outlet, username, password, + logger=None, dbg_lvl=0, desc=""): + """ + Construction of the RPS object + :param model: RPS Model Rariron/ServerTech/Avocent/AvocentRoot + :type model: basestring + :param ip: IPv4 Address to Telnet + :type ip: + :param port: IPv4 Port to Telnet + :type port: + :param outlet: + :type outlet: + :param username: + :type username: basestring + :param password: + :type password: basestring + :param logger: + :type logger: + """ + self.logger = logger or logging.getLogger() + self.tn = None + self.model = model + self.ip = ip + self.port = port + self.outlet = outlet + self.pdu_id = None + self.username = username + self.password = password + self.desc = desc + self.timeout = 600 + self.dbg_lvl = dbg_lvl + self.off_delay = 5 + self.on_delay = 90 + self.disc_delay = 0 + self.login_prompt_timeout = 60 + if self.model == "Raritan": + self.login_prompt = "Login:" + self.password_prompt = "Password:" + self.base_prompt = ">" + self.base_prompt = "clp:/->" + self.fail_msg = "Login failed." + self.multi_support = False + self.disc_delay = 10 + elif self.model == "ServerTech": + self.login_prompt = "Username:" + self.password_prompt = "Password:" + self.base_prompt = "Sentry:|Switched CDU:|Switched PDU:" + self.fail_msg = "Access denied" + self.multi_support = False + self.disc_delay = 10 + elif self.model == "Avocent": + self.login_prompt = "login:" + self.password_prompt = "Password:" + self.base_prompt = "cli->" + self.base_prompt2 = "cli->" + self.confirm_prompt = " (yes, no)" + self.fail_msg = "Access denied" + self.off_delay = 30 + self.on_delay = 120 + self.multi_support = True + elif self.model == "AvocentRoot": + self.login_prompt = "login:" + self.password_prompt = "Password:" + self.base_prompt2 = "#" + self.base_prompt = "cli->" + self.confirm_prompt = " (yes, no)" + self.fail_msg = "Access denied" + self.off_delay = 30 + self.on_delay = 120 + self.multi_support = True + elif self.model == "vsonic": + self.login_prompt = "" + self.password_prompt = "" + self.base_prompt2 = "#" + self.base_prompt = "#" + self.confirm_prompt = "" + self.fail_msg = "Access denied" + self.off_delay = 10 + self.on_delay = 10 + self.multi_support = False + else: + msg = "TODO: model={}".format(self.model) + self.logger.log(msg, lvl=logging.WARNING) + self.multi_support = False + + def logmsg(self, msg, lvl=logging.INFO): + if self.desc: + prefix = "RPS({}): ".format(self.desc) + else: + prefix = "RPS: " + msg2 = "{}{}".format(prefix, msg) + self.logger.log(lvl, msg2) + + def has_multi_support(self): + return self.multi_support + + def set_pdu_id(self, pdu_id): + """ + todo: Update Documentation + :param pdu_id: + :type pdu_id: + :return: + :rtype: + """ + self.pdu_id = pdu_id + + def _connect(self, base_prompt=None): + self.logmsg("connect", lvl=logging.WARNING) + self.tn = telnetlib.Telnet(self.ip, port=self.port) + self.tn.set_debuglevel(self.dbg_lvl) + + if len(self.username) == 0: + self._write("", self.base_prompt) + return True + + try: + self.logmsg("wait for login prompt") + self.tn.read_until(self.login_prompt, self.login_prompt_timeout) + except EOFError as e: + msg = "connection closed {}".format(e) + self.logmsg(msg, lvl=logging.ERROR) + return False + + if not base_prompt: + if self.model == "AvocentRoot": + base_prompt = self.base_prompt2 + else: + base_prompt = self.base_prompt + + if self.password: + self._write(self.username, self.password_prompt) + if not self._write(self.password, base_prompt, self.fail_msg): + self.logmsg("Invalid password", lvl=logging.WARNING) + return False + else: + self._write(self.username, base_prompt) + + if self.model == "AvocentRoot": + self._write("cli", self.base_prompt) + + return True + + def _disconnect(self): + self.logmsg("disconnect", lvl=logging.WARNING) + self.tn.close() + self.tn = None + if self.disc_delay > 0: + self._wait(self.disc_delay) + + def _wait(self, val): + time.sleep(val) + + def _write(self, cmd, prompt=None, fail_msg=None, timeout=10): + self.logmsg("sending {}".format(cmd)) + rv = self.tn.write(cmd + "\r\n") + if prompt: + try: + self.logmsg("wait for prompt {}".format(prompt)) + rv = self.tn.read_until(prompt, timeout) + if fail_msg: + if rv.strip() == fail_msg: + return None + return rv + except EOFError as e: + msg = "connection closed {}".format(e) + self.logmsg(msg, lvl=logging.ERROR) + return None + return rv + + def do_op(self, op, on_delay=None, off_delay=None, outlet=None): + """ + todo: Update Documentation + :param op: + :type op: + :return: + :rtype: + """ + op = op.lower() + if op == "on": + self.on(True, on_delay, outlet) + elif op == "off": + self.off(True, off_delay, outlet) + elif op == "reset": + self.reset(True, on_delay, off_delay, outlet) + else: + return False + return True + + def reset(self, disc=True, on_delay=None, off_delay=None, outlet=None): + """ + todo: Update Documentation + :param disc: + :type disc: + :param on_delay: + :type on_delay: + :param off_delay: + :type off_delay: + :param outlet: + :type outlet: + :return: + :rtype: + """ + if not self.tn: + self._connect() + self.off(False, off_delay, outlet) + self.on(False, on_delay, outlet) + if disc: + self._disconnect() + + def off(self, disc=True, off_delay=None, outlet=None): + """ + todo: Update Documentation + :param disc: + :type disc: + :param off_delay: + :type off_delay: + :param outlet: + :type outlet: + :return: + :rtype: + """ + self.logmsg("powering off", lvl=logging.WARNING) + if outlet is None: + outlet = self.outlet + if not self.tn: + if not self._connect(): + self.logmsg("failed to connect", lvl=logging.WARNING) + return + if self.model == "Raritan": + cmd = "set /system1/outlet{} powerstate=off".format(outlet) + self._write(cmd, self.base_prompt) + elif self.model == "ServerTech": + self._write("off {}".format(outlet), self.base_prompt) + elif self.model == "Avocent" or self.model == "AvocentRoot": + if not self.pdu_id: + self.logmsg("PDU_ID must be set for Avocent", lvl=logging.ERROR) + else: + self._cmd_avocent("off", disc, outlet) + elif self.model == "vsonic": + cmd = "vsh sgconf poweroff G {}".format(outlet) + self._write(cmd, "The device is powered off successfully", timeout=120) + self._write("", self.base_prompt) + else: + msg = "TODO: off {}".format(self.model) + self.logmsg(msg, lvl=logging.WARNING) + if off_delay is None: + self._wait(self.off_delay) + elif off_delay > 0: + self._wait(off_delay) + if disc: + self._disconnect() + + def on(self, disc=True, on_delay=None, outlet=None): + """ + todo: Update Documentation + :param disc: + :type disc: + :param on_delay: + :type on_delay: + :param outlet: + :type outlet: + :return: + :rtype: + """ + if outlet is None: + outlet = self.outlet + self.logmsg("powering on", lvl=logging.WARNING) + if not self.tn: + if not self._connect(): + self.logmsg("failed to connect", lvl=logging.WARNING) + return + if self.model == "Raritan": + cmd = "set /system1/outlet{} powerstate=on".format(outlet) + self._write(cmd, self.base_prompt) + elif self.model == "ServerTech": + self._write("on {}".format(outlet), self.base_prompt) + elif self.model == "Avocent" or self.model == "AvocentRoot": + if not self.pdu_id: + self.logmsg("PDU_ID must be set for Avocent", lvl=logging.ERROR) + else: + self._cmd_avocent("on", disc, outlet) + elif self.model == "vsonic": + cmd = "vsh sgconf poweron G {}".format(outlet) + self._write(cmd, "The device is powered on successfully", timeout=30) + self._write("", self.base_prompt) + else: + msg = "TODO: on {}".format(self.model) + self.logmsg(msg, lvl=logging.WARNING) + if on_delay is None: + self._wait(self.on_delay) + elif on_delay > 0: + self._wait(on_delay) + if disc: + self._disconnect() + + def _cmd_avocent(self, s, disc, outlet): + if isinstance(outlet, list): + outlet = ",".join(map(str, outlet)) + cmd = "cd /access/{}".format(self.pdu_id) + self._write(cmd, self.base_prompt) + self._write("{} {}".format(s, outlet), self.confirm_prompt) + rv = self._write("yes", self.base_prompt) + if "RPS: Error: Invalid Target name" in rv: + self.logmsg(rv, lvl=logging.ERROR) + + +if __name__ == "__main__": + logging.basicConfig() + rps = RPS("vsonic", "10.0.3.150", 7002, "D1", "", "", dbg_lvl=1, desc="VS") + rps.reset() + #rps = RPS("Avocent", "1.1.1.1", 23, 2, "user", "pass") + #rps = RPS("AvocentRoot", "1.1.1.1", 23, 12, "root", "rootpwd") + # rps = RPS("Avocent", "1.1.1.1", 23, 111, "user", "pass") + # rps.set_pdu_id("1b-3b-23P0_1") + # rps.set_pdu_id("12-81-b2P0_1") + #rps = RPS("Raritan", "1.2.3.4", 23, 10, "admin", "admin") + # rps.off() + # rps.on() + # rps.reset() + diff --git a/spytest/spytest/splugin.py b/spytest/spytest/splugin.py new file mode 100644 index 00000000000..73e7ef0a479 --- /dev/null +++ b/spytest/spytest/splugin.py @@ -0,0 +1,204 @@ +import pytest +import spytest.framework as stf +from utilities.common import get_proc_name + +def trace(fmt, *args): + if args: + stf.dtrace(fmt % args) + else: + stf.dtrace(fmt) + +def unused_pytest_collect_file(parent, path): + trace("\n%s: start", get_proc_name()) + trace("{} {}".format(parent, path)) + trace("%s: end\n", get_proc_name()) + +def pytest_itemcollected(item): + trace("\n%s: start", get_proc_name()) + trace("{} {} {}".format(item.name, item.fspath, item.nodeid)) + stf.collect_test(item) + trace("%s: end\n", get_proc_name()) + +@pytest.hookimpl(trylast=True) +def pytest_collection_modifyitems(session, config, items): + trace("\n%s: start", get_proc_name()) + trace("{}".format(items)) + stf.modify_tests(config, items) + trace("%s: end\n", get_proc_name()) + +def unused_pytest_runtest_logstart(nodeid, location): + trace("\n%s: start", get_proc_name()) + trace("{} {}".format(nodeid, location)) + trace("%s: end\n", get_proc_name()) + +# this gets called in xdist for every test completion +def pytest_runtest_logreport(report): + trace("\n%s: start", get_proc_name()) + trace("{}".format(report)) + stf.log_report(report) + trace("%s: end\n", get_proc_name()) + +def pytest_runtest_makereport(item, call): + trace("\n%s: start", get_proc_name()) + trace("{} {}".format(item, call)) + stf.make_report(item, call) + trace("%s: end\n", get_proc_name()) + +def unused_pytest_runtest_setup(item): + trace("\n%s: start", get_proc_name()) + trace("{}".format(item)) + trace("%s: end\n", get_proc_name()) + +def unused_pytest_runtest_call(item): + trace("\n%s: start", get_proc_name()) + trace("{}".format(item)) + trace("%s: end\n", get_proc_name()) + +@pytest.hookspec(firstresult=True) +def unused_pytest_runtest_protocol(item, nextitem): + print("\n%s: start", get_proc_name()) + print("{}".format(item)) + print("{}".format(nextitem)) + print("%s: end\n", get_proc_name()) + +def pytest_addoption(parser): + trace("\n%s: start", get_proc_name()) + stf.add_options(parser) + trace("%s: end\n", get_proc_name()) + +@pytest.hookimpl(trylast=True) +def pytest_configure(config): + trace("\n%s: start", get_proc_name()) + trace("{}".format(config)) + stf.configure(config) + trace("%s: end\n", get_proc_name()) + +def pytest_unconfigure(config): + trace("\n%s: start", get_proc_name()) + trace("{}".format(config)) + rv = stf.unconfigure(config) + trace("%s: end\n", get_proc_name()) + return rv + +def pytest_xdist_setupnodes(config, specs): + trace("\n%s: start", get_proc_name()) + trace("{}".format(config)) + stf.configure_nodes(config) + trace("%s: end\n", get_proc_name()) + +def pytest_configure_node(node): + trace("\n%s: start", get_proc_name()) + trace("{}".format(node)) + stf.configure_node(node) + trace("%s: end\n", get_proc_name()) + +def pytest_xdist_newgateway(gateway): + trace("\n%s: start", get_proc_name()) + trace("{}".format(gateway)) + stf.begin_node(gateway) + trace("%s: end\n", get_proc_name()) + +def pytest_testnodedown(node, error): + trace("\n%s: start", get_proc_name()) + trace("{} {}".format(node, error)) + stf.finish_node(node, error) + trace("%s: end\n", get_proc_name()) + +def pytest_exception_interact(node, call, report): + trace("\n%s: start", get_proc_name()) + if report.failed: + stf.log_test_exception(call.excinfo) + trace("%s: end\n", get_proc_name()) + +def pytest_xdist_make_scheduler(config, log): + trace("\n%s: start", get_proc_name()) + trace("{}".format(config)) + rv = stf.make_scheduler(config, log) + trace("%s: end\n", get_proc_name()) + return rv + +@pytest.hookimpl(hookwrapper=True) +def pytest_fixture_setup(fixturedef, request): + trace("\n%s: start", get_proc_name()) + trace("{}".format(fixturedef)) + trace("{}".format(request)) + stf.fixture_setup(fixturedef, request) + yield + stf.fixture_setup_finish(fixturedef, request) + trace("\n%s: end", get_proc_name()) + trace("{}".format(fixturedef)) + trace("{}".format(request)) + +@pytest.hookimpl(tryfirst=True) +@pytest.hookspec(firstresult=True) +def unused_pytest_fixture_setup(fixturedef, request): + trace("\n%s: start", get_proc_name()) + trace("{}".format(fixturedef)) + trace("{}".format(request)) + rv = stf.fixture_setup(fixturedef, request) + return rv + +def pytest_fixture_post_finalizer(fixturedef, request): + trace("\n%s: start", get_proc_name()) + trace("{}".format(fixturedef)) + trace("{}".format(request)) + rv = stf.fixture_post_finalizer(fixturedef, request) + trace("%s: end\n", get_proc_name()) + return rv + +def pytest_sessionstart(session): + trace("\n%s: start", get_proc_name()) + trace("{}".format(session)) + rv = stf.session_start(session) + trace("%s: end\n", get_proc_name()) + return rv + +def pytest_sessionfinish(session, exitstatus): + trace("\n%s: start", get_proc_name()) + trace("{}".format(session)) + trace("{}".format(exitstatus)) + rv = stf.session_finish(session, exitstatus) + trace("%s: end\n", get_proc_name()) + return rv + +def unused_pytest_keyboard_interrupt(excinfo): + trace("\n%s: start", get_proc_name()) + trace("{}".format(excinfo)) + trace("%s: end\n", get_proc_name()) + +@pytest.hookimpl(hookwrapper=True) +def pytest_pyfunc_call(pyfuncitem): + trace("\n%s: prolog", get_proc_name()) + stf.pyfunc_call(pyfuncitem, False) + yield + stf.pyfunc_call(pyfuncitem, True) + trace("\n%s: epilog", get_proc_name()) + +@pytest.fixture(scope="session", autouse=True) +def global_session_request(request): + trace("\n----------global session start------------\n") + stf.fixture_callback(request, "session", False) + yield + stf.fixture_callback(request, "session", True) + trace("\n----------global session end------------\n") + +@pytest.fixture(scope="module", autouse=True) +def global_module_hook(request): + trace("\n----------global module start------------\n") + rv = stf.fixture_callback(request, "module", False) + if rv: + return rv + def fin(): + rv = stf.fixture_callback(request, "module", True) + trace("\n----------global module end------------\n") + return rv + request.addfinalizer(fin) + +@pytest.fixture(scope="function", autouse=True) +def global_function_hook(request): + trace("\n----------global test start------------\n") + stf.fixture_callback(request, "function", False) + yield + stf.fixture_callback(request, "function", True) + trace("\n----------global test end------------\n") + diff --git a/spytest/spytest/spydist.py b/spytest/spytest/spydist.py new file mode 100644 index 00000000000..a73dac97bb4 --- /dev/null +++ b/spytest/spytest/spydist.py @@ -0,0 +1,276 @@ +from __future__ import print_function + +import os +import rpyc +import signal +import threading +import time + +import pytest +from multiprocessing import Process + +import spytest +from spytest.dicts import SpyTestDict +import utilities.common as utils + +wa = SpyTestDict() +wa.parse_logs = [] + +# new batch implementation variables +wa.start_slaves_from_master = True +wa.slave_index = 1 +wa.debug_level = 1 + +def debug(*args, **kwargs): + if wa.debug_level > 0: + trace(*args, **kwargs) + +def trace(*args, **kwargs): + msg = " ".join(map(str,args)) + print(msg) + +class BatchService(rpyc.Service): + def __init__(self): + self.ready = False + self.items = [] + self.status = [] + self.slave_pids = {} + + def set_items(self, items): + self.items = items + self.status = [] + for item in items: + self.status.append(0) + self.ready = True + + def on_connect(self, conn): + debug("BatchService connected", conn) + + def on_disconnect(self, conn): + debug("BatchService disconnected", conn) + + def exposed_shutdown(self): + for pid in self.slave_pids: + os.kill(pid, signal.SIGTERM) + + def exposed_is_ready(self, pid): + if pid not in self.slave_pids: + self.slave_pids[pid] = 0 + return self.ready + + def exposed_has_pending(self): + for i in range(0, len(self.status)): + if self.status[i] != 2: + return True + return False + + def exposed_finish_test(self, nodeid): + for i, ent in enumerate(self.items): + if ent.nodeid == nodeid: + self.status[i] = 2 + + def exposed_get_test(self): + for i, ent in enumerate(self.items): + if self.status[i] == 0: + self.status[i] = 1 + return self.items[i].nodeid + return None + +class BatchMaster(object): + def __init__(self, config, logs_path): + self.config = config + self.logs_path = logs_path + self.service = BatchService() + self.port = 0 + self.server = None + self.thread = None + + @pytest.hookimpl(trylast=True) + def pytest_collection_modifyitems(self, session, config, items): + debug("master:", session, config, items) + self.service.set_items(items) + + @pytest.mark.trylast + def pytest_sessionstart(self, session): + debug("master: pytest_sessionstart", session) + self.server = rpyc.utils.server.ThreadedServer(self.service) + self.port = self.server.port + filename = os.path.join(self.logs_path, "batch.server") + utils.write_file(filename, str(self.server.port)) + self.thread = threading.Thread(target=self.server.start) + self.thread.start() + + def pytest_sessionfinish(self, session): + debug("master: pytest_sessionfinish", session) + + def pytest_runtestloop(self): + if wa.start_slaves_from_master: + slaves_init(self.logs_path) + try: + conn = rpyc.connect("127.0.0.1", self.port) + while 1: + if not getattr(conn.root, "has_pending")(): + break + debug("master: pytest_runtestloop") + time.sleep(5) + except KeyboardInterrupt: + trace("master: interrupted") + getattr(conn.root, "shutdown")() + time.sleep(5) + os._exit(0) + + def pytest_terminal_summary(self, terminalreporter): + debug("master: pytest_terminal_summary", terminalreporter) + +class BatchSlave(object): + + def __init__(self, config, logs_path): + self.config = config + self.items = [] + self.logs_path = logs_path + + @pytest.mark.trylast + def pytest_sessionstart(self, session): + debug("slave: pytest_sessionstart", session) + + def pytest_sessionfinish(self, session): + debug("slave: pytest_sessionfinish", session) + + @pytest.hookimpl(trylast=True) + def pytest_collection_modifyitems(self, session, config, items): + debug("slave: pytest_collection_modifyitems", session, config, items) + self.items = items + + def pytest_runtestloop(self): + + def search_nodeid(entries, nodeid): + for ent in entries: + if nodeid == ent.nodeid: + return ent + return None + + def finish_test(item): + getattr(conn.root, "finish_test")(item.nodeid) + + def get_test(entries): + while 1: + nodeid = getattr(conn.root, "get_test")() + if not nodeid: + break + item = search_nodeid(entries, nodeid) + if item: + return item + return None + + # connect to batch server + conn = None + for i in range(0, 10): + try: + filename = os.path.join(self.logs_path, "..", "batch.server") + lines = utils.read_lines(filename) + port = int(lines[0]) + conn = rpyc.connect("127.0.0.1", port) + if conn and conn.root: + break + time.sleep(2) + except Exception as exp: + print("connect to batch server", exp, filename, port) + time.sleep(2) + + try: + item_list = [] + + # wait for master ready + is_ready = getattr(conn.root, "is_ready") + while not is_ready(os.getpid()): + trace("slave: waiting for master") + time.sleep(2) + + # get first item + item = get_test(self.items) + if item: + item_list.append(item) + + while 1: + # check if there is some thing to do + if not item_list: + break + + # get next item + item = get_test(self.items) + if item: + item_list.append(item) + + # get the item and next for the current execution + [item, nextitem] = [item_list.pop(0), None] + if item_list: + nextitem = item_list[-1] + + debug("slave: pytest_runtestloop", item, nextitem) + self.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem) + finish_test(item) + except KeyboardInterrupt: + trace("slave: interrupted") + conn.close() + trace("") + os._exit(0) + + def pytest_terminal_summary(self, terminalreporter): + debug("slave: pytest_terminal_summary", terminalreporter) + +def get_impl_type(): + return 0 # not yet supported + #new_bach_run = os.getenv("SPYTEST_BATCH_RUN_NEW") + #if new_bach_run == "2": + #return 2 + #return 1 if bool(new_bach_run) else 0 + +def shutdown(): + if get_impl_type() == 0: + return + if wa.server: wa.server.stop() + if wa.service: wa.service.close() + +def slave_main(index, testbed_file, logs_path): + + os.environ["PYTEST_XDIST_WORKER"] = str(index) + key = "SPYTEST_TESTBED_FILE_gw{}".format(index) + os.environ[key] = testbed_file + os.environ["SPYTEST_TESTBED_FILE"] = testbed_file + spytest.main(True) + +def slave_start(testbed_file, logs_path): + debug("starting slave", testbed_file, wa.slave_index) + p = Process(target=slave_main, args=(wa.slave_index,testbed_file,logs_path)) + p.start() + wa.slave_index = wa.slave_index + 1 + +def slaves_init(logs_path): + if get_impl_type() == 2: + # present auto slave init + return + count = wa.count + for index in range(0, count): + key = "SPYTEST_TESTBED_FILE_gw{}".format(index) + slave_start(os.getenv(key), logs_path) + +def configure(config, logs_path, is_slave): + if get_impl_type() == 0: + return + if is_slave: + debug("============== batch configure slave =====================") + slave = BatchSlave(config, logs_path) + config.pluginmanager.register(slave, "batch.slave") + else: + debug("============== batch configure master =====================") + if "SPYTEST_BATCH_RUN" in os.environ: + del os.environ["SPYTEST_BATCH_RUN"] + master = BatchMaster(config, logs_path) + config.pluginmanager.register(master, "batch.master") + +def parse_args(count, l): + if get_impl_type() == 0: + return l + wa.count = count + return [] + diff --git a/spytest/spytest/st_time.py b/spytest/spytest/st_time.py new file mode 100644 index 00000000000..e8c89836788 --- /dev/null +++ b/spytest/spytest/st_time.py @@ -0,0 +1,23 @@ +import datetime + +from utilities.common import time_diff + +def get_timestamp(ms=True, this=None): + if not this: + this = datetime.datetime.utcnow() + if ms: + return this.strftime('%Y-%m-%d %H:%M:%S,%f')[:-3] + else: + return this.strftime('%Y-%m-%d %H:%M:%S') + +def get_timenow(): + return datetime.datetime.utcnow() + +def get_elapsed(start, fmt=False, add=0): + return time_diff(start, get_timenow(), fmt, add) + +def parse(s, fmt="%Y-%m-%d %H:%M:%S"): + return datetime.datetime.strptime(s, fmt) + +def diff(s, fmt="%Y-%m-%d %H:%M:%S"): + return get_elapsed(parse(s, fmt)) diff --git a/spytest/spytest/template.py b/spytest/spytest/template.py new file mode 100644 index 00000000000..5b6131c891f --- /dev/null +++ b/spytest/spytest/template.py @@ -0,0 +1,90 @@ +import sys +import os + +import textfsm +try: + import clitable +except: + import textfsm.clitable as clitable + + +class Template(object): + """ + todo: Update Documentation + """ + + def __init__(self, platform=None): + """ + Construction of Template object + """ + self.root = os.path.join(os.path.dirname(__file__), '..', 'templates') + self.samples = os.path.join(self.root, 'test') + self.cli_table = clitable.CliTable('index', self.root) + self.platform = platform + + def read_sample(self, cmd): + try: + attrs = dict(Command=cmd) + row_idx = self.cli_table.index.GetRowMatch(attrs) + template = self.cli_table.index.index[row_idx]['Template'] + sample = os.path.splitext(template)[0]+'.txt' + sample = os.path.join(self.samples, sample) + fh = open(sample, 'r') + data = fh.read() + fh.close() + return data + except: + return "" + + def apply(self, output, cmd): + """ + todo: Update Documentation + :param output: + :type output: + :param cmd: + :type cmd: + :return: + :rtype: + """ + attrs = dict(Command=cmd) + if self.platform: attrs["Platform"] = self.platform + try: + self.cli_table.ParseCmd(output, attrs) + objs = [] + for row in self.cli_table: + temp_dict = {} + for index, element in enumerate(row): + if index >= len(self.cli_table.header): + print("HEADER: {} ROW: {}".format(self.cli_table.header, row)) + temp_dict[self.cli_table.header[index].lower()] = element + objs.append(temp_dict) + return objs + except clitable.CliTableError as e: + raise Exception('Unable to parse command "%s" - %s' % (cmd, str(e))) + + def apply_textfsm(self, tmpl_file, data): + """ + todo: Update Documentation + :param tmpl_file: + :type tmpl_file: + :param data: + :type data: + :return: + :rtype: + """ + tmpl_file2 = os.path.join(self.root, tmpl_file) + tmpl_fp = open(tmpl_file2, "r") + out = textfsm.TextFSM(tmpl_fp).ParseText(data) + tmpl_fp.close() + return out + +if __name__ == "__main__": + template = Template() + if len(sys.argv) > 2: + f = open(sys.argv[2], "r") + rv = template.apply(f.read(), sys.argv[1]) + else: + f = open(sys.argv[1], "r") + rv = template.apply_textfsm("unix_ifcfg.tmpl", f.read()) + print (rv) + diff --git a/spytest/spytest/testbed.py b/spytest/spytest/testbed.py new file mode 100644 index 00000000000..088d3a9bcd9 --- /dev/null +++ b/spytest/spytest/testbed.py @@ -0,0 +1,1821 @@ +import os +import sys +import re +import copy +import json +import shutil +import logging +import tempfile +import yaml +from itertools import permutations +from collections import OrderedDict + +from spytest.ordyaml import OrderedYaml +from spytest.dicts import SpyTestDict +import utilities.common as utils + +testbeds_root = os.path.join(os.path.dirname(__file__), '..') +testbeds_root = os.path.join(os.path.abspath(testbeds_root), "testbeds") + +class Testbed(object): + """ + todo: Update Documentation + """ + + def __init__(self, filename=None, logger=None, cfg=None, flex_dut=False): + """ + Construction of Testbed object + :param filename: + :type filename: + :param logger: + :type logger: + """ + self._paths = [] + self._paths.append(os.getenv("SPYTEST_USER_ROOT")) + self._paths.append(testbeds_root) + self.validation_errors = [] + self.oyaml = None + self.offset = None + self.common_tgen_ports = True + self.cfg = cfg + if cfg: + self.flex_dut = cfg.flex_dut + self.filemode = cfg.filemode + self.exclude_devices = cfg.exclude_devices + self.include_devices = cfg.include_devices + else: + self.flex_dut = flex_dut + self.filemode = False + self.exclude_devices = None + self.include_devices = None + if self.exclude_devices: + self.exclude_devices = utils.split_byall(self.exclude_devices, True) + if self.include_devices: + self.include_devices = utils.split_byall(self.include_devices, True) + self.derived = SpyTestDict() + self.derived.devices = None + self.topology = SpyTestDict() + self.devices = SpyTestDict() + self.colors = SpyTestDict() + self.services = SpyTestDict() + self.configs = SpyTestDict() + self.builds = SpyTestDict() + self.errors = SpyTestDict() + self.speeds = SpyTestDict() + self.instrument = SpyTestDict() + self.build_default_errors() + self.links = SpyTestDict() + self.reserved_links = SpyTestDict() + self.params = SpyTestDict() + self.global_params = SpyTestDict() + self.devices_state = SpyTestDict() + self.devices_port_state = SpyTestDict() + self.pertest_topo_checking = False + self.valid = False + self.logger = logger or logging.getLogger() + self._load_and_check(filename) + + def _locate(self, filename): + for path in self._paths: + filename1 = os.path.join(path, filename) + if os.path.isfile(filename1): + return filename1 + if os.path.isfile(filename): + return filename + return None + + def build_default_errors(self): + default = SpyTestDict() + default.containing_error = SpyTestDict() + default.containing_error.command = ".*" + default.containing_error.search = ".*Error:.*" + default.containing_error.action = ["raise"] + default.containing_bang = SpyTestDict() + default.containing_bang.command = ".*" + default.containing_bang.search = ".*\\^.*" + default.containing_bang.action = ["raise"] + self.errors.default = default + + def __del__(self): + pass + + def is_valid(self): + """ + todo: Update Documentation + :return: + :rtype: + """ + return self.valid + + def get_file_path(self): + return self.oyaml.get_file_path() + + def get_raw(self, expanded=False): + return self.oyaml.get_raw(expanded) + + def get_dut_access(self, dut_id): + retval = SpyTestDict() + for dinfo in self.topology.devices.values(): + if dinfo["__name__"] == dut_id: + retval["dut_name"] = dinfo.__name0__ + retval["alias"] = dinfo.alias + retval["ip"] = dinfo.access.ip + retval["port"] = dinfo.access.port + retval["mgmt_ipmask"] = dinfo.access.get("mgmt_ipmask" , None) + retval["mgmt_gw"] = dinfo.access.get("mgmt_gw" , None) + retval["username"] = dinfo.credentials.username + retval["password"] = dinfo.credentials.password + retval["altpassword"] = dinfo.credentials.altpassword + retval["errors"] = self.get_error(dut_id, None) + restore_build = self.get_build(dut_id, "restore") + current_build = self.get_build(dut_id, "current") + if restore_build: + retval["onie_image"] = restore_build + elif current_build: + retval["onie_image"] = current_build + else: + retval["onie_image"] = None + if dinfo.device_type in ["fastpath"]: + device_model = "fastpath" + elif dinfo.device_type in ["icos"]: + device_model = "icos" + else: + device_model = "sonic" + if dinfo.access.protocol == "ssh": + retval["access_model"] = "{}_ssh".format(device_model) + elif dinfo.access.protocol == "sshcon": + retval["sshcon_username"] = dinfo.access.username + retval["sshcon_password"] = dinfo.access.password + retval["access_model"] = "{}_sshcon".format(device_model) + else: + retval["access_model"] = "{}_terminal".format(device_model) + retval["device_model"] = device_model + return retval + return None + + def get_device_info(self, name, dtype=None): + for dut, dinfo in self.topology.devices.items(): + if not dtype or dinfo.type == dtype: + if dinfo["__name__"] == name: + return dinfo + return None + + def get_device_alias(self, name, only=False): + for dut, dinfo in self.topology.devices.items(): + if dinfo.__name__ == name or dinfo.__name0__ == name: + if only: + return dinfo.alias + return "{}({})".format(dinfo.__name0__, dinfo.alias) + return "UNKNOWN" + + def _load_and_check(self, filename): + # testbed file order: argument, env, default + if not filename: + filename = os.getenv("SPYTEST_TESTBED_FILE", "testbed.yaml") + if self._load_yaml(filename): + self.valid = True + if self._build_link_info(): + self._validate() + return self.topology + + def _valition_error(self, msg): + self.logger.error(msg) + self.validation_errors.append(msg) + + def _validate(self): + self.validation_errors = [] + self._validate_passwords() + self._validate_config_files() + tg_ips = self._validate_tgen_info() + rps_ips = self._validate_rps_info(tg_ips) + self._validate_consoles(tg_ips, rps_ips, []) + self._validate_links() + + # verify duplicate access details + def _validate_consoles(self, tg_ips, rps_ips, exclude): + consoles = [] + for dev, dinfo in self.topology.devices.items(): + if dinfo.type != "DUT": + continue + access = dinfo.access + if access.ip in tg_ips: + msg = "{}: IP {} already used for TG".format(dev, access.ip) + self._valition_error(msg) + self.valid = False + if access.ip in rps_ips: + msg = "{}: IP {} already used for RPS".format(dev, access.ip) + self._valition_error(msg) + self.valid = False + ent = "{}:{}:{}".format(access.protocol, access.ip, access.port) + if ent in exclude: + msg = "{}: already used".format(ent) + self._valition_error(msg) + self.valid = False + if ent not in consoles: + consoles.append(ent) + continue + msg = "Duplicate console info {}".format(ent) + self._valition_error(msg) + self.valid = True + return consoles + + # verify duplicate TG IP addresses + def _validate_tgen_info(self): + versions = dict() + types = dict() + ix_servers = [] + for dev in self.get_device_names("TG"): + tinfo = self.get_tg_info(dev) + if tinfo["type"] not in versions: + versions[tinfo.type] = tinfo.version + elif versions[tinfo.type] != tinfo.version: + msg = "multiple versions ({}, {}) of same TG type not supported" + msg = msg.format(versions[tinfo.type], tinfo.version) + self._valition_error(msg) + self.valid = False + if tinfo.ip not in types: + types[tinfo.ip] = tinfo.type + elif types[tinfo.ip] != tinfo.type: + msg = "same ip ({}) cant be used for multiple TG types" + msg = msg.format(tinfo.ip) + self._valition_error(msg) + self.valid = False + for dev in self.get_device_names("TG"): + tinfo = self.get_tg_info(dev) + if "ix_server" in tinfo: + for ix_server in utils.make_list(tinfo.ix_server): + if ix_server in ix_servers: + continue + ix_servers.append(ix_server) + if ix_server in types and tinfo.ip != ix_server: + msg = "ix_server ip ({}) already used as TG IP" + msg = msg.format(ix_server) + self._valition_error(msg) + self.valid = False + ix_servers.extend(types.keys()) + return ix_servers + + # verify duplicate RPS IP addresses + def _validate_rps_info(self, tg_ips): + outlets = dict() + models = dict() + for dev in self.get_device_names("DUT"): + tinfo = self.get_rps(dev) + if tinfo == None: + continue + if tinfo.ip not in models: + models[tinfo.ip] = tinfo.model + elif models[tinfo.ip] != tinfo.model: + msg = "same ip ({}) cant be used for multiple RPS models" + msg = msg.format(tinfo.ip) + self._valition_error(msg) + self.valid = False + for dev in self.get_device_names("DUT"): + tinfo = self.get_rps(dev) + if tinfo == None or tinfo.model == "vsonic": + continue + if tinfo.ip not in outlets: + outlets[tinfo.ip] = [] + if tinfo.outlet in outlets[tinfo.ip]: + msg = "RPS outlet ({}/{}) is already used" + msg = msg.format(tinfo.ip, tinfo.outlet) + self._valition_error(msg) + self.valid = False + else: + outlets[tinfo.ip].append(tinfo.outlet) + for ip in outlets: + if ip in tg_ips: + msg = "RPS IP {} already used for TG".format(ip) + self._valition_error(msg) + self.valid = False + return outlets.keys() + + def _validate_links(self): + pairs = dict() + for dev in self.get_device_names(): + for local, partner, remote in self.get_links(dev): + alias = self.get_device_alias(dev) + pair = "{}/{}".format(alias, local) + palias = self.get_device_alias(partner) + to = "{}/{}".format(palias, remote) + if pair in pairs: + msg = "Duplicate Links {} {} connecting to {}" + msg = msg.format(pairs[pair], to, pair) + self.logger.error(msg) + self.valid = False + else: + pairs[pair] = to + + # verify same passwords + def _validate_passwords(self): + for dev, dinfo in self.devices.items(): + if dinfo.type != "DUT" or self.get_device_type(dev) != "sonic": + continue + if dinfo.credentials.password in dinfo.credentials.altpassword or \ + dinfo.credentials.altpassword in dinfo.credentials.password: + msg = "password and altpasswords are alike for device {}".format(dev) + self.logger.error(msg) + self.valid = False + + # verify presence of config files if specified + def _validate_config_files(self): + for dut in self.get_device_names("DUT"): + # verify services + if not self.get_service(dut, None): + msg = "invalid services for {}".format(dut) + self.logger.error(msg) + self.valid = False + # verify builds + if not self.get_build(dut, None): + msg = "invalid build for {}".format(dut) + self.logger.error(msg) + self.valid = False + # verify configs section + if not self.get_config(dut, None): + msg = "invalid config for {}".format(dut) + self.logger.error(msg) + self.valid = False + continue + # verify config files + for scope in ["current", "restore"]: + files = self.get_config(dut, scope) + if files is None: + if scope in ["current", "restore"]: + self.valid = False + continue + for filename in files: + file_path = self.get_config_file_path(filename) + if file_path: + continue + msg = "{} config file {} not found".format(scope, filename) + self.logger.error(msg) + if not self.filemode: + self.valid = False + + def _is_ignored_device(self, dut): + if dut in self.devices: + if "reserved" not in self.devices[dut]: + return False + return True + + def _build_link_info(self): + #utils.print_data(self.topology, "self.topology-1") + + # ensure we have device type + for dev, dinfo in self.devices.items(): + dinfo.type = "DUT" if dinfo.device_type != "TGEN" else "TG" + # reserve the devices from include and exclude list + if self.include_devices: + if str(dev) not in self.include_devices: + dinfo.reserved = True + msg = "reserving device {} as it is NOT IN include_list" + msg = msg.format(dev) + self.logger.warning(msg) + elif self.exclude_devices: + if str(dev) in self.exclude_devices: + dinfo.reserved = True + msg = "reserving device {} as it is IN exclude_list" + msg = msg.format(dev) + self.logger.warning(msg) + + connected_links = [] + reserved_links = [] + # add devices if missing in topology but present in connections + add_devices = [] + unreserved_devices = SpyTestDict() + + # removed reserved/ignored devices from topology + for dut, dinfo in self.topology.devices.items(): + if not self._is_ignored_device(dut): + unreserved_devices[dut] = dinfo + self.topology.devices = unreserved_devices + + for dut, dinfo in self.topology.devices.items(): + #utils.print_data(dinfo, "dinfo - {}".format(dut)) + if not dinfo or "interfaces" not in dinfo: + continue + if not isinstance(dinfo.interfaces, dict): + msg = "interfaces section of {} is invalid - ignoring".format(dut) + self.logger.warning(msg) + #self.valid = False + del dinfo.interfaces + continue + for link, linfo in dinfo.interfaces.items(): + if "reserved" in linfo: + msg = "Reserved link: {}/{}".format(dut, link) + self.logger.debug(msg) + reserved_links.append([dut, link, linfo]) + continue + if "EndDevice" not in linfo: + msg = "EndDevice is not specified for interface {}/{}".format(dut, link) + self.logger.error(msg) + self.valid = False + continue + if "EndPort" not in linfo: + msg = "EndPort is not specified for interface {}/{}".format(dut, link) + self.logger.error(msg) + self.valid = False + continue + EndDevice = linfo.EndDevice + if EndDevice not in self.devices: + msg = "EndDevice {} is not found".format(EndDevice) + self.logger.error(msg) + self.valid = False + continue + if self._is_ignored_device(EndDevice): + msg = "EndDevice {} is reserved ignoring {}/{}".format(EndDevice, dut, link) + self.logger.debug(msg) + reserved_links.append([dut, link, linfo]) + continue + connected_links.append([dut, link, linfo]) + if EndDevice not in self.topology.devices: + add_devices.append(EndDevice) + del self.topology.devices[dut]["interfaces"] + for dut, dinfo in self.topology.devices.items(): + if dut not in self.devices: + msg = "Device {} is not present in devices section".format(dut) + self.logger.error(msg) + self.valid = False + return False + else: + if dinfo: + props = dinfo.get("properties", None) + else: + props = dict() + self.topology.devices[dut] = self.devices[dut] + self.topology.devices[dut]["topo_props"] = props + for dut in add_devices: + self.topology.devices[dut] = self.devices[dut] + #utils.print_data(self.topology, "self.topology-2") + + # add DUT internal name + dut_index = tg_index = 1 + for dut, dinfo in self.topology.devices.items(): + dinfo["type"] = "DUT" if self.devices[dut].device_type != "TGEN" else "TG" + if dinfo["type"] == "DUT": + if self.offset is None: + dinfo["__name__"] = dut + else: + dinfo["__name__"] = "D{}".format(dut_index + self.offset) + dinfo["__name0__"] = "D{}".format(dut_index) + dinfo.alias = dut + dut_index += 1 + elif dinfo["type"] == "TG": + if self.offset is None: + dinfo["__name__"] = dut + else: + dinfo["__name__"] = "T{}".format(tg_index + self.offset) + dinfo["__name0__"] = "T{}".format(tg_index) + dinfo.alias = dut + tg_index += 1 + #utils.print_yaml(self.topology, "self.topology-4") + + for dut, link, linfo in reserved_links: + ent = SpyTestDict({ + "from_port": link, "from_dut": self.devices[dut].__name__ + }) + link_name = "{}-{}".format(ent.from_dut, ent.from_port) + self.reserved_links[link_name] = ent + #utils.print_yaml(self.reserved_links, "self.reserved_links") + + self.links = SpyTestDict() + for dut, link, linfo in connected_links: + ent = SpyTestDict({ + "from_port": link, "from_dut": self.devices[dut].__name__, + "to_port": linfo.EndPort, "to_dut": self.devices[linfo.EndDevice].__name__, + "from_type": self.devices[dut].type, + "to_type": self.devices[linfo.EndDevice].type, + "speed": linfo.get("speed") + }) + ent.params = None if "params" not in linfo else linfo.params + link_name = "{}-{}-{}-{}".format(ent.from_dut, ent.from_port, + ent.to_dut, ent.to_port) + + # if what is added is duplicate + ent = SpyTestDict({ + "to_port": link, "to_dut": self.devices[dut].__name__, + "from_port": linfo.EndPort, "from_dut": self.devices[linfo.EndDevice].__name__, + "to_type": self.devices[dut].type, + "from_type": self.devices[linfo.EndDevice].type, + "speed": linfo.get("speed") + }) + link_name2 = "{}-{}-{}-{}".format(ent.from_dut, ent.from_port, + ent.to_dut, ent.to_port) + if link_name2 not in self.links: + self.links[link_name] = ent + else: + msg = "Ignoring duplicate link {} existing {}".format(link_name, link_name2) + self.logger.debug(msg) + + # add link name variables + link_indexes = SpyTestDict() + for link_name, linfo in self.links.items(): + from_dev = self.get_device_info(linfo.from_dut, linfo.from_type) + to_dev = self.get_device_info(linfo.to_dut, linfo.to_type) + index_key1 = "{}{}".format(linfo.from_dut, linfo.to_dut) + index_key2 = "{}{}".format(linfo.to_dut, linfo.from_dut) + index_key3 = "{}{}".format(from_dev.__name0__, to_dev.__name0__) + index_key4 = "{}{}".format(to_dev.__name0__, from_dev.__name0__) + index_key = index_key2 if linfo.to_dut < linfo.from_dut else index_key1 + index = 1 if index_key not in link_indexes else link_indexes[index_key] + 1 + link_indexes[index_key] = index + linfo.__name1__ = "{}P{}".format(index_key1, index) + linfo.__name2__ = "{}P{}".format(index_key2, index) + linfo.__name3__ = "{}P{}".format(index_key3, index) + linfo.__name4__ = "{}P{}".format(index_key4, index) + index += 1 + return True + + def _load_yaml(self, filename): + errs = [] + try: + user_root = os.getenv("SPYTEST_USER_ROOT") + if user_root: + self.oyaml = OrderedYaml(filename, [user_root, testbeds_root]) + else: + self.oyaml = OrderedYaml(filename, [testbeds_root]) + if not self.oyaml.is_valid(): + errs = self.oyaml.get_errors() + self.logger.error(errs) + return None + obj = self.oyaml.get_data() + #utils.print_yaml(obj, "TESTBED FILE CONTENT") + if "devices" not in obj: + errs.append("devices not found") + if "services" not in obj: + errs.append("services not found") + if "configs" not in obj: + errs.append("configs not found") + if "builds" not in obj: + errs.append("builds not found") + if "params" not in obj: + errs.append("params not found") + if "topology" not in obj: + errs.append("topology not found") + if errs: + raise ValueError("Invalid testbed file: " + filename, errs) + if "global" in obj and "params" in obj["global"]: + self.global_params = obj["global"]["params"] + self.devices = obj["devices"] + self.services = obj["services"] + self.configs = obj["configs"] + self.builds = obj["builds"] + if "errors" not in obj: + self.logger.warning("errors section not found - using defaults") + else: + self.errors = obj["errors"] + if "speeds" not in obj: + self.logger.warning("speeds section not found") + else: + self.speeds = obj["speeds"] + if "instrument" not in obj: + self.logger.debug("instrument section not found") + else: + self.instrument = obj["instrument"] + self.params = obj["params"] + self.topology = obj["topology"] + self.colors = SpyTestDict() + try: + self.colors.free = obj["colors"]["free"] + self.colors.used = obj["colors"]["used"] + assert(isinstance(self.colors.used, list)) + assert(isinstance(self.colors.free, str)) + except Exception as e: + #self.logger.warning("using default colors for topology diagram {}".format(e)) + self.colors.free = None + self.colors.used = ["red"] + if "devices" not in self.topology: + devices = SpyTestDict() + for k,v in self.topology.items(): + devices[k] = v + self.topology.clear() + self.topology.properties = SpyTestDict() + self.topology.properties.verifier = "NA" + self.topology.devices = devices + + # override device properties from command line + if self.cfg and self.cfg.dev_prop: + for k,v in self.cfg.dev_prop: + for dev, dinfo in self.devices.items(): + if dinfo.properties: + dinfo.properties[k] = v + + # override ixnetwork from command line + if self.cfg and self.cfg.ixserver: + ix_server = ",".join(self.cfg.ixserver) + for dev, dinfo in self.devices.items(): + if dinfo.device_type == "TGEN" and dinfo.properties: + dinfo.properties["ix_server"] = ix_server + + return self.topology + except ValueError as e: + msg = "\r\nInvalid testbed file {}.\n{}\n".format(filename, errs) + self.logger.error(msg) + except Exception as e: + #import pdb, traceback, sys + #extype, value, tb = sys.exc_info() + #traceback.print_exc() + #pdb.post_mortem(tb) + self.topology = None + self.devices = None + self.services = None + self.configs = None + self.builds = None + self.errors = None + self.speeds = None + self.instrument = None + self.params = None + msg = "\r\nInvalid testbed file {}.\n{}\n".format(filename, e) + self.logger.error(msg) + return None + + def _get_dut_property(self, dut, prop, table, subprop, defprop=None): + for d, dinfo in self.topology.devices.items(): + if dinfo["__name__"] == dut: + if "properties" not in dinfo: + msg = "properties not availbale for {}".format(d) + self.logger.info(msg) + return None + if prop not in dinfo.properties: + if not defprop: + print("'{}' not set in properties for {}".format(prop, d)) + return None + msg = "'{}' not set in properties for {} assuming '{}'".format(prop, d, defprop) + if prop != "instrument": + self.logger.debug(msg) + ref = defprop + else: + ref = dinfo.properties[prop] + table_object = getattr(self, table) + if not table_object or ref not in table_object: + msg = "{}/{} is not found".format(table, ref) + self.logger.debug(msg) + return None + if not subprop or not table_object[ref]: + return table_object[ref] + if subprop not in table_object[ref]: + msg = "{} is not specified in {}/{}".format(subprop, table, ref) + self.logger.info(msg) + return None + return table_object[ref][subprop] + return None + + def get_device_type(self, dut, default="sonic"): + dinfo = self.get_device_info(dut) + if not dinfo: + return default + if dinfo.device_type in ["DevSonic"]: + return "sonic" + return dinfo.device_type + + def get_service(self, dut, name): + """ + todo: Update Documentation + :param name: + :type name: + :param dut: + :type dut: + :return: + :rtype: + """ + return self._get_dut_property(dut, "services", "services", name) + + def get_config(self, dut, scope): + """ + todo: Update Documentation + :param dut: + :type dut: + :param scope: + :type scope: + :return: + :rtype: + """ + return self._get_dut_property(dut, "config", "configs", scope) + + def get_config_file_path(self, file_name): + return self._locate(file_name) + + def get_build(self, dut, scope): + """ + todo: Update Documentation + :param dut: + :type dut: + :param scope: + :type scope: + :return: + :rtype: + """ + return self._get_dut_property(dut, "build", "builds", scope) + + def get_error(self, dut, scope): + return self._get_dut_property(dut, "errors", "errors", scope, "default") + + def get_speed(self, dut, scope=None): + rv = self._get_dut_property(dut, "speed", "speeds", scope, None) + if not rv: rv = SpyTestDict() + for local, partner, remote in self.get_links(dut): + value = self.get_link_param(dut, local, "speed", None) + if value: rv[local] = value + return rv + + def get_instrument(self, dut, scope=None): + return self._get_dut_property(dut, "instrument", "instrument", scope, "default") + + def get_param(self, name, default): + """ + todo: Update Documentation + :param name: + :type name: + :param default: + :type default: + :return: + :rtype: + """ + if not self.global_params: + return default + if not name: + return self.global_params + if name not in self.global_params: + return default + return self.global_params[name] + + def get_device_param(self, dut, name, default): + """ + todo: Update Documentation + :param dut: + :type dut: + :param name: + :type name: + :param default: + :type default: + :return: + :rtype: + """ + for d, dinfo in self.topology.devices.items(): + if dinfo["__name__"] == dut: + if "properties" not in dinfo: + print("properties not availbale for {}".format(d)) + return default + if "params" not in dinfo.properties: + print("params not set in properties for {}".format(d)) + return default + + # check for per dut params overriden + if "params" in dinfo and name in dinfo.params: + return dinfo.params[name] + + ref = dinfo.properties.params + if ref not in self.params: + print("params {} not found".format(ref)) + return default + if not name: + return self.params[ref] + if name not in self.params[ref]: + return default + return self.params[ref][name] + return default + + def get_link_param(self, dut, local, name, default): + for link, linfo in self.links.items(): + if linfo["from_dut"] == dut and linfo["from_port"] == local: + pass + elif linfo["to_dut"] == dut and linfo["to_port"] == local: + pass + else: + continue + + # see if there is paramater overriden + if name in linfo: + return linfo[name] + + if "params" not in linfo or linfo.params is None: + print("params not set in properties for {}".format(link)) + return default + ref = linfo.params + if ref not in self.params: + print("params {} not found".format(ref)) + return default + if not name: + return self.params[ref] + if name not in self.params[ref]: + return default + return self.params[ref][name] + return default + + def get_breakout(self, dut): + retval = [] + dinfo = self.get_device_info(dut) + if dinfo: + breakout = dinfo.get("breakout") + if breakout: + for port, option in breakout.items(): + retval.append([port, option]) + return retval + + def get_device_names(self, dtype=None): + """ + Returns names of all devices of given type + :return: device names of given type + :rtype: list + """ + if self.flex_dut: + if dtype == "DUT" and self.derived.devices: + return self.derived.devices + retval = [] + for dut, dinfo in self.topology.devices.items(): + if not dtype or dinfo["type"] == dtype: + name = dinfo["__name__"] + if name not in retval: + retval.append(name) + return retval + + def get_rerved_links(self, dut): + """ + todo: Update Documentation + :param dut: + :type dut: + :return: + :rtype: + """ + retval = [] + for link, linfo in self.reserved_links.items(): + if linfo["from_dut"] == dut: + retval.append(linfo["from_port"]) + return retval + + def get_links(self, dut, peer=None, dtype=None): + """ + todo: Update Documentation + :param dut: + :type dut: + :return: + :rtype: + """ + retval = [] + for link, linfo in self.links.items(): + if peer: + if linfo["from_dut"] == dut and linfo["to_dut"] == peer: + if not dtype or dtype == linfo["to_type"]: + retval.append([linfo["from_port"], linfo["to_dut"], + linfo["to_port"]]) + if linfo["to_dut"] == dut and linfo["from_dut"] == peer: + if not dtype or dtype == linfo["from_type"]: + retval.append([linfo["to_port"], linfo["from_dut"], + linfo["from_port"]]) + else: + if linfo["from_dut"] == dut: + if not dtype or dtype == linfo["to_type"]: + retval.append([linfo["from_port"], linfo["to_dut"], + linfo["to_port"]]) + if linfo["to_dut"] == dut: + if not dtype or dtype == linfo["from_type"]: + retval.append([linfo["to_port"], linfo["from_dut"], + linfo["from_port"]]) + return retval + + def get_tg_info(self, tg): + """ + Get the properties of given TGEN device + :param tg: Name of the TGEN device + :type tg: string + :return: properties dictionary + :rtype: dict + """ + for ent, dinfo in self.topology.devices.items(): + if dinfo.type == "TG": + if not tg or dinfo["__name__"] == tg: + rv = SpyTestDict() + rv.name = dinfo.__name__ + rv.ip = dinfo.properties.ip + rv.type = dinfo.properties.type + rv.version = dinfo.properties.version + rv.card = getattr(dinfo.properties, "card", "") + if "ix_server" in dinfo.properties: + rv.ix_server = dinfo.properties.ix_server + if "ix_port" in dinfo.properties: + rv.ix_port = dinfo.properties.ix_port + return rv + return None + + def get_rps(self, dut): + """ + Returns RPS details read from testbed file for given DUT + :param dut: DUT identifier + :type dut: basestring + :return: RPS parameters dictionary + :rtype: dict + """ + rv = self.get_device_info(dut) + if rv and "rps" in rv: + return rv["rps"] + return None + + def get_testbed_vars(self): + """ + returns the testbed variables in a dictionary + :return: testbed variables dictionary + :rtype: dict + """ + rv = SpyTestDict() + rv.tgen_list = self.get_device_names("TG") + rv.tgen_ports = SpyTestDict() + rv.dut_list = self.get_device_names("DUT") + dut_index = 1 + for dut in rv.dut_list: + dut_name = "D{}".format(dut_index) + rv[dut_name] = dut + dut_index = dut_index + 1 + tg_index = 1 + tg_types = dict() + for tg in rv.tgen_list: + tg_info = self.get_tg_info(tg) + tg_name = "T{}".format(tg_index) + tg_types[tg_name] = tg_info.type + rv[tg_name] = tg + tg_index = tg_index + 1 + for from_index in range(1, dut_index): + for to_index in range(from_index+1, dut_index): + from_name = "D{}".format(from_index) + to_name = "D{}".format(to_index) + (from_dev, to_dev) = (rv[from_name], rv[to_name]) + links = self.get_links(from_dev, to_dev) + lnum = 1 + for local, partner, remote in links: + lname1 = "{}{}P{}".format(from_name, to_name, lnum) + lname2 = "{}{}P{}".format(to_name, from_name, lnum) + lnum = lnum + 1 + rv[lname1] = local + rv[lname2] = remote + if self.common_tgen_ports: + for to_index in range(1, dut_index): + lnum = 1 + for from_index in range(1, tg_index): + from_name = "T{}".format(from_index) + to_name = "D{}".format(to_index) + (from_dev, to_dev) = (rv[from_name], rv[to_name]) + links = self.get_links(from_dev, to_dev) + for local, partner, remote in links: + lname1 = "T1{}P{}".format(to_name, lnum) + lname2 = "{}T1P{}".format(to_name, lnum) + lnum = lnum + 1 + rv[lname1] = local + rv[lname2] = remote + rv.tgen_ports[lname1] = [from_name, tg_types[from_name], local] + else: + for from_index in range(1, tg_index): + for to_index in range(1, dut_index): + from_name = "T{}".format(from_index) + to_name = "D{}".format(to_index) + (from_dev, to_dev) = (rv[from_name], rv[to_name]) + links = self.get_links(from_dev, to_dev) + lnum = 1 + for local, partner, remote in links: + lname1 = "{}{}P{}".format(from_name, to_name, lnum) + lname2 = "{}{}P{}".format(to_name, from_name, lnum) + lnum = lnum + 1 + rv[lname1] = local + rv[lname2] = remote + rv.tgen_ports[lname1] = [from_name, tg_types[from_name], local] + + return rv + + def get_access(self, string=True): + retval = [] + for dut, dinfo in self.topology.devices.items(): + if dinfo["type"] != "DUT": + continue + retval.append("{} {} {} {} {} {} {}".format(dut, + dinfo.access.protocol, + dinfo.access.ip, + dinfo.access.port, + dinfo.credentials.username, + dinfo.credentials.password, + dinfo.credentials.altpassword)) + return "\n".join(retval) if string else retval + + def get_topo(self): + retval = [] + exclude = [] + for dut, dinfo in self.topology.devices.items(): + partners = OrderedDict() + for local, partner, remote in self.get_links(dut): + partners[partner] = partners.setdefault(partner, 0) + 1 + for partner in partners: + if "{}--{}".format(dut, partner) in exclude: + continue + exclude.append("{}--{}".format(partner, dut)) + pdinfo = self.get_device_info(partner) + retval.append("{}{}:{}".format(dinfo.__name0__,pdinfo.__name0__, partners[partner])) + return ",".join(retval) if retval else "D1" + + def _check_min_links(self, from_type, to_type, res, errs): + from_dev = self.get_device_name("{}{}".format(from_type, res.group(1))) + to_dev = self.get_device_name("{}{}".format(to_type, res.group(2))) + if not from_dev: + errs.append("no_dut") + return [False, from_dev, to_dev] + if int(res.group(3)) != 0 and not to_dev: + errs.append("no_dut") + return [False, from_dev, to_dev] + links = self.get_links(from_dev, to_dev) + if len(links) < int(res.group(3)): + errs.append("no_link") + return [False, from_dev, to_dev] + + if self.pertest_topo_checking and int(res.group(3)) > 0: + if from_type == 'D' and not self.devices_state[from_dev]: + errs.append("dut_down") + return [False, from_dev, to_dev] + if to_type == 'D' and not self.devices_state[to_dev]: + errs.append("dut_down") + return [False, from_dev, to_dev] + for local, partner, remote in links: + if not self.devices_port_state["{}:{}".format(from_dev, local)]: + errs.append("link_down") + return [False, from_dev, to_dev] + return [True, from_dev, to_dev] + + @staticmethod + def _split_args(*args): + sep=os.getenv("SPYTEST_TOPO_SEP", None) + arg_list = [] + for arg in args: + arg_list.extend(utils.split_byall(arg, sep=sep)) + return arg_list + + @staticmethod + def parse_topology (*args): + requests=[] + properties=dict() + errs = [] + arg_list = Testbed._split_args(*args) + for arg in arg_list: + if arg == "D1": + arg = "D1T1:0" + if re.compile(r"^D\d+$").match(arg): + res = re.search(r"^D(\d+)$", arg) + arg = "D{}T1:0".format(res.group(1)) + if re.compile(r"^D\d+D\d+:\d+$").match(arg): + res = re.search(r"^D(\d+)D(\d+):(\d+)$", arg) + if int(res.group(1)) == int(res.group(2)): + errs.append("{}: invalid".format(arg)) + continue + requests.append(["D", "D", res, arg]) + elif re.compile(r"^D\d+T\d+:\d+$").match(arg): + res = re.search(r"^D(\d+)T(\d+):(\d+)$", arg) + requests.append(["D", "T", res, arg]) + elif re.compile(r"^T\d+D\d+:\d+$").match(arg): + res = re.search(r"^T(\d+)D(\d+):(\d+)$", arg) + requests.append(["T", "D", res, arg]) + elif re.compile(r"^D\d+BUILD[:=]\S+$").match(arg): + res = re.search(r"^D(\d+)BUILD[:=](\S+)$", arg) + properties.setdefault("D{}".format(res.group(1)), dict())["BUILD"] = res.group(2) + elif re.compile(r"^D\d+CONFIG[:=]\S+$").match(arg): + res = re.search(r"^D(\d+)CONFIG[:=](\S+)$", arg) + properties.setdefault("D{}".format(res.group(1)), dict())["CONFIG"] = res.group(2) + elif re.compile(r"^D\d+MODEL[:=]\S+$").match(arg): + res = re.search(r"^D(\d+)MODEL[:=](\S+)$", arg) + properties.setdefault("D{}".format(res.group(1)), dict())["MODEL"] = res.group(2) + elif re.compile(r"^D\d+CHIP[:=]\S+$").match(arg): + res = re.search(r"^D(\d+)CHIP[:=](\S+)$", arg) + properties.setdefault("D{}".format(res.group(1)), dict())["CHIP"] = res.group(2) + elif re.compile(r"^D\d+NAME[:=]\S+$").match(arg): + res = re.search(r"^D(\d+)NAME[:=](\S+)$", arg) + properties.setdefault("D{}".format(res.group(1)), dict())["NAME"] = res.group(2) + elif re.compile(r"^BUILD[:=]\S+$").match(arg): + res = re.search(r"^BUILD[:=](\S+)$", arg) + properties.setdefault(None, dict())["BUILD"] = res.group(1) + elif re.compile(r"^CONFIG[:=]\S+$").match(arg): + res = re.search(r"^CONFIG[:=](\S+)$", arg) + properties.setdefault(None, dict())["CONFIG"] = res.group(1) + elif re.compile(r"^MODEL[:=]\S+$").match(arg): + res = re.search(r"^MODEL[:=](\S+)$", arg) + properties.setdefault(None, dict())["MODEL"] = res.group(1) + elif re.compile(r"^CHIP[:=]\S+$").match(arg): + res = re.search(r"^CHIP[:=](\S+)$", arg) + properties.setdefault(None, dict())["CHIP"] = res.group(1) + elif re.compile(r"^TGEN[:=]\S+$").match(arg): + res = re.search(r"^TGEN[:=](\S+)$", arg) + properties.setdefault(None, dict())["TGEN"] = res.group(1) + elif re.compile(r"^NAME[:=]\S+$").match(arg): + res = re.search(r"^NAME[:=](\S+)$", arg) + properties.setdefault(None, dict())["NAME"] = res.group(1) + elif re.compile(r"^NAMES[:=]\S+$").match(arg): + res = re.search(r"^NAMES[:=](\S+)$", arg) + properties.setdefault(None, dict())["NAMES"] = res.group(1) + elif re.compile(r"^CONSOLE_ONLY$").match(arg): + properties.setdefault(None, dict())["CONSOLE_ONLY"] = True + elif re.compile(r"^TGCARD[:=]\S+$").match(arg): + res = re.search(r"^TGCARD[:=](\S+)$", arg) + properties.setdefault(None, dict())["TGCARD"] = res.group(1) + else: + errs.append("{}: unsupported".format(arg)) + requests.append([None, None, 0, arg]) + if errs: + print("parse_topology--errors", errs) + return [requests, properties, errs] + + @staticmethod + def ensure_tgen_model_and_card(logger, tb, properties, errs=[]): + + # check tg model requirements + for tg in tb.get_device_names("TG"): + if not Testbed.check_tgen_model(logger, tb, tg, properties): + errs.append("no_tgen_model") + + # check tg card requirements + for tg in tb.get_device_names("TG"): + if not Testbed.check_tgen_card(logger, tb, tg, properties): + errs.append("no_tgen_card") + + return errs + + def ensure_min_topology(self, *args): + [requests, properties, errs] = self.parse_topology(*args) + if errs: return [errs, properties] + + logger = None #self.logger + + # bailout if TG card/model is not satified + Testbed.ensure_tgen_model_and_card(logger, self, properties, errs) + if errs: return [errs, properties] + + topo_dinfo = OrderedDict() + for from_type, to_type, res, arg in requests: + if not from_type or not to_type: + errs.append("no_dut") + continue + + # check link requirements + [rv, from_dev, to_dev] = self._check_min_links(from_type, to_type, res, errs) + if not rv: + errs.append(arg) + continue + + # collect the devices info to match model name + if properties and from_type == 'D': + dinfo = self.get_device_info(from_dev) + topo_dinfo[dinfo.__name0__] = dinfo + if properties and to_type == 'D': + dinfo = self.get_device_info(to_dev) + topo_dinfo[dinfo.__name0__] = dinfo + + # check model requirements + for dut in topo_dinfo: + if not Testbed.check_model(logger, self, dut, dut, properties): + errs.append("no_dut_model") + + # check chip requirements + for dut in topo_dinfo: + if not Testbed.check_chip(logger, self, dut, dut, properties): + errs.append("no_dut_chip") + + # bail out on errors if not flex dut + if not errs or not self.flex_dut: + return [errs, properties] + + [setup_list, properties, errs2] = self.identify_topology(logger, self, None, 1, *args) + if not setup_list: + return [errs, properties] + self.derived.devices = setup_list[0] + return [[], properties] + + def reset_derived_devices(self): + self.derived.devices = None + + @staticmethod + def sort_topo_dict(topo_dict): + topo_list = list(topo_dict.keys()) + l = len(topo_list) + for i in range(0, l): + for j in range(0, l-i-1): + [a_from_dev, a_from_index, a_to_dev, a_to_index, a_count] = topo_dict.get(topo_list[j]) + [b_from_dev, b_from_index, b_to_dev, b_to_index, b_count] = topo_dict.get(topo_list[j+1]) + if a_from_dev > b_from_dev or a_from_index > b_from_index or \ + a_to_dev > b_to_dev or a_to_index > b_to_index: + tmp = topo_list[j] + topo_list[j]= topo_list[j + 1] + topo_list[j + 1]= tmp + return topo_list + + @staticmethod + def normalize_topo(*args): + + arg_list = Testbed._split_args(*args) + [requests, properties, errs1] = Testbed.parse_topology(*arg_list) + req_duts = dict() + topo_dict = OrderedDict() + for from_dev, to_dev, res, arg in requests: + if not from_dev or not to_dev: + continue + if from_dev == "T": + val = "{}{}{}{}:{}".format(to_dev, res.group(2), \ + from_dev, res.group(1), res.group(3)) + topo_dict[val] = [to_dev, res.group(2), from_dev, res.group(1), res.group(3)] + req_duts["{}{}".format(to_dev, res.group(2))] = 1 + else: + req_duts["{}{}".format(from_dev, res.group(1))] = 1 + if to_dev == "D": + req_duts["{}{}".format(to_dev, res.group(2))] = 1 + if to_dev == "D" and int(res.group(1)) > int(res.group(2)): + val = "{}{}{}{}:{}".format(to_dev, res.group(2), \ + from_dev, res.group(1), res.group(3)) + topo_dict[val] = [to_dev, res.group(2), from_dev, res.group(1), res.group(3)] + else: + topo_dict[arg] = [from_dev, res.group(1), to_dev, res.group(2), res.group(3)] + topo_list_normalized = Testbed.sort_topo_dict(topo_dict) + for dut in properties: + for pname, pvalue in properties[dut].items(): + if dut: + topo_list_normalized.append("{}{}:{}".format(dut, pname, pvalue)) + else: + topo_list_normalized.append("{}:{}".format(pname, pvalue)) + [requests, properties, errs2] = Testbed.parse_topology(*topo_list_normalized) + errs1.extend(errs2) + return [requests, properties, req_duts.keys(), errs1] + + @staticmethod + def trace2(log, *args): + #print(args) + if log: + log.info(args) + + @staticmethod + def check_need_has(need, has): + if has is None: + return False + if has == "*": + return True + for n in need.split("|"): + if n.startswith("!"): + if has.lower() != n[1:].lower(): + return True + elif has.lower() == n.lower(): + return True + return False + + @staticmethod + def check_model(log, tb, dut, dut_tb, props): + need = None + if None in props: + if "MODEL" in props[None]: + need = props[None]["MODEL"] + + if dut in props: + if "MODEL" in props[dut]: + need = props[dut]["MODEL"] + + Testbed.trace2(log, "check_model_0", dut, dut_tb, props, need) + if need: + has = tb.get_device_param(dut_tb, "model", None) + Testbed.trace2(log, "check_model_1", dut, dut_tb, props, need, has) + if has is None: + return False + if not re.compile(need, re.IGNORECASE).match(has): + return False + return True + + @staticmethod + def check_chip(log, tb, dut, dut_tb, props): + need = None + if None in props: + if "CHIP" in props[None]: + need = props[None]["CHIP"] + + if dut in props: + if "CHIP" in props[dut]: + need = props[dut]["CHIP"] + + Testbed.trace2(log, "check_chip_0", dut, dut_tb, props, need) + if need: + has = tb.get_device_param(dut_tb, "chip", None) + Testbed.trace2(log, "check_chip_1", dut, dut_tb, props, need, has) + return Testbed.check_need_has(need, has) + return True + + @staticmethod + def check_tgen_model(log, tb, tg, props): + need = None + if None in props: + if "TGEN" in props[None]: + need = props[None]["TGEN"] + + Testbed.trace2(log, "check_tgen_model", tg, props, need) + if need: + iginfo = tb.get_tg_info(tg) + has = iginfo.type + Testbed.trace2(log, "check_tgen_model", tg, props, need, has) + return Testbed.check_need_has(need, has) + return True + + @staticmethod + def check_tgen_card(log, tb, tg, props): + need = None + if None in props: + if "TGCARD" in props[None]: + need = props[None]["TGCARD"] + + Testbed.trace2(log, "check_tgen_card", tg, props, need) + if need: + iginfo = tb.get_tg_info(tg) + has = getattr(iginfo, "card", "") + Testbed.trace2(log, "check_tgen_card", tg, props, need, has) + return Testbed.check_need_has(need, has) + return True + + @staticmethod + def check_dut_name_any(log, dut_list, props): + need_list = [] + for dut in props: + if "NAME" in props[dut]: + need_list.append(props[dut]["NAME"]) + + for need in need_list: + found = False + for has in dut_list: + if re.compile(need).match(has): + found = True + if not found: + return False + return True + + @staticmethod + def check_dut_names_any(log, dut_list, props): + need = None + if None in props: + if "NAMES" in props[None]: + need = props[None]["NAMES"] + + if need and "|" not in need: + dut_list_new = [] + for dut in need.split(","): + for has in dut_list: + if re.compile(dut).match(has): + if has not in dut_list_new: + dut_list_new.append(has) + return dut_list_new + return dut_list + + @staticmethod + def check_dut_name(log, tb, dut, dut_tb, props): + need = None + if None in props: + if "NAME" in props[None]: + need = props[None]["NAME"] + + if dut in props: + if "NAME" in props[dut]: + need = props[dut]["NAME"] + + Testbed.trace2(log, "check_dut_name", dut, dut_tb, props, need) + if need: + has = dut_tb + Testbed.trace2(log, "check_dut_name", dut, dut_tb, props, need, has) + if not re.compile(need).match(dut_tb): + return False + return True + + @staticmethod + def check_dut_names(log, tb, perm_list, props): + need = None + if None in props: + if "NAMES" in props[None]: + need = props[None]["NAMES"] + + Testbed.trace2(log, "check_dut_names", perm_list, props, need) + if need: + has = ",".join(perm_list) + Testbed.trace2(log, "check_dut_names", perm_list, props, need, has) + if not re.compile(need).match(has): + return False + return True + + @staticmethod + def get_dut_list(val): + if isinstance(val, list): + return val + return list(val.keys()) + + @staticmethod + def identify_topology(log, tb, rdict, num, *args): + + # normalize the topo and get the DUTs needed in topo + arg_list = Testbed._split_args(*args) + [requests, properties, req_duts, errs] = Testbed.normalize_topo(*arg_list) + + # bailout if TG card/model is not satified + Testbed.ensure_tgen_model_and_card(log, tb, properties, errs) + if errs: + Testbed.trace2(log, "tgen requirements not met", errs, properties) + return [None, None, None] + + # build available duts by excluding used ones from all + used_list = [] + if rdict: + for reqid, duts in rdict.items(): + used_list.extend(duts) + dut_list = [] + for dut in tb.get_device_names("DUT"): + if dut not in used_list: + dut_list.append(dut) + #print(dut_list) + + found_setups = [] + for setup in range(0, num): + dut_list2 = [] + used_list = [j for i in found_setups for j in i] + for dut in dut_list: + if dut not in used_list: + dut_list2.append(dut) + if not Testbed.check_dut_name_any(log, dut_list2, properties): + continue + dut_list2 = Testbed.check_dut_names_any(log, dut_list2, properties) + if len(dut_list2) < len(req_duts): + continue + found_match = [] + perm_iterator = permutations(dut_list2, len(req_duts)) + for perm in perm_iterator: + perm_list = list(perm) + perm_dict = {"D{}".format(i+1) : item for i, item in enumerate(perm_list)} + Testbed.trace2(log, perm, perm_list, perm_dict) + # check if names is enforced + if not Testbed.check_dut_names(log, tb, perm_list, properties): + Testbed.trace2(log, "no matching dut names", requests) + found_match = [] + continue + for from_dev, to_dev, res, arg in requests: + count = int(res.group(3)) + if from_dev == 'D' and to_dev == 'T': + dut1_req = "D{}".format(res.group(1)) + Testbed.trace2(log, "checking-tg", arg, perm_list, dut1_req) + if dut1_req not in perm_dict: + Testbed.trace2(log, "no match tg dut position", arg, count, dut1_req) + found_match = [] + break + dut1 = perm_dict[dut1_req] + # check if name is enforced + if not Testbed.check_dut_name(log, tb, dut1_req, dut1, properties): + Testbed.trace2(log, "no matching dut name", arg, count, dut1_req) + found_match = [] + break + # check if model is enforced + if not Testbed.check_model(log, tb, dut1_req, dut1, properties): + Testbed.trace2(log, "no matching dut model", arg, count, dut1_req) + found_match = [] + break + # check if chip is enforced + if not Testbed.check_chip(log, tb, dut1_req, dut1, properties): + Testbed.trace2(log, "no matching dut chip", arg, count, dut1_req, properties) + found_match = [] + break + # check if tg links are suffient + entries = tb.get_links(dut1, None, "TG") + if len(entries) < count: + Testbed.trace2(log, "no match tg links", arg, len(entries), count, dut1_req, dut1) + found_match = [] + break + found_match = perm_list + elif from_dev == 'D' and to_dev == 'D': + dut1_req = "D{}".format(res.group(1)) + dut2_req = "D{}".format(res.group(2)) + Testbed.trace2(log, "checking-dut", arg, perm_list, dut1_req, dut2_req, "links_req", count) + if dut1_req not in perm_dict: + Testbed.trace2(log, "no match dut links - 1", arg, count, perm_list, dut1_req, dut2_req) + found_match = [] + break + if dut2_req not in perm_dict: + Testbed.trace2(log, "no match dut links - 2", arg, count, perm_list, dut1_req, dut2_req) + found_match = [] + break + dut1 = perm_dict[dut1_req] + if not Testbed.check_model(log, tb, dut1_req, dut1, properties): + Testbed.trace2(log, "no matching dut-1 model", arg, count, dut1_req, dut1) + found_match = [] + break + if not Testbed.check_chip(log, tb, dut1_req, dut1, properties): + Testbed.trace2(log, "no matching dut-1 chip", arg, count, dut1_req, dut1) + found_match = [] + break + dut2 = perm_dict[dut2_req] + if not Testbed.check_model(log, tb, dut2_req, dut2, properties): + Testbed.trace2(log, "no matching dut-2 model", arg, count, dut2_req, dut2) + found_match = [] + break + if not Testbed.check_chip(log, tb, dut2_req, dut2, properties): + Testbed.trace2(log, "no matching dut-2 chip", arg, count, dut2_req, dut2) + found_match = [] + break + entries = tb.get_links(dut1, dut2, "DUT") + if len(entries) < count: + Testbed.trace2(log, "no match dut links", arg, len(entries), count, perm_list, dut1, dut2) + found_match = [] + break + found_match = perm_list + else: + print("UNKNOWN", arg) + if found_match: + Testbed.trace2(log, "found match", found_match, "req_duts", req_duts, properties) + found_setups.append(found_match) + break + + #import pdb;pdb.set_trace() + if not found_setups: + Testbed.trace2(log, "not found match", "req_duts", req_duts, properties) + return [None, None, None] + + # have match - create mini testbed + setup_list = [] + for setup in found_setups: + #new_rdict = OrderedDict() + dut_names = [] + for dut in setup: + #dinfo = tb.get_device_info(dut) + #new_rdict[dut] = dinfo.alias + dut_names.append(dut) + #setup_list.append(new_rdict) + setup_list.append(dut_names) + + return [setup_list, properties, errs] + + def get_device_name(self, name): + for dut, dinfo in self.topology.devices.items(): + if dinfo["__name0__"] == name: + return dinfo["__name__"] + return None + + def get_all_files(self): + return self.oyaml.get_files() + + def get_verifier(self, default="NA"): + try: + return self.topology.properties.verifier + except: + return default + + def get_config_profile(self, default="NA"): + try: + return self.topology.properties.profile + except: + return default + + def save_visjs(self, used=[]): + node_ids = dict() + result = SpyTestDict() + result.nodes = [] + result.links = [] + nid = 0 + lid = 0 + color_index = 0 + dut_colors = [] + for l in used: + if color_index < len(self.colors.used): + color = self.colors.used[color_index] + color_index = color_index + 1 + elif self.colors.used: + color = self.colors.used[0] + else: + color = "red" + dut_colors.append([l, color]) + for d in self.get_device_names(): + dinfo = self.get_device_info(d) + nid = nid + 1 + node = SpyTestDict() + node.id = nid + node.label = dinfo.alias + if self.colors.free: + node.color = self.colors.free + for l, color in dut_colors: + if used and d in l: + node.color = color + if "topo_props" in dinfo: + if "x" in dinfo.topo_props: + node.x = dinfo.topo_props.x * 100 + if "y" in dinfo.topo_props: + node.y = dinfo.topo_props.y * 100 + result.nodes.append(node) + node_ids[d] = nid + for d in self.get_device_names(): + for local, partner, remote in self.get_links(d): + if node_ids[d] > node_ids[partner]: + continue + lid = lid + 1 + link = SpyTestDict() + link.id = lid + link["from"] = node_ids[d] + link.to = node_ids[partner] + link.labelFrom = local + link.labelTo = remote + #link.label = "{}/{} -- {}/{}".format(d, local, partner, remote) + smooth = SpyTestDict() + smooth["type"] = 'curvedCW' + smooth.roundness = 0.2 + #link.smooth = smooth + result.links.append(link) + return json.dumps(result, indent=4) + + def rebuild_topo_file(self, devices, properties): + used_devices = dict() + topology = SpyTestDict() + for dut in devices: + dinfo = self.get_device_info(dut) + topology[dinfo.alias] = SpyTestDict() + topology[dinfo.alias].interfaces = SpyTestDict() + used_devices[dut] = 1 + for local, partner, remote in self.get_links(dut): + pdinfo = self.get_device_info(partner) + link_ent = SpyTestDict() + if pdinfo.type == "TG": + if None in properties and "TGEN" in properties[None] and \ + properties[None]["TGEN"] != pdinfo.properties.type: + link_ent.reserved = True + else: + link_ent.EndDevice = pdinfo.alias + link_ent.EndPort = remote + used_devices[partner] = 1 + elif partner in devices: + link_ent.EndDevice = pdinfo.alias + link_ent.EndPort = remote + used_devices[partner] = 1 + else: + link_ent.reserved = True + topology[dinfo.alias].interfaces[local] = link_ent + d2 = copy.deepcopy(self.oyaml.obj) + dev_nodes = SpyTestDict() + for d, dinfo in d2.devices.items(): + if "__name__" in dinfo and dinfo.__name__ in used_devices: + dev_nodes[d] = dinfo + d2.devices = dev_nodes + d2.topology.devices = topology + + current_configs = dict() + restore_configs = dict() + + # save current config files + for d, dinfo in d2.devices.items(): + if dinfo["type"] != "DUT": + continue + dut = dinfo.__name__ + current_configs[dut] = self.get_config(dut, "current") + restore_configs[dut] = self.get_config(dut, "restore") + try: + if None in properties and "CONFIG" in properties[None]: + current_configs[dut] = self.read_config_file(properties[None]["CONFIG"]) + elif dut in properties and "CONFIG" in properties[dut]: + current_configs[dut] = self.read_config_file(properties[dut]["CONFIG"]) + except Exception as e: + print("exception", str(e)) + + # change config profile and replace global config files + for d, dinfo in d2.devices.items(): + if dinfo["type"] != "DUT": + continue + dut = dinfo.__name__ + dinfo.properties.config = "config-{}".format(dinfo.alias) + obj = SpyTestDict() + obj.current = current_configs[dut] + obj.restore = restore_configs[dut] + d2.configs[dinfo.properties.config] = obj + + # remove temp data + for d, dinfo in d2.devices.items(): + del dev_nodes[d].type + del dev_nodes[d].__name__ + del dev_nodes[d].__name0__ + del dev_nodes[d].alias + + return yaml.dump(d2, default_flow_style=False) + + def validate_testbed(self, tb_list=[]): + tg_ips = [] + rps_ips = [] + consoles = [] + self.validation_errors = [] + for tb in tb_list: + tg_ips.extend(tb._validate_tgen_info()) + rps_ips.extend(tb._validate_rps_info([])) + consoles.extend(tb._validate_consoles([], [], [])) + + # validate the current testbed against collected info + tg_ips.extend(self._validate_tgen_info()) + rps_ips.extend(self._validate_rps_info(tg_ips)) + consoles.extend(self._validate_consoles(tg_ips, rps_ips, consoles)) + return self.validation_errors + + @staticmethod + def write_file(yaml, prefix="testbed_", suffix=".yaml", filename=None): + fp = tempfile.NamedTemporaryFile(delete=False, prefix=prefix, suffix=suffix, mode='w') + fp.write(yaml) + if not filename: + return fp.name + try: + fp2 = tempfile.mkdtemp(prefix=prefix, suffix=suffix) + new_filename = os.path.join(fp2, filename) + shutil.move(fp.name, new_filename) + return new_filename + except: + return fp.name + + @staticmethod + def read_config_file(filename): + try: + oyaml = OrderedYaml(filename, [testbeds_root]) + obj = oyaml.get_data() + assert(isinstance(obj.current, list)) + return obj.current + except: + return None + + def _unit_tests(self): + for d in self.get_device_names("DUT"): + print("DUT--NAME: " + d) + utils.print_yaml(self.links, "LINKS") + print("get_service(D1,ftp): ", self.get_service("D101", "ftp")) + print("get_service(D1,tftp): ", self.get_service("D101", "tftp")) + print("get_service(D1,None): ", self.get_service("D101", None)) + print("get_rps(D1): ", self.get_rps("D101")) + print("get_links(D1): ", self.get_links("D101")) + print("get_links(D2): ", self.get_links("D102")) + print("get_links(D3): ", self.get_links("D103")) + print("get_links(D1,D2): ", self.get_links("D101", "D102")) + print("get_links(D1,D3): ", self.get_links("D101", "D103")) + print("get_links(D2,D3): ", self.get_links("D102", "D103")) + print("get_links(D3,D2): ", self.get_links("D103", "D102")) + print("get_testbed_vars(): ", self.get_testbed_vars()) + + +if __name__ == "__main__": + logging.basicConfig() + log = logging.getLogger() + log.setLevel(logging.DEBUG) + def_filename = "../testbeds/sample_sonic_terminal_1d.yaml" + file_name = sys.argv[1] if len(sys.argv) >= 2 else def_filename + t = Testbed(file_name) + t.flex_dut = True + print(t.get_speed("D1", None)) + #print(t.identify_topology(None, t, None, 1, "D1T1:1", "CHIP=td2|th2")) + #print(t.identify_topology(None, t, None, 1, "D1T1:1", "CHIP=!td3")) + #print(t.identify_topology(None, t, None, 1, "D1T1:1", "CHIP=td3")) + #print(t.identify_topology(None, t, None, 1, "D1T1:1", "TGCARD=t1|t2")) + #print(t.identify_topology(None, t, None, 1, "D1")) + print(t.ensure_min_topology("D1D2:1 D2D3:1 D3D4:1 TGEN=stc|ixia")) + print(t.ensure_min_topology("D1D2:1 D2D3:1 D3D4:1 TGEN=stc")) + print(t.ensure_min_topology("D1D2:1 D2D3:1 D3D4:1 TGEN=ixia")) + sys.exit(0) + o = t.topology + with open('expanded-v2.yml', 'w') as outfile: + yaml.dump(o, outfile, default_flow_style=False) + topo = o + for dd, d_info in topo.devices.items(): + utils.print_data(d_info, "{}".format(dd)) + utils.print_yaml(topo, "topology") + print(t.ensure_min_topology("D1T1:20")) + print(t.ensure_min_topology("D1D2:20")) + print(t.get_all_files()) + print(t.get_device_alias("D101")) + print(t.parse_topology("D1T1:1", "D1D2:1")) + print(t.parse_topology("D1T1:1", "D1CONFIG:test.yaml", "D1MODEL:AS7712", "CONFIG:abc.yaml", "MODEL:AS7712")) + print(t.get_device_param("D101", "model", "unknown")) + print(t.get_build("D101", None)) + print(t.get_error("D101", None)) + print(t.get_breakout("D101")) + with open('all.json', 'w') as outfile: + outfile.write(t.save_visjs()) + t._unit_tests() + print(t.get_device_names()) + for dd in t.get_device_names("DUT"): + print(dd, t.get_device_alias(dd), t.get_rerved_links(dd)) + print("VALID", t.valid, t._validate()) + print(t.get_speed("D101", None)) + print(t.get_speed("D101", "Ethernet43")) + print(t.get_device_param("D101", "show_delay", "1")) + print(t.get_device_param("D101", "hostname", "unknown")) + print(t.ensure_min_topology("D1D2:3", "D2D3:3")) + print(t.ensure_min_topology("D1D2:3", "D1D3:3")) + #print(t.ensure_min_topology("D1T1:1", "TGEN:ixia2")) + #print(t.identify_topology(None, t, None, 1, "D1T1:1", "TGEN:ixia")) + ##print(t.identify_topology(None, t, None, 1, "D1D2:6 D2D3:4 D2D4:4 D4:D5:6 D5D6:4")) + tbvars = t.get_testbed_vars() + print("get_testbed_vars(): ", tbvars) + for dd in t.get_device_names("DUT"): + print("DUT--NAME: " + dd) + print("get_links({}): ", dd, t.get_links(dd)) + for dd in t.get_device_names("TG"): + print("DUT--NAME: " + dd) + print("get_links({}): ", dd, t.get_links(dd)) + print("========================================") + print(t.normalize_topo("D1D24", "D1NAME:DUT-8", "D2NAME:DUT-4")) + print("========================================") + #print(t.get_raw()) + print(t.identify_topology(None, t, None, 2, "D1 D2 D3 D4", "NAMES:DUT-1,DUT-2,DUT-7,DUT-8|DUT-5,DUT-6,DUT-3,DUT-4")) + print(t.identify_topology(None, t, None, 2, "D1T1:4 D1D2:6 D2D3:3 D1D3:3 D2T1:2 D3T1:2")) + print("========================================") + t.flex_dut = True + print(t.identify_topology(None, t, None, 2, "D1T1:1", "D2T1:2", "D1D2:2", "D1MODEL:Accton-AS7326-56X", "D2MODEL:Accton-AS7326-56X")) + print(t.ensure_min_topology("D1T1:1", "D2T1:2", "D1D2:2", "D1MODEL:Accton-AS7326-56X", "D2MODEL:Accton-AS7326-56X2" )) + print(t.identify_topology(log, t, None, 2, "D1 D2 D3 D4 NAMES:DUT-1,DUT-2,DUT-7,DUT-8|DUT-5,DUT-6,DUT-3,DUT-4")) + print(t.get_access()) + print(t.ensure_min_topology("D1", "CONSOLE_ONLY")) + print(t.get_topo()) + print(t.ensure_min_topology("D1", "CHIP=TD3|TD2")) + breakout_options = t.get_device_param("SS4-2", "breakout", None) + diff --git a/spytest/spytest/tgen/__init__.py b/spytest/spytest/tgen/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/spytest/spytest/tgen/init.py b/spytest/spytest/tgen/init.py new file mode 100644 index 00000000000..9809417fe8f --- /dev/null +++ b/spytest/spytest/tgen/init.py @@ -0,0 +1,138 @@ +import os +import sys + +tgen_path = os.getenv("SCID_TGEN_PATH", "/projects/scid/tgen") +def_tcl_path = "/projects/scid/tools/ActivTcl/current/bin" +tcl_custom_pkgdir = os.path.abspath(os.path.dirname(__file__)) + +def tg_stc_load(version, logger, logs_path=None): + + stc_version_map = {"4.67": "4.67", "4.91": "4.91"} + + # verify STC version provided + version_string = str(version) + if version_string not in stc_version_map: + logger.error("STC: unsupported version {}".format(version_string)) + return None + + # map STC version if needed + version_string = stc_version_map[version_string] + + # check if STC root folder is found + stc_root = os.path.join(tgen_path, "stc") + if not os.path.exists(stc_root): + logger.error("STC: not installed.") + return None + + # build STC version root folder + stc_ver_root = os.path.join(stc_root, version_string) + if not os.path.exists(stc_ver_root): + stc_ver_root = os.path.join(stc_root, "Spirent_TestCenter_{}".format(version_string)) + + # check if STC version root folder is found + if not os.path.exists(stc_ver_root): + logger.error("STC: not installed..") + return None + + # build STC app root folder + stc_app_root = os.path.join(stc_ver_root, "Spirent_TestCenter_Application_Linux") + + # check if STC app root folder is found + if not os.path.exists(stc_app_root): + logger.error("STC: not installed...") + return None + + stc_hl_src = stc_app_root + '/HltAPI/SourceCode' + stc_hl_api_path = stc_hl_src + '/hltapiForPython' + + # build tclsh PATH + stc_tcl_path = os.path.join(stc_app_root, "Tcl", "bin") + if not os.path.exists(stc_tcl_path): + stc_tcl_path = def_tcl_path + tcl_path = os.getenv("SCID_TCL85_BIN", "") + if not tcl_path or not os.path.exists(tcl_path): + tcl_path = stc_tcl_path + if float(version) < 4.91: + tcl_path = os.getenv("SCID_TCL84_BIN", stc_tcl_path) + tcl_lib_path = os.path.join(tcl_path, "..", "lib") + tclsh = os.path.join(tcl_path, "tclsh8.5") + if not os.path.exists(tclsh): + tclsh = os.path.join(tcl_path, "tclsh") + + old_ldpath = os.getenv("LD_LIBRARY_PATH") + ldpath = [old_ldpath] if old_ldpath else [] + ldpath.append(stc_app_root) + os.environ['LD_LIBRARY_PATH'] = ":".join(ldpath) + + os.environ['STC_VERSION'] = version_string + os.environ['STC_INSTALL_DIR'] = stc_app_root + os.environ['STC_PRIVATE_INSTALL_DIR'] = stc_app_root + os.environ["STC_TCL"] = tclsh + os.environ['TCLLIBPATH'] = "{} {} {} /usr/lib".format(stc_hl_src, tcl_custom_pkgdir, tcl_lib_path) + os.environ['HLPYAPI_LOG'] = logs_path or os.getenv("SPYTEST_USER_ROOT") + os.environ['HOME'] = logs_path or os.getenv("SPYTEST_USER_ROOT") + + sys.path.insert(0, tcl_path) + sys.path.insert(0, stc_hl_api_path) + + return version_string + + +def tg_ixia_load(version, logger, logs_path=None): + + ixia_version_map = {"7.4": "7.40", "7.40": "7.40", + "8.4": "8.40", "8.42": "8.42", + "9.0": "9.00", "9.00": "9.00"} + version_string = str(version) + if version_string not in ixia_version_map: + logger.error("IXIA: unsupported version {}".format(version_string)) + return None + + version_string = ixia_version_map[version_string] + + ixia_hltapi_map = {'7.40': 'HLTSET173', + '8.40': 'HLTSET219', + '8.42': 'HLTSET223', + '9.00': 'HLTSET231', + } + + ixnetwork = os.path.join(tgen_path, "ixia", version_string, "lib") + + if os.path.exists(ixnetwork): + hl_api = os.path.join(tgen_path, "ixia", version_string, + "lib", "hltapi", "library") + + ngpf_api = hl_api + str("/common/ixiangpf/python") + ixn_py_api = ixnetwork + "/PythonApi" + + os.environ["IXIA_VERSION"] = ixia_hltapi_map[version_string] + os.environ["IXIA_HOME"] = ixnetwork + os.environ["TCLLIBPATH"] = str(ixnetwork) + + sys.path.append(ngpf_api) + sys.path.append(ixn_py_api) + + return version_string + + # 9.0 onwards + tcl_path = os.getenv("SCID_TCL85_BIN", def_tcl_path) + tcl_lib_path = os.path.join(tcl_path, "..", "lib") + ixia_root = os.path.join(tgen_path, "ixia", "all", "ixia") + hlt_api = os.path.join(ixia_root, "hlapi", version_string) + ngpf_api = os.path.join(hlt_api, "library", "common", "ixiangpf", "python") + ixn_py_api = os.path.join(ixia_root, "ixnetwork", version_string, + "lib", "PythonApi") + ixn_tcl_api_1 = os.path.join(ixia_root, "ixnetwork", version_string, + "lib", "IxTclNetwork") + ixn_tcl_api_2 = os.path.join(ixia_root, "ixnetwork", version_string, + "lib", "TclApi", "IxTclNetwork") + os.environ["IXIA_VERSION"] = ixia_hltapi_map[version_string] + os.environ["TCLLIBPATH"] = " ".join([hlt_api, ixn_tcl_api_1, ixn_tcl_api_2, tcl_lib_path]) + sys.path.append(ngpf_api) + sys.path.append(ixn_py_api) + + return version_string + +def tg_scapy_load(version, logger, logs_path=None): + return version + diff --git a/spytest/spytest/tgen/pkgIndex.tcl b/spytest/spytest/tgen/pkgIndex.tcl new file mode 100644 index 00000000000..408b63dc90e --- /dev/null +++ b/spytest/spytest/tgen/pkgIndex.tcl @@ -0,0 +1,2 @@ +package ifneeded SpirentTestCenter $env(STC_VERSION) [list source [file join $env(STC_INSTALL_DIR) SpirentTestCenter.tcl]] +package ifneeded stclib $env(STC_VERSION) [list source [file join $env(STC_INSTALL_DIR) stclib.tcl]] diff --git a/spytest/spytest/tgen/scapy/afpacket.py b/spytest/spytest/tgen/scapy/afpacket.py new file mode 100644 index 00000000000..dbffd4247bc --- /dev/null +++ b/spytest/spytest/tgen/scapy/afpacket.py @@ -0,0 +1,126 @@ +""" +AF_PACKET receive support + +When VLAN offload is enabled on the NIC Linux will not deliver the VLAN tag +in the data returned by recv. Instead, it delivers the VLAN TCI in a control +message. Python 2.x doesn't have built-in support for recvmsg, so we have to +use ctypes to call it. The recv function exported by this module reconstructs +the VLAN tag if it was offloaded. +""" + +import struct +from ctypes import sizeof +from ctypes import get_errno +from ctypes import byref +from ctypes import c_void_p +from ctypes import cast +from ctypes import pointer +from ctypes import create_string_buffer +from ctypes import c_size_t +from ctypes import c_int +from ctypes import POINTER +from ctypes import CDLL +from ctypes import c_ushort +from ctypes import c_uint +from ctypes import Structure +from ctypes import c_uint32 + +ETH_P_8021Q = 0x8100 +SOL_PACKET = 263 +PACKET_AUXDATA = 8 +TP_STATUS_VLAN_VALID = 1 << 4 + +class struct_iovec(Structure): + _fields_ = [ + ("iov_base", c_void_p), + ("iov_len", c_size_t), + ] + +class struct_msghdr(Structure): + _fields_ = [ + ("msg_name", c_void_p), + ("msg_namelen", c_uint32), + ("msg_iov", POINTER(struct_iovec)), + ("msg_iovlen", c_size_t), + ("msg_control", c_void_p), + ("msg_controllen", c_size_t), + ("msg_flags", c_int), + ] + +class struct_cmsghdr(Structure): + _fields_ = [ + ("cmsg_len", c_size_t), + ("cmsg_level", c_int), + ("cmsg_type", c_int), + ] + +class struct_tpacket_auxdata(Structure): + _fields_ = [ + ("tp_status", c_uint), + ("tp_len", c_uint), + ("tp_snaplen", c_uint), + ("tp_mac", c_ushort), + ("tp_net", c_ushort), + ("tp_vlan_tci", c_ushort), + ("tp_padding", c_ushort), + ] + +libc = CDLL("libc.so.6") +recvmsg = libc.recvmsg +recvmsg.argtypes = [c_int, POINTER(struct_msghdr), c_int] +recvmsg.retype = c_int + +def enable_auxdata(sk): + """ + Ask the kernel to return the VLAN tag in a control message + + Must be called on the socket before afpacket.recv. + """ + sk.setsockopt(SOL_PACKET, PACKET_AUXDATA, 1) + +def recv(sk, bufsize): + """ + Receive a packet from an AF_PACKET socket + @sk Socket + @bufsize Maximum packet size + """ + buf = create_string_buffer(bufsize) + + ctrl_bufsize = sizeof(struct_cmsghdr) + sizeof(struct_tpacket_auxdata) + sizeof(c_size_t) + ctrl_buf = create_string_buffer(ctrl_bufsize) + + iov = struct_iovec() + iov.iov_base = cast(buf, c_void_p) + iov.iov_len = bufsize + + msghdr = struct_msghdr() + msghdr.msg_name = None + msghdr.msg_namelen = 0 + msghdr.msg_iov = pointer(iov) + msghdr.msg_iovlen = 1 + msghdr.msg_control = cast(ctrl_buf, c_void_p) + msghdr.msg_controllen = ctrl_bufsize + msghdr.msg_flags = 0 + + rv = recvmsg(sk.fileno(), byref(msghdr), 0) + if rv < 0: + msg = "recvmsg failed: rv=%d errno=%d", rv, get_errno() + raise RuntimeError(msg) + + # The kernel only delivers control messages we ask for. We + # only enabled PACKET_AUXDATA, so we can assume it's the + # only control message. + assert msghdr.msg_controllen >= sizeof(struct_cmsghdr) + + cmsghdr = struct_cmsghdr.from_buffer(ctrl_buf) # pylint: disable=E1101 + assert cmsghdr.cmsg_level == SOL_PACKET + assert cmsghdr.cmsg_type == PACKET_AUXDATA + + auxdata = struct_tpacket_auxdata.from_buffer(ctrl_buf, sizeof(struct_cmsghdr)) # pylint: disable=E1101 + + if auxdata.tp_vlan_tci != 0 or auxdata.tp_status & TP_STATUS_VLAN_VALID: + # Insert VLAN tag + tag = struct.pack("!HH", ETH_P_8021Q, auxdata.tp_vlan_tci) + return buf.raw[:12] + tag + buf.raw[12:rv] + else: + return buf.raw[:rv] diff --git a/spytest/spytest/tgen/scapy/bgp.py b/spytest/spytest/tgen/scapy/bgp.py new file mode 100644 index 00000000000..352d2dc7f6b --- /dev/null +++ b/spytest/spytest/tgen/scapy/bgp.py @@ -0,0 +1,163 @@ +import random +import time +import json +import traceback +import requests +from requests.auth import HTTPBasicAuth + +class YaBGPAgent(object): + + def __init__(self): + self.session = requests.Session() + self.peer = None + + def _get(self, path): + data = self.session.get('http://localhost/v1/' + path, auth=HTTPBasicAuth('admin', 'admin')) + return data.json() + + def connected(self): + for _ in range(60): + data = self._get('peers') + for peer in data.get('peers', []): + if peer['fsm'] == 'ESTABLISHED': + self.peer = peer + return True + return + + def _build_yabgp_msgs(self, update): + yabgp_attr_name_conversion = { + 'nexthop': 3, 'origin': 1, 'as_path': 2, 'local_pref': 5 } + yabgp_msg = {} + if not self.peer: + return {} + nlri = update.get('nlri') + if nlri: + attributes = {} + attr = update['attr'] + for at_name, at_value in attr.items(): + if at_name == 'as_path': + at_value = [[1, at_value]] + if at_name in yabgp_attr_name_conversion: + attributes[yabgp_attr_name_conversion[at_name]] = at_value + yabgp_msg['attr'] = attributes + yabgp_msg['nlri'] = nlri + #withdraw = update.get('withdraw') + #if 'withdraw' in data: + #yabgp_msg['withdraw'] = withdraw + return {self.peer['remote_addr']: yabgp_msg} + + def _send_yabgp(self, update): + yabgp_msg = self._build_yabgp_msgs(update) + for peer_ip, msg in yabgp_msg.items(): + headers = {'content-type':'application/json'} + res = self.session.post( + 'http://localhost/v1/peer/%s/send/update' % peer_ip, + data=json.dumps(msg), auth=HTTPBasicAuth('admin', 'admin'), + headers=headers) + print(res) + + def send_update(self, update): + if self.peer: + self._send_yabgp(update) + + +class BgpUpdateGenerator(object): + + def __init__(self, config): + self.config = config + self.agent = YaBGPAgent() + + def run(self): + """Start sending updates.""" + try: + if not self.agent.connected(): + print('no BGP router is connected') + return + time.sleep(1) + self._send_random_update() + except (KeyboardInterrupt, Exception): + traceback.print_exc() + + def _random_nexthop(self): + if not self.config['nexthop']: + return None + return str(random.choice(self.config['nexthop'])) + + def _send_random_update(self): + """generate updates randomly.""" + def random_prefix(): + prefix = ".".join(map(str, (random.randint(0,255) for _ in range(3)))) + prefix += '.0/24' + return prefix + + def random_prefixes(max_prefix): + prefixes = [] + for _ in range(random.randint(1, max_prefix)): + prefixes.append(random_prefix()) + return prefixes + + def random_as_path(max_length=5): + as_path = [self.config['local_as']] + for _ in range(random.randint(0, max_length)): + as_path.append(random.randint(1, 64999)) + return as_path + + def sample(seq, num): + if len(seq) >= num: + return random.sample(seq, num) + else: + return seq + sent = 0 + update_per_sec = self.config['rate'] or 1 + update_per_sec = float(update_per_sec) + announced_prefixes = set() + rate = self.config['rate'] + while sent < self.config['count'] or self.config['count'] == 0: + update = { + 'attr': { + 'nexthop': self._random_nexthop(), + 'med': random.randint(0, 100), + 'origin': random.choice(['igp', 'incomplete', 'egp']), + 'as_path': random_as_path(), + 'local_pref': random.randint(100, 150), + } + } + update['nlri'] = [] + update['withdraw'] = [] + if self.config['update_type'] == 'announce': + update['nlri'] = random_prefixes(self.config['max_prefix']) + announced_prefixes.update(update['nlri']) + elif self.config['update_type'] == 'withdraw': + update['withdraw'] = random_prefixes(self.config['max_prefix']) + else: + if random.getrandbits(1): + update['withdraw'] = sample(announced_prefixes, self.config['max_prefix']) + if random.getrandbits(1): + update['nlri'] = random_prefixes(self.config['max_prefix']) + announced_prefixes.update(update['nlri']) + self.agent.send_update(update) + sent += 1 + time.sleep(1/update_per_sec) + +DEFAULTS = { + 'live': None, + 'mrt': None, + 'rand': True, + 'peers': ['127.0.0.1:9179/65000'], + 'agent': 'yabgp', + 'count': 0, + 'rate': 0, + 'max_prefix': 1, + 'update_type': 'mixed', + 'nexthop': ['127.0.0.1'], + 'local_as': 65000, + 'local_ip': '127.0.0.1', + } + +def main(): + bgpgen = BgpUpdateGenerator(DEFAULTS) + bgpgen.run() + +if __name__ == '__main__': + main() + diff --git a/spytest/spytest/tgen/scapy/driver.py b/spytest/spytest/tgen/scapy/driver.py new file mode 100644 index 00000000000..4680dd1b74f --- /dev/null +++ b/spytest/spytest/tgen/scapy/driver.py @@ -0,0 +1,326 @@ +import os +import sys +import time +import traceback +import threading + +from packet import ScapyPacket +from or_event import OrEvent +from utils import Utils +from logger import Logger + +def tobytes(s): + if sys.version_info[0] < 3: + return buffer(s) + return s.encode() + +def isLinkUp(intf, dbg = False): + flags_path = "/sys/class/net/{}/operstate".format(intf) + if os.path.isfile(flags_path): + if dbg: + os.system("ifconfig %s" % (intf)) + with open(flags_path, 'r') as fp: + if fp.read().strip() != 'down': + return True + return False + +class ScapyDriver(object): + def __init__(self, port, dry=False, dbg=0, logger=None): + self.port = port + self.dry = dry + self.dbg = dbg + self.finished = False + self.logger = logger or Logger() + self.utils = Utils(self.dry, logger=self.logger) + self.iface = port.iface + self.packet = ScapyPacket(port.iface, dry=self.dry, dbg=self.dbg, + logger=self.logger) + self.rxInit() + self.txInit() + self.statState.set() + + def __del__(self): + self.logger.debug("ScapyDriver {} exiting...".format(self.iface)) + self.cleanup() + del self.packet + + def cleanup(self): + print("ScapyDriver {} cleanup...".format(self.iface)) + self.finished = True + self.captureState.clear() + self.statState.clear() + self.txState.clear() + self.protocolState.clear() + self.packet.cleanup() + + def rxInit(self): + self.captureQueueInit() + self.captureState = threading.Event() + self.captureState.clear() + self.protocolState = threading.Event() + self.protocolState.clear() + self.statState = threading.Event() + self.statState.clear() + self.rxThread = threading.Thread(target=self.rxThreadMain, args=()) + self.rxThread.daemon = True + self.rxThread.start() + + def captureQueueInit(self): + self.pkts_captured = [] + + def startCapture(self): + self.logger.debug("start-cap: {}".format(self.iface)) + self.pkts_captured = [] + self.captureState.set() + + def stopCapture(self): + self.logger.debug("stop-cap: {}".format(self.iface)) + self.captureState.clear() + time.sleep(1) + return len(self.pkts_captured) + + def clearCapture(self): + self.logger.debug("clear-cap: {}".format(self.iface)) + self.captureState.clear() + time.sleep(3) + self.pkts_captured = [] + return len(self.pkts_captured) + + def getCapture(self): + self.logger.debug("get-cap: {}".format(self.iface)) + retval = [] + for pkt in self.pkts_captured: + (data, hex_bytes) = (str(pkt), []) + for index in range(len(data)): + hex_bytes.append("%02X"% ord(data[index])) + retval.append(hex_bytes) + return retval + + def rx_any_enable(self): + return self.captureState.is_set() or self.statState.is_set() or self.protocolState.is_set() + + def rxThreadMain(self): + while not self.finished: + # wait till captures or stats collection is enabled + self.logger.debug("RX Thread {} start {}/{}/{}".format(self.iface, + self.captureState.is_set(), self.statState.is_set(), self.protocolState.is_set())) + while not self.rx_any_enable(): + time.sleep(1) + OrEvent(self.captureState, self.statState, self.protocolState).wait() + + # read packets + while self.rx_any_enable(): + try: + packet = self.packet.readp(iface=self.iface) + if packet: + self.handle_recv(None, packet) + except Exception as e: + if str(e) != "[Errno 100] Network is down": + self.logger.debug(e, traceback.format_exc()) + self.logger.debug("Driver(%s): '%s' - ignoring", self.iface, str(e)) + while self.rx_any_enable() and isLinkUp(self.iface) == False: + time.sleep(1) + + def handle_stats(self, packet): + pktlen = 0 if not packet else len(packet) + framesReceived = self.port.incrStat('framesReceived') + self.port.incrStat('bytesReceived', pktlen) + if self.dbg > 2: + self.logger.debug("{} framesReceived: {}".format(self.iface, framesReceived)) + if pktlen > 1518: + self.port.incrStat('oversizeFramesReceived') + for stream in self.port.track_streams: + if self.packet.match_stream(stream, packet): + stream.incrStat('framesReceived') + stream.incrStat('bytesReceived', pktlen) + + def handle_capture(self, packet): + self.pkts_captured.append(packet) + + def handle_recv(self, hdr, packet): + if self.statState.is_set(): + self.handle_stats(packet) + if self.captureState.is_set(): + self.handle_capture(packet) + + def txInit(self): + self.txState = threading.Event() + self.txState.clear() + self.txStateAck = threading.Event() + self.txStateAck.clear() + self.txThread = threading.Thread(target=self.txThreadMain, args=()) + self.txThread.daemon = True + self.txThread.start() + + def startTransmit(self, **kws): + self.logger.debug("start-tx: {} {}".format(self.iface, kws)) + duration = self.utils.intval(kws, 'duration', 0) + non_continuous = False + + # enable selected streams + handle = kws.get('handle', None) + if not handle: + handles = self.port.streams.keys() + else: + handles = self.utils.make_list(handle) + + for stream_id, stream in self.port.streams.items(): + stream.enable2 = bool(stream_id in handles) + if stream.kws.get("transmit_mode", "continuous") != "continuous": + non_continuous = True + + # signal the start + self.logger.debug("signal-tx: {} {}".format(self.iface, kws)) + self.txStateAck.clear() + self.txState.set() + + # wait for first packet to be sent + self.txStateAck.wait(10) + self.logger.debug("start-tx-ack: {} {}".format(self.iface, kws)) + + # wait for max 30 seconds to finish + if non_continuous: + for check in range(30): + if not self.txState.is_set(): + break + elif duration > 0: + self.logger.debug("waiting for duration: {}".format(duration)) + time.sleep(duration) + self.txState.clear() + else: + self.logger.debug("waiting 3 sec") + time.sleep(3) + + self.logger.debug("start-tx-finished: {} {}".format(self.iface, kws)) + + def stopTransmit(self, **kws): + + # disable selected streams + handle = kws.get('handle', None) + if handle: + for stream_id, stream in self.port.streams.items(): + if not handle or stream_id == handle: + stream.enable2 = False + return + + if not self.txState.is_set(): + return + self.logger.debug("stop-tx: {}".format(self.iface)) + self.txState.clear() + for index in range(10): + time.sleep(1) + if not self.txState.is_set(): + break + + def clear_stats(self): + self.packet.clear_stats() + + def txThreadMain(self): + while not self.finished: + while not self.txState.is_set(): + self.logger.debug("txThreadMain {} Wait".format(self.iface)) + self.txState.wait() + try: + self.txThreadMainInner() + except Exception as e: + self.logger.log_exception(e, traceback.format_exc()) + self.txState.clear() + + def txThreadMainInner(self): + self.logger.debug("txThreadMainInner {} start {}".format(self.iface, self.port.streams)) + pwa_list = [] + try: + for stream in self.port.streams.values(): + self.logger.debug(" start {} {}/{}".format(stream.stream_id, stream.enable, stream.enable2)) + if stream.enable and stream.enable2: + pwa = self.packet.build_first(stream) + pwa.tx_time = time.clock() + pwa_list.append(pwa) + except Exception as exp: + self.logger.log_exception(exp, traceback.format_exc()) + + if not pwa_list: + self.logger.debug("txThreadMainInner {} Nothing Todo".format(self.iface)) + self.txStateAck.set() + return + + # signal first packet ready + self.txStateAck.set() + + tx_count = 0 + while (self.txState.is_set()): + pwa_list.sort(key=self.pwa_sort) + pwa_next_list = [] + for pwa in pwa_list: + self.pwa_wait(pwa) + try: + send_start_time = time.clock() + pkt = self.send_packet(pwa) + if pwa.stream.track_port: + pwa.stream.track_pkts.append(pkt) + bytesSent = len(pkt) + send_time = time.clock() - send_start_time + framesSent = self.port.incrStat('framesSent') + self.port.incrStat('bytesSent', bytesSent) + if self.dbg > 2: + self.logger.debug("{} framesSent: {}".format(self.iface, framesSent)) + pwa.stream.incrStat('framesSent') + pwa.stream.incrStat('bytesSent', bytesSent) + tx_count = tx_count + 1 + except Exception as e: + self.logger.log_exception(e, traceback.format_exc()) + pwa.stream.enable2 = False + else: + pps = pwa.rate_pps + build_start_time = time.clock() + pwa = self.packet.build_next(pwa) + if not pwa: continue + build_time = time.clock() - build_start_time + if pps > self.packet.max_rate_pps: pps = self.packet.max_rate_pps + pwa.tx_time = time.clock() + 1.0/float(pps) - build_time - send_time + pwa_next_list.append(pwa) + pwa_list = pwa_next_list + self.logger.debug("txThreadMainInner Completed {}".format(tx_count)) + + def pwa_sort(self, pwa): + return pwa.tx_time + + def pwa_wait(self, pwa): + delay = pwa.tx_time - time.clock() + if self.dbg > 1: + self.logger.debug("stream: {} delay: {} pps: {}".format(pwa.stream.stream_id, delay, pwa.rate_pps)) + if delay <= 0: + # yield + time.sleep(0) + elif delay > 1.0/10: + self.utils.msleep(delay * 1000, 10) + elif delay > 1.0/100: + self.utils.msleep(delay * 1000, 1) + elif delay > 1.0/200: + self.utils.usleep(delay * 1000 * 1000, 100) + elif delay > 1.0/500: + self.utils.usleep(delay * 1000 * 1000, 10) + else: + self.utils.usleep(delay * 1000 * 1000) + + def send_packet(self, pwa): + return self.packet.send_packet(pwa, self.iface) + + def createInterface(self, intf): + return self.packet.if_create(intf) + + def deleteInterface(self, intf): + return self.packet.if_delete(intf) + + def ping(self, intf, ping_dst, index=0): + return self.packet.ping(intf, ping_dst, index) + + def send_arp(self, intf, index=0): + return self.packet.send_arp(intf, index) + + def config_bgp(self, enable, intf): + return self.packet.config_bgp(enable, intf) + + def config_igmp(self, mode, intf, host): + return self.packet.config_igmp(mode, intf, host) + diff --git a/spytest/spytest/tgen/scapy/logger.py b/spytest/spytest/tgen/scapy/logger.py new file mode 100644 index 00000000000..44c0113707e --- /dev/null +++ b/spytest/spytest/tgen/scapy/logger.py @@ -0,0 +1,108 @@ +import os +import sys +import logging +import threading +import traceback + +class Logger(object): + def __init__(self, dry=False, name="scapy-tgen"): + self.dry = dry + self.dbg = 1 + self.log_file = None + self.screen_lock = threading.Lock() + self.fmt = logging.Formatter('%(asctime)s %(levelname)s: %(message)s') + self.logger = logging.getLogger(name) + stdlog = logging.StreamHandler(sys.stdout) + stdlog.setLevel(logging.ERROR) + ch = logging.StreamHandler() + ch.setLevel(logging.DEBUG) + ch.setFormatter(self.fmt) + self.logger.addHandler(ch) + self.logger.removeHandler(stdlog) + self.logger.propagate = False + self.log_file = None + self.file_handler = None + self.logs_dir = os.getenv("SCAPY_TGEN_LOGS_PATH", "server") + self.set_log_file(None) + + @staticmethod + def ensure_parent(filename): + path = os.path.dirname(filename) + path = os.path.abspath(path) + if not os.path.exists(path): + os.makedirs(path) + + def set_log_file(self, log_file): + if self.file_handler: + self.logger.removeHandler(self.file_handler) + self.file_handler = None + + if log_file: + if self.logs_dir: + log_file = os.path.join(self.logs_dir, log_file) + self.ensure_parent(log_file) + self.file_handler = logging.FileHandler(log_file) + self.logger.addHandler(self.file_handler) + + self.log_file = log_file + + def get_log(self, filename=None): + if not filename: + filename = self.log_file + elif not os.path.exists(filename) and self.logs_dir: + filename = os.path.join(self.logs_dir, filename) + + if not filename: + return "" + try: + fh = open(filename, 'r') + data = fh.readlines() + fh.close() + data = map(str.strip, data) + return "\n".join(data) + except: + return "" + + def todo(self, etype, name, value): + msg = "{}: {} = {}".format(etype, name, value) + self.error(msg) + raise ValueError(msg) + + def debug(self, *args, **kwargs): + msg = " ".join(map(str,args)) + self._log(logging.DEBUG, msg) + + def _log(self, lvl, msg): + if not msg.strip(): return + with self.screen_lock: + self.logger.log(lvl, msg) + + def log(self, *args, **kwargs): + msg = " ".join(map(str,args)) + self._log(logging.INFO, msg) + + def info(self, *args, **kwargs): + msg = " ".join(map(str,args)) + self._log(logging.INFO, msg) + + def error(self, *args, **kwargs): + msg = "" + #msg = msg + "=================================== " + msg = msg + " ".join(map(str,args)) + #msg = msg + "=================================== " + self._log(logging.ERROR, msg) + + def log_exception(self, e, msg): + self.logger.error("=========== exception ==================") + self.logger.error(msg) + self.logger.error("=========== exception ==================") + self.logger.error(msg, traceback.format_exc()) + with self.screen_lock: + self.logger.exception(e) + + @staticmethod + def setup(): + logging.basicConfig() + logger = logging.getLogger() + logger.setLevel(logging.DEBUG) + diff --git a/spytest/spytest/tgen/scapy/or_event.py b/spytest/spytest/tgen/scapy/or_event.py new file mode 100644 index 00000000000..4e5ce6f5765 --- /dev/null +++ b/spytest/spytest/tgen/scapy/or_event.py @@ -0,0 +1,30 @@ +import threading + +def or_set(self): + self._set() + self.changed() + +def or_clear(self): + self._clear() + self.changed() + +def orify(e, changed_callback): + e._set = e.set + e._clear = e.clear + e.changed = changed_callback + e.set = lambda: or_set(e) + e.clear = lambda: or_clear(e) + +def OrEvent(*events): + or_event = threading.Event() + def changed(): + bools = [e.is_set() for e in events] + if any(bools): + or_event.set() + else: + or_event.clear() + for e in events: + orify(e, changed) + changed() + return or_event + diff --git a/spytest/spytest/tgen/scapy/packet.py b/spytest/spytest/tgen/scapy/packet.py new file mode 100644 index 00000000000..c1c8496e7e4 --- /dev/null +++ b/spytest/spytest/tgen/scapy/packet.py @@ -0,0 +1,1332 @@ +import os +import re +import sys +import zlib +import time +import copy +import random +import textwrap +import binascii +import socket +import afpacket +import traceback +import ipaddress + +from binascii import hexlify + +this_dir = os.path.join(os.path.dirname(__file__)) + +from scapy.all import hexdump, L2Socket +from scapy.packet import Padding +from scapy.layers.l2 import Ether, Dot1Q, ARP +from scapy.layers.inet import IP, UDP, TCP, ICMP +from scapy.layers.inet6 import IPv6, ICMPv6ND_NA +from scapy.contrib.igmp import IGMP +from scapy.config import Conf +from dicts import SpyTestDict +from utils import Utils +from logger import Logger + +#dbg > 1 --- recv/send packet +#dbg > 2 --- recv/send packet summary +#dbg > 3 --- recv/send packet hex + +stale_list_ignore = [ + "port_handle", + "port_handle2", + "stream_id", + "mode", + "rate_percent", #TODO + + # + "circuit_endpoint_type", + "enable_stream_only_gen", + # + + # filtered stats + "high_speed_result_analysis", #TODO + "vlan_id_tracking", + "ip_dscp_tracking", + "track_by", + # filtered stats + + "ip_protocol", #TODO + + "vlan_id", + "vlan_id_mode", + "vlan_id_step", + "vlan_id_count", + + "mac_src", + "mac_src_mode", + "mac_src_step", + "mac_src_count", + "mac_dst", + "mac_dst_mode", + "mac_dst_step", + "mac_dst_count", + + "udp_src_port", + "udp_src_port_mode", + "udp_src_port_step", + "udp_src_port_count", + "udp_dst_port", + "udp_dst_port_mode", + "udp_dst_port_step", + "udp_dst_port_count", + + "tcp_src_port", + "tcp_src_port_mode", + "tcp_src_port_step", + "tcp_src_port_count", + "tcp_dst_port", + "tcp_dst_port_mode", + "tcp_dst_port_step", + "tcp_dst_port_count", + + "arp_src_hw_addr", + "arp_src_hw_mode", + "arp_src_hw_step", + "arp_src_hw_count", + "arp_dst_hw_addr", + "arp_dst_hw_mode", + "arp_dst_hw_step", + "arp_dst_hw_count", + + "ip_src_addr", + "ip_src_mode", + "ip_src_step", + "ip_src_count", + "ip_dst_addr", + "ip_dst_mode", + "ip_dst_step", + "ip_dst_count", + + "ipv6_src_addr", + "ipv6_src_mode", + "ipv6_src_step", + "ipv6_src_count", + "ipv6_dst_addr", + "ipv6_dst_mode", + "ipv6_dst_step", + "ipv6_dst_count", +] + +class ScapyPacket(object): + + def __init__(self, iface, dbg=0, dry=False, hex=False, logger=None): + self.dry = dry + self.logger = logger or Logger() + try: self.logger.info("SCAPY VERSION = {}".format(Conf().version)) + except: self.logger.info("SCAPY VERSION = UNKNOWN") + self.utils = Utils(self.dry, logger=self.logger) + self.max_rate_pps = self.utils.get_env_int("SPYTEST_SCAPY_MAX_RATE_PPS", 100) + self.dbg = dbg + self.hex = hex + self.iface = iface + self.tx_count = 0 + self.rx_count = 0 + self.rx_sock = None + self.tx_sock = None + self.finished = False + self.cleanup() + if iface and not self.dry: + self.init_bridge(iface) + bridge = "{0}-br".format(iface) + #os.system("brctl addbr {0}".format(bridge)) + os.system("ip link add name {0} type bridge".format(bridge)) + #os.system("brctl addif {0} {1}".format(bridge, iface)) + os.system("ip link set dev {1} master {0}".format(bridge, iface)) + # let bridge proxy arp packets + #os.system("echo 1 > /proc/sys/net/ipv4/conf/{0}-br/proxy_arp".format(iface)) + os.system("ip link set dev {0}-br up".format(iface)) + os.system("ip link set dev {0} up".format(iface)) + os.system("ip link set dev {0} promisc on".format(iface)) + os.system("ip link set dev {0} mtu 9194".format(iface)) + os.system("ip link set dev {0}-br mtu 9194".format(iface)) + os.system("echo 0 > /sys/class/net/{0}-br/bridge/multicast_snooping".format(iface)) + #os.system("ip link del {0}-rx".format(iface)) + os.system("ip link add {0}-rx type dummy".format(iface)) + os.system("ip link set dev {0}-rx mtu 9194".format(iface)) + self.disable_ra_accept("{0}-rx".format(iface)) + os.system("tc qdisc del dev {0} ingress".format(iface)) + os.system("tc qdisc add dev {0} ingress".format(iface)) + #os.system("tc filter del dev {0} parent ffff: protocol all u32 match u8 0 0 action mirred egress mirror dev {0}-rx".format(iface)) + os.system("tc filter add dev {0} parent ffff: protocol all u32 match u8 0 0 action mirred egress mirror dev {0}-rx".format(iface)) + os.system("ip link set {0}-rx up".format(iface)) + if self.dbg > 2: + os.system("ifconfig") + self.rx_open() + + def init_bridge(self, iface): + if iface and not self.dry: + bridge = "{0}-br".format(iface) + os.system("ip link set dev {0} down".format(bridge)) + #os.system("brctl delif {0} {1}".format(bridge, iface)) + os.system("ip link set dev {1} nomaster".format(bridge, iface)) + #os.system("brctl delbr {0}".format(bridge)) + os.system("ip link del {0}".format(bridge)) + os.system("ip link del {0}-rx".format(iface)) + time.sleep(1) + + def disable_ra_accept(self, iface): + os.system("sysctl -w net.ipv6.conf.{}.forwarding=0".format(iface)) + os.system("sysctl -w net.ipv6.conf.all.forwarding=0") + os.system("sysctl -w net.ipv6.conf.{}.accept_ra=0".format(iface)) + os.system("sysctl -w net.ipv6.conf.all.accept_ra=0") + os.system("sysctl -w net.ipv6.conf.{}.forwarding=0".format(iface)) + + def __del__(self): + self.logger.info("packet cleanup todo: ", self.iface) + self.cleanup() + + def clear_stats(self): + self.tx_count = 0 + self.rx_count = 0 + + def close_sock(self, sock): + try: sock.close() + except: pass + return None + + def cleanup(self): + self.logger.info("ScapyPacket {} cleanup...".format(self.iface)) + self.finished = True + self.init_bridge(self.iface) + self.finished = False + self.rx_sock = self.close_sock(self.rx_sock) + self.tx_sock = self.close_sock(self.tx_sock) + + def rx_open(self): + if not self.iface or self.dry: return + ETH_P_ALL = 3 + self.rx_sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(ETH_P_ALL)) + self.rx_sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 12 * 1024) + self.rx_sock.bind((self.iface+"-rx", 3)) + afpacket.enable_auxdata(self.rx_sock) + + def readp(self, iface): + + if self.dry: + time.sleep(2) + return None + + if not self.iface: + return None + + try: + data = afpacket.recv(self.rx_sock, 12 * 1024) + except Exception as exp: + if self.finished: + return None + raise exp + packet = Ether(data) + self.rx_count = self.rx_count + 1 + self.trace_stats() + + if self.dbg > 1: + self.logger.debug("readp: {} len: {} count: {}".format(iface, len(data), self.rx_count)) + + if self.dbg > 2: + self.trace_packet(packet, self.hex) + + return packet + + def sendp(self, data, iface): + self.tx_count = self.tx_count + 1 + self.trace_stats() + + if self.dbg > 1: + self.logger.debug("sendp: {} len: {} count: {}".format(iface, len(data), self.tx_count)) + + if self.dbg > 2: + self.trace_packet(data, self.hex) + + if not self.dry: + #sendp(data, iface=iface, verbose=False) + if not self.tx_sock: + self.tx_sock = L2Socket(iface) + self.tx_sock.send(data) + + def trace_stats(self): + #self.logger.debug("Name: {} RX: {} TX: {}".format(self.iface, self.rx_count, self.tx_count)) + pass + + def show_pkt(self, pkt): + try: + self.logger.debug(pkt.show2(dump=True)) + except: + self.logger.debug(pkt.command()) + pkt.show2() + + def trace_packet(self, pkt, hex=True, fields=True): + if not fields and not hex: return + if isinstance(pkt, str): pkt = Ether(pkt) + if fields: self.show_pkt(pkt) + if hex: hexdump(pkt) + + def send_packet(self, pwa, iface): + if pwa.padding: + strpkt = str(pwa.pkt/pwa.padding) + else: + strpkt = str(pwa.pkt) + pkt_bytes = self.utils.tobytes(strpkt) + try: + crc1 = '{:08x}'.format(socket.htonl(zlib.crc32(pkt_bytes) & 0xFFFFFFFF)) + crc = binascii.unhexlify(crc1) + except: + crc1='{:08x}'.format(socket.htonl(zlib.crc32(pkt_bytes) & 0xFFFFFFFF)) + crc = binascii.unhexlify('00' * 4) + bstr = bytes(strpkt+crc) + self.sendp(bstr, iface) + return bstr + + def check(self, pkt): + pkt.do_build() + if self.dbg > 3: + self.show_pkt(pkt) + return pkt + + def pop_mac(self, d, prop, default): + val = d.pop(prop, None) + if not val: + val = "{}".format(default) + if isinstance(val, list): + values = val + else: + values = val.split(" ") + for index,val in enumerate(values): + values[index] = val.replace(".", ":") + return values + + def pop_str(self, d, prop, default): + val = d.pop(prop, None) + if not val: + val = "{}".format(default) + return val + + def pop_int(self, d, prop, default): + val = d.pop(prop, "{}".format(default)) + try: + return int(str(val)) + except: + self.logger.info(traceback.format_exc()) + + def pop_hex(self, d, prop, default): + val = d.pop(prop, "{}".format(default)) + try: + return int(str(val), 16) + except: + self.logger.info(traceback.format_exc()) + + def get_int(self, d, prop, default): + val = d.get(prop, "{}".format(default)) + try: + return int(str(val)) + except: + self.logger.info(traceback.format_exc()) + + def ensure_int(self, name, value, min_val, max_val): + if value < 0 or value > 13312: + msg = "invalid value {} = {} shoud be > {} and < {}" + msg = msg.format(name, value, min_val, max_val) + self.logger.error(msg) + + def build_udp(self, kws): + udp = UDP() + udp.sport = self.pop_int(kws, "udp_src_port", 0) + udp.dport = self.pop_int(kws, "udp_dst_port", 0) + #udp.len + #udp.chksum + return udp + + def build_tcp(self, kws): + tcp = TCP() + tcp.sport = self.pop_int(kws, "tcp_src_port", 0) + tcp.dport = self.pop_int(kws, "tcp_dst_port", 0) + tcp.seq = self.pop_int(kws, "tcp_seq_num", 0) + tcp.ack = self.pop_int(kws, "tcp_ack_num", 0) + tcp.flags = '' + if self.pop_int(kws, "tcp_syn_flag", 0): + tcp.flags = str(tcp.flags) + 'S' + if self.pop_int(kws, "tcp_fin_flag", 0): + tcp.flags = str(tcp.flags) + 'F' + if self.pop_int(kws, "tcp_urg_flag", 0): + tcp.flags = str(tcp.flags) + 'U' + if self.pop_int(kws, "tcp_psh_flag", 0): + tcp.flags = str(tcp.flags) + 'P' + if self.pop_int(kws, "tcp_ack_flag", 0): + tcp.flags = str(tcp.flags) + 'A' + if self.pop_int(kws, "tcp_rst_flag", 0): + tcp.flags = str(tcp.flags) + 'R' + #tcp.dataofs + #tcp.reserved + tcp.window = self.pop_int(kws, "tcp_window", 0) + #tcp.chksum + #tcp.urgptr + #tcp.options + return tcp + + def build_icmp(self, kws): + icmp = ICMP() + icmp.type = self.pop_int(kws, "icmp_type", 0) + #icmp.code = Driver.getConstValue(icmpCfg.code) + #TODO: icmp_type_count, icmp_type_mode + return icmp + + def build_icmp6(self, kws): + icmp_type = self.pop_int(kws, "icmp_type", 0) + if icmp_type == 136: + icmp = ICMPv6ND_NA() + icmp.R = self.pop_int(kws, "icmp_ndp_nam_r_flag", 1) + icmp.S = self.pop_int(kws, "icmp_ndp_nam_s_flag", 0) + icmp.O = self.pop_int(kws, "icmp_ndp_nam_o_flag", 1) + icmp.tgt = self.pop_str(kws, "icmp_target_addr", "::") + else: + icmp = ICMP() + return icmp + + def build_igmp(self, kws): + igmp = IGMP() + #igmp.mrcode + igmp.gaddr = self.pop_str(kws, "igmp_group_addr", "0.0.0.0") + #igmp.chksum = 0 + igmp_msg_type = self.pop_str(kws, "igmp_msg_type", "report") + if igmp_msg_type == "report": + igmp.type = 0x16 + elif igmp_msg_type == "query": + igmp.type = 0x11 + elif igmp_msg_type == "leave": + igmp.type = 0x17 + else: + self.logger.todo("unknown", "igmp_msg_type", igmp_msg_type) + igmp = None + return igmp + + def fill_emulation_params(self, stream): + + # read params from emulation interfaces + if "emulation_src_handle" in stream.kws: + emulation_src_handle = stream.kws.pop("emulation_src_handle") + intf_ip_addr = emulation_src_handle.kws.get("intf_ip_addr", "0.0.0.0") + ipv6_intf_addr = emulation_src_handle.kws.get("ipv6_intf_addr", "") + count = self.utils.intval(emulation_src_handle.kws, "count", 1) + if ipv6_intf_addr: + stream.kws["l3_protocol"] = "ipv6" + stream.kws["ipv6_src_addr"] = ipv6_intf_addr + if count > 1: + stream.kws["ipv6_src_count"] = count + stream.kws["ipv6_src_mode"] = "increment" + else: + stream.kws["l3_protocol"] = "ipv4" + stream.kws["ip_src_addr"] = intf_ip_addr + stream.kws["ip_src_count"] = count + if count > 1: + stream.kws["ip_src_count"] = count + stream.kws["ip_src_mode"] = "increment" + try: + stream.kws["mac_src"] = [self.get_my_mac(emulation_src_handle)] + stream.kws["mac_dst"] = [self.get_arp_mac(emulation_src_handle)] + except Exception as exp: + self.logger.info(exp) + self.logger.info(traceback.format_exc()) + self.logger.debug("updated stream.kws-1 = {}".format(stream.kws)) + + if "emulation_dst_handle" in stream.kws: + emulation_dst_handle = stream.kws.pop("emulation_dst_handle") + intf_ip_addr = emulation_dst_handle.kws.get("intf_ip_addr", "") + ipv6_intf_addr = emulation_dst_handle.kws.get("ipv6_intf_addr", "") + count = self.utils.intval(emulation_dst_handle.kws, "count", 1) + if ipv6_intf_addr: + stream.kws["l3_protocol"] = "ipv6" + stream.kws["ipv6_dst_addr"] = ipv6_intf_addr + if count > 1: + stream.kws["ipv6_dst_count"] = count + stream.kws["ipv6_dst_mode"] = "increment" + else: + stream.kws["l3_protocol"] = "ipv4" + stream.kws["ip_dst_addr"] = intf_ip_addr + if count > 1: + stream.kws["ip_dst_count"] = count + stream.kws["ip_dst_mode"] = "increment" + self.logger.debug("updated stream.kws-2 = {}".format(stream.kws)) + + def build_first(self, stream): + + self.fill_emulation_params(stream) + + kws = copy.deepcopy(stream.kws) + + self.logger.info("=========== build_first this {} = {}".format(stream.stream_id, kws)) + # fill default variables + mac_src = self.pop_mac(kws, "mac_src", "00:00:01:00:00:01") + mac_dst = self.pop_mac(kws, "mac_dst", "00:00:00:00:00:00") + + duration = self.pop_int(kws, "duration", 1) + rate_pps = self.pop_int(kws, "rate_pps", 1) + l2_encap = self.pop_str(kws, "l2_encap", "") + l3_protocol = self.pop_str(kws, "l3_protocol", "") + l4_protocol = self.pop_str(kws, "l4_protocol", "") + vlan_en = self.pop_str(kws, "vlan", "enable") + vlan_id = self.pop_int(kws, "vlan_id", 0) + vlan_cfi = self.pop_int(kws, "vlan_cfi", 0) + vlan_prio = self.pop_int(kws, "vlan_user_priority", 0) + frame_size = self.pop_int(kws, "frame_size", 64) + frame_size_min = self.pop_int(kws, "frame_size_min", 64) + frame_size_max = self.pop_int(kws, "frame_size_max", 9210) + frame_size_step = self.pop_int(kws, "frame_size_step", 64) + transmit_mode = self.pop_str(kws, "transmit_mode", "continuous") + data_pattern = self.pop_str(kws, "data_pattern", "") + pkts_per_burst = self.pop_int(kws, "pkts_per_burst", 1) + ethernet_value = self.pop_hex(kws, "ethernet_value", 0) + length_mode = self.pop_str(kws, "length_mode", "fixed") + l3_length = self.pop_int(kws, "l3_length", 110) + data_pattern_mode = self.pop_str(kws, "data_pattern_mode", "fixed") + + mac_dst_mode = kws.get("mac_dst_mode", "fixed").strip() + if mac_dst_mode not in ["fixed", "increment", "decrement", "list"]: + self.logger.error("unhandled option mac_dst_mode = {}".format(mac_dst_mode)) + mac_src_mode = kws.get("mac_src_mode", "fixed").strip() + if mac_src_mode not in ["fixed", "increment", "decrement", "list"]: + self.logger.error("unhandled option mac_src_mode = {}".format(mac_src_mode)) + + self.ensure_int("l3_length", l3_length, 44, 16365) + if length_mode in ["random", "increment", "incr"]: + self.ensure_int("frame_size_min", frame_size_min, 44, 13312) + self.ensure_int("frame_size_max", frame_size_max, 44, 13312) + self.ensure_int("frame_size_step", frame_size_step, 0, 13312) + elif length_mode != "fixed": + self.logger.error("unhandled option length_mode = {}".format(length_mode)) + + if data_pattern_mode != "fixed": + self.logger.error("unhandled option data_pattern_mode = {}".format(data_pattern_mode)) + + # update parsed values + stream.kws["mac_src"] = mac_src + stream.kws["mac_dst"] = mac_dst + + pkt = Ether() + pkt.src = mac_src[0] + pkt.dst = mac_dst[0] + + if ethernet_value: + pkt.type = ethernet_value + + # add l3_protocol + if l3_protocol == "arp": + arp = ARP() + arp.hwsrc = self.pop_str(kws, "arp_src_hw_addr", "00:00:01:00:00:02").replace(".", ":") + arp.hwdst = self.pop_str(kws, "arp_dst_hw_addr", "00:00:00:00:00:00").replace(".", ":") + arp.psrc = self.pop_str(kws, "ip_src_addr", "0.0.0.0") + arp.pdst = self.pop_str(kws, "ip_dst_addr", "192.0.0.1") + arp_oper = self.pop_str(kws, "arp_operation", "arpRequest") + if arp_oper == "arpRequest": + arp.op = 1 + elif arp_oper in ["arpResponse", "arpReply"]: + arp.op = 2 + else: + self.logger.debug("unknown ARP operation: {}".format(arp_oper)) + arp = None + if arp: + pkt = self.check(pkt/arp) + elif l3_protocol == "ipv4": + ip = IP() + #ip.id + #ip.chksum + ip.src = self.pop_str(kws, "ip_src_addr", "0.0.0.0") + ip.dst = self.pop_str(kws, "ip_dst_addr", "192.0.0.1") + ip.ttl = self.pop_int(kws, "ip_ttl", 255) + #ip.frag + #ip.len + #ip.flags + #ip.options + proto = self.pop_int(kws, "ip_proto", -1) + if proto >= 0: ip.proto = proto + ip_dscp = self.pop_int(kws, "ip_dscp", 0) + if ip_dscp: + ip.tos = int(bin(ip_dscp) + "00", 2) + else: + ip.tos = ip.tos | (self.pop_int(kws, "ip_precedence", 0) << 5) + ip.tos = ip.tos | (self.pop_int(kws, "ip_delay", 0) << 4) + ip.tos = ip.tos | (self.pop_int(kws, "ip_throughput", 0) << 3) + ip.tos = ip.tos | (self.pop_int(kws, "ip_reliability", 0) << 2) + ip.tos = ip.tos | (self.pop_int(kws, "ip_cost", 0) << 1) + ip.tos = ip.tos | (self.pop_int(kws, "ip_reserved", 0) << 0) + pkt = self.check(pkt/ip) + + # add l4_protocol + if l4_protocol in ["udp"]: + udp = self.build_udp(kws) + pkt = self.check(pkt/udp) + elif l4_protocol in ["tcp"]: + tcp = self.build_tcp(kws) + pkt = self.check(pkt/tcp) + elif l4_protocol in ["icmp"]: + icmp = self.build_icmp(kws) + pkt = self.check(pkt/icmp) + elif l4_protocol in ["igmp"]: + igmp = self.build_igmp(kws) + if igmp: + pkt = self.check(pkt/igmp) + elif l4_protocol: + self.logger.todo("unsupported-ipv4", "l4_protocol", l4_protocol) + elif l3_protocol == "ipv6": + ip6 = IPv6() + ip6.src = self.pop_str(kws, "ipv6_src_addr", "fe80:0:0:0:0:0:0:12") + ip6.dst = self.pop_str(kws, "ipv6_dst_addr", "fe80:0:0:0:0:0:0:22") + ip6.hlim = self.pop_int(kws, "ipv6_hop_limit", 255) + ip6.tc = self.pop_int(kws, "ipv6_traffic_class", 255) + nh = self.pop_int(kws, "ipv6_next_header", 0) + if nh: ip6.nh = nh + # add l4_protocol + pkt = self.check(pkt/ip6) + if l4_protocol in ["udp"]: + udp = self.build_udp(kws) + pkt = self.check(pkt/udp) + elif l4_protocol in ["tcp"]: + tcp = self.build_tcp(kws) + pkt = self.check(pkt/tcp) + elif l4_protocol in ["icmp"]: + icmp = self.build_icmp6(kws) + pkt = self.check(pkt/icmp) + elif l4_protocol in ["igmp"]: + igmp = self.build_igmp(kws) + if igmp: + pkt = self.check(pkt/igmp) + elif l4_protocol: + self.logger.todo("unsupported-ipv6", "l4_protocol", l4_protocol) + elif l3_protocol: + self.logger.todo("unsupported", "l3_protocol", l3_protocol) + return None + + # insert VLAN header if required + if l2_encap in ["ethernet_ii_vlan", "ethernet_ii"] and vlan_id > 0 and vlan_en == "enable": + (payload, payload_type) = (pkt.payload, pkt.type) + pkt.remove_payload() + pkt.type = 0x8100 + pkt = self.check(pkt/Dot1Q(vlan=vlan_id, id=vlan_cfi, prio=vlan_prio, type=payload_type)/payload) + #self.trace_packet(pkt) + + # handle transmit_mode + if transmit_mode == "continuous": + max_loops = 0 + elif transmit_mode == "single_burst": + max_loops = pkts_per_burst + if rate_pps < pkts_per_burst: + rate_pps = pkts_per_burst + else: + self.logger.debug("TODO: transmit_mode={}".format(transmit_mode)) + max_loops = 0 + + # append the data pattern if specified + if data_pattern: + padding = Padding() + tmp_pattern = ''.join(c for c in data_pattern if c not in ' ') + tmp_pattern = binascii.unhexlify(tmp_pattern) + padLen = int(frame_size - len(pkt) - 4 - len(padding)) + if len(tmp_pattern) > padLen: + padding = Padding(tmp_pattern[:padLen]) + else: + padding = Padding(tmp_pattern) + pkt = self.check(pkt/padding) + + # update padding length based on frame_size + if length_mode == "fixed": + padLen = int(frame_size - len(pkt) - 4) + if padLen > 0: + padding = Padding(binascii.unhexlify('00' * padLen)) + pkt = self.check(pkt/padding) + + # verify unhandled options + for key, value in kws.items(): + if key not in stale_list_ignore: + self.logger.error("unhandled option {} = {}".format(key, value)) + + pwa = SpyTestDict() + pwa.pkt = pkt + pwa.left = max_loops + pwa.transmit_mode = transmit_mode + if rate_pps > self.max_rate_pps: + self.logger.debug("drop the rate from {} to {}".format(rate_pps, self.max_rate_pps)) + rate_pps = self.max_rate_pps + pwa.rate_pps = rate_pps + pwa.duration = duration + pwa.stream = stream + pwa.mac_src_count = 0 + pwa.mac_dst_count = 0 + pwa.arp_src_hw_count = 0 + pwa.arp_dst_hw_count = 0 + pwa.ip_src_count = 0 + pwa.ip_dst_count = 0 + pwa.ipv6_src_count = 0 + pwa.ipv6_dst_count = 0 + pwa.vlan_id_count = 0 + pwa.tcp_src_port_count = 0 + pwa.tcp_dst_port_count = 0 + pwa.udp_src_port_count = 0 + pwa.udp_dst_port_count = 0 + ##self.trace_packet(pkt) + #self.logger.debug(pwa) + + pwa.length_mode = length_mode + pwa.frame_size_current = frame_size_min + pwa.frame_size_min = frame_size_min + pwa.frame_size_max = frame_size_max + pwa.frame_size_step = frame_size_step + self.add_padding(pwa, True) + + return pwa + + def add_padding(self, pwa, first): + pwa.padding = None + if pwa.length_mode == "random": + pktLen = len(pwa.pkt) + frame_size = random.randrange(pwa.frame_size_min, pwa.frame_size_max+1) + padLen = int(frame_size - pktLen - 4) + if padLen > 0: + pwa.padding = Padding(binascii.unhexlify('00' * padLen)) + elif pwa.length_mode in ["increment", "incr"]: + pktLen = len(pwa.pkt) + if first: + frame_size = pwa.frame_size_min + else: + frame_size = pwa.frame_size_current + pwa.frame_size_step + if frame_size > pwa.frame_size_max: + pwa.frame_size_current = pwa.frame_size_min + else: + pwa.frame_size_current = frame_size + padLen = int(pwa.frame_size_current - pktLen - 4) + if padLen > 0: + pwa.padding = Padding(binascii.unhexlify('00' * padLen)) + + def build_next_dma(self, pwa): + + # Change Ether SRC MAC + mac_src_mode = pwa.stream.kws.get("mac_src_mode", "fixed").strip() + mac_src_step = pwa.stream.kws.get("mac_src_step", "00:00:00:00:00:01") + mac_src_count = self.utils.intval(pwa.stream.kws, "mac_src_count", 0) + if mac_src_mode in ["increment", "decrement"]: + if mac_src_mode in ["increment"]: + pwa.pkt[0].src = self.utils.incrementMac(pwa.pkt[0].src, mac_src_step) + else: + pwa.pkt[0].src = self.utils.decrementMac(pwa.pkt[0].src, mac_src_step) + pwa.mac_src_count = pwa.mac_src_count + 1 + if mac_src_count > 0 and pwa.mac_src_count >= mac_src_count: + pwa.pkt[0].src = pwa.stream.kws["mac_src"][0] + pwa.mac_src_count = 0 + elif mac_src_mode in ["list"]: + pwa.mac_src_count = pwa.mac_src_count + 1 + if pwa.mac_src_count >= len(pwa.stream.kws["mac_src"]): + pwa.pkt[0].src = pwa.stream.kws["mac_src"][0] + pwa.mac_src_count = 0 + else: + pwa.pkt[0].src = pwa.stream.kws["mac_src"][pwa.mac_src_count] + elif mac_src_mode != "fixed": + self.logger.todo("unhandled", "mac_src_mode", mac_src_mode) + + # Change Ether DST MAC + mac_dst_mode = pwa.stream.kws.get("mac_dst_mode", "fixed").strip() + mac_dst_step = pwa.stream.kws.get("mac_dst_step", "00:00:00:00:00:01") + mac_dst_count = self.utils.intval(pwa.stream.kws, "mac_dst_count", 0) + if mac_dst_mode in ["increment", "decrement"]: + if mac_dst_mode in ["increment"]: + pwa.pkt[0].dst = self.utils.incrementMac(pwa.pkt[0].dst, mac_dst_step) + else: + pwa.pkt[0].dst = self.utils.decrementMac(pwa.pkt[0].dst, mac_dst_step) + pwa.mac_dst_count = pwa.mac_dst_count + 1 + if mac_dst_count > 0 and pwa.mac_dst_count >= mac_dst_count: + pwa.pkt[0].dst = pwa.stream.kws["mac_dst"][0] + pwa.mac_dst_count = 0 + elif mac_dst_mode in ["list"]: + pwa.mac_dst_count = pwa.mac_dst_count + 1 + if pwa.mac_dst_count >= len(pwa.stream.kws["mac_dst"]): + pwa.pkt[0].dst = pwa.stream.kws["mac_dst"][0] + pwa.mac_dst_count = 0 + else: + pwa.pkt[0].dst = pwa.stream.kws["mac_dst"][pwa.mac_dst_count] + elif mac_dst_mode != "fixed": + self.logger.todo("unhandled", "mac_dst_mode", mac_dst_mode) + + # Change ARP SRC MAC + if ARP in pwa.pkt: + arp_src_hw_mode = pwa.stream.kws.get("arp_src_hw_mode", "fixed").strip() + arp_src_hw_step = pwa.stream.kws.get("arp_src_hw_step", "00:00:00:00:00:01") + arp_src_hw_count = self.utils.intval(pwa.stream.kws, "arp_src_hw_count", 0) + if arp_src_hw_mode in ["increment", "decrement"]: + if arp_src_hw_mode in ["increment"]: + pwa.pkt[ARP].hwsrc = self.utils.incrementMac(pwa.pkt[ARP].hwsrc, arp_src_hw_step) + else: + pwa.pkt[ARP].hwsrc = self.utils.decrementMac(pwa.pkt[ARP].hwsrc, arp_src_hw_step) + pwa.arp_src_hw_count = pwa.arp_src_hw_count + 1 + if arp_src_hw_count > 0 and pwa.arp_src_hw_count >= arp_src_hw_count: + pwa.pkt[ARP].hwsrc = pwa.stream.kws.get("arp_src_hw_addr", "00:00:01:00:00:02").replace(".", ":") + pwa.arp_src_hw_count = 0 + elif arp_src_hw_mode != "fixed": + self.logger.todo("unhandled", "arp_src_hw_mode", arp_src_hw_mode) + + # Change ARP DST MAC + if ARP in pwa.pkt: + arp_dst_hw_mode = pwa.stream.kws.get("arp_dst_hw_mode", "fixed").strip() + arp_dst_hw_step = pwa.stream.kws.get("arp_dst_hw_step", "00:00:00:00:00:01") + arp_dst_hw_count = self.utils.intval(pwa.stream.kws, "arp_dst_hw_count", 0) + if arp_dst_hw_mode in ["increment", "decrement"]: + if arp_dst_hw_mode in ["increment"]: + pwa.pkt[ARP].hwdst = self.utils.incrementMac(pwa.pkt[ARP].hwdst, arp_dst_hw_step) + else: + pwa.pkt[ARP].hwdst = self.utils.decrementMac(pwa.pkt[ARP].hwdst, arp_dst_hw_step) + pwa.arp_dst_hw_count = pwa.arp_dst_hw_count + 1 + if arp_dst_hw_count > 0 and pwa.arp_dst_hw_count >= arp_dst_hw_count: + pwa.pkt[ARP].hwdst = pwa.stream.kws.get("arp_dst_hw_addr", "00:00:00:00:00:00").replace(".", ":") + pwa.arp_dst_hw_count = 0 + elif arp_dst_hw_mode != "fixed": + self.logger.todo("unhandled", "arp_dst_hw_mode", arp_dst_hw_mode) + + # Change SRC IP + if IP in pwa.pkt: + ip_src_mode = pwa.stream.kws.get("ip_src_mode", "fixed").strip() + ip_src_step = pwa.stream.kws.get("ip_src_step", "0.0.0.1") + ip_src_count = self.utils.intval(pwa.stream.kws, "ip_src_count", 0) + if ip_src_mode in ["increment", "decrement"]: + if ip_src_mode in ["increment"]: + pwa.pkt[IP].src = self.utils.incrementIPv4(pwa.pkt[IP].src, ip_src_step) + else: + pwa.pkt[IP].src = self.utils.decrementIPv4(pwa.pkt[IP].src, ip_src_step) + pwa.ip_src_count = pwa.ip_src_count + 1 + if ip_src_count > 0 and pwa.ip_src_count >= ip_src_count: + pwa.pkt[IP].src = pwa.stream.kws.get("ip_src_addr", "0.0.0.0") + pwa.ip_src_count = 0 + elif ip_src_mode != "fixed": + self.logger.todo("unhandled", "ip_src_mode", ip_src_mode) + + # Change DST IP + if IP in pwa.pkt: + ip_dst_mode = pwa.stream.kws.get("ip_dst_mode", "fixed").strip() + ip_dst_step = pwa.stream.kws.get("ip_dst_step", "0.0.0.1") + ip_dst_count = self.utils.intval(pwa.stream.kws, "ip_dst_count", 0) + if ip_dst_mode in ["increment", "decrement"]: + if ip_dst_mode in ["increment"]: + pwa.pkt[IP].dst = self.utils.incrementIPv4(pwa.pkt[IP].dst, ip_dst_step) + else: + pwa.pkt[IP].dst = self.utils.decrementIPv4(pwa.pkt[IP].dst, ip_dst_step) + pwa.ip_dst_count = pwa.ip_dst_count + 1 + if ip_dst_count > 0 and pwa.ip_dst_count >= ip_dst_count: + pwa.pkt[IP].dst = pwa.stream.kws.get("ip_dst_addr", "192.0.0.1") + pwa.ip_dst_count = 0 + elif ip_dst_mode != "fixed": + self.logger.todo("unhandled", "ip_dst_mode", ip_dst_mode) + + # Change SRC IPv6 + if IPv6 in pwa.pkt: + ipv6_src_mode = pwa.stream.kws.get("ipv6_src_mode", "fixed").strip() + ipv6_src_step = pwa.stream.kws.get("ipv6_src_step", "::1") + ipv6_src_count = self.utils.intval(pwa.stream.kws, "ipv6_src_count", 0) + if ipv6_src_mode in ["increment", "decrement"]: + if ipv6_src_mode in ["increment"]: + pwa.pkt[IPv6].src = self.utils.incrementIPv6(pwa.pkt[IPv6].src, ipv6_src_step) + else: + pwa.pkt[IPv6].src = self.utils.decrementIPv6(pwa.pkt[IPv6].src, ipv6_src_step) + pwa.ipv6_src_count = pwa.ipv6_src_count + 1 + if ipv6_src_count > 0 and pwa.ipv6_src_count >= ipv6_src_count: + pwa.pkt[IPv6].src = pwa.stream.kws.get("ipv6_src_addr", "fe80:0:0:0:0:0:0:12") + pwa.ipv6_src_count = 0 + elif ipv6_src_mode != "fixed": + self.logger.todo("unhandled", "ipv6_src_mode", ipv6_src_mode) + + # Change DST IPv6 + if IPv6 in pwa.pkt: + ipv6_dst_mode = pwa.stream.kws.get("ipv6_dst_mode", "fixed").strip() + ipv6_dst_step = pwa.stream.kws.get("ipv6_dst_step", "::1") + ipv6_dst_count = self.utils.intval(pwa.stream.kws, "ipv6_dst_count", 0) + if ipv6_dst_mode in ["increment", "decrement"]: + if ipv6_dst_mode in ["increment"]: + pwa.pkt[IPv6].dst = self.utils.incrementIPv6(pwa.pkt[IPv6].dst, ipv6_dst_step) + else: + pwa.pkt[IPv6].dst = self.utils.decrementIPv6(pwa.pkt[IPv6].dst, ipv6_dst_step) + pwa.ipv6_dst_count = pwa.ipv6_dst_count + 1 + if ipv6_dst_count > 0 and pwa.ipv6_dst_count >= ipv6_dst_count: + pwa.pkt[IPv6].dst = pwa.stream.kws.get("ipv6_dst_addr", "fe80:0:0:0:0:0:0:22") + pwa.ipv6_dst_count = 0 + elif ipv6_dst_mode != "fixed": + self.logger.todo("unhandled", "ipv6_dst_mode", ipv6_dst_mode) + + # Change VLAN + if Dot1Q in pwa.pkt: + vlan_id_mode = pwa.stream.kws.get("vlan_id_mode", "fixed").strip() + vlan_id_step = self.utils.intval(pwa.stream.kws, "vlan_id_step", 1) + vlan_id_count = self.utils.intval(pwa.stream.kws, "vlan_id_count", 0) + if vlan_id_mode in ["increment", "decrement"]: + if vlan_id_mode in ["increment"]: + pwa.pkt[Dot1Q].vlan = pwa.pkt[Dot1Q].vlan + vlan_id_step + else: + pwa.pkt[Dot1Q].vlan = pwa.pkt[Dot1Q].vlan - vlan_id_step + pwa.vlan_id_count = pwa.vlan_id_count + 1 + if vlan_id_count > 0 and pwa.vlan_id_count >= vlan_id_count: + pwa.pkt[Dot1Q].vlan = self.utils.intval(pwa.stream.kws, "vlan_id", 0) + pwa.vlan_id_count = 0 + elif vlan_id_mode != "fixed": + self.logger.todo("unhandled", "vlan_id_mode", vlan_id_mode) + + # Change TCP SRC PORT + if TCP in pwa.pkt: + tcp_src_port_mode = pwa.stream.kws.get("tcp_src_port_mode", "fixed").strip() + tcp_src_port_step = self.utils.intval(pwa.stream.kws, "tcp_src_port_step", 1) + tcp_src_port_count = self.utils.intval(pwa.stream.kws, "tcp_src_port_count", 0) + if tcp_src_port_mode in ["increment", "decrement", "incr", "decr"]: + if tcp_src_port_mode in ["increment", "incr"]: + pwa.pkt[TCP].sport = pwa.pkt[TCP].sport + tcp_src_port_step + else: + pwa.pkt[TCP].sport = pwa.pkt[TCP].sport - tcp_src_port_step + pwa.tcp_src_port_count = pwa.tcp_src_port_count + 1 + if tcp_src_port_count > 0 and pwa.tcp_src_port_count >= tcp_src_port_count: + pwa.pkt[TCP].sport = self.utils.intval(pwa.stream.kws, "tcp_src_port", 0) + pwa.tcp_src_port_count = 0 + elif tcp_src_port_mode != "fixed": + self.logger.todo("unhandled", "tcp_src_port_mode", tcp_src_port_mode) + + # Change TCP DST PORT + if TCP in pwa.pkt: + tcp_dst_port_mode = pwa.stream.kws.get("tcp_dst_port_mode", "fixed").strip() + tcp_dst_port_step = self.utils.intval(pwa.stream.kws, "tcp_dst_port_step", 1) + tcp_dst_port_count = self.utils.intval(pwa.stream.kws, "tcp_dst_port_count", 0) + if tcp_dst_port_mode in ["increment", "decrement", "incr", "decr"]: + if tcp_dst_port_mode in ["increment", "incr"]: + pwa.pkt[TCP].dport = pwa.pkt[TCP].sport + tcp_dst_port_step + else: + pwa.pkt[TCP].dport = pwa.pkt[TCP].sport - tcp_dst_port_step + pwa.tcp_dst_port_count = pwa.tcp_dst_port_count + 1 + if tcp_dst_port_count > 0 and pwa.tcp_dst_port_count >= tcp_dst_port_count: + pwa.pkt[TCP].dport = self.utils.intval(pwa.stream.kws, "tcp_dst_port", 0) + pwa.tcp_dst_port_count = 0 + elif tcp_dst_port_mode != "fixed": + self.logger.todo("unhandled", "tcp_dst_port_mode", tcp_dst_port_mode) + + # Change UDP SRC PORT + if UDP in pwa.pkt: + udp_src_port_mode = pwa.stream.kws.get("udp_src_port_mode", "fixed").strip() + udp_src_port_step = self.utils.intval(pwa.stream.kws, "udp_src_port_step", 1) + udp_src_port_count = self.utils.intval(pwa.stream.kws, "udp_src_port_count", 0) + if udp_src_port_mode in ["increment", "decrement", "incr", "decr"]: + if udp_src_port_mode in ["increment", "incr"]: + pwa.pkt[UDP].sport = pwa.pkt[UDP].sport + udp_src_port_step + else: + pwa.pkt[UDP].sport = pwa.pkt[UDP].sport - udp_src_port_step + pwa.udp_src_port_count = pwa.udp_src_port_count + 1 + if udp_src_port_count > 0 and pwa.udp_src_port_count >= udp_src_port_count: + pwa.pkt[UDP].sport = self.utils.intval(pwa.stream.kws, "udp_src_port", 0) + pwa.udp_src_port_count = 0 + elif udp_src_port_mode != "fixed": + self.logger.todo("unhandled", "udp_src_port_mode", udp_src_port_mode) + + # Change UDP DST PORT + if UDP in pwa.pkt: + udp_dst_port_mode = pwa.stream.kws.get("udp_dst_port_mode", "fixed").strip() + udp_dst_port_step = self.utils.intval(pwa.stream.kws, "udp_dst_port_step", 1) + udp_dst_port_count = self.utils.intval(pwa.stream.kws, "udp_dst_port_count", 0) + if udp_dst_port_mode in ["increment", "decrement", "incr", "decr"]: + if udp_dst_port_mode in ["increment", "incr"]: + pwa.pkt[UDP].dport = pwa.pkt[UDP].sport + udp_dst_port_step + else: + pwa.pkt[UDP].dport = pwa.pkt[UDP].sport - udp_dst_port_step + pwa.udp_dst_port_count = pwa.udp_dst_port_count + 1 + if udp_dst_port_count > 0 and pwa.udp_dst_port_count >= udp_dst_port_count: + pwa.pkt[UDP].dport = self.utils.intval(pwa.stream.kws, "udp_dst_port", 0) + pwa.udp_dst_port_count = 0 + elif udp_dst_port_mode != "fixed": + self.logger.todo("unhandled", "udp_dst_port_mode", udp_dst_port_mode) + + # add padding based on length_mode + self.add_padding(pwa, False) + + return pwa + + def build_next(self, pwa): + if self.dbg > 1: + self.logger.debug("build_next transmit_mode={} left={}".format(pwa.transmit_mode, pwa.left)) + + if pwa.transmit_mode in ["continuous", "continuous_burst"] or pwa.left > 1: + pwa = self.build_next_dma(pwa) + if not pwa: return None + + if pwa.transmit_mode in ["continuous", "continuous_burst"]: + return pwa + elif pwa.left > 1: + pwa.left = pwa.left - 1 + return pwa + elif pwa.transmit_mode in ["single_burst"]: + return None + else: + self.logger.debug("TODO: transmit_mode = {}".format(pwa.transmit_mode)) + return None + + def match_stream(self, stream, pkt): + rx_str = hexlify(bytes(str(pkt))) + #print("CMP: STREAM: {}".format(stream.kws)) + #print("CMP: TRACK_RX: {}".format(rx_str)) + for track_pkt in stream.track_pkts: + tx_str = hexlify(track_pkt) + #print("CMP: TRACK_TX: {}".format(tx_str)) + if len(tx_str) <= len(rx_str): + if tx_str == rx_str[:len(tx_str)]: + return True + return False + #if pkt.src != stream.kws.get("mac_src"): return False + #if pkt.dst != stream.kws.get("mac_dst"): return False + + #if IPv6 in pkt and stream.kws.get("l3_protocol") != "ipv6": return False + #if IP in pkt and stream.kws.get("l3_protocol") != "ipv4": return False + + #if IP in pkt: + #if pkt[IP].src != stream.kws.get("ip_src_addr"): return False + #if pkt[IP].dst != stream.kws.get("ip_dst_addr"): return False + #if TCP in pkt: + #if pkt[TCP].sport != self.get_int(stream.kws, "tcp_src_port", 0): return False + #if pkt[TCP].dport != self.get_int(stream.kws, "tcp_dst_port", 0): return False + #if UDP in pkt: + #if pkt[UDP].sport != self.get_int(stream.kws, "udp_src_port", 0): return False + #if pkt[UDP].dport != self.get_int(stream.kws, "udp_dst_port", 0): return False + #return True + + def if_create_one(self, index, intf, ip4addr, ip4gw, ip6addr, ip6gw, smac, **kws): + ns = "{}_{}".format(intf.name, index) + + vlan_enable = self.utils.intval(kws, "vlan", 0) + vlan_id = self.utils.intval(kws, "vlan_id", 1) + veth_name = "veth0" if vlan_enable else "veth1" + + #self.logger.debug("vlan_enable={} vlan_id={}".format(vlan_enable, vlan_id)) + + # remove existing + self.if_delete_one(index, intf) + + # create the linux interface + cmds = textwrap.dedent(""" + ip netns add ns_{0} + ip link add veth_{0} type veth peer name {2} + ip link set {2} netns ns_{0} + ip netns exec ns_{0} ethtool --offload {2} rx off tx off > /dev/null 2>&1 + ip netns exec ns_{0} ip link set dev {2} up + ip netns exec ns_{0} ethtool --offload {2} rx off tx off + ip link set dev veth_{0} up + #brctl addif {1}-br veth_{0} + ip link set dev veth_{0} master {1}-br + """.format(ns, intf.iface, veth_name)) + + if vlan_enable: + cmds += textwrap.dedent(""" + ip netns exec ns_{0} ip link add link veth0 name veth1 type vlan id {1} + ip netns exec ns_{0} ip link set veth1 up + """.format(ns, vlan_id)) + + # set interface mac address + if smac != "00:00:00:00:00:00": + cmds += "\nip netns exec ns_{0} ip link set veth1 address {1}".format(ns, smac) + + # assign IPv4 to linux interface + if ip4addr: + cmds += "\nip netns exec ns_{0} ip addr add {1}/{2} dev veth1".format(ns, ip4addr, 24) + if ip4gw: + cmds += "\nip netns exec ns_{0} ip route add default via {1}".format(ns, ip4gw) + + # assign IPv6 to linux interface + if ip6addr: + ip6prefix = self.utils.intval(intf.kws, "ipv6_prefix_length", 64) + cmds += "\nip netns exec ns_{0} ip -6 addr add {1}/{2} dev veth1".format(ns, ip6addr, ip6prefix) + if ip6gw: + cmds += "\nip netns exec ns_{0} ip -6 route add default via {1}".format(ns, ip6gw) + + # send Arp request + arp_send_req = self.utils.intval(kws, "arp_send_req", 1) + if arp_send_req: + if ip4gw: + cmds += textwrap.dedent(""" + ip netns exec ns_{0} arping -c 1 -I veth1 {1} + ip netns exec ns_{0} arp -n + """.format(ns, ip4gw)) + + if self.dbg > 1: + cmds += textwrap.dedent(""" + ip netns exec ns_{0} ifconfig veth1 + ip netns exec ns_{0} ip addr ls veth1 + """.format(ns)) + + # execute the commands + self.utils.shexec(cmds) + + def if_create(self, intf): + smac = intf.kws.get("src_mac_addr", "00:00:00:00:00:00").replace(".", ":") + count = self.utils.intval(intf.kws, "count", 1) + + # set IPv4 Address + ip4addr = intf.kws.get("intf_ip_addr", "") + ip4addr_step = intf.kws.get("intf_ip_addr_step", "0.0.0.1") + ip4gw = intf.kws.get("gateway", "") + ip4gw_step = intf.kws.get("gateway_step", "0.0.0.0") + if ip4addr: + for index in range(count): + self.if_create_one(index, intf, ip4addr, ip4gw, None, None, smac, **intf.kws) + ip4addr = self.utils.incrementIPv4(ip4addr, ip4addr_step) + ip4gw = self.utils.incrementIPv4(ip4gw, ip4gw_step) + if smac != "00:00:00:00:00:00": + smac = self.utils.incrementMac(smac, "00:00:00:00:00:01") + # set IPv6 Address + ip6addr = intf.kws.get("ipv6_intf_addr", "") + ip6gw = intf.kws.get("ipv6_gateway", "") + if ip6addr: + self.if_create_one(0, intf, None, None, ip6addr, ip6gw, smac, **intf.kws) + + def if_delete_one(self, index, intf): + ns = "{}_{}".format(intf.name, index) + + # remove the linux interface + cmds = textwrap.dedent(""" + ip netns del ns_{0} + ip link del veth_{0} + """.format(ns)) + self.utils.shexec(cmds) + + def if_delete(self, intf): + count = self.utils.intval(intf.kws, "count", 1) + for index in range(count): + self.if_delete_one(index, intf) + + def get_my_mac(self, intf, default="00:00:00:00:00:00"): + ns = "{}_{}".format(intf.name, 0) + cmd = "ip netns exec ns_{0} cat /sys/class/net/veth1/address".format(ns) + output = self.utils.cmdexec(cmd).lower() + self.logger.debug("{} = {}".format(cmd, output)) + return output + + def get_arp_mac(self, intf, default="00:00:00:00:00:00"): + ipv6gw = intf.kws.get("ipv6_gateway", "") + ipv4gw = intf.kws.get("gateway", "0.0.0.0") + ns = "{}_{}".format(intf.name, 0) + self.logger.debug("get_arp_mac {} {}".format(ipv4gw, ipv6gw)) + + # try getting from ARP cache + try: + cmd = "ip netns exec ns_{0} cat /proc/net/arp".format(ns) + output = self.utils.cmdexec(cmd).lower() + self.logger.debug("{} = \n {}".format(cmd, output)) + + cmd = "ip netns exec ns_{0} arp -n {1}".format(ns, ipv4gw) + output = self.utils.cmdexec(cmd).lower() + self.logger.debug("{} = \n {}".format(cmd, output)) + + return re.search(r"(([a-f\d]{1,2}\:){5}[a-f\d]{1,2})", output).groups()[0] + except Exception as exp: + self.logger.debug("Fail-1: get_arp_mac {} {} {}".format(ipv4gw, ipv6gw, exp)) + + # try getting from arping output + try: + cmd = "ip netns exec ns_{0} arping -c 1 -I veth1 {1}".format(ns, ipv4gw) + output = self.utils.cmdexec(cmd).lower() + self.logger.debug("{} = \n {}".format(cmd, output)) + return re.search(r"(([a-f\d]{1,2}\:){5}[a-f\d]{1,2})", output).groups()[0] + except Exception as exp: + self.logger.debug("Fail-2: get_arp_mac {} {} {}".format(ipv4gw, ipv6gw, exp)) + + return default + + def ping(self, intf, ping_dst, index=0): + ns = "{}_{}".format(intf.name, index) + + cmd = "ip netns exec ns_{0} ping -c 1 {1}".format(ns, ping_dst) + try: + ip = ipaddress.ip_address(unicode(ping_dst)) + if ip._version == 6: + cmd = "ip netns exec ns_{0} ping6 -c 1 {1}".format(ns, ping_dst) + except Exception as exp: + self.logger.error(exp) + + # execute the command + return self.utils.cmdexec(cmd) + + def send_arp(self, intf, index=0): + ns = "{}_{}".format(intf.name, index) + ip4gw = intf.kws.get("gateway", "0.0.0.0") + cmd = "ip netns exec ns_{0} arping -c 1 -I veth1 {1}".format(ns, ip4gw) + return self.utils.cmdexec(cmd) + + def config_yabgp_one(self, enable, intf, index): + ns = "{}_{}".format(intf.name, index) + intf_ip_addr = intf.kws.get("intf_ip_addr", "") + #ipv6_intf_addr = intf.kws.get("ipv6_intf_addr", "") + remote_ip_addr = intf.bgp_kws.get("remote_ip_addr", "0.0.0.0") + remote_as = self.utils.intval(intf.bgp_kws, "remote_as", 65001) + local_as = self.utils.intval(intf.bgp_kws, "local_as", 65007) + cwd = os.getcwd() + logfile = "{}/logs/current/bgpd_{}.log".format(cwd, ns) + pidfile = "{}/logs/current/bgpd_{}.pid".format(cwd, ns) + if not enable: + cmd = "pkill -F {}".format(pidfile) + return self.utils.cmdexec(cmd) + cmds = textwrap.dedent(""" + ip netns exec ns_{0} \ + yabgpd --bgp-afi_safi=ipv4 \ + --bgp-local_addr={1} --bgp-local_as={2} \ + --bgp-remote_addr={3} --bgp-remote_as={4} \ + --rest-bind_host 127.0.0.1 --rest-bind_port 5555 \ + --nouse-stderr --log-file={5} \ + & echo $! > {6} + """.format(ns, intf_ip_addr, local_as, remote_ip_addr, remote_as, logfile, pidfile)) + self.utils.shexec(cmds) + + def config_yabgp(self, enable, intf): + count = self.utils.intval(intf.kws, "count", 1) + for index in range(count): + self.config_yabgp_one(enable, intf, index) + + def config_exabgp_one(self, enable, intf, index=0): + ns = "{}_{}".format(intf.name, index) + + cwd = os.getcwd() + logfile = "{}/logs/current/exabgpd_{}.log".format(cwd, ns) + pidfile = "{}/logs/current/exabgpd_{}.pid".format(cwd, ns) + envfile = "{}/logs/current/exabgpd_{}.env".format(cwd, ns) + cfgfile = "{}/logs/current/exabgpd_{}.cfg".format(cwd, ns) + if not enable: + cmd = "pkill -F {}".format(pidfile) + self.utils.cmdexec(cmd) + return True + + intf_ip_addr = intf.kws.get("intf_ip_addr", "") + ipv6_intf_addr = intf.kws.get("ipv6_intf_addr", "") + if ipv6_intf_addr: intf_ip_addr = ipv6_intf_addr + + remote_ip_addr = intf.bgp_kws.get("remote_ip_addr", "0.0.0.0") + remote_ipv6_addr = intf.bgp_kws.get("remote_ipv6_addr", "") + if remote_ipv6_addr: remote_ip_addr = remote_ipv6_addr + + remote_as = self.utils.intval(intf.bgp_kws, "remote_as", 65001) + local_as = self.utils.intval(intf.bgp_kws, "local_as", 65007) + ip_version = self.utils.intval(intf.bgp_kws, "ip_version", 4) + + cmds = textwrap.dedent(""" + neighbor {3} {{ + router-id {1}; + local-address {1}; + local-as {2}; + peer-as {4}; + }} + """.format(ns, intf_ip_addr, local_as, remote_ip_addr, remote_as)) + self.utils.fwrite(cmds, cfgfile) + + cmds = textwrap.dedent(""" + [exabgp.api] + pipename = '{0}' + + [exabgp.daemon] + pid = '{1}' + daemonize = true + drop = false + + [exabgp.log] + all = true + destination = '{2}' + """.format(ns, pidfile, logfile)) + self.utils.fwrite(cmds, envfile) + + cmds = textwrap.dedent(""" + cat {1} {2} + mkfifo //run/{0}.{{in,out}} + chmod 600 //run/{0}.{{in,out}} + exabgp --env {1} {2} + """.format(ns, envfile, cfgfile)) + sh_file = self.utils.fwrite(cmds) + + cmds = textwrap.dedent(""" + ip netns exec ns_{0} bash {1} + """.format(ns, sh_file)) + self.utils.shexec(cmds) + + return True + + def control_exabgp(self, intf): + num_routes = self.utils.intval(intf.bgp_kws, "num_routes", 0) + ns = "{}_{}".format(intf.name, 0) + prefix = intf.bgp_kws.get("prefix", "") + if not prefix: + msg = "Prefix not specified num_routes={}".format(num_routes) + self.logger.error(msg) + #return False + for index in range(num_routes): + cwd = os.getcwd() + envfile = "{}/logs/current/exabgpd_{}.env".format(cwd, ns) + remote_ipv6_addr = intf.bgp_kws.get("remote_ipv6_addr", "") + if remote_ipv6_addr: + cmd = textwrap.dedent(""" + ip netns exec ns_{0} exabgpcli --env {1} \ + announce route {2}/128 next-hop self + """.format(ns, envfile, prefix)) + prefix = self.utils.incrementIPv6(prefix, "0:0:0:1::") + else: + cmd = textwrap.dedent(""" + ip netns exec ns_{0} exabgpcli --env {1} \ + announce route {2}/24 next-hop self + """.format(ns, envfile, prefix)) + prefix = self.utils.incrementIPv4(prefix, "0.0.1.0") + output = self.utils.cmdexec(cmd) + if "could not send command to ExaBGP" in output: + self.logger.error(output) + return False + return True + + def config_exabgp(self, enable, intf): + retval = self.config_exabgp_one(enable, intf) + if retval and enable: + retval = self.control_exabgp(intf) + return retval + + def config_bgp(self, enable, intf): + return self.config_exabgp(enable, intf) + #self.config_yabgp(enable, intf) + + def config_igmp(self, mode, intf, host): + ns = "{}_{}".format(intf.name, 0) + igmp_version = host.get("igmp_version", "v3") + num_groups = self.utils.intval(host, "grp_num_groups", 1) + ip4_addr = host.get("grp_ip_addr_start", "224.0.0.1") + ip4_step = "0.0.1.0" + + self.logger.error("TODO: config_igmp {} = {} {}".format(mode, intf.iface, host)) + + # force IGMP version + if igmp_version == "v2": + cmd = "ip netns exec ns_{0} echo 2 > /proc/sys/net/ipv4/conf/veth1/force_igmp_version".format(ns) + else: + cmd = "ip netns exec ns_{0} echo 3 > /proc/sys/net/ipv4/conf/veth1/force_igmp_version".format(ns) + self.utils.cmdexec(cmd) + + # add ip addresses + for i in range(num_groups): + if mode in ["start", "join"]: + cmd = "ip netns exec ns_{0} ip addr add {1}/32 dev veth1 autojoin".format(ns, ip4_addr) + else: + cmd = "ip netns exec ns_{0} ip addr del {1}/32 dev veth1".format(ns, ip4_addr) + self.utils.cmdexec(cmd) + ip4_addr = self.utils.incrementIPv4(ip4_addr, ip4_step) + + +if __name__ == '__main__': + from port import ScapyStream + from ut_streams import ut_stream_get + + Logger.setup() + iface = sys.argv[1] if len(sys.argv) > 1 else None + packet = ScapyPacket(iface, 3, bool(not iface), False) + + kwargs = ut_stream_get(0) + kwargs = ut_stream_get(0, mac_dst_mode='list', mac_dst=["00.00.00.00.00.02", "00.00.00.00.00.04", "00.00.00.00.00.06"]) + kwargs = ut_stream_get(0, mac_src_mode='list', mac_src="00.00.00.00.00.02 00.00.00.00.00.04 00.00.00.00.00.06") + s = ScapyStream(0, None, None, **kwargs) + pwa = packet.build_first(s) + while pwa: + packet.logger.info("=======================================================") + packet.logger.info(kwargs) + packet.logger.info("=======================================================") + # wait to proceed + #if iface: raw_input("press any key to send packet") + packet.send_packet(pwa, iface) + pwa = packet.build_next(pwa) + if pwa: time.sleep(1) + diff --git a/spytest/spytest/tgen/scapy/port.py b/spytest/spytest/tgen/scapy/port.py new file mode 100644 index 00000000000..e48ed53e860 --- /dev/null +++ b/spytest/spytest/tgen/scapy/port.py @@ -0,0 +1,377 @@ +import copy + +from dicts import SpyTestDict +from driver import ScapyDriver +from logger import Logger +from utils import Utils + +def initStatistics(stats): + stats.clear() + stats["framesSent"] = 0 + stats["bytesSent"] = 0 + stats["framesReceived"] = 0 + stats["bytesReceived"] = 0 + stats["oversizeFramesReceived"] = 0 + stats["userDefinedStat1"] = 0 + stats["userDefinedStat2"] = 0 + stats["captureFilter"] = 0 + +def incrStat(stats, name, val = 1): + if name in stats: + val = val + stats[name] + stats[name] = val + return val + +class ScapyStream(object): + def __init__(self, port, stream_id, track_port, *args, **kws): + self.port = port + self.track_port = track_port + self.stream_id = stream_id + self.args = args + self.kws = copy.copy(kws) + self.enable = True + self.enable2 = False + self.stats = SpyTestDict() + initStatistics(self.stats) + #print("ScapyStream: {} {} {}".format(self.port, self.stream_id, kws)) + if track_port: + track_port.track_streams.append(self) + self.track_pkts = [] + + def __del__(self): + print("ScapyStream {} exiting...".format(self.stream_id)) + if self.track_port: + self.track_port.track_streams.remove(self) + + def incrStat(self, name, val = 1): + val = incrStat(self.stats, name, val) + #print("incrStat: {} {} {} = {}".format(self.port, self.stream_id, name, val)) + return val + + def __str__(self): + return ''.join([('%s=%s' % x) for x in self.kws.items()]) + +class ScapyInterface(object): + def __init__(self, port, index, *args, **kws): + self.port = port.name + self.iface = port.iface + self.index = index + self.args = args + self.kws = copy.copy(kws) + self.enable = True + self.name = "{}_{}".format(self.port, self.index) + self.name = self.name.replace("/", "_") + self.igmp_hosts = SpyTestDict() + + def add_igmp_host(self, *args, **kws): + index = len(self.igmp_hosts) + host_handle = "igmp-{}-{}-{}".format(self.port, self.index, index) + host = SpyTestDict() + host.host_handle = host_handle + host.update(kws) + self.igmp_hosts[host_handle] = host + return host_handle + +class ScapyPort(object): + def __init__(self, name, iface, dry=False, dbg=0, logger=None): + self.name = name + self.port_handle = "port-{}".format(name) + self.iface = iface + self.dry = dry + self.dbg = dbg + self.logger = logger or Logger() + self.utils = Utils(self.dry, logger=self.logger) + self.streams = SpyTestDict() + self.track_streams = [] + self.interfaces = SpyTestDict() + self.stats = SpyTestDict() + initStatistics(self.stats) + self.driver = ScapyDriver(self, self.dry, self.dbg, self.logger) + self.admin_status = True + + def __del__(self): + self.logger.debug("ScapyPort {} exiting...".format(self.name)) + self.cleanup() + del self.driver + + def clean_interfaces(self): + for handle in self.interfaces.keys(): + self.driver.deleteInterface(self.interfaces[handle]) + del self.interfaces[handle] + + def clean_streams(self): + self.driver.stopTransmit() + self.streams.clear() + for stream in self.track_streams: + stream.track_port = None + stream.track_pkts = [] + self.track_streams = [] + + def cleanup(self): + self.logger.debug("ScapyPort {} cleanup...".format(self.name)) + self.clean_interfaces() + self.driver.cleanup() + self.clean_streams() + + def set_admin_status(self, val): + self.admin_status = val + + def get_admin_status(self): + return self.admin_status + + def incrStat(self, name, val = 1): + return incrStat(self.stats, name, val) + + def getStats(self): + return self.stats + + def getStreamStats(self): + res = [] + for stream_id, stream in self.streams.items(): + res.append([stream, stream.stats]) + return res + + def traffic_control(self, *args, **kws): + action = kws.get('action', None) + if action == "run": + for intf in self.interfaces.values(): + arp_send_req = kws.get('arp_send_req', "0") + if arp_send_req == "1": + self.driver.send_arp(intf, intf.index) + self.driver.startTransmit(**kws) + elif action == "stop": + self.driver.stopTransmit(**kws) + elif action == "reset": + self.clean_streams() + elif action == "clear_stats": + initStatistics(self.stats) + for stream in self.streams.values(): + initStatistics(stream.stats) + self.driver.clear_stats() + else: + self.error("unsupported", "traffic_control: action", action) + return True + + def packet_control(self, *args, **kws): + action = kws.get('action', None) + if action == "start": + self.driver.startCapture() + elif action == "stop": + return self.driver.stopCapture() + elif action == "reset": + return self.driver.clearCapture() + else: + self.error("unsupported", "packet_control: action", action) + return True + + def packet_stats(self, *args, **kws): + return self.driver.getCapture() + + def stream_validate(self, handle): + return bool(handle in self.streams) + + def stream_encode(self, index): + return "stream-{}-{}".format(self.name, index) + + def traffic_config(self, track_port, *args, **kws): + res = SpyTestDict() + mode = kws.get('mode', None) + if mode == "create": + index = len(self.streams) + res.stream_id = self.stream_encode(index) + stream = ScapyStream(self.name, res.stream_id, track_port, *args, **kws) + self.streams[res.stream_id] = stream + elif mode == "remove": + stream_id = kws.get('stream_id', None) + if stream_id not in self.streams: + self.error("invalid", "stream_id", stream_id) + self.driver.stopTransmit(handle = stream_id) + elif mode == "enable": + stream_id = kws.get('stream_id', None) + if stream_id not in self.streams: + self.error("invalid", "stream_id", stream_id) + self.streams[stream_id].enable = True + elif mode == "disable": + stream_id = kws.get('stream_id', None) + if stream_id not in self.streams: + self.error("invalid", "stream_id", stream_id) + self.streams[stream_id].enable = False + elif mode == "modify": + stream_id = kws.get('stream_id', None) + if stream_id not in self.streams: + self.error("invalid", "stream_id", stream_id) + self.streams[stream_id].kws.update(kws) + else: + self.error("unsupported", "traffic_config: mode", mode) + return res + + def find_interface(self, handle): + if handle in self.interfaces: + return self.interfaces[handle] + return None + + def igmp_host_validate(self, handle): + for intf in self.interfaces.values(): + if handle in intf.igmp_hosts: + return True + return False + + def interface_validate(self, handle): + return bool(handle in self.interfaces) + + def interface_encode(self, index): + return "iface-{}-{}".format(self.name, index) + + def interface_config(self, *args, **kws): + res = SpyTestDict() + res.status = "1" + mode = kws.get('mode', None) + send_ping = kws.get('send_ping', None) + ping_dst = kws.get('ping_dst', None) + arp_send_req = kws.get('arp_send_req', None) + count = self.utils.intval(kws, "count", 1) + if mode == "config": + index = len(self.interfaces) + handle = self.interface_encode(index) + interface = ScapyInterface(self, index, *args, **kws) + self.interfaces[handle] = interface + self.driver.createInterface(interface) + if count > 1: + res.handle = [handle for i in range(count)] + else: + res.handle = handle + elif mode == "destroy": + handle = kws.get('handle', None) + if isinstance(handle, list): + handle = handle[0] + if not self.interface_validate(handle): + self.error("invalid", "handle", handle) + self.driver.deleteInterface(self.interfaces[handle]) + del self.interfaces[handle] + elif mode == None and send_ping and ping_dst: + handle = kws.get('protocol_handle', None) + if isinstance(handle, list): + handle = handle[0] + if not self.interface_validate(handle): + self.error("invalid", "protocol_handle", handle) + index = self.interfaces[handle].index + rv = self.driver.ping(self.interfaces[handle], ping_dst, index) + res[self.port_handle] = SpyTestDict() + res[self.port_handle].ping_details = rv + elif mode == None and arp_send_req: + handle = kws.get('protocol_handle', None) + if isinstance(handle, list): + handle = handle[0] + if not self.interface_validate(handle): + self.error("invalid", "protocol_handle", handle) + index = self.interfaces[handle].index + rv = self.driver.send_arp(self.interfaces[handle], index) + res[self.port_handle] = SpyTestDict() + else: + self.error("unsupported", "interface_config: mode", mode) + return res + + def emulation_bgp_config(self, *args, **kws): + res = SpyTestDict() + res.status = "1" + mode = kws.get('mode', None) + if mode in ["enable", "disable"]: + handle = kws.get('handle', None) + res.handle = handle + if isinstance(handle, list): + handle = handle[0] + if not self.interface_validate(handle): + self.error("invalid", "handle", handle) + intf = self.interfaces[handle] + intf.bgp_kws = copy.copy(kws) + else: + self.error("unsupported", "emulation_bgp_config: mode", mode) + return res + + def emulation_bgp_route_config(self, *args, **kws): + res = SpyTestDict() + res.status = "1" + mode = kws.get('mode', None) + if mode in ["add", "remove"]: + handle = kws.get('handle', None) + res.handle = handle + if isinstance(handle, list): + handle = handle[0] + if not self.interface_validate(handle): + self.error("invalid", "handle", handle) + intf = self.interfaces[handle] + intf.bgp_kws.update(kws) + else: + self.error("unsupported", "emulation_bgp_route_config: mode", mode) + return res + + def emulation_bgp_control(self, *args, **kws): + res = SpyTestDict() + res.status = "1" + mode = kws.get('mode', None) + if mode in ["start", "stop"]: + handle = kws.get('handle', None) + if isinstance(handle, list): + handle = handle[0] + if not self.interface_validate(handle): + self.error("invalid", "handle", handle) + intf = self.interfaces[handle] + if mode == "start": + retval = self.driver.config_bgp(True, intf) + else: + retval = self.driver.config_bgp(False, intf) + if not retval: + self.error("Failed", "emulation_bgp_control: mode", mode) + else: + self.error("unsupported", "emulation_bgp_control: mode", mode) + return res + + def emulation_igmp_config(self, *args, **kws): + res = SpyTestDict() + res.status = "1" + mode = kws.get('mode', None) + if mode in ["create"]: + handle = kws.get('handle', None) + res.handle = handle + if isinstance(handle, list): + handle = handle[0] + if not self.interface_validate(handle): + self.error("invalid", "handle", handle) + intf = self.interfaces[handle] + res.host_handle = intf.add_igmp_host(*args, **kws) + else: + self.error("unsupported", "emulation_igmp_config: mode", mode) + return res + + def emulation_igmp_group_config(self, *args, **kws): + res = SpyTestDict() + res.status = "1" + mode = kws.get('mode', "create") + handle = kws.get('handle', None) + host_handle = kws.get('session_handle', handle) + for intf in self.interfaces.values(): + if host_handle in intf.igmp_hosts: + if mode == "clear_all": + intf.igmp_hosts[host_handle].update(kws) + else: + intf.igmp_hosts[host_handle].update(kws) + res.group_handle = host_handle + return res + self.error("Invalid", "emulation_igmp_group_config: session_handle", host_handle) + + def emulation_igmp_control(self, *args, **kws): + res = SpyTestDict() + res.status = "1" + host_handle = kws.get('handle', None) + mode = kws.get('mode', "start") + for intf in self.interfaces.values(): + if host_handle in intf.igmp_hosts: + self.driver.config_igmp(mode, intf, intf.igmp_hosts[host_handle]) + return res + self.error("Invalid", "emulation_igmp_control: handle", host_handle) + + def error(self, etype, name, value): + msg = "{}: {} = {}".format(etype, name, value) + self.logger.error("=================== {} ==================".format(msg)) + raise ValueError(msg) + diff --git a/spytest/spytest/tgen/scapy/pyro-service.py b/spytest/spytest/tgen/scapy/pyro-service.py new file mode 100644 index 00000000000..666d379d9e9 --- /dev/null +++ b/spytest/spytest/tgen/scapy/pyro-service.py @@ -0,0 +1,104 @@ + +import os +import sys +import time +import logging +import warnings + +root = os.path.abspath(os.path.join(os.path.dirname(__file__))) +sys.path.append(root) +sys.path.insert(0, os.path.join(root, "scapy-2.4.3")) +sys.path.insert(0, os.path.join(root, "Pyro4-4.77", "src")) +sys.path.insert(0, os.path.join(root, "Serpent-serpent-1.29")) + +import Pyro4 +from server import ScapyServer + +from scapy.config import Conf +try: print("SCAPY VERSION = {}".format(Conf().version)) +except: print("SCAPY VERSION = UNKNOWN") + +warnings.filterwarnings("ignore", "BaseException.message") + +logging.basicConfig() +logger = logging.getLogger() +logger.setLevel(logging.DEBUG) + +#os.environ["PYRO_LOGFILE"] = "pyro.log" +#os.environ["PYRO_LOGLEVEL"] = "DEBUG" +logging.getLogger("Pyro4").setLevel(logging.DEBUG) +logging.getLogger("Pyro4.core").setLevel(logging.DEBUG) + +@Pyro4.expose +@Pyro4.behavior(instance_mode="single") +class ScapyService(object): + def __init__(self): + self.server = ScapyServer(dry=False) + + def server_control(self, *args, **kws): + return self.server.exposed_server_control(*args, **kws) + def tg_connect(self, *args, **kws): + return self.server.exposed_tg_connect(*args, **kws) + def tg_disconnect(self, *args, **kws): + return self.server.exposed_tg_disconnect(*args, **kws) + def tg_traffic_control(self, *args, **kws): + return self.server.exposed_tg_traffic_control(*args, **kws) + def tg_interface_control(self, *args, **kws): + return self.server.exposed_tg_interface_control(*args, **kws) + def tg_packet_control(self, *args, **kws): + return self.server.exposed_tg_packet_control(*args, **kws) + def tg_packet_stats(self, *args, **kws): + return self.server.exposed_tg_packet_stats(*args, **kws) + def tg_traffic_config(self, *args, **kws): + return self.server.exposed_tg_traffic_config(*args, **kws) + def tg_interface_config(self, *args, **kws): + return self.server.exposed_tg_interface_config(*args, **kws) + def tg_traffic_stats(self, *args, **kws): + return self.server.exposed_tg_traffic_stats(*args, **kws) + def tg_emulation_bgp_config(self, *args, **kws): + return self.server.exposed_tg_emulation_bgp_config(*args, **kws) + def tg_emulation_bgp_route_config(self, *args, **kws): + return self.server.exposed_tg_emulation_bgp_route_config(*args, **kws) + def tg_emulation_bgp_control(self, *args, **kws): + return self.server.exposed_tg_emulation_bgp_control(*args, **kws) + def tg_emulation_igmp_config(self, *args, **kws): + return self.server.exposed_tg_emulation_igmp_config(*args, **kws) + def tg_emulation_multicast_group_config(self, *args, **kws): + return self.server.exposed_tg_emulation_multicast_group_config(*args, **kws) + def tg_emulation_multicast_source_config(self, *args, **kws): + return self.server.exposed_tg_emulation_multicast_source_config(*args, **kws) + def tg_emulation_igmp_group_config(self, *args, **kws): + return self.server.exposed_tg_emulation_igmp_group_config(*args, **kws) + def tg_emulation_igmp_control(self, *args, **kws): + return self.server.exposed_tg_emulation_igmp_control(*args, **kws) + +# install packages needed +install_packages = False +if install_packages: + os.system("apt-get install -y iputils-arping") + #os.system("pip install pybrctl") + #os.system("pip install pyroute2") + +def main(): + Pyro4.config.SERIALIZERS_ACCEPTED.add('pickle') + Pyro4.config.SERIALIZERS_ACCEPTED.remove('serpent') + os.environ["PYRO_DETAILED_TRACEBACK"] = "1" + for i in range(10): + try: + custom_daemon = Pyro4.Daemon(port=8009, host="0.0.0.0") + break + except Exception as exp: + print(exp) + custom_daemon = None + time.sleep(2) + + print("PYRO ScapyService started: {}".format(custom_daemon)) + Pyro4.Daemon.serveSimple( + { + ScapyService: "scapy-tgen" + }, + ns=False, daemon = custom_daemon) + +if __name__=="__main__": + main() + diff --git a/spytest/spytest/tgen/scapy/rpyc-service.py b/spytest/spytest/tgen/scapy/rpyc-service.py new file mode 100644 index 00000000000..3d23fa98cc5 --- /dev/null +++ b/spytest/spytest/tgen/scapy/rpyc-service.py @@ -0,0 +1,64 @@ +import os +import sys +import logging +import warnings +from signal import signal, SIGINT +from sys import exit + +root = os.path.join(os.path.dirname(__file__)) +sys.path.append(os.path.abspath(root)) +root = os.path.join(os.path.dirname(__file__), "scapy-2.4.3") +sys.path.append(os.path.abspath(root)) +root = os.path.join(os.path.dirname(__file__), "rpyc-4.1.2") +sys.path.append(os.path.abspath(root)) + +import rpyc +from rpyc.utils.server import ThreadedServer +from server import ScapyServer + +warnings.filterwarnings("ignore", "BaseException.message") + +logging.basicConfig() +logger = logging.getLogger() +logger.setLevel(logging.DEBUG) + +class ScapyService(rpyc.Service, ScapyServer): + def on_connect(self, conn): + print("connected") + conn._config.update(dict( + allow_all_attrs = True, + allow_public_attrs = True, + allow_pickle = True, + allow_getattr = True, + allow_setattr = True, + allow_delattr = True, + import_custom_exceptions = False, + propagate_SystemExit_locally=True, + propagate_KeyboardInterrupt_locally=True, + instantiate_custom_exceptions = True, + instantiate_oldstyle_exceptions = True, + )) + + def on_disconnect(self, conn): + print("disconnected") + +scapyServiceObj = ScapyService() +def handler(signal_received, frame): + # Handle any cleanup here + global scapyServiceObj + del scapyServiceObj + print('SIGINT or CTRL-C detected. Exiting gracefully') + exit(0) + +# install packages needed +install_packages = False +if install_packages: + os.system("apt-get install -y iputils-arping") + #os.system("pip install pybrctl") + #os.system("pip install pyroute2") + +protocol_config={"allow_pickle" : True, "sync_request_timeout": 300, "allow_public_attrs": True, "allow_all_attrs": True, "instantiate_oldstyle_exceptions" : True} +signal(SIGINT, handler) +t = ThreadedServer(scapyServiceObj, port = 8009, logger=logger, protocol_config=protocol_config, backlog=1) +t.start() + diff --git a/spytest/spytest/tgen/scapy/server.py b/spytest/spytest/tgen/scapy/server.py new file mode 100644 index 00000000000..29f5686dada --- /dev/null +++ b/spytest/spytest/tgen/scapy/server.py @@ -0,0 +1,510 @@ +import os +import sys +import json +import time + +from dicts import SpyTestDict +from port import ScapyPort +from logger import Logger + +# create port map +portmap = SpyTestDict() +tgen_portmap_model = os.getenv("SCAPY_TGEN_PORTMAP", "eth1") +for i in range(0,16): + if tgen_portmap_model == "ens6": + portmap["1/{}".format(i+1)] = "ens{}".format(i+6) + portmap["{}".format(i+1)] = "ens{}".format(i+6) + elif tgen_portmap_model == "eth1": + portmap["1/{}".format(i+1)] = "eth{}".format(i) + portmap["{}".format(i+1)] = "eth{}".format(i) + else: + portmap["1/{}".format(i)] = "eth{}".format(i) + portmap["{}".format(i)] = "eth{}".format(i) + +class ScapyServer(object): + def __init__(self, dry=False, dbg=0): + self.dry = dry + self.dbg = dbg + self.ports = SpyTestDict() + self.mgrps = SpyTestDict() + self.msrcs = SpyTestDict() + self.logger = Logger() + os.system("ip -all netns del") + os.system("sysctl -w net.bridge.bridge-nf-call-arptables=0") + os.system("sysctl -w net.bridge.bridge-nf-call-ip6tables=0") + os.system("sysctl -w net.bridge.bridge-nf-call-iptables=0") + + def __del__(self): + self.logger.debug("ScapyServer exiting...") + self.cleanup_ports() + self.ports = SpyTestDict() + self.mgrps = SpyTestDict() + self.msrcs = SpyTestDict() + + def cleanup_ports(self): + for key in self.ports.keys(): + self.ports[key].cleanup() + for key in self.mgrps.keys(): + self.mgrps[key].cleanup() + for key in self.msrcs.keys(): + self.msrcs[key].cleanup() + + def trace_args(self, *args, **kws): + func = sys._getframe(1).f_code.co_name + self.logger.debug(func, args, kws) + + def trace_result(self, res, min_dbg=2): + if self.dbg >= min_dbg: + self.logger.debug("RESULT:", json.dumps(res)) + return res + + def error(self, etype, name, value): + msg = "{}: {} = {}".format(etype, name, value) + self.logger.error("=================== {} ==================".format(msg)) + raise ValueError(msg) + + def make_list(self, arg): + if isinstance(arg, list): + return arg + return [arg] + + def exposed_server_control(self, req, data, *args, **kws): + self.trace_args(req, data, *args, **kws) + retval = "" + if req == "set-dbg-lvl": + self.dbg = int(data) + elif req == "set-dry-run": + self.dry = data + elif req == "set-max-pps": + os.environ["SPYTEST_SCAPY_MAX_RATE_PPS"] = str(data) + elif req == "init-log": + self.logger.set_log_file(data) + elif req == "read-log": + retval = self.logger.get_log(data) + elif req == "add-log": + self.logger.info("#"*80) + self.logger.info(data) + self.logger.info("#"*80) + elif req == "clean-all": + for port in self.ports.values(): + port.clean_streams() + port.clean_interfaces() + else: + self.error("Invalid", "server request", req) + return retval + + def fix_port_name(self, pname): + return pname.split("/")[-1].strip() + + def exposed_tg_connect(self, *args, **kws): + self.trace_args(*args, **kws) + port_list = kws.get('port_list', []) + res = SpyTestDict() + res.port_handle = SpyTestDict() + delete_ports = [] + for pname0 in port_list: + pname = self.fix_port_name(pname0) + for pobj in self.ports.values(): + if pobj.name == pname: + delete_ports.append(pobj) + pobj = ScapyPort(pname, portmap.get(pname), dry=self.dry, + dbg=self.dbg, logger=self.logger) + self.ports[pobj.port_handle] = pobj + res.port_handle[pname0] = pobj.port_handle + for pobj in delete_ports: + del pobj + res.status = 1 + return self.trace_result(res) + + def exposed_tg_disconnect(self, *args, **kws): + self.trace_args(*args, **kws) + res = SpyTestDict() + self.cleanup_ports() + res.status = 1 + return self.trace_result(res) + + def exposed_tg_traffic_control(self, *args, **kws): + self.trace_args(*args, **kws) + handle = kws.get('handle', None) + port_handle = kws.get('port_handle', None) + res = True + if port_handle: + for port_handle in self.make_list(port_handle): + port = self.ports[port_handle] + if not port.traffic_control(*args, **kws): + res = False + elif handle: + for handle in self.make_list(handle): + for port in self.ports.values(): + if port.stream_validate(handle): + if not port.traffic_control(*args, **kws): + res = False + break + else: + for port in self.ports.values(): + if not port.traffic_control(*args, **kws): + res = False + + action = kws.get('action', None) + if action in ["stop", "start"]: + for port in self.ports.values(): + stats = port.getStats() + msg = "{}: TX: {} RX: {}".format(port.name, + stats.framesSent, stats.framesReceived) + self.logger.debug(msg) + return self.trace_result(res) + + def ensure_port_handle(self, port_handle): + if not port_handle: + msg = "port_handle is not specified" + raise ValueError(msg) + if port_handle not in self.ports: + msg = "port_handle {} is not valid".format(port_handle) + raise ValueError(msg) + return self.ports[port_handle] + + def exposed_tg_interface_control(self, *args, **kws): + self.trace_args(*args, **kws) + port_handle = kws.get('port_handle', None) + port = self.ensure_port_handle(port_handle) + mode = kws.get('mode', "aggregate") + if mode == "break_link": + port.set_admin_status(False) + return True + elif mode == "restore_link": + port.set_admin_status(True) + return True + elif mode == "check_link": + return port.get_admin_status() + self.error("Invalid", "mode", mode) + + def exposed_tg_packet_control(self, *args, **kws): + self.trace_args(*args, **kws) + port_handle = kws.get('port_handle', None) + if not port_handle: + msg = "port_handle is not specified" + raise ValueError(msg) + port = self.ports[port_handle] + res = port.packet_control(*args, **kws) + return self.trace_result(res) + + def exposed_tg_packet_stats(self, *args, **kws): + self.trace_args(*args, **kws) + res = SpyTestDict() + port_handle = kws.get('port_handle', None) + mode = kws.get('mode', "aggregate") + res["status"] = "1" + if port_handle and port_handle in self.ports: + port = self.ports[port_handle] + pkts = port.packet_stats(*args, **kws) + res[port_handle] = SpyTestDict() + res[port_handle][mode] = SpyTestDict() + res[port_handle][mode]["num_frames"] = len(pkts) + res[port_handle]["frame"] = SpyTestDict() + for i, pkt in enumerate(pkts): + index = str(i) + res[port_handle]["frame"][index] = SpyTestDict() + res[port_handle]["frame"][index]["length"] = len(pkt) + res[port_handle]["frame"][index]["frame_pylist"] = pkt + res[port_handle]["frame"][index]["frame"] = " ".join(pkt) + return self.trace_result(res, 3) + + def exposed_tg_traffic_config(self, *args, **kws): + self.trace_args(*args, **kws) + port_handle = kws.get('port_handle', None) + stream_id = kws.get('stream_id', None) + port_handle2 = kws.get('port_handle2', None) + + # check if track port is specified + if port_handle2 and port_handle2 in self.ports: + track_port = self.ports[port_handle2] + else: + track_port = None + + # get the port handle from stream if not specified + if not port_handle and stream_id: + for phandle, port in self.ports.items(): + if port.stream_validate(stream_id): + port_handle = phandle + break + + # bailout: we must have port handle + if not port_handle: + msg = "port_handle is not specified" + raise ValueError(msg) + + # we may get multiple port handles also + if isinstance(port_handle, list): + port_handle_list = port_handle + else: + port_handle_list = [port_handle] + + # validate src emulation handle before adding stream + emulation_src_handle = kws.get('emulation_src_handle', None) + if emulation_src_handle: + intf = self.find_intf(emulation_src_handle) + if not intf: + self.error("Invalid", "emulation_src_handle", emulation_src_handle) + else: + kws["emulation_src_handle"] = intf + + # validate dst emulation handle before adding stream + emulation_dst_handle = kws.get('emulation_dst_handle', None) + if emulation_dst_handle: + intf = self.find_intf(emulation_dst_handle) + if not intf: + self.error("Invalid", "emulation_dst_handle", emulation_dst_handle) + else: + kws["emulation_dst_handle"] = intf + + # all well add stream to port + res = SpyTestDict() + for port_handle in port_handle_list: + port = self.ports[port_handle] + rv = port.traffic_config(track_port, *args, **kws) + res.update(rv) + + return self.trace_result(res) + + def find_intf(self, handle): + if isinstance(handle, list): + handle = handle[0] + for port in self.ports.values(): + intf = port.find_interface(handle) + if intf: + return intf + return None + + def verify_handle(self, *args, **kws): + port_handle = kws.get('port_handle', None) + protocol_handle = kws.get('protocol_handle', None) + handle = kws.get('handle', None) + if not protocol_handle and not port_handle and not handle: + msg = "Neither handle nor port_handle specified" + raise ValueError(msg) + if port_handle: + return self.ports[port_handle] + elif protocol_handle: + if isinstance(protocol_handle, list): + protocol_handle = protocol_handle[0] + for port in self.ports.values(): + if port.interface_validate(protocol_handle): + return port + self.error("Invalid", "protocol_handle", protocol_handle) + else: + if isinstance(handle, list): + handle = handle[0] + for port in self.ports.values(): + if port.interface_validate(handle): + return port + self.error("Invalid", "handle", handle) + + def exposed_tg_interface_config(self, *args, **kws): + self.trace_args(*args, **kws) + port = self.verify_handle(*args, **kws) + res = port.interface_config(*args, **kws) + return self.trace_result(res) + + def exposed_tg_emulation_bgp_config(self, *args, **kws): + self.trace_args(*args, **kws) + port = self.verify_handle(*args, **kws) + res = port.emulation_bgp_config(*args, **kws) + return self.trace_result(res) + + def exposed_tg_emulation_bgp_route_config(self, *args, **kws): + self.trace_args(*args, **kws) + port = self.verify_handle(*args, **kws) + res = port.emulation_bgp_route_config(*args, **kws) + return self.trace_result(res) + + def exposed_tg_emulation_bgp_control(self, *args, **kws): + self.trace_args(*args, **kws) + port = self.verify_handle(*args, **kws) + res = port.emulation_bgp_control(*args, **kws) + return self.trace_result(res) + + def exposed_tg_emulation_igmp_config(self, *args, **kws): + self.trace_args(*args, **kws) + port = self.verify_handle(*args, **kws) + res = port.emulation_igmp_config(*args, **kws) + return self.trace_result(res) + + def exposed_tg_emulation_multicast_group_config(self, *args, **kws): + self.trace_args(*args, **kws) + mode = kws.get('mode', "create") + index = len(self.mgrps) + if mode == "create": + group = SpyTestDict() + group.update(kws) + group.index = index + group.name = "mgrp-{}".format(group.index) + self.mgrps[group.name] = group + res = SpyTestDict() + res["status"] = "1" + res["mul_group_handle"] = group.name + return self.trace_result(res) + self.error("Invalid", "emulation_multicast_group_config: mode", mode) + + def exposed_tg_emulation_multicast_source_config(self, *args, **kws): + self.trace_args(*args, **kws) + mode = kws.get('mode', "create") + index = len(self.msrcs) + if mode == "create": + group = SpyTestDict() + group.update(kws) + group.index = index + group.name = "msrc-{}".format(group.index) + self.msrcs[group.name] = group + res = SpyTestDict() + res["status"] = "1" + res["mul_source_handle"] = group.name + return self.trace_result(res) + self.error("Invalid", "emulation_multicast_source_config: mode", mode) + + def exposed_tg_emulation_igmp_group_config(self, *args, **kws): + self.trace_args(*args, **kws) + mode = kws.get('mode', "create") + if mode not in ["create", "clear_all"]: + self.error("Invalid", "emulation_igmp_group_config: mode", mode) + elif mode in ["create"]: + group_pool_handle = kws.get('group_pool_handle', None) + if group_pool_handle not in self.mgrps: + self.error("Invalid", "emulation_igmp_group_config: group_pool_handle", group_pool_handle) + source_pool_handle = kws.get('source_pool_handle', None) + if source_pool_handle not in self.msrcs: + self.error("Invalid", "emulation_igmp_group_config: source_pool_handle", source_pool_handle) + host_handle = kws.get('session_handle', kws.get('handle', None)) + for port in self.ports.values(): + if port.igmp_host_validate(host_handle): + if mode in ["create"]: + grp = self.mgrps[group_pool_handle] + grp_ip_addr_start = grp.get("ip_addr_start") + grp_num_groups = grp.get("num_groups", "1") + src = self.msrcs[source_pool_handle] + src_ip_addr_start = src.get("ip_addr_start") + src_num_groups = src.get("num_groups", "1") + else: + grp_ip_addr_start = None + grp_num_groups = "0" + src_ip_addr_start = None + src_num_groups = "0" + kws["grp_ip_addr_start"] = grp_ip_addr_start + kws["grp_num_groups"] = grp_num_groups + kws["src_ip_addr_start"] = src_ip_addr_start + kws["src_num_groups"] = src_num_groups + res = port.emulation_igmp_group_config(*args, **kws) + return self.trace_result(res) + self.error("Invalid", "emulation_igmp_group_config: session_handle", host_handle) + + def exposed_tg_emulation_igmp_control(self, *args, **kws): + self.trace_args(*args, **kws) + mode = kws.get('mode', "start") + if mode not in ["start", "stop", "join", "leave"]: + self.error("Invalid", "emulation_igmp_control: mode", mode) + host_handle = kws.get('handle', None) + for port in self.ports.values(): + if port.igmp_host_validate(host_handle): + res = port.emulation_igmp_control(*args, **kws) + return self.trace_result(res) + + def exposed_tg_traffic_stats(self, *args, **kws): + self.trace_args(*args, **kws) + res = SpyTestDict() + res["status"] = "1" + res["waiting_for_stats"] = "0" + port_handle = kws.get('port_handle', None) + stream_id = kws.get('stream', None) + mode = kws.get('mode', "aggregate") + time.sleep(5) + if mode == "aggregate" and stream_id: + for port in self.ports.values(): + for stream, stats in port.getStreamStats(): + if stream_id == stream.stream_id: + res[mode] = SpyTestDict() + self.fill_stats(res[mode], stats, stats, True) + elif mode == "aggregate": + if not port_handle or port_handle not in self.ports: + self.error("Invalid", "port_handle", port_handle) + stats = self.ports[port_handle].getStats() + res[port_handle] = SpyTestDict() + res[port_handle][mode] = SpyTestDict() + self.fill_stats(res[port_handle][mode], stats, stats) + elif mode == "traffic_item": + res[mode] = SpyTestDict() + for port in self.ports.values(): + #self.fill_stats(res[mode]["aggregate"], stats, stats) + for stream, stats in port.getStreamStats(): + stream_id = stream.stream_id + res[mode][stream_id] = SpyTestDict() + self.fill_stats(res[mode][stream_id], stats, stats) + elif mode in ["stream", "streams"]: + res[port_handle] = SpyTestDict() + res[port_handle]["stream"] = SpyTestDict() + for port in self.ports.values(): + for stream, stats in port.getStreamStats(): + stream_id = stream.stream_id + res[port_handle]["stream"][stream_id] = SpyTestDict() + self.fill_stats(res[port_handle]["stream"][stream_id], stats, stats) + elif mode == "flow": + if not port_handle or port_handle not in self.ports: + self.error("Invalid", "port_handle", port_handle) + stats = self.ports[port_handle].getStats() + tracking = SpyTestDict() + tracking["count"] = "2" + tracking["1"] = SpyTestDict() + tracking["1"]["tracking_name"] = "Traffic_Item" + tracking["1"]["tracking_value"] = "stream_id" + tracking["2"] = SpyTestDict() + tracking["2"]["tracking_name"] = "vlanId" + tracking["2"]["tracking_value"] = "100" + res[mode] = SpyTestDict() + res[mode]["1"] = SpyTestDict() + res[mode]["1"]["rx"] = SpyTestDict() + res[mode]["1"]["pgid_value"] = 'N/A' + res[mode]["1"]["tracking"] = tracking + res[mode]["1"]["flow_name"] = 'stream id' + res[mode]["1"]["tx"] = SpyTestDict() + res[mode]["2"] = SpyTestDict() + res[mode]["2"]["rx"] = SpyTestDict() + res[mode]["2"]["pgid_value"] = 'N/A' + res[mode]["2"]["tracking"] = tracking + res[mode]["2"]["flow_name"] = 'stream id' + res[mode]["2"]["tx"] = SpyTestDict() + self.fill_stats(res[mode]["1"], stats, stats) + self.fill_stats(res[mode]["2"], stats, stats) + else: + self.logger.todo("unhandled", "mode", mode) + return self.trace_result(res) + + def stat_value(self, val, detailed=False): + if not detailed: + return val + return {"count":val, "max":0, "min":0, "sum":0, "avg":0} + + def fill_stats(self, res, tx_stats, rx_stats, detailed=False): + res["tx"] = SpyTestDict() + res["tx"]["total_pkt_rate"] = self.stat_value(1, detailed) + res["tx"]["raw_pkt_count"] = self.stat_value(tx_stats.framesSent, detailed) + res["tx"]["pkt_byte_count"] = self.stat_value(tx_stats.bytesSent, detailed) + res["tx"]["total_pkts"] = self.stat_value(tx_stats.framesSent, detailed) + res["rx"] = SpyTestDict() + res["rx"]["raw_pkt_rate"] = self.stat_value(1, detailed) + res["rx"]["raw_pkt_count"] = self.stat_value(rx_stats.framesReceived, detailed) + res["rx"]["pkt_byte_count"] = self.stat_value(rx_stats.bytesReceived, detailed) + res["rx"]["total_pkts"] = self.stat_value(rx_stats.framesReceived, detailed) + res["rx"]["oversize_count"] = self.stat_value(rx_stats.oversizeFramesReceived, detailed) + +if __name__ == '__main__': + Logger.setup() + from ut_streams import ut_stream_get + server = ScapyServer(True, dbg=3) + res = server.exposed_tg_connect(port_list=["1/1", "1/2"]) + (tg_ph_1, tg_ph_2) = res.port_handle.values() + kwargs = ut_stream_get(0, port_handle=tg_ph_1, mac_dst_mode='list', mac_dst=["00.00.00.00.00.02", "00.00.00.00.00.04"]) + #res1 = server.exposed_tg_traffic_config(**kwargs) + #server.exposed_tg_traffic_control(action="run", handle=res1["stream_id"]) + server.exposed_tg_interface_config (arp_send_req='1', src_mac_addr='00:00:00:00:00:02', + vlan=1, intf_ip_addr='192.168.12.2', port_handle='port-1/2', mode='config', + gateway='192.168.12.1', vlan_id=64) + + diff --git a/spytest/spytest/tgen/scapy/service.sh b/spytest/spytest/tgen/scapy/service.sh new file mode 100644 index 00000000000..21a49133dad --- /dev/null +++ b/spytest/spytest/tgen/scapy/service.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +set -x + +cd $(dirname $0) + +export HOME=$PWD/logs/current +if [ -f $HOME/service.pid ]; then + pkill -F $HOME/service.pid + rm -f $HOME/service.pid +fi + +mkdir -p logs +VER=$(date +%Y_%d_%m-%H_%M_%S) +mv -f logs/current logs/$VER +mkdir -p logs/current +export HOME=$PWD/logs/current + +export SCAPY_TGEN_LOGS_PATH=$HOME + +ulimit -n 50000 + +# remove problematic files +rm -f cgi.py* + +python pyro-service.py 2>&1 | tee $HOME/service.log + diff --git a/spytest/spytest/tgen/scapy/unit_test.py b/spytest/spytest/tgen/scapy/unit_test.py new file mode 100644 index 00000000000..08a31a13db6 --- /dev/null +++ b/spytest/spytest/tgen/scapy/unit_test.py @@ -0,0 +1,207 @@ +import os +import sys +import json +import time +import logging + +from spytest.dicts import SpyTestDict +from spytest.tgen.tg_scapy import ScapyClient +import utilities.common as utils + +class TGScapyTest(ScapyClient): + def __init__(self, tg_ip=None, tg_port=8009, tg_port_list=None): + self.tg_ip = tg_ip + self.tg_port_list = tg_port_list + self.tg_port_handle = SpyTestDict() + ScapyClient.__init__(self, None, tg_port) + #self.scapy_connect() + self.scapy_connect(True) + + def log_call(self, fname, **kwargs): + pass + + def save_log(self, name, data): + utils.write_file(os.path.join("client", name), data) + +def test_clear(tg): + port_list = tg.tg_port_handle.values() + tg.tg_traffic_control(action="reset",port_handle=port_list) + tg.tg_traffic_control(action="clear_stats",port_handle=port_list) + for tg_ph in port_list: + tx_stats = tg.tg_traffic_stats(port_handle=tg_ph,mode="aggregate") + print ("TX-STATS", json.dumps(tx_stats)) + rx_stats = tg.tg_traffic_stats(port_handle=tg_ph,mode="aggregate") + print ("RX-STATS", json.dumps(rx_stats)) + +def test_single_burst(tg): + print("============= test_single_burst ==============") + (tg_ph_1, tg_ph_2) = tg.tg_port_handle.values() + tg.tg_traffic_config(mac_src = '00.00.00.00.10.01',mac_dst='00.00.00.00.10.02',rate_pps='2',mode='create',\ + port_handle=tg_ph_1,transmit_mode='single_burst', pkts_per_burst=10, frame_size=64) + tg.tg_traffic_control(action='run',port_handle=tg_ph_1) + time.sleep(2) + tx_stats = tg.tg_traffic_stats(port_handle=tg_ph_1,mode="aggregate") + print (tx_stats) + +def test_untagged(tg, count=2): + print("============= test_untagged ==============") + (tg_ph_1, tg_ph_2) = tg.tg_port_handle.values() + res = tg.tg_traffic_config(mac_src = '00.00.10.00.00.02',mac_dst='00.00.10.00.00.01', + rate_pps=count,mode='create', mac_src_mode="increment", \ + mac_src_count=20, mac_src_step="00:00:00:00:00:01", + port_handle=tg_ph_1,transmit_mode='continuous', frame_size='128') + tg.tg_traffic_control(action='run',handle=res.stream_id,duration=20) + time.sleep(2) + tg.tg_traffic_control(action='stop',port_handle=tg_ph_1) + tx_stats = tg.tg_traffic_stats(port_handle=tg_ph_1,mode="aggregate") + print ("TX-STATS", json.dumps(tx_stats)) + rx_stats = tg.tg_traffic_stats(port_handle=tg_ph_2,mode="aggregate") + print ("RX-STATS", json.dumps(rx_stats)) + +def test_tagged(tg, count=2): + print("============= test_tagged ==============") + (tg_ph_1, tg_ph_2) = tg.tg_port_handle.values() + tg.tg_traffic_config(mac_src = '00.00.00.00.00.01',mac_dst='00.00.00.00.00.02',rate_pps=count,mode='create',\ + port_handle=tg_ph_1,transmit_mode='continuous',l2_encap='ethernet_ii_vlan',vlan_id='10', frame_size='256', vlan="enable") + tg.tg_traffic_control(action='run',port_handle=tg_ph_1,duration=2) + time.sleep(5) + tg.tg_traffic_control(action='stop',port_handle=tg_ph_1) + tx_stats = tg.tg_traffic_stats(port_handle=tg_ph_1,mode="aggregate") + print ("TX-STATS", json.dumps(tx_stats)) + rx_stats = tg.tg_traffic_stats(port_handle=tg_ph_2,mode="aggregate") + print ("RX-STATS", json.dumps(rx_stats)) + +def test_capture(tg): + print("============= test_capture ==============") + (tg_ph_1, tg_ph_2) = tg.tg_port_handle.values() + tg.tg_traffic_config(mac_src = '00.00.00.00.10.01',mac_dst='00.00.00.00.10.02',rate_pps='2',mode='create',\ + port_handle=tg_ph_1,transmit_mode='single_burst', pkts_per_burst=10, frame_size=64) + tg.tg_packet_control(port_handle=tg_ph_2,action='start') + tg.tg_traffic_control(action='run',port_handle=tg_ph_1) + time.sleep(5) + tg.tg_traffic_control(action='stop',port_handle=tg_ph_1) + tx_stats = tg.tg_traffic_stats(port_handle=tg_ph_2,mode="aggregate") + print (tx_stats) + totPackets = tg.tg_packet_control(port_handle=tg_ph_2,action='stop') + print (totPackets) + packet_dict = tg.tg_packet_stats(port_handle=tg_ph_2,format='var') + print (packet_dict) + +def test_interface(tg): + print("============= test_interface ==============") + (tg_ph_1, tg_ph_2) = tg.tg_port_handle.values() + res1 = tg.tg_interface_config(port_handle=tg_ph_1, mode='config', arp_send_req='1', \ + intf_ip_addr='21.1.1.100', gateway='21.1.1.1', src_mac_addr='00:0a:01:01:00:01') + tg.tg_interface_config(port_handle=tg_ph_1, handle=res1['handle'], mode='destroy') + res2 = tg.tg_interface_config(port_handle=tg_ph_1, mode='config', intf_ip_addr='12.12.0.2', + gateway='12.12.0.1', netmask='255.255.0.0', vlan='1', + vlan_id=10, vlan_id_step=0, arp_send_req='1', + gateway_step='0.0.0.0', intf_ip_addr_step='0.0.0.1', count='1') + print(res2) + res = tg.tg_interface_config(protocol_handle=res2["handle"], send_ping='1', ping_dst='12.12.0.1') + print("PING_RES: "+str(res)) + tg.tg_interface_config(port_handle=tg_ph_1, handle=res2['handle'], mode='destroy') + +def test_nat(tg): + tg.tg_interface_config(count=1,arp_send_req=1,intf_ip_addr="12.12.0.2",port_handle="port-1/1",netmask="255.255.0.0",mode="config",gateway_step="0.0.0.0",gateway="12.12.0.1") + tg.tg_interface_config(count=1,arp_send_req=1,intf_ip_addr="125.56.90.1",port_handle="port-1/2",netmask="255.255.255.0",mode="config",gateway_step="0.0.0.0",gateway="125.56.90.12") + mac_src = "00:00:23:11:14:08" + mac_src = "e2:8d:ab:ee:fb:6c" + mac_src2 = raw_input() + if mac_src2: mac_src = mac_src2 + res = tg.tg_traffic_config(mac_src=mac_src,mac_dst="80:a2:35:26:0a:5e",ip_dst_addr="129.2.30.12", + pkts_per_burst=1,ip_src_addr="12.12.0.2",port_handle="port-1/1",transmit_mode="single_burst", + rate_pps=10,mode="create",l3_protocol="ipv4") + tg.tg_traffic_control(action="run",handle=res["stream_id"]) + +def test_nat0(tg): + tg.tg_traffic_control(action="reset",port_handle=['port-1/2', 'port-1/1']) + tg.tg_traffic_control(action="clear_stats",port_handle=['port-1/2', 'port-1/1']) + tg.tg_interface_config(count=10,arp_send_req=1,intf_ip_addr="12.12.0.2",port_handle="port-1/1",netmask="255.255.0.0",mode="config",gateway_step="0.0.0.0",gateway="12.12.0.1") + tg.tg_interface_config(count=10,arp_send_req=1,intf_ip_addr="125.56.90.1",port_handle="port-1/2",netmask="255.255.255.0",mode="config",gateway_step="0.0.0.0",gateway="125.56.90.12") + tg.tg_traffic_config(mac_src="00:00:23:11:14:08",mac_dst="80:a2:35:26:0a:5e",ip_dst_addr="129.2.30.12",pkts_per_burst=10,ip_src_addr="12.12.0.2",port_handle="port-1/1",transmit_mode="single_burst",rate_pps=10,mode="create",l3_protocol="ipv4") + tg.tg_traffic_config(mac_src="00:00:23:11:14:08",l4_protocol="tcp",tcp_src_port=1002,mac_dst="80:a2:35:26:0a:5e",ip_dst_addr="129.2.30.12",pkts_per_burst=10,ip_src_addr="12.12.0.3",port_handle="port-1/1",transmit_mode="single_burst",rate_pps=10,mode="create",tcp_dst_port=3345,l3_protocol="ipv4") + tg.tg_traffic_config(mac_src="00:00:23:11:14:08",mac_dst="80:a2:35:26:0a:5e",ip_dst_addr="129.2.30.12",pkts_per_burst=10,ip_src_addr="88.98.128.2",port_handle="port-1/1",transmit_mode="single_burst",rate_pps=10,mode="create",l3_protocol="ipv4") + tg.tg_traffic_config(mac_src="00:00:23:11:14:08",l4_protocol="udp",udp_src_port=7781,mac_dst="80:a2:35:26:0a:5e",ip_dst_addr="129.2.30.12",pkts_per_burst=10,ip_src_addr="12.12.0.4",port_handle="port-1/1",transmit_mode="single_burst",rate_pps=10,mode="create",l3_protocol="ipv4",udp_dst_port=8812) + tg.tg_traffic_config(mac_src="00:00:23:11:14:08",mac_dst="80:a2:35:26:0a:5e",ip_dst_addr="15.15.0.1",pkts_per_burst=10,ip_src_addr="12.12.0.5",port_handle="port-1/1",transmit_mode="single_burst",rate_pps=10,mode="create",l3_protocol="ipv4") + tg.tg_traffic_config(mac_src="00:00:23:11:14:08",l4_protocol="udp",udp_src_port=251,mac_dst="80:a2:35:26:0a:5e",ip_dst_addr="129.2.30.12",pkts_per_burst=10,ip_src_addr="12.12.0.11",port_handle="port-1/1",transmit_mode="single_burst",rate_pps=10,mode="create",l3_protocol="ipv4",udp_dst_port=444) + tg.tg_traffic_config(mac_src="00:00:43:32:1A",mac_dst="80:a2:35:26:0a:5e",ip_dst_addr="125.56.90.12",pkts_per_burst=10,ip_src_addr="129.2.30.12",port_handle="port-1/2",transmit_mode="single_burst",rate_pps=10,mode="create",l3_protocol="ipv4") + tg.tg_traffic_config(mac_src="00:00:43:32:1A",l4_protocol="tcp",tcp_src_port=345,mac_dst="80:a2:35:26:0a:5e",ip_dst_addr="125.56.90.13",pkts_per_burst=10,ip_src_addr="129.2.30.12",port_handle="port-1/2",transmit_mode="single_burst",rate_pps=10,mode="create",tcp_dst_port=100,l3_protocol="ipv4") + tg.tg_traffic_config(mac_src="00:00:43:32:1A",mac_dst="80:a2:35:26:0a:5e",ip_dst_addr="11.11.11.2",pkts_per_burst=10,ip_src_addr="129.2.30.12",port_handle="port-1/2",transmit_mode="single_burst",rate_pps=10,mode="create",l3_protocol="ipv4") + tg.tg_traffic_config(mac_src="00:00:43:32:1A",l4_protocol="udp",udp_src_port=5516,mac_dst="80:a2:35:26:0a:5e",ip_dst_addr="125.56.90.14",pkts_per_burst=10,ip_src_addr="129.2.30.12",port_handle="port-1/2",transmit_mode="single_burst",rate_pps=10,mode="create",l3_protocol="ipv4",udp_dst_port=7811) + tg.tg_traffic_config(mac_src="00:00:43:32:1A",mac_dst="80:a2:35:26:0a:5e",ip_dst_addr="125.56.90.15",pkts_per_burst=10,ip_src_addr="99.99.99.1",port_handle="port-1/2",transmit_mode="single_burst",rate_pps=10,mode="create",l3_protocol="ipv4") + tg.tg_traffic_config(mac_src="00:00:43:32:1A",l4_protocol="udp",udp_src_port=12001,mac_dst="80:a2:35:26:0a:5e",ip_dst_addr="125.56.90.23",pkts_per_burst=10,ip_src_addr="129.2.30.12",port_handle="port-1/2",transmit_mode="single_burst",rate_pps=10,mode="create",l3_protocol="ipv4",udp_dst_port=333) + tg.tg_traffic_config(mac_src="00:00:43:32:1A",l4_protocol="udp",udp_src_port=12001,mac_dst="80:a2:35:26:0a:5e",ip_dst_addr="125.56.90.23",pkts_per_burst=10,ip_src_addr="129.2.30.12",port_handle="port-1/2",transmit_mode="single_burst",rate_pps=10,mode="create",l3_protocol="ipv4",udp_dst_port=334) + tg.tg_traffic_config(mac_src="00:00:43:32:1A",l4_protocol="udp",udp_src_port=12001,mac_dst="80:a2:35:26:0a:5e",ip_dst_addr="125.56.90.24",pkts_per_burst=10,ip_src_addr="129.2.30.12",port_handle="port-1/2",transmit_mode="single_burst",rate_pps=10,mode="create",l3_protocol="ipv4",udp_dst_port=333) + tg.tg_traffic_config(mac_src="00:00:43:32:1A",l4_protocol="udp",udp_src_port=12001,mac_dst="80:a2:35:26:0a:5e",ip_dst_addr="125.56.90.24",pkts_per_burst=10,ip_src_addr="129.2.30.12",port_handle="port-1/2",transmit_mode="single_burst",rate_pps=10,mode="create",l3_protocol="ipv4",udp_dst_port=334) + tg.tg_traffic_control(action="run",handle="stream-1/1-0") + tg.tg_traffic_control(action="run",handle="stream-1/1-2") + tg.tg_traffic_control(action="run",handle="stream-1/2-2") + tg.tg_traffic_control(action="run",handle="stream-1/1-1") + tg.tg_traffic_control(action="stop",port_handle="port-1/1") + tg.tg_traffic_control(action="run",handle="stream-1/2-1") + tg.tg_traffic_control(action="stop",port_handle="port-1/2") + +def test_bgp(tg): + res = tg.tg_interface_config(arp_send_req='1',intf_ip_addr='193.1.1.2',port_handle='port-1/1',netmask='255.255.255.0',mode='config',gateway='193.1.1.1', count=2) + #tg_interface_config(ipv6_prefix_length='64',arp_send_req='1',ipv6_intf_addr='1093:1:1::2',port_handle='port-1/5',mode='config',ipv6_gateway='1093:1:1::1') + tg.tg_emulation_bgp_config(handle=res["handle"],local_as=63001,active_connect_enable='1',mode='enable',remote_as=650002,enable_4_byte_as='1',remote_ip_addr='193.1.1.1') + +def test_emulated(tg): + (tg_ph_1, tg_ph_2) = tg.tg_port_handle.values() + tg.tg_traffic_config(**{'ip_src_count': 1, 'mac_src': '00:0a:01:00:11:01', 'l3_length': '500', 'length_mode': 'fixed', 'mac_dst': '00:00:00:00:00:00', 'port_handle': 'port-1/1', 'ip_src_addr': '192.168.13.2', 'transmit_mode': 'continuous', 'rate_pps': '1000', 'mode': 'create', 'l3_protocol': 'ipv4'}) + tg.tg_traffic_control(action='run',port_handle=tg_ph_1) + +def test_main(ipaddr, port=8009): + logging.basicConfig() + tg = TGScapyTest(ipaddr, port, ["1/1", "1/2"]) + (tg_ph_1, tg_ph_2) = tg.tg_port_handle.values() + + test_emulated(tg) + raw_input("press any key") + + tg.server_control("pre-module-prolog", "mod-0") + test_clear(tg) + tg.server_control("post-module-epilog", "mod-0") + + tg.server_control("pre-module-prolog", "mod-2") + test_single_burst(tg) + tg.server_control("post-module-epilog", "mod-2") + raw_input("press any key") + + tg.server_control("pre-module-prolog", "mod-2") + test_untagged(tg, 10) + tg.server_control("post-module-epilog", "mod-2") + + raw_input("press any key") + tg.server_control("pre-module-prolog", "mod-3") + test_tagged(tg, 10) + tg.server_control("post-module-epilog", "mod-3") + + raw_input("press any key") + tg.server_control("pre-module-prolog", "mod-4") + test_capture(tg) + tg.server_control("post-module-epilog", "mod-4") + + raw_input("press any key") + tg.server_control("pre-module-prolog", "mod-5") + test_interface(tg) + tg.server_control("post-module-epilog", "mod-5") + + tg.server_control("pre-module-prolog", "mod-6") + test_nat(tg) + tg.server_control("post-module-epilog", "mod-6") + + tg.server_control("pre-module-prolog", "mod-7") + test_bgp(tg) + tg.server_control("post-module-epilog", "mod-7") + + return tg + +if __name__ == '__main__': + ipaddr = sys.argv[1] if len(sys.argv) > 1 else "127.0.0.1" + port = sys.argv[2] if len(sys.argv) > 2 else 8009 + tg = test_main(ipaddr, port) + raw_input("press any key") + tg.tg_disconnect() + diff --git a/spytest/spytest/tgen/scapy/ut_streams.py b/spytest/spytest/tgen/scapy/ut_streams.py new file mode 100644 index 00000000000..35bd306613f --- /dev/null +++ b/spytest/spytest/tgen/scapy/ut_streams.py @@ -0,0 +1,81 @@ + +def _build(**kwargs): + kwargs.setdefault("mac_src", '00.00.00.00.00.01') + kwargs.setdefault("mac_dst", '00.00.00.00.00.02') + kwargs.setdefault("mac_src_step", '00.00.00.00.00.01') + kwargs.setdefault("mac_dst_step", '00.00.00.00.00.01') + kwargs.setdefault("arp_src_hw_addr", '01.00.00.00.00.01') + kwargs.setdefault("arp_dst_hw_addr", '01.00.00.00.00.02') + kwargs.setdefault("ip_src_addr", '11.1.1.1') + kwargs.setdefault("ip_dst_addr", '225.1.1.1') + kwargs.setdefault("ip_src_step", '0.0.0.1') + kwargs.setdefault("ip_dst_step", '0.0.0.1') + kwargs.setdefault("mac_src_count", 20) + kwargs.setdefault("mac_dst_count", 20) + kwargs.setdefault("arp_src_hw_count", 20) + kwargs.setdefault("arp_dst_hw_count", 10) + kwargs.setdefault("ip_src_count", 20) + kwargs.setdefault("ip_dst_count", 20) + kwargs.setdefault("transmit_mode", 'continuous') + kwargs.setdefault("length_mode", 'fixed') + kwargs.setdefault("vlan_id", 10) + kwargs.setdefault("vlan_id_count", 10) + kwargs.setdefault("vlan_id_step", 3) + kwargs.setdefault("l2_encap", 'ethernet_ii') + kwargs.setdefault("frame_size", 64) + kwargs.setdefault("pkts_per_burst", 10) + kwargs.setdefault("mode", "create") + + return kwargs + +def _build2(index=0): + + if index == 0: return _build() + if index == 1: return _build(length_mode='random', transmit_mode='single_burst') + if index == 2: return _build(length_mode='increment', transmit_mode='single_burst', frame_size_step=2000) + if index == 3: return _build(mac_dst_mode="increment") + if index == 4: return _build(mac_src_mode="increment", transmit_mode='single_burst') + if index == 5: return _build(l3_protocol='arp', arp_src_hw_mode="increment", arp_dst_hw_mode="decrement") + if index == 6: return _build(rate_pps=1, l3_protocol='ipv4', ip_src_mode='increment', ip_dst_mode='decrement') + if index == 7: return _build(vlan_user_priority="3", l2_encap="ethernet_ii_vlan", vlan_id_mode="increment") + if index == 8: return _build(vlan="enable", l3_protocol='ipv4', ip_src_addr='1.1.1.1', ip_dst_addr='5.5.5.5', + ip_dscp="8", high_speed_result_analysis=0, track_by='trackingenabled0 ipv4DefaultPhb0', + ip_dscp_tracking=1) + if index == 9: return _build(l2_encap='ethernet_ii', ethernet_value='88CC', + data_pattern='02 07 04 00 11 97 2F 8E 80 04 07 03 00 11 97 2F 8E 82 06 02 00 78 00 00 00 00 ' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00') + if index == 10: return _build(l2_encap='ethernet_ii', ethernet_value='8809', data_pattern_mode='fixed', + data_pattern='02 07 04 00 11 97 2F 8E 80 04 07 03 00 11 97 2F 8E 82 06 02 00 78 00 00 00 00 ' + '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00') + if index == 11: return _build(data_pattern='FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 2D 01 04 00 C8 00 5A 05 05 ' + '05 05 10 02 0E 01 04 00 01 00 01 02 00 41 04 00 00 00 C8', l3_protocol='ipv4', ip_protocol=6, + ip_src_addr='1.1.1.1', l4_protocol='tcp', ip_precedence=5, frame_size=103, + ip_dst_addr='1.1.1.2', tcp_dst_port=179, tcp_src_port=54821, tcp_window=115, + tcp_seq_num=1115372998, tcp_ack_num=1532875182,tcp_ack_flag=1, tcp_psh_flag=1, ip_ttl=1) + if index == 12: return _build(l3_protocol='ipv6', data_pattern='01 D1 49 5E 00 08 00 02 00 78 00 01 00 0A 00 03 00 01 00 13 ' + '5F 1F F2 80 00 06 00 06 00 19 00 17 00 18 00 19 00 0C 00 33 ' '00 01 00 00 00 00 00 00 00 00', + frame_size=116, ipv6_dst_addr="FF02:0:0:0:0:0:1:2", ipv6_src_addr="FE80:0:0:0:201:5FF:FE00:500", + ipv6_next_header=17, ipv6_traffic_class=224,l4_protocol='udp',udp_dst_port=546, + udp_src_port=547, ipv6_hop_limit=255) + if index == 13: return _build(l3_protocol='arp', arp_src_hw_addr="00:00:00:11:11:80", + arp_dst_hw_addr="00:00:00:00:00:00", arp_operation='arpRequest', ip_src_addr='1.1.1.1', ip_dst_addr='1.1.1.2') + if index == 14: return _build(l3_protocol='ipv6', data_pattern='FF FF', l4_protocol="icmp", ipv6_dst_addr="fe80::ba6a:97ff:feca:bb98", + ipv6_src_addr="2001::2", ipv6_next_header=58, icmp_target_addr='2001::2', icmp_type=136, icmp_ndp_nam_o_flag=0, + icmp_ndp_nam_r_flag=1, icmp_ndp_nam_s_flag=1, ipv6_hop_limit=255) + if index == 15: return _build(rate_pps=1, l3_protocol='ipv4',ip_src_addr='11.1.1.1', ip_dst_addr='225.1.1.1',ip_protocol=2, \ + l4_protocol='igmp',igmp_msg_type='report',igmp_group_addr='225.1.1.1',high_speed_result_analysis=0) + return None + +def ut_stream_get(index=0, **kws): + kwargs = _build2(index) + if kwargs: kwargs.update(kws) + return kwargs + +if __name__ == '__main__': + print ut_stream_get(0) + for i in range(100): + d = ut_stream_get(i) + if not d: + break + print d + diff --git a/spytest/spytest/tgen/scapy/utils.py b/spytest/spytest/tgen/scapy/utils.py new file mode 100644 index 00000000000..4c92d9248a1 --- /dev/null +++ b/spytest/spytest/tgen/scapy/utils.py @@ -0,0 +1,228 @@ +import os +import sys +import time +import random +import socket +import struct +import inspect + +from tempfile import mkstemp +import subprocess + +from logger import Logger + +class Utils(object): + def __init__(self, dry=False, logger=None): + self.dry = dry + self.logger = logger or Logger() + + def fread(self, fname, default=""): + with open(fname, 'r') as myfile: + return myfile.read() + return default + + def fwrite(self, content, fname = ""): + if fname == "" or self.dry: + _, fname = mkstemp() + else: + directory = os.path.dirname(fname) + if not os.path.exists(directory): + os.makedirs(directory) + with open(fname, "w") as fd: + fd.write(content) + return fname + + def cmdexec(self, cmd, msg=None): + self.logger.debug("cmdexec: " + cmd) + if self.dry: return "skipped-for-dry-run" + p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + (out, err) = p.communicate() + p.wait() + return out or err + + def shexec(self, cmds): + fname = self.fwrite(cmds) + logfile = fname + ".1" + self.logger.debug("shexec: " + cmds) + if not self.dry: + cmd = "sh %s > %s 2>&1" % (fname, logfile) + os.system(cmd) + output = self.fread(logfile) + self.logger.debug(output) + os.unlink(logfile) + os.unlink(fname) + + def line_info(self): + stk = inspect.stack() + self.logger.debug(stk[1][1],":",stk[1][2],":", stk[1][3]) + + @staticmethod + def incrementMac(mac, step): + step = step.replace(':', '').replace(".",'') + mac = mac.replace(':', '').replace(".",'') + nextMac = int(mac, 16) + int(step, 16) + return ':'.join(("%012X" % nextMac)[i:i+2] for i in range(0, 12, 2)) + + @staticmethod + def decrementMac(mac, step): + step = step.replace(':', '').replace(".",'') + mac = mac.replace(':', '').replace(".",'') + nextMac = int(mac, 16) - int(step, 16) + return ':'.join(("%012X" % nextMac)[i:i+2] for i in range(0, 12, 2)) + + @staticmethod + def randomMac(): + return "02:00:00:%02x:%02x:%02x" % (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) + + @staticmethod + def incrementIPv4(ip, step): + ip2int = lambda ipstr: struct.unpack('!I', socket.inet_aton(ipstr))[0] + int2ip = lambda n: socket.inet_ntoa(struct.pack('!I', n)) + return int2ip(ip2int(ip)+ip2int(step)) + + @staticmethod + def decrementIPv4(ip, step): + ip2int = lambda ipstr: struct.unpack('!I', socket.inet_aton(ipstr))[0] + int2ip = lambda n: socket.inet_ntoa(struct.pack('!I', n)) + return int2ip(ip2int(ip)-ip2int(step)) + + @staticmethod + def randomIpv4(): + return ".".join(map(str, (random.randint(0, 255) for _ in range(4)))) + + @staticmethod + def valid_ip6(address): + VALID_CHARACTERS = 'ABCDEFabcdef:0123456789' + address_list = address.split(':') + return ( + len(address_list) == 8 + and all(len(current) <= 4 for current in address_list) + and all(current in VALID_CHARACTERS for current in address) + ) + + @staticmethod + def ipv4_ip2long(ip): + quads = ip.split('.') + if len(quads) == 1: + # only a network quad + quads = quads + [0, 0, 0] + elif len(quads) < 4: + # partial form, last supplied quad is host address, rest is network + host = quads[-1:] + quads = quads[:-1] + [0, ] * (4 - len(quads)) + host + + lngip = 0 + for q in quads: + lngip = (lngip << 8) | int(q) + return lngip + + @staticmethod + def ipv4_long2ip(l): + return '%d.%d.%d.%d' % (l >> 24 & 255, l >> 16 & 255, l >> 8 & 255, l & 255) + + @staticmethod + def ipv6_ip2long(ip): + if '.' in ip: + # convert IPv4 suffix to hex + chunks = ip.split(':') + v4_int = Utils.ipv4_ip2long(chunks.pop()) + if v4_int is None: + return None + chunks.append('%x' % ((v4_int >> 16) & 0xffff)) + chunks.append('%x' % (v4_int & 0xffff)) + ip = ':'.join(chunks) + + halves = ip.split('::') + hextets = halves[0].split(':') + if len(halves) == 2: + h2 = halves[1].split(':') + for z in range(8 - (len(hextets) + len(h2))): + hextets.append('0') + for h in h2: + hextets.append(h) + # end if + + lngip = 0 + for h in hextets: + if h == '': h = '0' + lngip = (lngip << 16) | int(h, 16) + return lngip + + @staticmethod + def ipv6_long2ip(l): + # format as one big hex value + hex_str = '%032x' % l + # split into double octet chunks without padding zeros + hextets = ['%x' % int(hex_str[x:x + 4], 16) for x in range(0, 32, 4)] + + # find and remove left most longest run of zeros + dc_start, dc_len = (-1, 0) + run_start, run_len = (-1, 0) + for idx, hextet in enumerate(hextets): + if hextet == '0': + run_len += 1 + if run_start == -1: + run_start = idx + if run_len > dc_len: + dc_len, dc_start = (run_len, run_start) + else: + run_len, run_start = (0, -1) + # end for + if dc_len > 1: + dc_end = dc_start + dc_len + if dc_end == len(hextets): + hextets += [''] + hextets[dc_start:dc_end] = [''] + if dc_start == 0: + hextets = [''] + hextets + # end if + + return ':'.join(hextets) + + @staticmethod + def incrementIPv6(ip, step): + return Utils.ipv6_long2ip(Utils.ipv6_ip2long(ip)+Utils.ipv6_ip2long(step)) + + @staticmethod + def decrementIPv6(ip, step): + return Utils.ipv6_long2ip(Utils.ipv6_ip2long(ip)-Utils.ipv6_ip2long(step)) + + @staticmethod + def intval(d, prop, default): + val = d.get(prop, "{}".format(default)) + return int(val) + + @staticmethod + def tobytes(s): + if sys.version_info[0] < 3: + return buffer(s) + return s.encode() + + @staticmethod + def get_env_int(name, default): + try: + return int(os.getenv(name, default)) + except: + pass + return default + + @staticmethod + def msleep(delay, block=1): + mdelay = delay /1000.0 + now = time.time() + while now + mdelay > time.time(): + time.sleep(block/1000.0) + + @staticmethod + def usleep(delay, block=1): + mdelay = delay /1000000.0 + now = time.time() + while now + mdelay > time.time(): + time.sleep(block/1000000.0) + + @staticmethod + def make_list(arg): + if isinstance(arg, list): + return arg + return [arg] + diff --git a/spytest/spytest/tgen/scapy/yabgp.ini b/spytest/spytest/tgen/scapy/yabgp.ini new file mode 100644 index 00000000000..50f0bf6ae58 --- /dev/null +++ b/spytest/spytest/tgen/scapy/yabgp.ini @@ -0,0 +1,125 @@ +[DEFAULT] + +# log file name and location +# log-file = + +# show debug output +# verbose = False + +# log to standard error +# use-stderr = True + +# log file directory +# log-dir + +# log configuration file +# log-config-file = + +# run mode +# standalone = True + +# pid file +# pid-file = None + +[message] + +# how to process parsed BGP message? + +# Whether the BGP message is written to disk +write_disk = False + +# the BGP messages storage path +write_dir = /tmp/data/bgp/ + +# The Max size of one BGP message file, the unit is MB +# write_msg_max_size = 500 + +# Whether write keepalive message to disk +# write_keepalive = False + +# The output format of bgp messagees +# two options: list and json, default value is list +# format = json + + +[bgp] + +# BGP global configuration items + +# peer configuration file +# config_file = + +# The interval to start each BGP peer +# peer_start_interval = 10 + +# The Global config for address family and sub address family +# if you want to support more than one address family, you can set afi_safi = ipv4, ipv6, .... +# we support: ipv4, ipv6, flowspec, vpnv4, evpn, vpnv6 +# afi_safi = ipv4 + +# role tag +# tag = + +# ===================== items for peer configuration ================================ +# the following parameters will be ignored if conf_file is configured +# and this configuration only support one bgp peer, if you need start more peers in +# one yabgp process, please use conf_file to configure them. + +# remote as number +# remote_as = + +# remote ip address +# remote_addr = + +# local as number +# local_as = + +# local ip address +# local_addr = + +# The MD5 string +# md5 = + +# Whether maintain bgp rib table +# rib = False + +# ======================= BGP capacity ============================= + +# support 4 bytes AS +# four_bytes_as = True + +# support route refresh +# route_refresh = True + +# support cisco route refresh +# cisco_route_refresh = True + +# BGP add path feature.This field indicates whether the sender is +# (a) able to receive multiple paths from its peer +# (b) able to send multiple paths to its peer +# (c) both send and receive for the . +# now we only support ipv4, so there are +# 'ipv4_send', 'ipv4_receive' and 'ipv4_both', the default value is +# ipv4_both + +# add_path = + +# suport graceful restart or not +# graceful_restart = True + +# support cisco multi session or not +# cisco_multi_session = True + +# support enhanced route refresh or not +# enhanced_route_refresh = True + +[rest] +# Address to bind the API server to. +# bind_host = 0.0.0.0 + +# Port the bind the API server to. +# bind_port = 8801 + +# username and password for api server +# username = admin +# password = admin diff --git a/spytest/spytest/tgen/tg.py b/spytest/spytest/tgen/tg.py new file mode 100644 index 00000000000..958b9f2993b --- /dev/null +++ b/spytest/spytest/tgen/tg.py @@ -0,0 +1,2094 @@ +import os +import re +import time +import json +import copy +import inspect +import requests +from collections import OrderedDict +import utilities.common as utils +import utilities.parallel as putils +from spytest.logger import Logger +from spytest.tgen.init import tg_stc_load,tg_scapy_load,tg_ixia_load +from spytest.tgen.tg_scapy import ScapyClient +from spytest.dicts import SpyTestDict +from netaddr import IPAddress + +workarea = None +logger = None +skip_tgen = False +tg_stc_pkg_loaded = False +tg_ixia_pkg_loaded = False +tg_scapy_pkg_loaded = False +tg_version_list = dict() +tgen_obj_dict = {} + +def tgen_profiling_start(msg, max_time=300): + return workarea.profiling_start(msg, max_time) + +def tgen_profiling_stop(pid): + return workarea.profiling_stop(pid) + +def tgen_wait(val): + workarea.tg_wait(val) + +def tgen_exception(ex): + workarea.report_tgen_exception(ex) + +def tgen_abort(dbg_msg, msgid, *args): + logger.error('TG API Fatal Error: %s' % dbg_msg) + workarea.report_tgen_abort(msgid, *args) + +def tgen_fail(dbg_msg, msgid, *args): + logger.error('TG API Fatal Error: %s' % dbg_msg) + workarea.report_tgen_fail(msgid, *args) + +def tgen_script_error(dbg_msg, msgid, *args): + logger.error('TG API Script Error: %s' % dbg_msg) + workarea.report_scripterror(msgid, *args) + +def tgen_ftrace(*args): + workarea.tgen_ftrace(*args) + +def tgen_get_logs_path(for_file=None): + return workarea.get_logs_path(for_file) + +def tgen_get_logs_path_folder(for_file=None): + tgen_folder = for_file if for_file else 'tgen' + tgen_folder_path = workarea.get_logs_path(tgen_folder) + if not os.path.exists(tgen_folder_path): + os.makedirs(os.path.abspath(tgen_folder_path)) + return tgen_folder_path + +def tgen_log_call(fname, **kwargs): + args_list=[] + for key, value in kwargs.items(): + if isinstance(value, str): + args_list.append("%s='%s'" %(key, value)) + elif isinstance(value, int): + args_list.append("%s=%s" %(key, value)) + elif isinstance(value, list): + args_list.append("%s=%s" %(key, value)) + else: + args_list.append("%s=%s[%s]" %(key, value, type(value))) + text = "{}({})\n".format(fname, ",".join(args_list)) + logger.debug('REQ: {}'.format(text.strip())) + file_prefix = os.getenv("SPYTEST_FILE_PREFIX", "results") + hltApiLog = os.path.join(tgen_get_logs_path_folder(), "{}_{}".format(file_prefix, 'hltApiLog.txt')) + utils.write_file(hltApiLog, text, "a") + +analyzer_filter = {"ipv4Precedence0": "ip_precedence_tracking", + "ipv4DefaultPhb0": "ip_dscp_tracking", + "vlanVlanUserPriority0": "vlan_user_priority_tracking", + "vlanVlanUserPriority1": "vlan_id_tracking", + "vlanVlanId0": "vlan_id_tracking", + "vlanVlanId1": "vlan_id_tracking"} + +def get_sth(): + return globals().get("sth") + +def get_ixiatcl(): + return globals().get("ixiatcl") + +def get_ixiangpf(): + return globals().get("ixiangpf") + +def get_ixnet(): + return get_ixiangpf().ixnet + +class TGBase(object): + def __init__(self, tg_type, tg_version, tg_ip=None, tg_port_list=None): + logger.info('TG Base Init...start') + self.tg_ns = "" + self.tg_type = tg_type + self.tg_version = tg_version + self.tg_ip = tg_ip + self.tg_port_list = tg_port_list + self.skip_traffic = skip_tgen + self.tg_connected = False + self.in_module_start_cleanup = False + self.cached_interface_config_handles = OrderedDict() + self.tg_port_handle = dict() + self.tg_port_analyzer = dict() + if self.tg_ip == None or self.tg_port_list == None: + return + if self.skip_traffic: + return + + for i in range(0, 10): + ret_ds = self.connect() + if ret_ds: + msg = "UNKNOWN" if "log" not in ret_ds else ret_ds['log'] + logger.warning('TG Connect Error: %s try: %d' % (msg, i)) + tgen_wait(10) + else: + logger.info('TG Connection: Success') + self.tg_connected = True + break + + def manage_interface_config_handles(self, mode, port_handle, handle): + logger.debug("manage_interface_config_handles: {} {} {}".format(mode, port_handle, handle)) + if port_handle is None or handle is None: + return + if port_handle not in self.cached_interface_config_handles: + self.cached_interface_config_handles[port_handle] = [] + if mode == 'destroy': + if handle in self.cached_interface_config_handles[port_handle]: + self.cached_interface_config_handles[port_handle].remove(handle) + else: + if handle not in self.cached_interface_config_handles[port_handle]: + self.cached_interface_config_handles[port_handle].append(handle) + + def manage_traffic_config_handles(self, ret_ds, **kwargs): + pass + + def ensure_traffic_control(self, timeout=60, skip_fail=False, **kwargs): + pass + + def ensure_traffic_stats(self, timeout=60, skip_fail=False, **kwargs): + pass + + def clean_all(self): + logger.error("should be overriden") + return None + + def connect(self): + logger.error("should be overriden") + return None + + def show_status(self): + logger.error("should be overriden") + return None + + def instrument(self, phase, context): + pass + + def wait(self, msg, val, soft=None): + if soft is not None and self.tg_type in ["scapy"]: + val = soft + if val > 0: + tgen_wait(val) + + def warn(self, dbg_msg): + logger.error('TG API Error: %s' % dbg_msg) + self.ensure_connected(dbg_msg) + + def fail(self, dbg_msg, msgid, *args): + self.ensure_connected(dbg_msg) + tgen_fail(dbg_msg, msgid, *args) + + def exception(self, exp): + logger.error('TG API Fatal Exception: %s' % str(exp)) + self.ensure_connected(str(exp)) + tgen_exception(exp) + + def has_disconnected(self, msg): + if "Failed to parse stack trace not connected" in msg: + return True + + if "Ixnetwork error occured" in msg: + if "Connection reset by peer" in msg or "not connected" in msg: + return True + + return False + + def ensure_connected(self, msg): + if os.getenv("SPYTEST_ENSURE_CONNECTED", "1") == "0": + return + + if self.has_disconnected(msg): + tgen_abort(msg, "tgen_failed_abort", msg) + return + + if self.tg_type in ['stc', 'scapy']: + return + + try: + # try getting the ixnetwork build number to check connection status + get_ixiangpf().ixnet.getAttribute('::ixNet::OBJ-/globals', '-buildNumber') + except Exception as exp: + tgen_abort(msg, "tgen_failed_abort", str(msg)) + + def debug_show(self, ph, msg=""): + stats = self.tg_traffic_stats(port_handle=ph,mode="aggregate") + total_tx = stats[ph]['aggregate']['tx']['total_pkts'] + total_rx = stats[ph]['aggregate']['rx']['total_pkts'] + logger.info("{} PORT: {} TX: {} RX: {}".format(msg, ph, total_tx, total_rx)) + + def tgen_eval(self, msg, func, **kwargs): + + logger.info('Executing: {}'.format(msg)) + (pid, ret_ds) = (0, dict()) + try: + pid = tgen_profiling_start(msg) + ret_ds = eval(func)(**kwargs) + tgen_profiling_stop(pid) + except Exception as exp: + tgen_profiling_stop(pid) + logger.info('Error {} executing: {}'.format(msg, func)) + if not self.in_module_start_cleanup: + self.exception(exp) + self.show_status() + + return ret_ds + + def get_port_handle(self, port): + return self.tg_port_handle.get(port, None) + + def get_port_handle_list(self): + ph_list = list() + for port, handle in self.tg_port_handle.items(): + ph_list.append(handle) + return ph_list + + def get_hltapi_name(self, fname): + ns = self.tg_ns + '.' + self.cur_hltapi = re.sub(r'tg_', ns, fname) + return self.cur_hltapi + + def tgen_check_parallel(self, fname): + if fname not in ["tg_interface_config", "tg_traffic_config"]: + return + if not putils.get_in_parallel(): + tgen_ftrace("{} not called in parallel".format(fname)) + + def tg_bgp_routes_control(self, handle, route_handle, mode): + if mode == 'withdraw': + return self.tg_withdraw_bgp_routes(route_handle) + if mode == 'readvertise': + return self.tg_readvertise_bgp_routes(handle,route_handle) + + def map_field(self, src, dst, d): + if d.get(src) != None: + if dst: + d[dst] = d[src] + else: + logger.warning("TG API Field Unsupported: {}".format(src)) + d.pop(src) + return True + return False + + def modify_tgen_return_params(self, res, actual, modify): + if modify: + res[modify] = res[actual] if actual in res else '' + return res + + def tg_topology_test_control(self, stack=None, skip_wait=False, tg_wait=2, **kwargs): + if kwargs.get('handle') != None and stack != None: + kwargs['handle'] = re.search(r'.*{}:(\d)+'.format(stack), kwargs['handle']).group(0) + kwargs.pop('tg_wait', '') + for i in range(1, 30): + if kwargs.get('action') == 'apply_on_the_fly_changes': + res = get_ixiangpf().test_control(action='apply_on_the_fly_changes') + else: + res = self.tg_test_control(**kwargs) + logger.debug(res) + if res.get('status', '0') == '1': + logger.debug('{}: Success'.format(kwargs['action'])) + break + tgen_wait(tg_wait) + if not skip_wait: + tgen_wait(10) + + def trgen_pre_proc(self, fname, **kwargs): + if self.skip_traffic: + return 0 + if fname == 'tg_connect' and self.tg_connected: + logger.info('TG is already connnected') + return + self.tgen_check_parallel(fname) + tgen_log_call(fname, **kwargs) + kwargs_port_handle = kwargs.get('port_handle') + kwargs_handle = kwargs.get('handle') + kwargs = self.trgen_adjust_mismatch_params(fname, **kwargs) + func = self.get_hltapi_name(fname) + + if self.tg_version == 8.40 and fname == 'tg_traffic_config': + if kwargs.get('rate_pps') != None: + kwargs['rate_pps'] = 5 + + # Handling the cleanup here, if mode='destroy' is called. + # if 'stc', replace interface_config with cleanup_session. + # if 'ixia', replace handle with protocol_handle (already set). + if fname == 'tg_interface_config': + if kwargs.get('mode') == 'destroy': + if self.tg_type == 'stc': + func = self.get_hltapi_name('tg_cleanup_session') + kwargs.pop('mode','') + kwargs.pop('handle','') + kwargs['reset']='0' + elif self.tg_type == 'ixia': + func = self.get_hltapi_name('tg_topology_config') + han = kwargs['handle'] + han = han[0] if type(han) is list else han + han = re.search(r'.*deviceGroup:(\d)+',han).group(0) + logger.debug("Starting Destroy...") + logger.debug(han) + self.tg_test_control(handle=han, action='stop_protocol') + tgen_wait(10) + kwargs['topology_handle'] = han + kwargs.pop('handle','') + kwargs.pop('port_handle','') + if fname == 'tg_interface_control': + if kwargs.get('mode') == 'break_link': + if self.tg_type == 'ixia': + func = self.get_hltapi_name('tg_interface_config') + kwargs.pop('mode','') + kwargs['op_mode']='sim_disconnect' + elif kwargs.get('mode') == 'restore_link': + if self.tg_type == 'ixia': + func = self.get_hltapi_name('tg_interface_config') + kwargs.pop('mode','') + kwargs['op_mode']='normal' + elif kwargs.get('mode') == 'check_link': + if self.tg_type == 'stc': + func = self.get_hltapi_name('tg_interface_stats') + kwargs.pop('mode','') + desired_status=kwargs.pop('desired_status','') + kwargs["properties"] = "link" + elif self.tg_type == 'ixia': + func = self.get_hltapi_name('tg_test_control') + kwargs.pop('mode','') + kwargs['action']='check_link_state' + + if fname == 'tg_traffic_control': + if self.tg_type == 'ixia' and kwargs.get('action') == 'reset': + traffic_items = get_ixiangpf().session_info(mode='get_traffic_items') + if traffic_items.get('traffic_config') != None: + logger.debug("stopping streams before reset") + ret_ds = self.tg_traffic_control(action='stop', stream_handle=traffic_items['traffic_config'].split()) + logger.debug(ret_ds) + tgen_wait(2) + + if fname == 'tg_packet_stats' and kwargs.get('format') == 'var': + op_type = kwargs.pop('output_type',None) + if self.tg_type == 'ixia' and op_type == 'hex': + func = self.get_hltapi_name('self.local_get_captured_packets') + + if fname == 'tg_packet_control': + if self.tg_type == 'stc' and kwargs['action'] == 'start': + port_handle=kwargs.get('port_handle') + if isinstance(port_handle,list): + ret_ds = None + kwargs.pop('port_handle','') + for ph in port_handle: + ret_ds = self.tg_packet_control(port_handle=ph, **kwargs) + return ret_ds + + if fname == 'tg_traffic_stats' and self.tg_type == 'ixia': + self.ensure_traffic_stats(**kwargs) + + msg = "{} {}".format(func, kwargs) + ret_ds = self.tgen_eval(msg, func, **kwargs) + # logger.info(ret_ds) + + if "status" not in ret_ds and fname == 'tg_traffic_stats': + for i in range(1, 5): + logger.warning('Failed to fetch Traffic stats, Executing: {} again, after 3 sec...Try: {}'.format(func, i)) + tgen_wait(3) + msg = "{} {}".format(func, kwargs) + ret_ds = self.tgen_eval(msg, func, **kwargs) + logger.info(ret_ds) + if ret_ds.get('status') != None: + break + if ret_ds.get('status') == None: + logger.error('Traffic stats not collected properly, even after waiting for 15 sec...') + if "status" not in ret_ds: + logger.warning(ret_ds) + msg = "Unknown" if "log" not in ret_ds else ret_ds['log'] + self.fail("nolog", "tgen_failed_api", msg) + elif ret_ds['status'] == '1': + logger.debug('TG API Run Status: Success') + if fname == 'tg_traffic_control' and self.tg_type == 'ixia': + self.ensure_traffic_control(**kwargs) + if fname == 'tg_traffic_config' and self.tg_type == 'ixia': + self.manage_traffic_config_handles(ret_ds, **kwargs) + if fname == 'tg_connect': + self.tg_connected = True + for port in kwargs['port_list']: + self.tg_port_handle[port] = ret_ds['port_handle'][self.tg_ip][port] + if fname == 'tg_interface_config': + if self.tg_type == 'stc': + if kwargs.get('enable_ping_response') != None and kwargs.get('netmask') != None: + ret_val = self.tg_interface_handle(ret_ds) + prefix_len = IPAddress(kwargs.get('netmask', '255.255.255.0')).netmask_bits() + for device in utils.make_list(ret_val['handle']): + self.local_stc_tapi_call( + 'stc::config ' + device + ' -enablepingresponse ' + str(kwargs['enable_ping_response'])) + ipv4if = self.local_stc_tapi_call('stc::get ' + device + ' -children-ipv4if') + self.local_stc_tapi_call('stc::config ' + ipv4if + ' -PrefixLength ' + str(prefix_len)) + get_sth().invoke("stc::apply") + if re.search('cleanup_session',func): + if re.search('sth',func): + get_sth().invoke("stc::apply") + tgen_wait(1) + elif kwargs.get('mode') == 'destroy': + tgen_wait(10) + self.tg_topology_test_control(action='apply_on_the_fly_changes', skip_wait=True) + tgen_wait(2) + self.manage_interface_config_handles(kwargs.get('mode'), kwargs_port_handle, kwargs_handle) + elif kwargs.get('mode') == 'config': + ret_ds = self.tg_interface_handle(ret_ds) + if self.tg_type == 'ixia': + tgen_wait(10) + self.tg_topology_test_control(action='apply_on_the_fly_changes', skip_wait=True) + logger.info('start the host.') + temp = ret_ds['handle'] if type(ret_ds['handle'])!=list else ret_ds['handle'][0] + self.tg_topology_test_control(handle=temp, stack='deviceGroup', action='start_protocol') + self.manage_interface_config_handles(kwargs.get('mode'), kwargs_port_handle, ret_ds['handle']) + else: + ret_ds = self.tg_interface_handle(ret_ds) + if fname == 'tg_emulation_bgp_config': + if self.tg_type == 'ixia': + ret_ds = self.modify_tgen_return_params(ret_ds, 'bgp_handle', 'handle') + if self.tg_type == 'stc': + ret_ds = self.modify_tgen_return_params(ret_ds, 'handles', 'handle') + if fname == 'tg_emulation_bgp_route_config': + logger.info(ret_ds) + if self.tg_type == 'ixia': + if 'bgp_routes' in ret_ds: + ret_ds = self.modify_tgen_return_params(ret_ds, 'bgp_routes', 'handle') + elif 'ip_routes' in ret_ds: + ret_ds = self.modify_tgen_return_params(ret_ds, 'ip_routes', 'handle') + if self.tg_type == 'stc': + ret_ds = self.modify_tgen_return_params(ret_ds, 'handles', 'handle') + if fname == 'tg_emulation_igmp_config': + if self.tg_type == 'ixia': + ret_ds = self.modify_tgen_return_params(ret_ds, 'igmp_host_handle', 'host_handle') + if self.tg_type == 'stc': + ret_ds = self.modify_tgen_return_params(ret_ds, 'handles', 'host_handle') + if fname == 'tg_emulation_multicast_group_config': + if self.tg_type == 'ixia': + ret_ds = self.modify_tgen_return_params(ret_ds, 'multicast_group_handle', 'mul_group_handle') + if self.tg_type == 'stc': + ret_ds = self.modify_tgen_return_params(ret_ds, 'handle', 'mul_group_handle') + if fname == 'tg_emulation_multicast_source_config': + if self.tg_type == 'ixia': + ret_ds = self.modify_tgen_return_params(ret_ds, 'multicast_source_handle', 'mul_source_handle') + if self.tg_type == 'stc': + ret_ds = self.modify_tgen_return_params(ret_ds, 'handle', 'mul_source_handle') + if fname == 'tg_emulation_igmp_group_config': + if self.tg_type == 'ixia': + ret_ds = self.modify_tgen_return_params(ret_ds, 'igmp_group_handle', 'group_handle') + if self.tg_type == 'stc': + ret_ds = self.modify_tgen_return_params(ret_ds, 'handle', 'group_handle') + if fname == 'tg_emulation_igmp_querier_config': + if self.tg_type == 'ixia': + ret_ds = self.modify_tgen_return_params(ret_ds, 'igmp_querier_handle', 'handle') + if fname == 'tg_emulation_ospf_config': + if self.tg_type == 'ixia': + ret_ds = self.modify_tgen_return_params(ret_ds, 'ospfv2_handle', 'handle') + if self.tg_type == 'stc': + if kwargs['mode'] == 'create': + ret_ds = self.tg_emulation_ospf_config(handle=ret_ds['handle'], mode='modify', + router_id=kwargs['router_id']) + if fname == 'tg_emulation_ospf_topology_route_config': + prefix_type = {'summary_routes': 'summary', 'ext_routes': 'external', 'nssa_routes': 'nssa', + 'network': 'net', 'router': 'router'} + ret_ds['handle'] = ret_ds[prefix_type[kwargs['type']]]['connected_routers'] + if fname == 'tg_emulation_ospf_network_group_config': + if 'ipv4_prefix_pools_handle' in ret_ds: + ret_ds = self.modify_tgen_return_params(ret_ds, 'ipv4_prefix_pools_handle', 'handle') + if fname == 'tg_emulation_dhcp_server_config': + if self.tg_type == 'stc': + if ret_ds.get('handle') != None: + if ret_ds['handle'].get('dhcp_handle') != None: + ret_ds['dhcp_handle'] = ret_ds['handle']['dhcp_handle'] + else: + ret_ds['dhcp_handle'] = ret_ds['handle']['dhcpv6_handle'] + if self.tg_type == 'ixia': + if ret_ds.get('dhcpv4server_handle'): + ret_ds = self.modify_tgen_return_params(ret_ds, 'dhcpv4server_handle', 'dhcp_handle') + else: + ret_ds = self.modify_tgen_return_params(ret_ds, 'dhcpv6server_handle', 'dhcp_handle') + if fname == 'tg_emulation_dhcp_config': + if self.tg_type == 'ixia': + ret_ds = self.modify_tgen_return_params(ret_ds, 'handle', 'handles') + if fname == 'tg_emulation_dhcp_group_config': + if self.tg_type == 'ixia': + if ret_ds.get('dhcpv4client_handle'): + ret_ds = self.modify_tgen_return_params(ret_ds, 'dhcpv4client_handle', 'handles') + else: + ret_ds = self.modify_tgen_return_params(ret_ds, 'dhcpv6client_handle', 'handles') + if fname == 'tg_cleanup_session': + self.tg_connected = False + self.tg_port_handle.clear() + if fname == 'tg_interface_control': + if self.tg_type == 'stc': + if func == self.get_hltapi_name('tg_interface_stats'): + result = ret_ds['link'] + # Dictionary to compare the result. + res_dict = { 'up' : '1', + 'down' : '0' + } + ret_ds = True if res_dict.get(desired_status.lower(),'') == result else False + elif self.tg_type == 'ixia': + if func == self.get_hltapi_name('tg_test_control'): + ret_ds = bool("log" not in ret_ds) + if fname == 'tg_traffic_control' and kwargs['action'] == 'stop' and \ + ret_ds.get('stopped') == '0' and os.getenv("SPYTEST_ENSURE_TRAFFIC_CONTROL", "0") =='0': + for i in range(1, 10): + logger.warning( + 'Traffic is still running, Executing: {} again, after 3 sec...Try: {}'.format(func, i)) + tgen_wait(3) + msg = "{} {}".format(func, kwargs) + ret_ds = self.tgen_eval(msg, func, **kwargs) + logger.info(ret_ds) + if ret_ds['status'] == '1': + logger.debug('TG API Run Status: Success') + if ret_ds.get('stopped') == '1': + break + if ret_ds.get('stopped') == '0': + logger.error('Traffic is still running, even after waiting for 30 sec...') + if fname == 'tg_packet_stats' and kwargs.get('format') == 'var': + if self.tg_type == 'ixia': + logger.info('Disabling control and data plane options') + self.tg_packet_config_buffers(port_handle=kwargs['port_handle'], + control_plane_capture_enable='0', data_plane_capture_enable='0') + else: + + if "not found in mandatory or optional argument list" in ret_ds['log']: + self.fail(ret_ds['log'], "tgen_failed_invalid_option") + if "cannot be executed while other actions are in progress" in ret_ds['log']: + self.fail(ret_ds['log'], "tgen_failed_apply_changes") + if "Unsupported dynamic traffic operation" in ret_ds['log']: + self.fail(ret_ds['log'], "tgen_failed_apply_changes") + if "Oversubscription detected" in ret_ds['log']: + self.fail(ret_ds['log'], "tgen_failed_oversubscription") + if "RuntimeError in apply" in ret_ds['log']: + self.fail(ret_ds['log'], "tgen_failed_runtime_error") + if "Failed to add endpointsets" in ret_ds['log']: + self.fail(ret_ds['log'], "tgen_failed_add_endpoint_sets") + if "Capture action start failed" in ret_ds['log']: + self.fail(ret_ds['log'], "tgen_failed_start_capture") + if "::ixia::test_control: Failed to start Protocols" in ret_ds['log']: + self.fail(ret_ds['log'], "tgen_failed_start_protocols") + if "::ixia::traffic_config: Could not configure stack" in ret_ds['log']: + self.fail(ret_ds['log'], "tgen_failed_configure_stack") + if "At least one port must be selected to apply the changes" in ret_ds['log']: + self.fail(ret_ds['log'], "tgen_failed_apply_changes") + if "::ixia::traffic_stats: Could not find Traffic Item Statistics view" in ret_ds['log']: + self.fail(ret_ds['log'], "tgen_failed_missing_traffic_item") + if "parse_dashed_args: Invalid value" in ret_ds['log']: + self.fail(ret_ds['log'], "tgen_failed_invalid_value") + + # warning + self.warn(ret_ds['log']) + + if fname == 'tg_interface_control': + if self.tg_type == 'ixia': + if func == self.get_hltapi_name('tg_test_control'): + ret_ds = bool("log" not in ret_ds) + return ret_ds + + def trgen_post_proc(self, fname, **kwargs): + pass + +class TGStc(TGBase): + def __init__(self, tg_type, tg_version, tg_ip=None, tg_port_list=None): + TGBase.__init__(self, tg_type, tg_version, tg_ip, tg_port_list) + logger.info('TG STC Init...done') + + def clean_all(self): + + ph_list = self.get_port_handle_list() + + logger.info("TG CLEAN ALL: stop and remove all streams on {}". format(ph_list)) + ret_ds = self.tg_traffic_control(action="reset", port_handle=ph_list) + logger.debug(ret_ds) + tgen_wait(2) + + for port_handle, handle_list in self.cached_interface_config_handles.items(): + for handle in handle_list: + logger.info("removing interface handle {} on {}".format(handle, port_handle)) + self.tg_interface_config(port_handle=port_handle, handle=handle, mode='destroy') + self.cached_interface_config_handles = OrderedDict() + + logger.debug("TG CLEAN ALL FINISHED") + + def get_port_status(self, port_list): + port_handle_list=[] + for port in port_list: + port_handle_list.append(self.get_port_handle(port)) + ret_ds = get_sth().interface_stats(port_handle_list=port_handle_list, properties="link") + retval = {} + for port,port_handle in zip(port_list, port_handle_list): + retval[port] = ret_ds[port_handle]["link"] + return retval + + def show_status(self): + pass + + def connect(self): + self.tg_ns = 'sth' + ret_ds = get_sth().connect(device=self.tg_ip, port_list=self.tg_port_list, + break_locks=1) + logger.info(ret_ds) + if ret_ds['status'] != '1': + return ret_ds + port_handle_list=[] + for port in self.tg_port_list: + self.tg_port_handle[port] = ret_ds['port_handle'][self.tg_ip][port] + port_handle_list.append(self.tg_port_handle[port]) + port_details_all = self.tg_interface_stats(port_handle=port_handle_list) + intf_speed_list = port_details_all['intf_speed'].split() + for intf_speed,port,port_handle in zip(intf_speed_list, self.tg_port_list, port_handle_list): + if intf_speed == '100000': + logger.info('disabling FEC as spirent port {} is of 100G'.format(port)) + self.tg_interface_config(port_handle=port_handle, \ + mode="modify",forward_error_correct="false") + return None + + def trgen_adjust_mismatch_params(self, fname, **kwargs): + if fname == 'tg_traffic_config': + self.map_field("ethernet_value", "ether_type", kwargs) + self.map_field("data_pattern", "custom_pattern", kwargs) + self.map_field("icmp_ndp_nam_o_flag", "icmpv6_oflag", kwargs) + self.map_field("icmp_ndp_nam_r_flag", "icmpv6_rflag", kwargs) + self.map_field("icmp_ndp_nam_s_flag", "icmpv6_sflag", kwargs) + self.map_field("data_pattern_mode", None, kwargs) + self.map_field("icmp_target_addr", None, kwargs) + if kwargs.get('custom_pattern') != None: + kwargs['custom_pattern'] = kwargs['custom_pattern'].replace(" ","") + kwargs['disable_signature'] = '1' + if kwargs.get("l4_protocol") == "icmp" and kwargs.get("l3_protocol") == "ipv6": + kwargs['l4_protocol'] = 'icmpv6' + self.map_field("icmp_type", "icmpv6_type", kwargs) + if kwargs.get('vlan_id') != None: + if kwargs.get('l2_encap') == None: + kwargs['l2_encap'] = 'ethernet_ii_vlan' + if type(kwargs.get('vlan_id')) != list: + x = [kwargs.get('vlan_id')] + else: + x = kwargs.get('vlan_id') + if len(x) > 1: + vlan_list = kwargs.get('vlan_id') + kwargs['vlan_id'] = vlan_list[0] + kwargs['vlan_id_outer'] = vlan_list[1] + + for param in ('enable_time_stamp', 'enable_pgid', 'vlan', 'duration'): + if kwargs.get(param) != None: + kwargs.pop(param) + + for param in ('udp_src_port_mode', 'udp_dst_port_mode', + 'tcp_src_port_mode', 'tcp_dst_port_mode'): + if kwargs.get(param) == 'incr': + kwargs[param] = 'increment' + if kwargs.get(param) == 'decr': + kwargs[param] = 'decrement' + if (kwargs.get('transmit_mode') != None or + kwargs.get('l3_protocol') != None) and \ + kwargs.get('length_mode') == None: + kwargs['length_mode'] = 'fixed' + + if kwargs.get('port_handle2') != None: + kwargs['dest_port_list'] = kwargs.pop('port_handle2') + + if kwargs.get('high_speed_result_analysis') != None and \ + kwargs.get('track_by') != None: + attr = kwargs.get('track_by') + attr = attr.split()[1] + kwargs.pop('track_by') + kwargs.pop(analyzer_filter[attr]) + if kwargs.get('circuit_endpoint_type') != None: + kwargs.pop('circuit_endpoint_type') + + if re.search(r'ip_delay |ip_throughput | ip_reliability |ip_cost |ip_reserved ',' '.join(kwargs.keys())): + delay = kwargs.get('ip_delay',0) + throughput = kwargs.get('ip_throughput',0) + reliability = kwargs.get('ip_reliability',0) + cost = kwargs.get('ip_cost',0) + reserved = kwargs.get('ip_reserved',0) + + bin_val = str(delay) + str(throughput) + str(reliability) + str(cost) + kwargs['ip_tos_field'] = int(bin_val,2) + kwargs['ip_mbz'] = reserved + # ignore step,mode,count for now + for param in ('qos_type_ixn', 'ip_delay', 'ip_delay_mode', 'ip_delay_tracking', + 'ip_throughput', 'ip_throughput_mode', 'ip_throughput_tracking', + 'ip_reliability', 'ip_reliability_mode', 'ip_reliability_tracking', + 'ip_cost', 'ip_cost_mode', 'ip_cost_tracking','ip_reserved'): + + kwargs.pop(param,None) + + if kwargs.get('mac_dst_mode') != None: + if type(kwargs.get('mac_dst')) == list: + kwargs['mac_dst'] = ' '.join(kwargs['mac_dst']) + kwargs.pop('mac_dst_mode', '') + + #disabling high_speed_result_analysis by default, as saw few instances where it is needed and not by disabled by scripts. + if kwargs.get('high_speed_result_analysis') == None: + kwargs['high_speed_result_analysis'] = 0 + + elif fname == 'tg_traffic_stats': + if kwargs.get('mode') == None: + kwargs['mode'] = 'aggregate' + kwargs.pop('csv_path', '') + elif fname == 'tg_traffic_control': + self.map_field("max_wait_timer", None, kwargs) + if kwargs.get('db_file') == None: + kwargs['db_file'] = 0 + if kwargs.get('handle') != None: + kwargs['stream_handle'] = kwargs['handle'] + kwargs.pop('handle') + elif fname == 'tg_interface_config': + self.map_field("ipv4_resolve_gateway", "resolve_gateway_mac", kwargs) + if kwargs.get("resolve_gateway_mac") != None: + kwargs['resolve_gateway_mac'] = 'false' if kwargs['resolve_gateway_mac'] == 0 else 'true' + if "vlan_id_count" in kwargs: + kwargs['count'] = '1' + if "count" in kwargs: + if 'create_host' not in kwargs: + kwargs['create_host'] = 'false' + elif fname == 'tg_emulation_bgp_config': + if kwargs.get('enable_4_byte_as') != None: + l_as = int(kwargs['local_as']) / 65536 + l_nn = int(kwargs['local_as']) - (l_as * 65536) + r_as = int(kwargs['remote_as']) / 65536 + r_nn = int(kwargs['remote_as']) - (r_as * 65536) + kwargs['local_as4'] = str(l_as)+":"+str(l_nn) + kwargs['remote_as4'] = str(r_as)+":"+str(r_nn) + # 23456 has to be set due to spirent limiation. + kwargs['local_as'] = '23456' + kwargs['remote_as'] = '23456' + kwargs.pop('enable_4_byte_as') + elif fname in ['tg_emulation_multicast_group_config', 'tg_emulation_multicast_source_config']: + self.map_field("active", None, kwargs) + if kwargs.get('ip_addr_step') != None: + kwargs['ip_addr_step'] = kwargs['ip_addr_step_val'] if kwargs.get('ip_addr_step_val') else 1 + kwargs.pop('ip_addr_step_val', '') + elif fname == 'tg_emulation_igmp_querier_config': + self.map_field("active", None, kwargs) + elif fname == 'tg_emulation_igmp_group_config': + self.map_field("g_filter_mode", "filter_mode", kwargs) + if kwargs.get('source_pool_handle') != None: + kwargs['device_group_mapping'] = 'MANY_TO_MANY' + kwargs['enable_user_defined_sources'] = '1' + kwargs['specify_sources_as_list'] = '0' + elif fname == 'tg_emulation_igmp_control': + if kwargs.get('mode') == 'start': + kwargs['mode'] = 'join' + if kwargs.get('mode') == 'stop': + kwargs['mode'] = 'leave' + elif fname == 'tg_emulation_ospf_config': + kwargs.pop('validate_received_mtu', '') + kwargs.pop('max_mtu', '') + return kwargs + + def tg_interface_handle(self, ret_ds): + temp = '0' + if "handle_list_pylist" in ret_ds: + temp = ret_ds['handle_list_pylist'] + elif "handles_pylist" in ret_ds: + temp = ret_ds['handles_pylist'] + elif "handles" in ret_ds: + temp = ret_ds['handles'] + if type(temp) == list: + temp = temp[0] if len(temp)==1 else temp + ret_ds['handle'] = temp + return ret_ds + + def tg_withdraw_bgp_routes(self, route_handle): + result = self.tg_emulation_bgp_route_config(route_handle=route_handle, mode='withdraw') + logger.info("withdraw action completed: {}".format(result)) + return result if result['status'] == '1' else None + + def tg_readvertise_bgp_routes(self, handle, route_handle): + result = self.tg_emulation_bgp_config(handle=handle, mode='readvertise') + logger.info("readvertise action completed: {}".format(result)) + return result if result['status'] == '1' else None + + def tg_igmp_querier_control(self, mode, handle): + result = self.tg_emulation_igmp_querier_control(mode=mode, handle=handle) + logger.info("IGMP Querier action completed: {}".format(result)) + return result if result['status'] == '1' else None + + def tg_emulation_ospf_route_config(self, **kwargs): + result = self.tg_emulation_ospf_topology_route_config(**kwargs) + logger.info('OSPF route config completed: {}'.format(result)) + return result if result['status'] == '1' else None + + def tg_ospf_lsa_config(self, **kwargs): + result = self.tg_emulation_ospf_lsa_config(**kwargs) + logger.info('OSPF route config completed: {}'.format(result)) + return result if result['status'] == '1' else None + + def tg_disconnect(self,**kwargs): + if self.skip_traffic: return 0 + logger.info('Executing: {} {}'.format('sth.cleanup_session',kwargs)) + port_handle_list = self.get_port_handle_list() + ret_ds = get_sth().cleanup_session(port_handle=port_handle_list) + logger.info(ret_ds) + if ret_ds['status'] == '1': + logger.debug('TG API Run Status: Success') + else: + logger.warning('TG API Error: %s' % ret_ds['log']) + self.tg_connected = False + self.tg_port_handle.clear() + + def local_stc_tapi_call(self,param): + res = get_sth().invoke(param) + return res + + def _custom_filter_delete(self,port_handle): + + current_analyzer = self.local_stc_tapi_call('stc::get ' + port_handle + ' -children-Analyzer') + filter_list_32 = self.local_stc_tapi_call('stc::get ' + current_analyzer + ' -children-Analyzer32BitFilter').split() + filter_list_16 = self.local_stc_tapi_call('stc::get ' + current_analyzer + ' -children-Analyzer16BitFilter').split() + + for filter in filter_list_32: + self.local_stc_tapi_call('stc::delete ' + filter) + for filter in filter_list_16: + self.local_stc_tapi_call('stc::delete ' + filter) + + self.local_stc_tapi_call('stc::apply') + self.tg_port_analyzer[port_handle] = {} + self.tg_port_analyzer[port_handle]['pattern_list'] = list() + + + def _custom_filter_config(self,**kwargs): + ret_dict = dict() + ret_dict['status'] = '1' + + if not kwargs.get('mode') or not kwargs.get('port_handle'): + logger.info("Missing Mandatory parameter: mode or port_handle") + ret_dict['status'] = '0' + return ret_dict + + port_handle = kwargs['port_handle'] + mode = kwargs['mode'].lower() + + if mode != 'create' and self.tg_port_analyzer[port_handle]['analyzer_handle'] == None: + logger.error("Custom Filter is not configured for port: {}".format(port_handle)) + ret_dict['status'] = '0' + return ret_dict + + project_handle = 'Project1' + #This is the default name/handle. Things might not work if it is different. + # Need a way to find this. + res = self.local_stc_tapi_call('stc::get ' + project_handle) + if not re.search(r'-Name\s+\{Project 1\}',res): + logger.error('Could not find project handle') + ret_dict['status'] = '0' + return ret_dict + + if mode == 'create': + if not kwargs.get('offset_list') or not kwargs.get('pattern_list'): + logger.info('Both offset_list and pattern_list are must when mode=create') + ret_dict['status'] = '0' + return ret_dict + + if len(kwargs['offset_list']) != len(kwargs['pattern_list']): + logger.info('offset_list and pattern_list must be of same length') + ret_dict['status'] = '0' + return ret_dict + + #Delete existing filters, if any + self._custom_filter_delete(port_handle) + self.tg_port_analyzer[port_handle]['pattern_list'].append(kwargs['pattern_list']) + + #subscribe for the result + self.tg_port_analyzer[port_handle]['stats_handle'] = self.local_stc_tapi_call('stc::subscribe -Parent ' + project_handle + ' -ResultParent ' + port_handle + ' -ConfigType Analyzer -resulttype FilteredStreamResults ') + self.local_stc_tapi_call('stc::apply') + + current_analyzer = self.local_stc_tapi_call('stc::get ' + port_handle + ' -children-Analyzer') + self.tg_port_analyzer[port_handle]['analyzer_handle'] = current_analyzer + + f_index = 1 + for offset in kwargs['offset_list']: + filter_name = 'CustomFilter'+str(f_index) + custom_filter = self.local_stc_tapi_call('stc::create Analyzer16BitFilter -under ' + current_analyzer) + self.local_stc_tapi_call('stc::config ' + custom_filter + ' -FilterName ' + filter_name + ' -Offset ' + str(offset)) + f_index += 1 + + self.local_stc_tapi_call('stc::apply') + + else: + current_analyzer = self.tg_port_analyzer[port_handle]['analyzer_handle'] + if mode in ['start','stop']: + self.local_stc_tapi_call('stc::perform Analyzer' + mode.capitalize() + ' -AnalyzerList ' + current_analyzer) + self.local_stc_tapi_call('stc::apply') + tgen_wait(2) + if mode in ['delete']: + self._custom_filter_delete(port_handle) + + if mode in ['getstats']: + filter_list_16 = self.local_stc_tapi_call('stc::get ' + current_analyzer + ' -children-Analyzer16BitFilter').split() + if len(filter_list_16) == 0: + logger.error('No filters are configured on this port') + ret_dict['status'] = '0' + return ret_dict + if self.tg_port_analyzer[port_handle]['pattern_list'] != None: + pattern_list = self.tg_port_analyzer[port_handle]['pattern_list'] + exp_filter_pattern_list = list() + for p_list in pattern_list: + if not isinstance(p_list,list): + logger.info('Each pattern must be a list') + ret_dict['status'] = '0' + return ret_dict + if len(p_list) != len(filter_list_16): + logger.info('pattern_list and offset_list must be of equal length') + ret_dict['status'] = '0' + return ret_dict + + logger.debug('Pattern: {}'.format(p_list)) + exp_filter_pattern_list.append(':'.join(p_list).lower()) + else: + logger.info('pattern_list is mandatory parameter') + ret_dict['status'] = '0' + return ret_dict + + self.local_stc_tapi_call('stc::perform RefreshResultView -ResultDataSet ' + self.tg_port_analyzer[port_handle]['stats_handle']) + tgen_wait(3) + + rx_result_handle_list = self.local_stc_tapi_call('stc::get ' + self.tg_port_analyzer[port_handle]['stats_handle'] + ' -resulthandlelist').split() + logger.debug('rx_result_handle_list: {}'.format(rx_result_handle_list)) + if len(rx_result_handle_list) == 0: + logger.error('Filtered result is not received, check the configurations') + ret_dict['status'] = '0' + return ret_dict + + + ret_dict[port_handle] = {} + ret_dict[port_handle].update({'custom_filter': {}}) + total_rx_count = 0 + ret_dict[port_handle]['custom_filter'].update({'filtered_frame_count': 0}) + for rxresult in rx_result_handle_list: + logger.debug('rxresult row: {}'.format(rxresult)) + rx_result_hash = self.local_stc_tapi_call('stc::get ' + rxresult) + logger.debug('RX Result: {}'.format(rx_result_hash)) + + # Using stc::get we can get info for each key in rx_result_hash, examples below. + # We are interested in counts and rate only for now. + #hanalyzerPort = get_sth().invoke('stc::get ' + rx_result_hash + " -parent" ) + #PStreamName = get_sth().invoke('stc::get ' + hanalyzerPort + " -name") + #StreamID = get_sth().invoke('stc::get ' + rxresult + " -Comp32") + + found_filter_pattern = '' + for i in range(1,len(filter_list_16)+1): + filter_pattern = self.local_stc_tapi_call('stc::get ' + rxresult + ' -FilteredValue_' + str(i)) + if found_filter_pattern == '': + found_filter_pattern = ''.join(filter_pattern.split()) + else: + found_filter_pattern = found_filter_pattern + ':' + ''.join(filter_pattern.split()) + logger.info('exp_filter_pattern_list: {} found_filter_pattern: {}'.format(exp_filter_pattern_list,found_filter_pattern)) + if found_filter_pattern.lower() in exp_filter_pattern_list: + rx_frame_count = self.local_stc_tapi_call('stc::get ' + rxresult + ' -FrameCount') + rx_frame_rate = self.local_stc_tapi_call('stc::get ' + rxresult + ' -FrameRate') + logger.info('rx_frame_count: {} rx_frame_rate: {}'.format(rx_frame_count,rx_frame_rate)) + ret_dict[port_handle]['custom_filter']['filtered_frame_count'] = int(ret_dict[port_handle]['custom_filter']['filtered_frame_count']) + int(rx_frame_count) + total_rx_count += int(rx_frame_count) + else: + logger.info('Ignoring filter_pattern: {}'.format(found_filter_pattern.lower())) + total_rx_count += int(self.local_stc_tapi_call('stc::get ' + rxresult + ' -FrameCount')) + + ret_dict[port_handle]['custom_filter'].update({'total_rx_count': total_rx_count}) + + return ret_dict + + + def tg_custom_filter_config(self,**kwargs): + ret_dict = dict() + ret_dict['status'] = '1' + stc_kwargs = dict() + + if not kwargs.get('mode') or not kwargs.get('port_handle'): + logger.info("Missing Mandatory parameter: port_handle or mode") + ret_dict['status'] = '0' + return ret_dict + + mode = kwargs.get('mode').lower() + port_handle = kwargs.get('port_handle') + stc_kwargs['port_handle'] = port_handle + + if mode != 'getstats': + offset_count = 0 + offset_list = list() + pattern_list = list() + for offset,pattern in zip(['pattern_offset1'],['pattern1']): + if not kwargs.get(offset) or not kwargs.get(pattern) or len(kwargs[pattern]) != 4: + logger.info('Missing Mandatory parameter {} or {} and pattern length must be 16 bits'.format(offset,pattern)) + ret_dict['status'] = '0' + return ret_dict + offset_count += 1 + offset_list.append(kwargs[offset]) + pattern_list.append(kwargs[pattern]) + + for offset,pattern in zip(['pattern_offset2'],['pattern2']): + if kwargs.get(offset) and kwargs.get(pattern) and len(kwargs[pattern]) == 4: + offset_count += 1 + offset_list.append(kwargs[offset]) + pattern_list.append(kwargs[pattern]) + elif not kwargs.get(offset) and not kwargs.get(pattern): + pass + else: + logger.info('Both parameter {} and {} need to be provided and pattern length must be 16 bits'.format(offset,pattern)) + ret_dict['status'] = '0' + return ret_dict + if mode == 'create': + self._custom_filter_config(mode='create',port_handle=port_handle,offset_list=offset_list,pattern_list=pattern_list) + self._custom_filter_config(mode='start',port_handle=port_handle) + if mode == 'getstats': + ret_dict[port_handle] = {} + ret_dict[port_handle].update({'custom_filter': {}}) + tgen_wait(3) + result = self._custom_filter_config(mode='getstats',port_handle=port_handle) + filtered_frame_count = result[port_handle]['custom_filter']['filtered_frame_count'] + total_rx_count = result[port_handle]['custom_filter']['total_rx_count'] + ret_dict[port_handle]['custom_filter'].update({'filtered_frame_count': filtered_frame_count}) + ret_dict[port_handle]['custom_filter'].update({'total_rx_count': total_rx_count}) + + return ret_dict + +class TGIxia(TGBase): + def __init__(self, tg_type, tg_version, tg_ip=None, tg_port_list=None, ix_server=None, ix_port=8009): + self.ix_server = ix_server + self.ix_port = str(ix_port) + self.topo_handle = {} + self.traffic_config_handles = {} + TGBase.__init__(self, tg_type, tg_version, tg_ip, tg_port_list) + logger.info('TG Ixia Init...done') + + def clean_all(self): + self.traffic_config_handles.clear() + ph_list = self.get_port_handle_list() + + traffic_items = get_ixiangpf().session_info(mode='get_traffic_items') + if traffic_items.get('traffic_config') != None: + items = traffic_items['traffic_config'].split() + logger.info("TG CLEAN ALL: stop and remove all streams on {}". format(items)) + ret_ds = self.tg_traffic_control(action="reset", stream_handle=items) + logger.debug(ret_ds) + tgen_wait(2) + else: + logger.info("TG CLEAN ALL: No traffic items configured to reset") + + if os.getenv('TGEN_REMOVE_CACHED_INTERFACE_HANDLE'): + for port_handle, handle_list in self.cached_interface_config_handles.items(): + for handle in handle_list: + logger.info("removing interface handle {} on {}".format(handle, port_handle)) + self.tg_interface_config(port_handle=port_handle, handle=handle, mode='destroy') + self.cached_interface_config_handles = dict() + else: + topo_handles = [] + for ph in ph_list: + topo_handle=self.topo_handle[ph] + if topo_handle: + topo_handles.append(topo_handle) + self.topo_handle[ph] = None + + if topo_handles: + logger.info("remove all topology handles") + ret_ds = self.tg_test_control(action='stop_all_protocols') + logger.info(ret_ds) + tgen_wait(10) + for topo_handle in topo_handles: + logger.info("removing cached {}".format(topo_handle)) + ret_ds=self.tg_topology_config(topology_handle=topo_handle, mode='destroy') + logger.info(ret_ds) + tgen_wait(2) + + logger.debug("TG CLEAN ALL FINISHED") + + def get_port_status(self, port_list): + retval = {} + for port in port_list: + ret_ds = get_ixiangpf().test_control(action='check_link_state', port_handle=self.get_port_handle(port)) + retval[port] = bool("log" not in ret_ds) + return retval + + def get_ixnetwork_status(self,**kwargs): + ix_server = kwargs.get('ix_server',None) + ix_rest_port = kwargs.get('ix_port','8006') + retries = int(kwargs.get('retries','1')) + ret_dict = dict() + ret_dict['status'] = '0' + ret_dict['total_session'] = 0 + ret_dict['session_in_use'] = 0 + ret_dict['user_list'] = list() + ret_dict['user_id'] = list() + while ret_dict['status'] == '0' and retries > 0: + try: + rest_cmd = 'http://' + ix_server.split(':')[0] + ':' + ix_rest_port + '/api/v1/sessions' + response = requests.get(rest_cmd,verify=False, allow_redirects=True, timeout=30) + if response.status_code == 200: + ret_dict['status'] = '1' + resp_dict = json.loads(response.content) + for s_dict in resp_dict: + logger.debug('Connection Manager, session info: {}'.format(s_dict)) + ret_dict['total_session'] += 1 + if s_dict['state'].lower() == 'active' and re.match(r'IN\s+USE',s_dict['subState']): + ret_dict['session_in_use'] += 1 + m = re.search(r'automation\s+client\s+(.+?)\s',s_dict['subState']) + ret_dict['user_list'].append(str(m.group(1))) + ret_dict['user_id'].append(str(s_dict['userId'])) + + elif response.status_code != 200: + logger.debug('Response from Connection Manager: {}'.format(response)) + retries -= 1 + + except requests.ConnectionError as conn_error: + retries -= 1 + logger.info('Either Connection Manager is not running or REST port 8006 is not enabled') + logger.info('Error: {}'.format(conn_error)) + + except Exception as conn_error: + retries -= 1 + logger.info('Unknown Exception: {}'.format(conn_error)) + + return ret_dict + + def show_status(self): + if self.ix_port == "443" or os.getenv("SPYTEST_IXIA_CONNMGR_STATUS", "1") == "0": + return None + ret_ds = self.get_ixnetwork_status(ix_server=self.ix_server) + logger.info('Connection Manager Info: {}'.format(ret_ds)) + return ret_ds + + def connect(self): + self.tg_ns = 'ixiangpf' + ret_ds = self.show_status() + if ret_ds and ret_ds['status'] == '1' and ret_ds['session_in_use'] > 1: + logger.error('Max recommended connection is reached, should abort the run') + + params = SpyTestDict(device=self.tg_ip, port_list=self.tg_port_list, + ixnetwork_tcl_server=self.ix_server, break_locks=1, reset=1) + if self.ix_port == "443": + # ixnetwork linux VM + params.user_name = "admin" + params.user_password = "admin" + ret_ds = get_ixiangpf().connect(**params) + + logger.info(ret_ds) + if ret_ds['status'] != '1': + return ret_ds + ports_100g=[] + res=get_ixiangpf().traffic_stats() + for port in self.tg_port_list: + # ixia output is different for key 'port_handle': {'10': {'59': {'130': {'4': {'1/5': '1/1/5'}}}}} + key1, key2, key3, key4 = self.tg_ip.split('.') + self.tg_port_handle[port] = \ + ret_ds['port_handle'][key1][key2][key3][key4][port] + # For topology_handle. + self.topo_handle[self.tg_port_handle[port]]=None + # To get 100G ports. + if res[self.tg_port_handle[port]]['aggregate']['tx']['line_speed'] == '100GE': + ports_100g.append(self.tg_port_handle[port]) + if ports_100g != []: + logger.info('Disabling FEC for 100G ports: {}'.format(ports_100g)) + res=get_ixiangpf().interface_config(port_handle=ports_100g, mode="modify", autonegotiation=0, ieee_media_defaults=0, enable_rs_fec=0) + logger.info(res) + # Initial setting for ARP/ND. + h1=get_ixiangpf().topology_config(port_handle=self.tg_port_handle[self.tg_port_list[0]], mode='config') + logger.info(h1) + res=get_ixiangpf().interface_config(protocol_handle='/globals', single_arp_per_gateway=0, single_ns_per_gateway=0) + logger.info(res) + res=get_ixiangpf().topology_config(topology_handle=h1['topology_handle'], mode='destroy') + logger.info(res) + return None + + def trgen_adjust_mismatch_params(self, fname, **kwargs): + if fname == 'tg_traffic_config': + self.map_field("ether_type", "ethernet_value", kwargs) + self.map_field("custom_pattern", "data_pattern", kwargs) + self.map_field("icmpv6_oflag", "icmp_ndp_nam_o_flag", kwargs) + self.map_field("icmpv6_rflag", "icmp_ndp_nam_r_flag", kwargs) + self.map_field("icmpv6_sflag", "icmp_ndp_nam_s_flag", kwargs) + self.map_field("vlan_tpid", "vlan_protocol_tag_id", kwargs) + + if kwargs.get('vlan_protocol_tag_id') != None: + eth_type = kwargs.pop('ethernet_value', None) + kwargs['ethernet_value'] = hex(int(kwargs.pop('vlan_protocol_tag_id'))).lstrip('0x') + if eth_type != None: + kwargs['vlan_protocol_tag_id'] = eth_type + + if kwargs.get('vlan_id_outer') != None: + # If vlan-id_outer is present then vlan_id will also be there + outer_vlan_id = kwargs['vlan_id_outer'] + vlan_id = kwargs['vlan_id'] + kwargs['vlan_id'] = [vlan_id, outer_vlan_id] + kwargs.pop('vlan_id_outer') + + if kwargs.get('vlan_id') != None and kwargs.get('vlan') == None: + kwargs['vlan'] = 'enable' + + # for stream level stats, circuit_type and track_by arguments required + if kwargs.get('port_handle2') != None: + if kwargs.get('track_by') == None: + kwargs['track_by'] = 'trackingenabled0' + if kwargs.get('circuit_type') == None: + kwargs['circuit_type'] = 'raw' + if kwargs.get('emulation_src_handle') != None and kwargs.get('emulation_dst_handle') != None: + kwargs['circuit_type'] = 'none' + kwargs.pop('port_handle2') + + for param in ('mac_discovery_gw', 'vlan_priority_mode', 'high_speed_result_analysis', + 'enable_stream_only_gen', 'enable_stream', 'ipv6_dstprefix_len', 'ipv6_srcprefix_len'): + if kwargs.get(param) != None: + kwargs.pop(param) + + for param in ('udp_src_port_mode', 'udp_dst_port_mode', + 'tcp_src_port_mode', 'tcp_dst_port_mode'): + if kwargs.get(param) == 'increment': + kwargs[param] = 'incr' + if kwargs.get(param) == 'decrement': + kwargs[param] = 'decr' + + if kwargs.get('ip_tos_field') != None: + bin_tos_val = bin(kwargs['ip_tos_field'])[2:].zfill(4) + + kwargs['qos_type_ixn'] = 'tos' + kwargs['ip_precedence'] = kwargs.get('ip_precedence',0) + # configuring ip_precedence is mandatory if use qos_type_ixn=tos + kwargs['ip_delay'] = bin_tos_val[0] + kwargs['ip_throughput'] = bin_tos_val[1] + kwargs['ip_reliability'] = bin_tos_val[2] + kwargs['ip_cost'] = bin_tos_val[3] + kwargs['ip_reserved'] = kwargs.get('ip_mbz',0) + + kwargs.pop('ip_tos_field') + kwargs.pop('ip_mbz',None) + + if kwargs.get('emulation_dst_handle') != None: + foundIgmp = 1 + if isinstance(kwargs['emulation_dst_handle'], list): + for ele in kwargs['emulation_dst_handle']: + if not re.search(r'.*igmpMcastIPv4GroupList.*', ele): + foundIgmp = 0 + break + else: + if not re.search(r'.*igmpMcastIPv4GroupList.*', kwargs['emulation_dst_handle']): + foundIgmp = 0 + if foundIgmp == 1: + kwargs['emulation_multicast_dst_handle'] = 'all_multicast_ranges' + kwargs['emulation_multicast_dst_handle_type'] = [['0']] + kwargs['emulation_multicast_rcvr_handle'] = [[kwargs['emulation_dst_handle']]] + kwargs['emulation_multicast_rcvr_port_index'] = [['0']] + kwargs['emulation_multicast_rcvr_host_index'] = [['0']] + kwargs['emulation_multicast_rcvr_mcast_index'] = [['0']] + kwargs['emulation_dst_handle'] = [['0']] + + if fname == 'tg_traffic_control': + if kwargs.get('stream_handle') != None: + kwargs['handle'] = kwargs['stream_handle'] + kwargs.pop('stream_handle') + for param in ('get', 'enable_arp'): + if kwargs.get(param) != None: + kwargs.pop(param) + if kwargs.get('action') in ['run', 'stop'] and kwargs.get('port_handle') == None: + #kwargs['max_wait_timer'] = 120 + #temp change to roll back the HF from ixia + if os.getenv("SPYTEST_ENSURE_TRAFFIC_CONTROL", "0") == "0": + kwargs['max_wait_timer'] = 30 + else: + kwargs['max_wait_timer'] = 180 + + if fname == 'tg_interface_config': + self.map_field("resolve_gateway_mac", "ipv4_resolve_gateway", kwargs) + self.map_field("control_plane_mtu", "mtu", kwargs) + self.map_field("flow_control", "enable_flow_control", kwargs) + if kwargs.get('mode') == 'config': + topo_han=self.topo_handle[kwargs.get('port_handle')] + if topo_han == None: + res=self.tg_topology_config(port_handle=kwargs.get('port_handle')) + logger.info(res) + topo_han = res['topology_handle'] + self.topo_handle[kwargs.get('port_handle')] = topo_han + logger.info(self.topo_handle) + tgen_wait(10) + mul=kwargs.get('count','1') + if 'vlan_id_count' in kwargs: + mul=kwargs.get('vlan_id_count','1') + res=self.tg_topology_config(topology_handle=topo_han, device_group_multiplier=mul) + logger.info(res) + tgen_wait(10) + kwargs['protocol_handle'] = res['device_group_handle'] + kwargs.pop('port_handle') + if kwargs.get('enable_flow_control') != None: + kwargs['enable_flow_control'] = 1 if kwargs['enable_flow_control'] == 'true' else 0 + for param in ('count', 'block_mode', 'enable_ping_response'): + if kwargs.get(param) != None: + kwargs.pop(param) + + if fname == 'tg_packet_control': + if kwargs['action'] == 'start': + self.tg_traffic_control(action='apply') + # suggested by Ixia for more accurate results + logger.info('Enabling control and data plane options') + self.tg_packet_config_buffers(port_handle=kwargs['port_handle'], + control_plane_capture_enable='1', + data_plane_capture_enable='1') + + if fname == 'tg_traffic_stats': + if kwargs.get('csv_path') == None: + kwargs['csv_path'] = tgen_get_logs_path_folder() + + if fname == 'tg_emulation_bgp_route_config': + if 'ipv6_prefix_length' in kwargs: + kwargs['prefix_from'] = kwargs['ipv6_prefix_length'] + kwargs.pop('ipv6_prefix_length') + logger.info('Disabling protocol before adding the route') + self.tg_topology_test_control(handle=kwargs['handle'], stack='ethernet', action='stop_all_protocols', + tg_wait=10) + topo = re.search(r'.*topology:(\d)+', kwargs['handle']).group(0) + logger.debug('Topology: {}'.format(topo)) + tg_port = self.topo_handle.keys()[self.topo_handle.values().index(topo)] + logger.debug('port_handle: {}'.format(tg_port)) + for i in range(1,30): + res = self.tg_protocol_info(mode='global_per_port') + total=res['global_per_port'][tg_port]['sessions_total'] + total_ns=res['global_per_port'][tg_port]['sessions_not_started'] + logger.debug(total) + logger.debug(total_ns) + if total == total_ns: + break + tgen_wait(2) + tgen_wait(10) + + if fname == 'tg_emulation_bgp_control': + logger.info('Applying changes for IXIA before starting BGP') + self.tg_topology_test_control(action='apply_on_the_fly_changes', tg_wait=10) + + if fname == 'tg_emulation_bgp_config': + if kwargs.get('local_as') != None and kwargs.get('remote_as') != None: + if int(kwargs['local_as']) != int(kwargs['remote_as']): + kwargs['neighbor_type'] = 'external' + else: + kwargs['neighbor_type'] = 'internal' + kwargs.pop('remote_as') + + if fname == 'tg_emulation_igmp_config': + kwargs['handle'] = kwargs['handle'][0] if type(kwargs['handle']) is list else kwargs['handle'] + self.tg_topology_test_control(handle=kwargs['handle'], stack='deviceGroup', action='stop_protocol') + if kwargs.get('mode') == 'create': + kwargs['handle'] = re.search(r'.*ipv4:(\d)+', kwargs['handle']).group(0) + + if fname == 'tg_emulation_igmp_control': + if kwargs.get('mode') in ['join', 'leave'] : + kwargs['group_member_handle'] = kwargs['handle'] + kwargs.pop('handle', None) + + if fname == 'tg_emulation_multicast_group_config': + if kwargs.get('active') == None: + kwargs['active'] = '1' + kwargs.pop('ip_addr_step_val', '') + + if fname == 'tg_emulation_multicast_source_config': + if kwargs.get('active') == None: + kwargs['active'] = '1' + kwargs.pop('ip_addr_step_val', '') + + if fname == 'tg_emulation_igmp_group_config': + if kwargs.get('source_pool_handle') == None and kwargs.get('mode') == 'create': + res = self.tg_emulation_multicast_source_config(mode='create', ip_addr_start='21.1.1.100', + num_sources=1, active=0) + kwargs['source_pool_handle'] = res['multicast_source_handle'] + if kwargs.get('mode') == 'clear_all': + self.map_field("handle", "session_handle", kwargs) + + if fname == 'tg_emulation_igmp_querier_config': + if kwargs.get('active') == None and kwargs.get('mode') == 'create': + kwargs['active'] = '1' + if kwargs.get('mode') == 'create': + kwargs['handle'] = re.search(r'.*ipv4:(\d)+', kwargs['handle']).group(0) + self.tg_topology_test_control(handle=kwargs['handle'], stack='topology', action='stop_protocol') + + if fname =='tg_emulation_ospf_config': + kwargs['handle'] = kwargs['handle'][0] if isinstance(kwargs['handle'], list) else kwargs['handle'] + self.tg_topology_test_control(handle=kwargs['handle'], stack='ethernet', action='stop_protocol') + if kwargs.get('mode') == 'create': + kwargs['handle'] = re.search(r'.*ipv(4|6):(\d)+', kwargs['handle']).group(0) + kwargs['area_id_type'] = 'ip' + kwargs.pop('gateway_ip_addr', '') + if fname == 'tg_emulation_dhcp_group_config': + self.map_field("ipv4_gateway_address", "dhcp4_gateway_address", kwargs) + self.map_field("gateway_ipv6_addr", "dhcp6_gateway_address", kwargs) + if str(kwargs.get("dhcp_range_ip_type")) == '4': + kwargs['dhcp_range_ip_type'] = 'ipv4' + else: + kwargs['dhcp_range_ip_type'] = 'ipv6' + if fname == 'tg_emulation_dhcp_server_config': + self.map_field("gateway_ipv6_addr", "ipv6_gateway", kwargs) + self.map_field("remote_mac", "manual_gateway_mac", kwargs) + self.map_field("encapsulation", "", kwargs) + return kwargs + + def tg_interface_handle(self, ret_ds): + if "interface_handle" in ret_ds: + if "ipv4_handle" in ret_ds or "ipv6_handle" in ret_ds: + temp = ret_ds['interface_handle'].split() + # Removing extra ethernet handles. + temp = temp[:len(temp)/2] + ret_ds['handle'] = temp[0] if len(temp)==1 else temp + else: + ret_ds['handle'] = ret_ds['interface_handle'] + return ret_ds + + def tg_igmp_querier_control(self, mode, handle): + result = self.tg_emulation_igmp_control(mode=mode, handle=handle) + tgen_wait(10) + logger.info("IGMP Querier action completed: {}".format(result)) + return result if result['status'] == '1' else None + + def tg_withdraw_bgp_routes(self, route_handle): + result = self.tg_emulation_bgp_control(handle=route_handle, mode='stop') + logger.info('withdraw action completed: {}'.format(result)) + return result if result['status'] == '1' else None + + def tg_readvertise_bgp_routes(self, handle, route_handle): + result = self.tg_emulation_bgp_control(handle=route_handle, mode='start') + logger.info('readvertise action completed: {}'.format(result)) + return result if result['status'] == '1' else None + + def tg_ospf_lsa_config(self, **kwargs): + prefix_type = {'summary_pool': 'summary', 'ext_pool': 'external', 'nssa_ext_pool': 'nssa'} + for type, prefix in prefix_type.items(): + if kwargs['type'] == type: + prefix_ret = prefix + if type == 'ext_pool': + prefix_ret = 'external1' if str(kwargs['external_prefix_type']) == '1' else 'external2' + self.map_field('{}_prefix_start'.format(prefix), '{}_network_address'.format(prefix_ret), kwargs) + self.map_field('{}_number_of_prefix'.format(prefix), '{}_number_of_routes'.format(prefix_ret), kwargs) + self.map_field('{}_prefix_length'.format(prefix), '{}_prefix'.format(prefix_ret), kwargs) + self.map_field('{}_prefix_metric'.format(prefix), '{}_metric'.format(prefix_ret), kwargs) + kwargs.pop('{}_prefix_type'.format(prefix), '') + kwargs['{}_active'.format(prefix_ret)] = '1' + kwargs['type'] = 'linear' + kwargs['linear_nodes'] = '1' + kwargs['from_ip'] = '1.0.0.1' + kwargs['to_ip'] = '1.0.0.2' + result = self.tg_emulation_ospf_network_group_config(**kwargs) + logger.info('OSPF route config completed: {}'.format(result)) + return result if result['status'] == '1' else None + + def tg_emulation_ospf_route_config(self, **kwargs): + if kwargs.get('mode') == 'delete': + kwargs['handle'] = re.search(r'.*networkGroup:(\d)+', kwargs['handle']).group(0) + elif kwargs.get('mode') == 'create': + ret = re.search(r'.*ospfv(\d):*', kwargs['handle']).group(1) + ver = '4' if str(ret) == '2' else '6' + if kwargs['type'] == 'summary_routes': + kwargs['ipv{}_prefix_route_origin'.format(ver)] = 'another_area' + if kwargs['type'] == 'ext_routes': + route_origin = 'external_type_1' if str(kwargs['external_prefix_type']) == '1' else 'external_type_2' + kwargs['ipv{}_prefix_route_origin'.format(ver)] = route_origin + kwargs.pop('external_prefix_type', '') + if kwargs['type'] == 'network': + # TODO: Code need to implenented for network type route prefixes + pass + if kwargs['type'] == 'nssa_routes': + kwargs['ipv{}_prefix_route_origin'.format(ver)] = 'nssa' + kwargs.pop('nssa_prefix_type', '') + + prefix_type = {'summary_routes': 'summary', 'ext_routes': 'external', 'nssa_routes': 'nssa', + 'network': 'net', 'router': 'router'} + for type, prefix in prefix_type.items(): + if kwargs['type'] == type and kwargs['type'] not in ['network', 'router']: + self.map_field('{}_prefix_start'.format(prefix), 'ipv{}_prefix_network_address'.format(ver), kwargs) + self.map_field('{}_number_of_prefix'.format(prefix), 'ipv{}_prefix_number_of_addresses'.format(ver), + kwargs) + self.map_field('{}_prefix_length'.format(prefix), 'ipv{}_prefix_length'.format(ver), kwargs) + self.map_field('{}_prefix_metric'.format(prefix), 'ipv{}_prefix_metric'.format(ver), kwargs) + kwargs['type'] = 'ipv4-prefix' if ver == '4' else 'ipv6-prefix' + elif kwargs['type'] == 'router': + # TODO: Code need to implenented for router prefixes + kwargs['type'] = 'linear' + kwargs['linear_nodes'] = '100' # Prefix count + kwargs['from_ip'] = '1.0.0.1' + kwargs['to_ip'] = '1.0.0.2' + else: + # TODO: Code need to implenented for network prefixes + pass + result = self.tg_emulation_ospf_network_group_config(**kwargs) + self.tg_topology_test_control(action='apply_on_the_fly_changes') + logger.info('OSPF route config completed: {}'.format(result)) + return result if result['status'] == '1' else None + + def trgen_post_proc(self, fname, **kwargs): + if fname == 'tg_emulation_bgp_route_config': + logger.info('Enabling protocol after adding the route') + self.tg_topology_test_control(handle=kwargs['handle'], stack='ipv(4|6)', action='start_all_protocols') + + if fname == 'tg_emulation_igmp_group_config': + if kwargs.get('mode') == 'create': + logger.info('Enabling protocol after adding the igmp host') + kwargs['handle'] = kwargs['session_handle'][0] if type(kwargs['session_handle']) is list else kwargs['session_handle'] + self.tg_topology_test_control(handle=kwargs['handle'], stack='deviceGroup', action='start_protocol') + logger.info('Disabling IGMP Host after starting the devicegroup ...') + res = self.tg_emulation_igmp_control(handle=kwargs['session_handle'], mode='stop') + logger.debug('{}'.format(res)) + tgen_wait(5) + + if fname == 'tg_emulation_igmp_querier_config': + if kwargs.get('mode') == 'create': + logger.info('Enabling protocol after adding the igmp host') + self.tg_topology_test_control(handle=kwargs['handle'], stack='topology', action='start_protocol') + + if fname == 'tg_emulation_ospf_config': + kwargs['handle'] = kwargs['handle'][0] if isinstance(kwargs['handle'], list) else kwargs['handle'] + self.tg_topology_test_control(handle=kwargs['handle'], stack='ipv(4|6)', action='start_protocol') + + def tg_arp_control(self, **kwargs): + if 'handle' in kwargs: + han = kwargs['handle'] + if type(han) is list: + han = re.search(r'.*ipv(4|6):(\d)+',han[0]).group(0) + result = self.tg_interface_config(protocol_handle=han, arp_send_req='1') + elif 'port_handle' in kwargs: + result = self.tg_interface_config(port_handle=kwargs['port_handle'], arp_send_req='1') + logger.info ('Sending ARP completed: {}'.format(result)) + return result + + def tg_disconnect(self,**kwargs): + if self.skip_traffic: return 0 + logger.info('Executing: {} {}'.format('ixiangpf.cleanup_session',kwargs)) + port_handle_list = self.get_port_handle_list() + ret_ds = get_ixiangpf().cleanup_session( + maintain_lock=0, port_handle=port_handle_list, reset=1) + logger.info(ret_ds) + if ret_ds['status'] == '1': + logger.debug('TG API Run Status: Success') + else: + logger.warning('TG API Error: %s' % ret_ds['log']) + self.tg_connected = False + self.tg_port_handle.clear() + + def local_ixnet_call(self,method,*args): + #::ixNet::OK' + #IxNetError: + func_call = 'ixiangpf.ixnet.'+method + res = eval(func_call)(*args) + if re.search(r'Error',res): + logger.info('Error in ixNet call {}: {}'.format(func_call,res)) + return res + + def local_get_captured_packets(self,**kwargs): + packet_type = kwargs.get('packet_type','data') + if packet_type.lower() == 'control': + packet_type = 'control' + + ret_dict = dict() + captured_packets = dict() + #Add code to check if any packets are captured and return 0 if none + ret_dict['status'] = '1' + + #get vport info + res = self.tg_convert_porthandle_to_vport(port_handle=kwargs['port_handle']) + vport_handle = res['handle'] + cap_pkt_count = self.local_ixnet_call('getAttribute',vport_handle+'/capture','-'+packet_type+'PacketCounter') + pkts_in_buffer = int(cap_pkt_count) if int(cap_pkt_count) <= 20 else 20 + captured_packets.update({'aggregate': {'num_frames': pkts_in_buffer}}) + captured_packets['frame'] = dict() + for pkt_count in range(0,pkts_in_buffer): + sub_method = 'getPacketFrom'+packet_type.title()+'Capture' + self.local_ixnet_call('execute', sub_method, vport_handle+'/capture/currentPacket', pkt_count) + hp = self.local_ixnet_call('getAttribute',vport_handle+'/capture/currentPacket', '-packetHex') + frame_in_hex = hp.encode('ascii','ignore').split()[2:] + frame_in_hex_upper = [byte.upper() for byte in frame_in_hex] + captured_packets['frame'].update({str(pkt_count): {'frame_pylist': frame_in_hex_upper}}) + + ret_dict[kwargs['port_handle']] = captured_packets + return ret_dict + + def tg_custom_filter_config(self,**kwargs): + ret_dict = dict() + ret_dict['status'] = '1' + ixia_kwargs = dict() + + if not kwargs.get('mode') or not kwargs.get('port_handle'): + logger.info("Missing Mandatory parameter: port_handle or mode") + ret_dict['status'] = '0' + return ret_dict + + mode = kwargs.get('mode').lower() + port_handle = kwargs.get('port_handle') + ixia_kwargs['port_handle'] = port_handle + offset_count = 0 + if mode != 'getstats': + for offset,pattern in zip(['pattern_offset1'],['pattern1']): + if not kwargs.get(offset) or not kwargs.get(pattern): + logger.info('Missing Mandatory parameter {} or {}'.format(offset,pattern)) + ret_dict['status'] = '0' + return ret_dict + offset_count += 1 + ixia_kwargs['pattern_offset1'] = kwargs['pattern_offset1'] + ixia_kwargs['pattern1'] = kwargs['pattern1'] + capture_filter_pattern = 'pattern1' + + for offset,pattern in zip(['pattern_offset2'],['pattern2']): + if kwargs.get(offset) and kwargs.get(pattern): + offset_count += 1 + ixia_kwargs['pattern_offset2'] = kwargs['pattern_offset2'] + ixia_kwargs['pattern2'] = kwargs['pattern2'] + capture_filter_pattern = 'pattern1and2' + + elif not kwargs.get(offset) and not kwargs.get(pattern): + pass + else: + logger.info('Both parameter {} and {} need to be provided'.format(offset,pattern)) + ret_dict['status'] = '0' + return ret_dict + + #capture_filter_pattern = kwargs.get('capture_filter_pattern','pattern1and2') + if mode == 'create': + self.tg_packet_config_buffers(port_handle=port_handle,capture_mode = 'trigger', + before_trigger_filter = 'all', after_trigger_filter = 'filter') + self.tg_packet_config_filter(**ixia_kwargs) + self.tg_packet_config_triggers(port_handle=port_handle,capture_trigger=1, + capture_filter=1,capture_filter_pattern=capture_filter_pattern) + self.tg_packet_control(port_handle=port_handle,action='start') + + if mode == 'getstats': + self.tg_packet_control(port_handle=port_handle,action='stop') + ret_dict[port_handle] = {} + ret_dict[port_handle].update({'custom_filter': {}}) + filtered_frame_count = 0 + total_rx_count = 0 + tgen_wait(5) + result = self.tg_packet_stats(port_handle=port_handle) + if result['status'] != '1': + for i in range(1,5): + logger.info('Get Filtered Stats Failed, Trying again, after 5 sec...Try: {}'.format(i)) + tgen_wait(5) + result = self.tg_packet_stats(port_handle=port_handle) + if result['status'] == '1': + break + + logger.debug(result) + if result['status'] == '1': + filtered_frame_count = result[port_handle]['aggregate']['num_frames'] + total_rx_count = result[port_handle]['aggregate']['uds5_frame_count'] + + ret_dict[port_handle]['custom_filter'].update({'filtered_frame_count': filtered_frame_count}) + ret_dict[port_handle]['custom_filter'].update({'total_rx_count': total_rx_count}) + + return ret_dict + + def ensure_traffic_stats(self, timeout=60, skip_fail=False, **kwargs): + if os.getenv("SPYTEST_ENSURE_TRAFFIC_STATS", "0") == "0": + return + logger.debug("Waiting to stabilize the traffic stats...") + mode = kwargs.get('mode') + traffic_mode = {'traffic_item': 'Traffic Item Statistics', 'flow': 'Flow Statistics', + 'aggregate': 'Port Statistics'} + if traffic_mode.get(mode) != None: + if mode == 'all': + view_traffic_page = get_ixnet().getList('/statistics', 'view') + else: + view_traffic_page = r'::ixNet::OBJ-/statistics/view:"{}"/page'.format(traffic_mode[mode]) + for page in utils.make_list(view_traffic_page): + # logger.debug("Traffic Mode: {}, Traffic View: {}".format(mode, page)) + tgen_wait(3 * self._get_pooling_interval(page, timeout, skip_fail)) + + def _get_pooling_interval(self, view_page, timeout, skip_fail): + ts1 = int(get_ixnet().getAttribute(view_page, '-timestamp')) + count = 0 + while (ts1 >= int(get_ixnet().getAttribute(view_page, '-timestamp'))): + time.sleep(1) + count += 1 + if count > timeout: + msg = 'no stats refresh happened for more than {} sec'.format(timeout) + logger.error(msg) + if not skip_fail: + self.fail(msg, "tgen_failed_api", msg) + ts2 = int(get_ixnet().getAttribute(view_page, '-timestamp')) + return (ts2 - ts1) / 1000 + + def ensure_traffic_control(self, timeout=180, skip_fail=False, **kwargs): + if os.getenv("SPYTEST_ENSURE_TRAFFIC_CONTROL", "0") == "0": + return + action = kwargs.get('action') + handle=kwargs.get('handle') + port_handle=kwargs.get('port_handle') + duration = utils.integer_parse(kwargs.get('duration', 0)) + traffic_elems = [] + if handle: + for h in utils.make_list(handle): + traffic_elems.extend(self._get_traffic_elem_from_stream_id(h)) + elif port_handle: + for ph in utils.make_list(port_handle): + traffic_elems.extend(self._get_traffic_elem_from_port_handle(ph)) + else: + logger.error("neither handle nor port_handle specified") + return + for traffic_elem in traffic_elems: + if action == "run": + self._ensure_traffic_elem_start(traffic_elem, duration, timeout, skip_fail) + elif action == "stop": + self._ensure_traffic_elem_stop(traffic_elem, duration, timeout, skip_fail) + if action == 'reset': + if not port_handle: return + port_handle = utils.make_list(port_handle) + for han in port_handle: + if han in self.traffic_config_handles: self.traffic_config_handles.pop(han) + + def _get_traffic_elem_from_stream_id(self, stream_id): + retval = [] + for port_handle in self.traffic_config_handles: + for ent in self.traffic_config_handles[port_handle]: + if ent["res"]["stream_id"] == stream_id: + retval.append(ent) + return retval + + def _get_traffic_elem_from_port_handle(self, port_handle): + if port_handle not in self.traffic_config_handles: + # logger.error("port_handle {} not found in cache".format(port_handle)) + return [None] + return self.traffic_config_handles[port_handle] + + def _read_traffic_elem_duration_and_mode(self, traffic_elem, duration): + kws = traffic_elem["kwargs"] + config_duration = utils.integer_parse(kws.get("duration", 0)) + if config_duration > duration: + duration = config_duration + return duration, kws.get("transmit_mode") + + def _ensure_traffic_elem_start(self, traffic_elem, duration, timeout=180, skip_fail=False): + if traffic_elem is None: + logger.info("skip checking for start traffic elem") + return True + duration, transmit_mode = self._read_traffic_elem_duration_and_mode(traffic_elem, duration) + if duration > 0 or transmit_mode in ['single_burst']: + # need to wait for completion of traffic when duration is specified + # Stopping traffic in fixed duration and single_burst scenarios + tgen_wait(duration) + res = get_ixiangpf().traffic_control(action='stop', handle=traffic_elem["res"]['stream_id']) + logger.info('Traffic Stop: {}'.format(res)) + return self._ensure_traffic_elem_stop(traffic_elem, duration, timeout, skip_fail) + timeout = timeout if timeout > duration else duration + 10 + end_time = time.time() + timeout + msg = "Verifying stream_id start: {}, trafficItem: {}".format(traffic_elem["res"]['stream_id'], + traffic_elem['traffic_item']) + logger.debug(msg) + while True: + try: + state = get_ixnet().getAttribute(traffic_elem["traffic_item"], '-state') + except Exception as exp: + msg = "Traffic item not found for stream_id: {}, Exception: {}".format(traffic_elem["res"]['stream_id'], exp) + logger.error(msg) + return False + if state == "started": + return True + if "unapplied" in state: + for errr_type in ['errors', 'warnings']: + err_logs = get_ixnet().getAttribute(traffic_elem["traffic_item"], '-' + errr_type) + logger.error("Unapplied {}: {}".format(errr_type.upper(), err_logs)) + #msg = "traffic is not configured" + #self.fail(msg, "tgen_failed_api", msg) + return False + time.sleep(1) + if time.time() > end_time: + break + if not skip_fail: + logger.debug("Verifying stream_id: {}, State: {}".format(traffic_elem["res"]['stream_id'], state)) + msg = "Failed to start the traffic in {} seconds".format(timeout) + self.fail(msg, "tgen_failed_api", msg) + return False + + def _ensure_traffic_elem_stop(self, traffic_elem, duration, timeout=180, skip_fail=False): + if traffic_elem is None: + logger.info("skip checking for stop traffic elem") + return True + timeout = timeout if timeout > duration else duration + 10 + end_time = time.time() + timeout + msg = "Verifying stream_id stop: {}, trafficItem: {}".format(traffic_elem["res"]['stream_id'], traffic_elem['traffic_item']) + logger.debug(msg) + while True: + try: + state = get_ixnet().getAttribute(traffic_elem["traffic_item"], '-state') + except Exception as exp: + msg = "Traffic item not found for stream_id: {}, Exception: {}".format(traffic_elem["res"]['stream_id'], exp) + logger.debug(msg) + return False + if state == "stopped": + return True + if "unapplied" in state: + for errr_type in ['errors', 'warnings']: + err_logs = get_ixnet().getAttribute(traffic_elem["traffic_item"], '-' + errr_type) + logger.error("Unapplied {}: {}".format(errr_type.upper(), err_logs)) + # msg = "traffic is not configured" + # self.fail(msg, "tgen_failed_api", msg) + return False + time.sleep(1) + if time.time() > end_time: + break + if not skip_fail: + logger.debug("Verifying stream_id: {}, State: {}".format(traffic_elem["res"]['stream_id'], state)) + msg = "Failed to stop the traffic in {} seconds".format(timeout) + self.fail(msg, "tgen_failed_api", msg) + return False + + def manage_traffic_config_handles(self, ret_ds, **kwargs): + if os.getenv("SPYTEST_ENSURE_TRAFFIC_CONTROL", "0") == "0": + return + port_handle = kwargs.get('port_handle') + mode = kwargs.get('mode') + if mode == "create": + if not port_handle: return + if port_handle not in self.traffic_config_handles: + self.traffic_config_handles[port_handle] = [] + ent = {"res": copy.deepcopy(ret_ds), "kwargs": copy.deepcopy(kwargs)} + ent["traffic_item"] = "/".join(ret_ds["traffic_item"].split("/")[:-1]) + self.traffic_config_handles[port_handle].append(ent) + elif mode == "remove": + for port_handle, port_values in self.traffic_config_handles.items(): + for val in port_values: + if kwargs.get('stream_id') in val['res']['stream_id']: + port_values.remove(val) + if not self.traffic_config_handles[port_handle]: self.traffic_config_handles.pop(port_handle) + +class TGScapy(TGBase, ScapyClient): + def __init__(self, tg_type, tg_version, tg_ip=None, tg_port=8009, tg_port_list=None): + logger.info('TG Scapy Init') + ScapyClient.__init__(self, logger, tg_port) + TGBase.__init__(self, tg_type, tg_version, tg_ip, tg_port_list) + + def clean_all(self): + self.server_control("clean-all", "") + + def show_status(self): + pass + + def instrument(self, phase, context): + self.server_control(phase, context) + + def log_call(self, fname, **kwargs): + tgen_log_call(fname, **kwargs) + + def api_fail(self, msg): + tgen_fail("", "tgen_failed_api", msg) + + def save_log(self, name, data): + lfile = tgen_get_logs_path(name) + utils.write_file(lfile, data) + + def connect(self): + logger.info('TG Scapy Connect {}:{}'.format(self.tg_ip, self.tg_port)) + return self.scapy_connect() + + def tg_arp_control(self, **kwargs): + if 'handle' in kwargs: + result = self.tg_interface_config(protocol_handle=kwargs['handle'], arp_send_req='1') + elif 'port_handle' in kwargs: + result = self.tg_interface_config(port_handle=kwargs['port_handle'], arp_send_req='1') + logger.info ('Sending ARP completed: {}'.format(result)) + return result + + def tg_withdraw_bgp_routes(self, route_handle): + result = self.tg_emulation_bgp_control(handle=route_handle, mode='stop') + logger.info('withdraw action completed: {}'.format(result)) + return result if result['status'] == '1' else None + + def tg_readvertise_bgp_routes(self, handle, route_handle): + result = self.tg_emulation_bgp_control(handle=route_handle, mode='start') + logger.info('readvertise action completed: {}'.format(result)) + return result if result['status'] == '1' else None + +def generate_tg_methods(tg_type, afnl): + for func in afnl: + #logger.info("creating wrapper for {}".format(func)) + dummy_func_name = 'tg_' + func[0] + if tg_type == 'ixia': + real_func_name = 'ixiangpf.' + func[0] + elif tg_type == 'stc': + real_func_name = 'sth.' + func[0] + tg_wrapper_func = \ + "def dummy_func_name(self,**kwargs):\n" + \ + " #logger.info('Calling TG Wrapper: ')\n" + \ + " res=self.trgen_pre_proc('dummy_func_name',**kwargs)\n" + \ + " self.trgen_post_proc('dummy_func_name',**kwargs)\n" + \ + " return res\n" + + tg_wrapper_func = re.sub( + r'dummy_func_name', dummy_func_name, tg_wrapper_func) + tg_wrapper_func = re.sub( + r'real_func_name', real_func_name, tg_wrapper_func) + + #exec tg_wrapper_func in globals() + exec(tg_wrapper_func, globals()) + if tg_type == 'ixia': + setattr(TGIxia, dummy_func_name, eval(dummy_func_name)) + else: + setattr(TGStc, dummy_func_name, eval(dummy_func_name)) + +def close_tgen(tgen_dict): + try: + tg_obj = tgen_obj_dict[tgen_dict['name']] + tg_obj.tg_disconnect() + except: + pass + +def init_tgen(workarea_in, logger_in, skip_tgen_in): + global workarea, logger, skip_tgen + workarea = workarea_in + logger = logger_in or Logger() + skip_tgen = skip_tgen_in + hltApiLog = tgen_get_logs_path('hltApiLog.txt') + utils.delete_file(hltApiLog) + +def instrument_tgen(tgen_dict, phase, context): + tg = tgen_obj_dict[tgen_dict["name"]] + tg.instrument(phase, context) + +def load_tgen(tgen_dict): + global tg_stc_pkg_loaded, tg_ixia_pkg_loaded, tg_scapy_pkg_loaded, tg_version_list + file_prefix = os.getenv("SPYTEST_FILE_PREFIX", "results") + # Abort if same TG type are having different version + tg_type = tgen_dict['type'] + tg_version = tgen_dict['version'] + + if tg_version_list.get(tg_type, None) == None: + tg_version_list[tg_type] = tg_version + elif tg_version_list.get(tg_type, None) != tg_version: + logger.error("Only one version per TG type is supported: %s %s %s" + % (tg_type, tg_version_list.get(tg_type, None), tg_version)) + return False + + tg_ip = tgen_dict['ip'] + tg_port_list = tgen_dict['ports'] + logger.info("Loading {}:{} {} Ports: {}".format( + tg_type, tg_version, tg_ip, tg_port_list)) + + if not utils.ipcheck(tg_ip): + logger.error("TGEN IP Address: {} is not reachable".format(tg_ip)) + return False + + if tg_type == 'stc': + os.environ['STC_LOG_OUTPUT_DIRECTORY'] = tgen_get_logs_path_folder() + logger.debug("STC_TGEN_LOGS_PATH: {}".format(os.getenv('STC_LOG_OUTPUT_DIRECTORY'))) + if not tg_stc_pkg_loaded: + if not tg_stc_load(tg_version, logger, tgen_get_logs_path()): + return False + code = "import sth \n" + exec (code, globals(), globals()) + if os.getenv('SPYTEST_LOGS_LEVEL') == 'debug': + logger.info("Setting Stc Debugs...") + hltExportLog = os.path.join(tgen_get_logs_path_folder(), "{}_{}".format(file_prefix, 'hltExportLog')) + hltDbgLog = os.path.join(tgen_get_logs_path_folder(), "{}_{}".format(file_prefix, 'hltDbgLog')) + stcExportLog = os.path.join(tgen_get_logs_path_folder(), "{}_{}".format(file_prefix, 'stcExportLog')) + hltMapLog = os.path.join(tgen_get_logs_path_folder(), "{}_{}".format(file_prefix,'hltMapLog')) + logger.info('STC Cmd Log File: {}*'.format(hltExportLog)) + logger.info('STC Dbg Log File: {}*'.format(hltDbgLog)) + logger.info('STC Vendor Log File: {}*'.format(stcExportLog)) + logger.info('STC Map Log File: {}*'.format(hltMapLog)) + get_sth().test_config(log=1, log_level=7, logfile=hltDbgLog, + vendorlog=1, vendorlogfile=stcExportLog, + hltlog=1, hltlogfile=hltExportLog, + hlt2stcmapping=1, hlt2stcmappingfile=hltMapLog, + custom_path=tgen_get_logs_path_folder()) + all_func_name_list = inspect.getmembers(get_sth(), inspect.isfunction) + generate_tg_methods(tg_type, all_func_name_list) + + # work around for isEOTResults + if os.getenv('SPYTEST_STC_IS_EOT_FIXUP', "0") == '1': + ResultOptions1 = get_sth().invoke('stc::get project1 -children-ResultOptions') + get_sth().invoke('stc::config ' + ResultOptions1 + ' -TimedRefreshResultViewMode CONTINUOUS') + get_sth().invoke('stc::subscribe -parent project1 -configType StreamBlock -resultType RxStreamBlockResults ') + get_sth().invoke('stc::subscribe -parent project1 -configType StreamBlock -resultType TxStreamBlockResults ') + + tg_stc_pkg_loaded = True + tg_obj = TGStc(tg_type, tg_version, tg_ip, tg_port_list) + + if tg_type == 'ixia': + for ix_server in utils.make_list(tgen_dict['ix_server']): + if not utils.ipcheck(ix_server): + logger.error("IxNetWork IP Address: {} is not reachable".format(ix_server)) + return False + tg_ix_port = tgen_dict.get('ix_port', 8009) + tg_ix_server = "{}:{}".format(ix_server, tg_ix_port) + if not tg_ixia_pkg_loaded: + if not tg_ixia_load(tg_version, logger, tgen_get_logs_path()): + return False + code = \ + "from ixiatcl import IxiaTcl \n" + \ + "from ixiahlt import IxiaHlt \n" + \ + "from ixiangpf import IxiaNgpf \n" + \ + "from ixiaerror import IxiaError \n" + \ + "ixiatcl = IxiaTcl() \n" + \ + "ixiahlt = IxiaHlt(ixiatcl) \n" + \ + "ixiangpf = IxiaNgpf(ixiahlt) \n" + + exec(code, globals(), globals()) + if os.getenv('SPYTEST_LOGS_LEVEL') == 'debug': + logger.info("Setting Ixia Debugs...") + hltCmdLog = os.path.join(tgen_get_logs_path_folder(), "{}_{}".format(file_prefix, 'hltCmdLog.txt')) + hltDebugLog = os.path.join(tgen_get_logs_path_folder(), + "{}_{}".format(file_prefix, 'hltDebugLog.txt')) + logger.info('Ixia Cmd Log File: {}*'.format(hltCmdLog)) + logger.info('Ixia Dbg Log File: {}*'.format(hltDebugLog)) + get_ixiatcl().set('::ixia::logHltapiCommandsFlag', '1') + get_ixiatcl().set('::ixia::logHltapiCommandsFileName', hltCmdLog) + get_ixiatcl().set('::ixia::debug', '3') + get_ixiatcl().set('::ixia::debug_file_name', hltDebugLog) + + all_func_name_list = inspect.getmembers(get_ixiangpf(), inspect.ismethod) + generate_tg_methods(tg_type, all_func_name_list) + tg_ixia_pkg_loaded = True + tg_obj = TGIxia(tg_type, tg_version, tg_ip, tg_port_list, tg_ix_server, tg_ix_port) + if tg_obj.tg_connected: + break + + if tg_type == 'scapy': + if not tg_scapy_pkg_loaded: + if not tg_scapy_load(tg_version, logger, tgen_get_logs_path()): + return False + tg_scapy_pkg_loaded = True + tg_ix_port = tgen_dict.get('ix_port', 8009) + tg_obj = TGScapy(tg_type, tg_version, tg_ip, tg_ix_port, tg_port_list) + + tgen_obj_dict[tgen_dict['name']] = tg_obj + return tg_obj.tg_connected + +def module_init(tgen_dict): + tg_type = tgen_dict['type'] + tg_version = tgen_dict['version'] + tg_ip = tgen_dict['ip'] + tg_port_list = tgen_dict['ports'] + + # add any thing to be done before start of user module + # like clear streams or port reset etc. + logger.info("TG Module init {}:{} {} Ports: {}".format( + tg_type, tg_version, tg_ip, tg_port_list)) + + tg = None + try: + tg = tgen_obj_dict[tgen_dict["name"]] + tg.in_module_start_cleanup = True + tg.clean_all() + tg.in_module_start_cleanup = False + return True + except Exception as exp: + if tg: tg.in_module_start_cleanup = False + msg = "Failed to reset port list {} : {}".format(",".join(tg_port_list), exp) + logger.exception(msg) + return False + +def get_tgen_handler(): + return { + 'ixia_handler': get_ixiangpf() if 'ixiangpf' in globals() else None, + 'stc_handler': get_sth() if 'sth' in globals() else None + } + +def get_tgen(port, name=None): + if name is None: + try: name = tgen_obj_dict.keys()[0] + except: pass + elif name not in tgen_obj_dict: + return (None, None) + tg = tgen_obj_dict[name] + ph = tg.get_port_handle(port) + return (tg, ph) + +if __name__ == "__main__": + tg_ixia_load("8.42", None, None) + code = \ + "from ixiatcl import IxiaTcl \n" + \ + "from ixiahlt import IxiaHlt \n" + \ + "from ixiangpf import IxiaNgpf \n" + \ + "from ixiaerror import IxiaError \n" + \ + "ixiatcl = IxiaTcl() \n" + \ + "ixiahlt = IxiaHlt(ixiatcl) \n" + \ + "ixiangpf = IxiaNgpf(ixiahlt) \n" + + exec(code, globals(), globals()) + diff --git a/spytest/spytest/tgen/tg_scapy.py b/spytest/spytest/tgen/tg_scapy.py new file mode 100644 index 00000000000..f0c51f5d1e8 --- /dev/null +++ b/spytest/spytest/tgen/tg_scapy.py @@ -0,0 +1,183 @@ +import os +import sys +import copy +import logging + +class ScapyClient(object): + + def __init__(self, logger, port=8009): + self.conn = None + self.tg_port = port + self.logger = logger or logging.getLogger() + self.use_pyro = True + self.filemode = bool(os.getenv("SPYTEST_FILE_MODE")) + self.tg_ip = getattr(self, "tg_ip", None) + self.tg_port_list = getattr(self, "tg_port_list", []) + self.tg_port_handle = getattr(self, "tg_port_handle", {}) + + def log_call(self, fname, **kwargs): + self.logger.info("TODO {} {}".format(fname, **kwargs)) + + def save_log(self, name, data): + self.logger.info("TODO {} {}".format(name, data)) + + def api_fail(self, msg): + self.logger.info("TODO {}".format(msg)) + + def log_info(self, *args): + self.logger.info(*args) + + def server_control(self, phase, context): + if self.filemode: + return + elif phase == "clean-all": + self.execute(self.conn.server_control, "clean-all", "") + elif phase == "pre-test": + self.execute(self.conn.server_control, "add-log", "test-start " + context) + elif phase == "post-test": + self.execute(self.conn.server_control, "add-log", "test-finish " + context) + elif phase == "pre-module-prolog": + self.execute(self.conn.server_control, "init-log", context) + elif phase == "post-module-epilog": + data = self.execute(self.conn.server_control, "read-log", context) + self.log_info("ScapyClient instrument: {} {}".format(phase, context)) + context = "tests_{}".format(context.replace(".py", ".tgen")) + context = context.replace("/", "_") + self.log_info("ScapyClient instrument: {} {}".format(phase, context)) + self.save_log(context, data) + else: + self.log_info("ScapyClient instrument: ignored {} {}".format(phase, context)) + + def rpyc_connect(self): + import rpyc + try: + config={"allow_pickle" : True, "sync_request_timeout": 300, + "allow_public_attrs": True, "allow_all_attrs": True, + "instantiate_oldstyle_exceptions" : True} + return rpyc.connect(self.tg_ip, self.tg_port, config=config) + except Exception as e: + print (e) + raise ValueError("Failed to connect to scapy server {}".format(e)) + + def scapy_connect(self, dry_run=False): + self.tg_ns = 'scapy' + + if self.filemode: + return None + + if self.use_pyro: + import Pyro4 + uri = "PYRO:scapy-tgen@{}:{}".format(self.tg_ip, self.tg_port) + Pyro4.config.SERIALIZER = "pickle" + self.conn = Pyro4.Proxy(uri) + else: + self.conn2 = self.rpyc_connect() + self.conn = self.conn2.root + + try: dbg_lvl = int(os.getenv("SPYTEST_SCAPY_DBG_LVL", "1")) + except: dbg_lvl = 1 + try: max_pps = int(os.getenv("SPYTEST_SCAPY_MAX_PPS", "100")) + except: max_pps = 100 + + self.execute(self.conn.server_control, "set-dbg-lvl", dbg_lvl) + self.execute(self.conn.server_control, "set-dry-run", dry_run) + self.execute(self.conn.server_control, "set-max-pps", max_pps) + self.execute(self.conn.server_control, "init-log", "default") + res = self.tg_connect(port_list=self.tg_port_list) + self.tg_port_handle.clear() + for port in self.tg_port_list: + self.tg_port_handle[port] = res['port_handle'][port] + return None + + def log_api(self, *args, **kws): + func = sys._getframe(1).f_code.co_name + self.log_call(func, **kws) + + def fix_newstr(self, kws): + from future.types import newstr + for key, value in kws.items(): + if isinstance(value, newstr): + kws[key] = str(value) + + def execute(self, func, *args, **kws): + try: + res = func(*args, **kws) + return copy.copy(res) + except Exception as exp: + msg = "{}".format(exp) + if self.use_pyro: + import Pyro4 + msg = msg + "".join(Pyro4.util.getPyroTraceback()) + self.api_fail(msg) + raise exp + + def tg_connect(self, *args, **kws): + self.log_api(*args, **kws) + if self.filemode: return None + return self.execute(self.conn.tg_connect, *args, **kws) + def tg_disconnect(self, *args, **kws): + self.log_api(*args, **kws) + if self.filemode: return None + return self.execute(self.conn.tg_disconnect, *args, **kws) + def tg_traffic_control(self, *args, **kws): + self.log_api(*args, **kws) + if self.filemode: return None + return self.execute(self.conn.tg_traffic_control, *args, **kws) + def tg_interface_control(self, *args, **kws): + self.log_api(*args, **kws) + if self.filemode: return None + return self.execute(self.conn.tg_interface_control, *args, **kws) + def tg_packet_control(self, *args, **kws): + self.log_api(*args, **kws) + if self.filemode: return None + return self.execute(self.conn.tg_packet_control, *args, **kws) + def tg_packet_stats(self, *args, **kws): + self.log_api(*args, **kws) + if self.filemode: return None + return self.execute(self.conn.tg_packet_stats, *args, **kws) + def tg_traffic_config(self, *args, **kws): + self.log_api(*args, **kws) + if self.filemode: return None + self.fix_newstr(kws) + return self.execute(self.conn.tg_traffic_config, *args, **kws) + def tg_interface_config(self, *args, **kws): + self.log_api(*args, **kws) + if self.filemode: return None + return self.execute(self.conn.tg_interface_config, *args, **kws) + def tg_traffic_stats(self, *args, **kws): + self.log_api(*args, **kws) + if self.filemode: return None + return self.execute(self.conn.tg_traffic_stats, *args, **kws) + def tg_emulation_bgp_config(self, *args, **kws): + self.log_api(*args, **kws) + if self.filemode: return None + return self.execute(self.conn.tg_emulation_bgp_config, *args, **kws) + def tg_emulation_bgp_route_config(self, *args, **kws): + self.log_api(*args, **kws) + if self.filemode: return None + return self.execute(self.conn.tg_emulation_bgp_route_config, *args, **kws) + def tg_emulation_bgp_control(self, *args, **kws): + self.log_api(*args, **kws) + if self.filemode: return None + return self.execute(self.conn.tg_emulation_bgp_control, *args, **kws) + def tg_emulation_igmp_config(self, *args, **kws): + self.log_api(*args, **kws) + if self.filemode: return None + return self.execute(self.conn.tg_emulation_igmp_config, *args, **kws) + def tg_emulation_multicast_group_config(self, *args, **kws): + self.log_api(*args, **kws) + if self.filemode: return None + return self.execute(self.conn.tg_emulation_multicast_group_config, *args, **kws) + def tg_emulation_multicast_source_config(self, *args, **kws): + self.log_api(*args, **kws) + if self.filemode: return None + return self.execute(self.conn.tg_emulation_multicast_source_config, *args, **kws) + def tg_emulation_igmp_group_config(self, *args, **kws): + self.log_api(*args, **kws) + if self.filemode: return None + return self.execute(self.conn.tg_emulation_igmp_group_config, *args, **kws) + def tg_emulation_igmp_control(self, *args, **kws): + self.log_api(*args, **kws) + if self.filemode: return None + return self.execute(self.conn.tg_emulation_igmp_control, *args, **kws) + diff --git a/spytest/spytest/tgen/tgen_utils.py b/spytest/spytest/tgen/tgen_utils.py new file mode 100644 index 00000000000..2bd182d9903 --- /dev/null +++ b/spytest/spytest/tgen/tgen_utils.py @@ -0,0 +1,972 @@ +import re +from spytest import st +from spytest.dicts import SpyTestDict + + +def _log_call(fname, **kwargs): + args_list=[] + for key, value in kwargs.items(): + args_list.append("%s=%s" %(key, value)) + text = "{}({})\n".format(fname, ",".join(args_list)) + st.log('TGenUtil REQ: {}'.format(text.strip())) + +def _validate_parameters(tr_details): + + mandatory_params = ['tx_ports','tx_obj','exp_ratio','rx_ports','rx_obj'] + for tr_pair in range(1,len(tr_details)+1): + tr_pair = str(tr_pair) + for param in mandatory_params: + if tr_details[tr_pair].get(param) == None: + st.log('{} parameter missing in traffic pair: {}'.format(param,tr_pair)) + return False + if len(tr_details[tr_pair]['tx_ports']) != len(tr_details[tr_pair]['tx_obj']) != len(tr_details[tr_pair]['exp_ratio']): + st.log('tx_ports, tx_obj and exp_ratio must be of same length, in traffic pair: {}'.format(tr_pair)) + return False + if len(tr_details[tr_pair]['rx_ports']) != len(tr_details[tr_pair]['rx_obj']): + st.log('rx_ports and rx_obj must be of length, in traffic pair: {}'.format(tr_pair)) + return False + if tr_details[tr_pair].get('stream_list',None) != None: + if len(tr_details[tr_pair]['tx_ports']) != len(tr_details[tr_pair]['stream_list']): + st.log('tx_ports, stream_list must be of same length, in traffic pair: {}'.format(tr_pair)) + return False + if tr_details[tr_pair].get('filter_param',None) != None and tr_details[tr_pair].get('stream_list',None) != None: + if len(tr_details[tr_pair]['filter_param']) != len(tr_details[tr_pair]['stream_list']): + st.log('stream_list and filter list must be of same length, in traffic pair: {}'.format(tr_pair)) + return False + if tr_details[tr_pair].get('filter_val',None) != None and tr_details[tr_pair].get('filter_param',None) != None: + if len(tr_details[tr_pair]['filter_val']) != len(tr_details[tr_pair]['filter_param']): + st.log('filer value and filter list must be of same length, in traffic pair: {}'.format(tr_pair)) + return False + st.log('param validation successful') + return True + + +def get_counter_name(mode,tg_type,comp_type,direction): + tg_type = "ixia" if tg_type == "scapy" else tg_type + traffic_counters = { + 'aggregate': { + 'stc': { + 'tx': { + 'packet_count': 'pkt_count', + 'packet_rate': 'pkt_rate', + 'oversize_count': 'pkt_count', + }, + 'rx': { + 'packet_count': 'pkt_count', + 'packet_rate': 'pkt_rate', + 'oversize_count': 'pkt_count', + }, + }, + 'ixia': { + 'tx': { + 'packet_count': 'raw_pkt_count', + 'packet_rate': 'total_pkt_rate', + 'oversize_count': 'raw_pkt_count', + }, + 'rx': { + 'packet_count': 'raw_pkt_count', + 'packet_rate': 'raw_pkt_rate', + 'oversize_count': 'oversize_count', + }, + }, + }, + 'streamblock' : { + 'stc': { + 'tx': { + 'packet_count': 'total_pkts', + 'packet_rate': 'total_pkt_rate', + 'drop_count': 'total_pkts', + 'drop_rate': 'total_pkt_rate', + }, + 'rx': { + 'packet_count': 'total_pkts', + 'packet_rate': 'total_pkt_rate', + 'drop_count': 'dropped_pkts', + 'drop_rate': 'dropped_pkts_percent', + }, + }, + 'ixia': { + 'tx': { + 'packet_count': 'total_pkts', + 'packet_rate': 'total_pkt_rate', + 'drop_count': 'total_pkts', + 'drop_rate': 'total_pkt_rate', + }, + 'rx': { + 'packet_count': 'total_pkts', + 'packet_rate': 'total_pkt_rate', + 'drop_count': 'loss_pkts', + 'drop_rate': 'loss_percent', + }, + }, + }, + 'filter' : { + 'stc': { + 'tx': { + 'packet_count': 'total_pkts', + 'packet_rate': 'total_pkt_rate', + }, + 'rx': { + 'packet_count': 'count', + 'packet_rate': 'rate_pps', + }, + }, + 'ixia': { + 'tx': { + 'packet_count': 'total_pkts', + 'packet_rate': 'total_pkt_rate', + }, + 'rx': { + 'packet_count': 'total_pkts', + 'packet_rate': 'total_pkt_rate', + }, + }, + } + } + + counter_name = traffic_counters[mode][tg_type][direction][comp_type] + st.log('TG type: {}, Comp_type: {}, Direction: {}, Counter_name: {}'.format(tg_type, comp_type, direction, counter_name)) + return counter_name + + +def _verify_aggregate_stats(tr_details,mode,comp_type,tolerance_factor,delay_factor,retry,return_all): + + return_value = True + ret_all=[] + delay = 5 * float(delay_factor) + tolerance = 5 * float(tolerance_factor) + + ################### scapy ###################### + for tr_pair in range(1,len(tr_details)+1): + tr_pair = str(tr_pair) + tx_objs = tr_details[tr_pair]['tx_obj'] + for obj in tx_objs: + if obj.tg_type == 'scapy': + delay = 5 * delay + break + break + ################### scapy ###################### + + st.tg_wait(delay) + + for tr_pair in range(1,len(tr_details)+1): + tr_pair = str(tr_pair) + tx_ports = tr_details[tr_pair]['tx_ports'] + tx_obj = tr_details[tr_pair]['tx_obj'] + exp_ratio = tr_details[tr_pair]['exp_ratio'] + rx_ports = tr_details[tr_pair]['rx_ports'] + rx_obj = tr_details[tr_pair]['rx_obj'] + + st.log('Validating Traffic, Pair: {}, Transmit Ports: {}, Receive Ports: {}'.format(tr_pair,tx_ports,rx_ports)) + exp_val = 0 + for port,obj,ratio in zip(tx_ports,tx_obj,exp_ratio): + tx_ph = obj.get_port_handle(port) + tx_stats = obj.tg_traffic_stats(port_handle=tx_ph,mode=mode) + #st.debug(tx_stats) + counter_name = get_counter_name(mode,obj.tg_type,comp_type,'tx') + cur_tx_val = int(tx_stats[tx_ph][mode]['tx'][counter_name]) + st.log('Transmit counter_name: {}, counter_val: {}'.format(counter_name,cur_tx_val)) + if cur_tx_val == 0: + msg = 'Transmit Counters is 0 (zero) for port: {}'.format(port) + st.log(msg) + st.report_tgen_fail('tgen_failed_api', msg) + exp_val += cur_tx_val * ratio +# st.log(exp_val) + st.log('Total Tx from ports {}: {}'.format(tx_ports,exp_val)) + + for port,obj in zip(rx_ports,rx_obj): + rx_ph = obj.get_port_handle(port) + rx_stats = obj.tg_traffic_stats(port_handle=rx_ph,mode=mode) + #st.debug(rx_stats) + counter_name = get_counter_name(mode,obj.tg_type,comp_type,'rx') + real_rx_val = int(rx_stats[rx_ph][mode]['rx'][counter_name]) + st.log('Receive counter_name: {}, counter_val: {}'.format(counter_name,real_rx_val)) + + st.log('Total Rx on ports {}: {}'.format(rx_ports,real_rx_val)) + + diff = (abs(exp_val - real_rx_val) * 100.0) / exp_val if exp_val > 0 else abs(real_rx_val * 100.0 / cur_tx_val) + if diff <= tolerance: + st.log('Traffic Validation: {}, Pair: {}, Transmit Ports: {}, Receive Ports: {}'.format('Success',tr_pair,tx_ports,rx_ports)) + st.log('Got expected values; Expected: {}, Actual: {}, diff%: {}'.format(exp_val,real_rx_val,diff)) + ret_all.append(True) + else: + return_value = False + ret_all.append(False) + st.log('Traffic Validation: {}, Pair: {}, Transmit Ports: {}, Receive Ports: {}'.format('Failure',tr_pair,tx_ports,rx_ports)) + st.log('Expected: {}, Actual: {}, diff%: {}'.format(exp_val,real_rx_val,diff)) + return return_value if return_all==0 else (return_value, ret_all) + + +def _verify_streamlevel_stats(tr_details,mode,comp_type,tolerance_factor,delay_factor,retry,return_all): + + return_value = True + ret_all=[] + delay = 5 * float(delay_factor) + tolerance = 5 * float(tolerance_factor) + + st.tg_wait(delay) + + for tr_pair in range(1,len(tr_details)+1): + tr_pair = str(tr_pair) + tx_ports = tr_details[tr_pair]['tx_ports'] + tx_obj = tr_details[tr_pair]['tx_obj'] + exp_ratio = tr_details[tr_pair]['exp_ratio'] + rx_ports = tr_details[tr_pair]['rx_ports'] + rx_obj = tr_details[tr_pair]['rx_obj'] + stream_list = tr_details[tr_pair]['stream_list'] + + st.log('Validating Traffic, Pair: {}, Transmit Ports: {}, Receive Ports: {}, stream_list: {}'.format(tr_pair,tx_ports,rx_ports,stream_list)) + + rx_obj = rx_obj[0] + rx_port = rx_ports[0] + rx_ph = rx_obj.get_port_handle(rx_port) + for txPort,txObj,ratio,stream in zip(tx_ports,tx_obj,exp_ratio,stream_list): + if type(ratio) is not list: + ratio = [ratio] + if len(stream) != len(ratio): + ratio = ratio * len(stream) + tx_ph = txObj.get_port_handle(txPort) + for strelem,ratelem in zip(stream,ratio): + exp_val = 0 + if rx_obj.tg_type == 'stc': + tx_counter_name = get_counter_name(mode,rx_obj.tg_type,comp_type,'tx') + rx_counter_name = get_counter_name(mode,rx_obj.tg_type,comp_type,'rx') + rx_stats = rx_obj.tg_traffic_stats(port_handle=rx_ph,mode='streams',streams=strelem) + exp_val = int(rx_stats[tx_ph]['stream'][strelem]['tx'][tx_counter_name]) + tx_val=exp_val + if exp_val == 0: + msg = 'Transmit Counters is 0 (zero) for port: {}'.format(strelem) + st.log(msg) + st.report_tgen_fail('tgen_failed_api', msg) + exp_val = int(exp_val * float(ratelem)) + real_rx_val = int(rx_stats[tx_ph]['stream'][strelem]['rx'][rx_counter_name]) + elif rx_obj.tg_type in ['ixia', 'scapy']: + tx_counter_name = get_counter_name(mode,rx_obj.tg_type,comp_type,'tx') + rx_counter_name = get_counter_name(mode,rx_obj.tg_type,comp_type,'rx') + rx_stats = rx_obj.tg_traffic_stats(port_handle=rx_ph,mode='traffic_item') + + #Following check is to avoid KeyError traffic_item. Reason is traffic was not started. + #Ixia team is looking into why traffic was not started - might be setup issue + + if rx_stats['status'] != '1': + st.log('Could not get traffic_stats from the TGEN, Please check if traffic was started') + return False + + exp_val = float(rx_stats['traffic_item'][strelem]['tx'][tx_counter_name]) + tx_val=exp_val + if exp_val == 0: + msg = 'Transmit Counters is 0 (zero) for port: {}'.format(strelem) + st.log(msg) + st.report_tgen_fail('tgen_failed_api', msg) + exp_val = int(exp_val * float(ratelem)) + real_rx_val = float(rx_stats['traffic_item'][strelem]['rx'][rx_counter_name]) + + st.log('Receive counter_name: {}, counter_val: {}'.format(rx_counter_name,real_rx_val)) + diff = (abs(exp_val - real_rx_val) * 100.0) / exp_val if exp_val > 0 else abs(real_rx_val * 100.0 / tx_val) + if diff <= tolerance: + st.log('Traffic Validation: {}, Pair: {}, Transmit Ports: {}, Receive Ports: {}\ + streamid: {}'.format('Success',tr_pair,tx_ports,rx_ports,strelem)) + st.log('Got expected values; Expected: {}, Actual: {}, diff%: {}'.format(exp_val,real_rx_val,diff)) + ret_all.append(True) + else: + return_value = False + ret_all.append(False) + st.log('Traffic Validation: {}, Pair: {}, Transmit Ports: {}, Receive Ports: {}\ + streamid: {}'.format('Failure',tr_pair,tx_ports,rx_ports,strelem)) + st.log('Expected: {}, Actual: {}, diff%: {}'.format(exp_val,real_rx_val,diff)) + return return_value if return_all==0 else (return_value, ret_all) + +def _verify_analyzer_filter_stats(tr_details,mode,comp_type,tolerance_factor,delay_factor,retry,return_all): + + return_value = True + ret_all=[] + delay = 5 * float(delay_factor) + tolerance = 5 * float(tolerance_factor) + + st.tg_wait(delay) + + for tr_pair in range(1,len(tr_details)+1): + tr_pair = str(tr_pair) + tx_ports = tr_details[tr_pair]['tx_ports'] + tx_obj = tr_details[tr_pair]['tx_obj'] + exp_ratio = tr_details[tr_pair]['exp_ratio'] + rx_ports = tr_details[tr_pair]['rx_ports'] + rx_obj = tr_details[tr_pair]['rx_obj'] + stream_list = tr_details[tr_pair]['stream_list'] + filter_param = tr_details[tr_pair]['filter_param'] + filter_val = tr_details[tr_pair]['filter_val'] + st.log('Validating Traffic, Pair: {}, Transmit Ports: {}, Receive Ports: {}, stream_list: {}, filter_param: {}, filter_val: {}'.format(tr_pair,tx_ports,rx_ports,stream_list,filter_param,filter_val)) + + rx_obj = rx_obj[0] + rx_port = rx_ports[0] + rx_ph = rx_obj.get_port_handle(rx_port) + for txPort,txObj,ratio,stream,fparam,fvalue in zip(tx_ports,tx_obj,exp_ratio,stream_list,filter_param,filter_val): + tx_ph = txObj.get_port_handle(txPort) + i = 1 + j = str(i) + if type(stream) == str: + st.log('only one stream is configured, form a list') + stream = [stream] + fparam = [fparam] + fvalue = [fvalue] + for strelem,fpelem,fvelem in zip(stream,fparam,fvalue): + exp_val = 0 + if rx_obj.tg_type == 'stc': + tx_counter_name = get_counter_name(mode,rx_obj.tg_type,comp_type,'tx') + rx_counter_name = get_counter_name(mode,rx_obj.tg_type,comp_type,'rx') + tx_stats = txObj.tg_traffic_stats(mode='streams',port_handle=tx_ph) + exp_val = int(tx_stats[tx_ph]['stream'][strelem]['tx'][tx_counter_name]) + tx_val=exp_val + if exp_val == 0: + msg = 'Transmit Counters is 0 (zero) for port: {}'.format(strelem) + st.log(msg) + st.report_tgen_fail('tgen_failed_api', msg) + exp_val = exp_val * ratio + rx_stats = rx_obj.tg_traffic_stats(port_handle=rx_ph,mode='aggregate') + try: + real_rx_val = int(rx_stats[rx_ph]['aggregate']['rx'][fpelem][fvelem][rx_counter_name]) + except KeyError: + st.log("traffic not found for the parameter: {} {}".format(fpelem,fvelem)) + real_rx_val = 0 + elif rx_obj.tg_type in ['ixia', 'scapy']: + tx_counter_name = get_counter_name(mode,rx_obj.tg_type,comp_type,'tx') + rx_counter_name = get_counter_name(mode,rx_obj.tg_type,comp_type,'rx') + tx_stats = txObj.tg_traffic_stats(mode='streams',port_handle=tx_ph) + exp_val = int(float(tx_stats[tx_ph]['stream'][strelem]['tx'][tx_counter_name])) + tx_val=exp_val + if exp_val == 0: + msg = 'Transmit Counters is 0 (zero) for port: {}'.format(strelem) + st.log(msg) + st.report_tgen_fail('tgen_failed_api', msg) + exp_val = exp_val * ratio + # need to get total flows and verify whether particular flow matching the filter value + # rx['flow'].keys() & rx['flow']['1']['tracking']['2']['tracking_value'] prints '10' example vlan id 10 + rx_stats = rx_obj.tg_traffic_stats(port_handle=rx_ph,mode='flow') + real_rx_val = int(float(rx_stats['flow'][j]['rx'][rx_counter_name])) + i += 1 + j = str(i) + st.log('Receive counter_name: {}, counter_val: {}'.format(rx_counter_name,real_rx_val)) + diff = (abs(exp_val - real_rx_val) * 100.0) / exp_val if exp_val > 0 else abs(real_rx_val * 100.0 / tx_val) + if diff <= tolerance: + st.log('Traffic Validation: {}, Pair: {}, Transmit Ports: {}, Receive Ports: {}\ + streamid: {}, filter param: {}, filter value: {}'.format('Success',tr_pair,tx_ports,rx_ports,strelem,fpelem,fvelem)) + st.log('Got expected values; Expected: {}, Actual: {}, diff%: {}'.format(exp_val,real_rx_val,diff)) + ret_all.append(True) + else: + return_value = False + ret_all.append(False) + st.log('Traffic Validation: {}, Pair: {}, Transmit Ports: {}, Receive Ports: {}\ + streamid: {}, filter param: {}, filter value: {}'.format('Failure',tr_pair,tx_ports,rx_ports,strelem,fpelem,fvelem)) + st.log('Expected: {}, Actual: {}, diff%: {}'.format(exp_val,real_rx_val,diff)) + return return_value if return_all==0 else (return_value, ret_all) + + +def _verify_custom_filter_stats(tr_details,mode,comp_type,tolerance_factor,delay_factor,retry,return_all): + + return_value = True + ret_all=[] + delay = 5 * float(delay_factor) + tolerance = 5 * float(tolerance_factor) + + st.tg_wait(delay) + + for tr_pair in range(1,len(tr_details)+1): + tr_pair = str(tr_pair) + tx_ports = tr_details[tr_pair]['tx_ports'] + tx_obj = tr_details[tr_pair]['tx_obj'] + exp_ratio = tr_details[tr_pair]['exp_ratio'] + rx_ports = tr_details[tr_pair]['rx_ports'] + rx_obj = tr_details[tr_pair]['rx_obj'] + + st.log('Validating Traffic, Pair: {}, Transmit Ports: {}, Receive Ports: {}'.format(tr_pair,tx_ports,rx_ports)) + exp_val = 0 + for port,obj,ratio in zip(tx_ports,tx_obj,exp_ratio): + tx_ph = obj.get_port_handle(port) + mode = 'aggregate' + tx_stats = obj.tg_traffic_stats(port_handle=tx_ph,mode=mode) + counter_name = get_counter_name(mode,obj.tg_type,comp_type,'tx') + cur_tx_val = int(tx_stats[tx_ph][mode]['tx'][counter_name]) + st.log('Transmit counter_name: {}, counter_val: {}'.format(counter_name,cur_tx_val)) + if cur_tx_val == 0: + msg = 'Transmit Counters is 0 (zero) for port: {}'.format(port) + st.log(msg) + st.report_tgen_fail('tgen_failed_api', msg) + exp_val += cur_tx_val * ratio +# st.log(exp_val) + st.log('Total Tx from ports {}: {}'.format(tx_ports,exp_val)) + + for port,obj in zip(rx_ports,rx_obj): + rx_ph = obj.get_port_handle(port) + mode = 'custom_filter' + rx_stats = obj.tg_custom_filter_config(mode='getStats',port_handle=rx_ph) +# st.log(rx_stats) +# counter_name = get_counter_name(mode,obj.tg_type,comp_type,'rx') + print(rx_stats) + counter_name = 'filtered_frame_count' + real_rx_val = int(rx_stats[rx_ph][mode][counter_name]) + st.log('Receive counter_name: {}, counter_val: {}'.format(counter_name,real_rx_val)) + + st.log('Total Rx on ports {}: {}'.format(rx_ports,real_rx_val)) + + diff = (abs(exp_val - real_rx_val) * 100.0) / exp_val if exp_val > 0 else abs(real_rx_val * 100.0 / cur_tx_val) + if diff <= tolerance: + st.log('Traffic Validation: {}, Pair: {}, Transmit Ports: {}, Receive Ports: {}'.format('Success',tr_pair,tx_ports,rx_ports)) + st.log('Got expected values; Expected: {}, Actual: {}, diff%: {}'.format(exp_val,real_rx_val,diff)) + ret_all.append(True) + else: + return_value = False + ret_all.append(False) + st.log('Traffic Validation: {}, Pair: {}, Transmit Ports: {}, Receive Ports: {}'.format('Failure',tr_pair,tx_ports,rx_ports)) + st.log('Expected: {}, Actual: {}, diff%: {}'.format(exp_val,real_rx_val,diff)) + return return_value if return_all==0 else (return_value, ret_all) + + +def validate_tgen_traffic(**kwargs): + + ''' + traffic_details = { + '1' : { + 'tx_ports' : ['5/7', '5/8'], + 'tx_obj' : ['tg2','tg2'], + 'exp_ratio' : [1,1], + 'rx_ports' : ['5/9'], + 'rx_obj' : ['tg2'], + }, + '2' : { + 'tx_ports' : ['5/7', '5/8'], + 'tx_obj' : ['tg2','tg2'], + 'exp_ratio' : [1,1], + 'rx_ports' : ['5/9'], + 'rx_obj' : ['tg2'], + }, + } + + comp_type : ' + mode : + tolernace_factor: <0...20>. Default is 1 and tolerance is 5% + delay_factor: <0...24>. Default is 1 and delay is 5 sec + retry: <1..3>. Default is 1 and retry is 1 + return_all: <0/1>. To get the result of all streams. + Default is 0 + Return value will be tuple with (final_result, all_result) + ''' + + _log_call("validate_tgen_traffic", **kwargs) + + if kwargs.get('traffic_details') == None: + st.log('Mandatory param traffic_details is missing') + return False + + traffic_details = kwargs['traffic_details'] + st.log(traffic_details) + if not _validate_parameters(traffic_details): + return False + + delay_factor = kwargs.get('delay_factor',1) + tolerance_factor = kwargs.get('tolerance_factor',1) + retry = kwargs.get('retry',1) + comp_type = kwargs.get('comp_type','packet_count') + return_all = kwargs.get('return_all',0) + mode = kwargs.get('mode').lower() + + if mode == 'aggregate': + return _verify_aggregate_stats(traffic_details, mode=mode, comp_type=comp_type, \ + tolerance_factor=tolerance_factor, delay_factor=delay_factor, retry=retry, return_all=return_all) + elif mode == 'streamblock': + return _verify_streamlevel_stats(traffic_details, mode=mode, comp_type=comp_type, \ + tolerance_factor=tolerance_factor, delay_factor=delay_factor, retry=retry, return_all=return_all) + elif mode == 'filter': + return _verify_analyzer_filter_stats(traffic_details, mode=mode, comp_type=comp_type, \ + tolerance_factor=tolerance_factor, delay_factor=delay_factor, retry=retry, return_all=return_all) + elif mode == 'custom_filter': + return _verify_custom_filter_stats(traffic_details, mode=mode, comp_type=comp_type, \ + tolerance_factor=tolerance_factor, delay_factor=delay_factor, retry=retry, return_all=return_all) + +def _verify_packet_capture(pkt_dict, offset_list, value_list,port_handle, max_count=20): + + tot_pkts = int(pkt_dict[port_handle]['aggregate']['num_frames']) + if max_count > 0 and tot_pkts > max_count: + tot_pkts = max_count + + for pkt_num in range(tot_pkts): + st.log('Parsing packet: {}, port_handle: {}'.format(pkt_num,port_handle)) + ret_val = len(value_list) + for offset,value in zip(offset_list,value_list): + if ":" in value: + value = value.split(':') + elif "." in value: + value = [hex(int(i))[2:].zfill(2).upper() for i in value.split('.')] + else: + hex_string = value.upper() + if len(hex_string) % 2 != 0: + hex_string = hex_string.zfill(len(hex_string)+1) + value = [(hex_string[i:i+2]) for i in range(0, len(hex_string), 2)] + + if not isinstance(value,list): + value = [value] + + start_range = offset + end_range = offset + len(value) + + try: + found_value = pkt_dict[port_handle]['frame'][str(pkt_num)]['frame_pylist'][start_range:end_range] + except: + found_value = [] + + if found_value == value: + st.log('Match found in packet: {} for {} at offset: {}'.format(pkt_num,value,offset)) + ret_val -= 1 + else: + st.log('Match not found in packet: {} at offset: {}, Expected: {}, Found: {}'.format(pkt_num,offset,value,found_value)) + + if ret_val == 0: + return True + +def _parse_ixia_packet(pkt_dict,header,field,value,offset='not_set'): + + for k,v in pkt_dict.items(): + if isinstance(v,dict): + if "display_name" in v: + if re.search(r'Generic Routing Encapsulation',header) and re.search(r'Data',field,re.I): + if header in k and v['display_name'] == field: + data_value = v['value'] + start_index = int(offset) + end_index = int(offset) + len(value) + new_data_value = data_value[start_index:end_index] + if new_data_value.upper() == value.upper(): + ixia_pckt_cap_ret_val.append(True) + return ixia_pckt_cap_ret_val + elif v['display_name'] == field and v['value'].upper() == value.upper() and header in k: + ixia_pckt_cap_ret_val.append(True) + return ixia_pckt_cap_ret_val + _parse_ixia_packet(v,header,field,value,offset) + + ixia_pckt_cap_ret_val.append(False) + return ixia_pckt_cap_ret_val + +def _verify_packet_capture_ixia(pkt_dict,header_list,value_list,port_handle): + + frame_format = { + 'ETH': { + 'h_name': 'Ethernet', + 'fname_list': ['Ethernet','Source','Destination','Type'], + }, + 'VLAN': { + 'h_name': '1Q Virtual LAN', + 'fname_list': ['1Q Virtual LAN','CFI','ID','Priority','Type'], + }, + 'IP': { + 'h_name': 'Internet Protocol', + 'fname_list': ['Version','Total Length','Source','Destination','Protocol','Time to live','Header Length','Identification','Precedence', 'Differentiated Services Codepoint', 'Reliability','Explicit Congestion Notification', 'Fragment offset', 'More fragments'], + }, + 'IP6': { + 'h_name': 'Internet Protocol Version 6$', + 'fname_list': ['Source','Destination','Protocol'], + }, + 'TCP': { + 'h_name': 'Transmission Control Protocol', + 'fname_list': ['Source Port','Destination Port'], + }, + 'UDP': { + 'h_name': 'User Datagram Protocol', + 'fname_list': ['Source Port','Destination Port'], + }, + 'GRE': { + 'h_name': 'Generic Routing Encapsulation', + 'fname_list': ['Data','Protocol Type'], + } + } + + + last_pkt_count = int(pkt_dict[port_handle]['frame']['data']['frame_id_end'])+1 + # last_pkt_count = 2 + for pkt_num in range(1,last_pkt_count): + st.log('Parsing packet: {}, port_handle: {}'.format(pkt_num,port_handle)) + p_d = pkt_dict[port_handle]['frame']['data'][str(pkt_num)] + #p_d = pkt_dict + ret_val = len(value_list) + for header_field,value in zip(header_list,value_list): + header = header_field.split(':')[0] + field = header_field.split(':')[1] + offset = 'not_set' + code = "ixia_pckt_cap_ret_val = []" + exec(code, globals(), globals()) + + if header == 'GRE' and re.search(r'Data',field,re.I): + offset = header_field.split(':')[2] + if re.search(r':',value): + value = ''.join(value.split(':')).upper() + elif re.search(r'\.',value): + value = ''.join([hex(int(i))[2:].zfill(2).upper() for i in value.split('.')]) + + elif not re.search(r':|\.',value): + if re.search(r'vlan',header,re.I): +# header = '802' + vid = value[1:4] + value = str(int(vid,16)) + # special code for following, one elif for each header + # vlan priority + # tos + # dscp + else: + value = str(int(value,16)) + print(header, field, value) + + res = _parse_ixia_packet(p_d,frame_format[header]['h_name'],field,value,offset) + if True in res: + st.log('Match found in packet: {} for {} in {}:{} header'.format(pkt_num,value,header,field)) + ret_val -= 1 + else: + st.log('Match not found in packet: {} for {} in {}:{} header'.format(pkt_num,value,header,field)) + st.log('Packet: {}'.format(p_d)) + + if ret_val == 0: + return True + +def validate_packet_capture(**kwargs): + pkt_dict = kwargs['pkt_dict'] + header_list = kwargs.get('header_list','new_ixia_format') + offset_list = kwargs['offset_list'] + value_list = kwargs['value_list'] + + _log_call("validate_packet_capture", **kwargs) + + if len(pkt_dict.keys()) > 2: + st.log('Packets have caputred on more than one port. Pass packet info for only one port') + return False + + for key,value in pkt_dict.items(): + if key != 'status': + port_handle = key + + if pkt_dict[port_handle]['aggregate']['num_frames'] in ['0', 'N/A']: + st.log("No packets were captured") + return False + else: + st.log('Number of packets captured: {}'.format(pkt_dict[port_handle]['aggregate']['num_frames'])) + + if kwargs['tg_type'] in ['stc']: + return _verify_packet_capture(pkt_dict,offset_list,value_list,port_handle) + + if kwargs['tg_type'] in ['scapy']: + return _verify_packet_capture(pkt_dict,offset_list,value_list,port_handle, 0) + + if kwargs['tg_type'] in ['ixia']: + if header_list == 'new_ixia_format': + ### _verify_packet_capture_ixia is obsolete from now on, it is there only for legacy script. + return _verify_packet_capture(pkt_dict,offset_list,value_list,port_handle) + else: + return _verify_packet_capture_ixia(pkt_dict,header_list,value_list,port_handle) + +def verify_ping(src_obj,port_handle,dev_handle,dst_ip,ping_count=5,exp_count=5): + ping_count,exp_count = int(ping_count),int(exp_count) + if src_obj.tg_type == 'stc': + result = src_obj.tg_emulation_ping(handle=dev_handle,host=dst_ip,count=ping_count) + st.log("ping output: {}".format(result)) + return True if int(result['tx']) == ping_count and int(result['rx']) == exp_count else False + elif src_obj.tg_type in ['ixia', 'scapy']: + count = 0 + for num in range(ping_count): + result = src_obj.tg_interface_config(protocol_handle=dev_handle,send_ping='1',ping_dst=dst_ip) + st.log("ping output: {}".format(result)) + try: + result = result[port_handle]['ping_details'] + if src_obj.tg_type == 'scapy': + ping_out = re.search(r'([0-9]+)\s+packets transmitted,\s+([0-9]+)\s+received', result) + else: + ping_out = re.search(r'([0-9]+)\s+requests sent,\s+([0-9]+)\s+replies received', result) + tx_pkt,rx_pkt = ping_out.group(1),ping_out.group(2) + if int(tx_pkt) == int(rx_pkt): + count += 1 + except AttributeError: + st.log("ping command o/p not matching regular expression or port_handle details not found in o/p") + return True if count == exp_count else False + else: + st.log("Need to add code for this tg type: {}".format(src_obj.tg_type)) + return False + +def tg_bgp_config(**kwargs): + + """ + Description: + Configures the BGP parameters on the already created host. + + Returns: + Returns the dict of statuses of different procedures used. + Empty dict {} on error. + + Parameters: + tg = (Mandatory) Tgen object. + handle = (Mandatory) Host handle (returned by tg_create_host) if conf_var is sent. + or bgp_handle if conf_var is not used. + conf_var = Dict variable for tg_emulation_bgp_config. + route_var = Single or List of route variables (of type dict). + ctrl_var = Dict variable for tg_emulation_bgp_control. + + Usage: + tg_bgp_conf1 = { 'mode' : 'enable', + 'active_connect_enable' : '1', + 'local_as' : '100', + 'remote_as' : '100', + 'remote_ip_addr' : '21.1.1.1' + } + tg_bgp_route1 = { 'mode' : 'add', + 'num_routes' : '10', + 'prefix' : '121.1.1.0' + } + tg_bgp_ctrl1 = { 'mode' : 'start'} + + bgp_host1 = tg_bgp_config(tg = tg1, + handle = h1['handle'], + conf_var = tg_bgp_conf1, + route_var = tg_bgp_route1, + ctrl_var = tg_bgp_ctrl1) + # Only BGP neighborship and not the routes. + bgp_host1 = tg_bgp_config(tg = tg1, + handle = h1['handle'], + conf_var = tg_bgp_conf1, + ctrl_var = tg_bgp_ctrl1) + """ + + import copy + ret = {} + def_conf_var = { 'mode' : 'enable', + 'active_connect_enable' : '1' + } + def_route_var = { 'mode' : 'add', + } + def_ctrl_var = { 'mode' : 'start', + } + + _log_call("tg_bgp_config", **kwargs) + + for param in ['tg', 'handle']: + if param not in kwargs: + st.log("BGP_ERROR: Mandatory parameter {} is missing.".format(str(param))) + return ret + handle = kwargs['handle'] + tg = kwargs['tg'] + handle = handle['handle'] if 'handle' in handle else handle + hand = handle + + conf_var = {} + route_var = [{}] + ctrl_var = {} + if 'conf_var' in kwargs: + conf_var = copy.deepcopy(kwargs['conf_var']) + if 'route_var' in kwargs: + route_var = copy.deepcopy(kwargs['route_var']) + route_var = list(route_var) if type(route_var) is list else [route_var] + if 'ctrl_var' in kwargs: + ctrl_var = copy.deepcopy(kwargs['ctrl_var']) + + # Copying default values to var. + for k in def_conf_var.keys(): + conf_var[k] = conf_var.get(k,def_conf_var[k]) + for i in range(len(route_var)): + for k in def_route_var.keys(): + route_var[i][k] = route_var[i].get(k,def_route_var[k]) + for k in def_ctrl_var.keys(): + ctrl_var[k] = ctrl_var.get(k,def_ctrl_var[k]) + + bgp_conf={} + bgp_route = [] + bgp_ctrl={} + if 'conf_var' in kwargs: + bgp_conf = tg.tg_emulation_bgp_config(handle=handle, **conf_var) + print(bgp_conf) + hand = bgp_conf['handle'] + + if 'route_var' in kwargs: + for i,var in enumerate(route_var): + bgp_route.append(tg.tg_emulation_bgp_route_config(handle=hand, **var)) + print(bgp_route) + + if 'ctrl_var' in kwargs: + bgp_ctrl = tg.tg_emulation_bgp_control(handle=hand,**ctrl_var) + print(bgp_ctrl) + + ret['conf'] = copy.deepcopy(bgp_conf) + ret['route'] = copy.deepcopy(bgp_route) + ret['ctrl'] = copy.deepcopy(bgp_ctrl) + st.log('Return Status : '+str(ret)) + + return ret + +def tg_igmp_config(**kwargs): + + """ + Description: + Configures the IGMP parameters on the already created host. + + Returns: + Returns the dict of statuses of different procedures used. + Empty dict {} on error. + + Parameters: + tg = (Mandatory) Tgen object. + handle = (Mandatory) Host handle (returned by tg_create_host) if conf_var is sent. + or igmp_handle if conf_var is not used. + session_var = Dict variable for tg_emulation_igmp_config. + group_var = Dict variable for tg_emulation_multicast_group_config. + source_var = Dict variable for tg_emulation_multicast_source_config(Applicable only for V3). + igmp_group_var = Dict variable for tg_emulation_igmp_group_config + + Usage: + session_conf1 = { 'mode' : 'create', + 'count' : '1', + 'version' : 'v3' + } + group_conf1 = { 'mode' : 'create', + 'num_groups' : '10', + 'ip_addr_start' : '225.1.1.1' + 'active' : '1' + } + source_conf1 = { 'mode' : 'create', + 'num_sources' : '10', + 'ip_addr_start' : '11.1.1.1' + 'active' : '1' + } + igmp_group_conf1 = { 'mode' : 'create', + 'g_filter_mode' : 'include' + } + + igmp_host1 = tg_igmp_config(tg = tg1, + handle = h1['handle'], + session_var = session_conf1, + group_var = group_conf1, + source_var = source_conf1, + igmp_group_var = igmp_group_conf1, + """ + + import copy + ret = {} + def_session_var = { 'mode' : 'create', + 'igmp_version' : 'v2' + } + def_group_var = { 'mode' : 'create', + 'active' : '1' + } + def_source_var = {'mode': 'create', + 'active': '0', + 'ip_addr_start': '21.1.1.100', + 'num_sources': '5' + } + def_igmp_group_var = { 'mode' : 'create' } + + _log_call("tg_igmp_config", **kwargs) + + for param in ['tg', 'handle']: + if param not in kwargs: + st.log("IGMP_ERROR: Mandatory parameter {} is missing.".format(str(param))) + return ret + handle = kwargs['handle'] + tg = kwargs['tg'] + handle = handle['handle'] if 'handle' in handle else handle + handle = handle[0] if type(handle) is list else handle + + session_var = {} + group_var = {} + source_var = {} + igmp_group_var = {} + + if 'session_var' in kwargs: + session_var = copy.deepcopy(kwargs['session_var']) + if 'group_var' in kwargs: + group_var = copy.deepcopy(kwargs['group_var']) + if 'source_var' in kwargs: + source_var = copy.deepcopy(kwargs['source_var']) + if 'igmp_group_var' in kwargs: + igmp_group_var = copy.deepcopy(kwargs['igmp_group_var']) + + # Copying default values to var. + for k in def_session_var.keys(): + session_var[k] = session_var.get(k,def_session_var[k]) + for k in def_group_var.keys(): + group_var[k] = group_var.get(k,def_group_var[k]) + for k in def_source_var.keys(): + source_var[k] = source_var.get(k,def_source_var[k]) + for k in def_igmp_group_var.keys(): + igmp_group_var[k] = igmp_group_var.get(k, def_igmp_group_var[k]) + + igmp_session = {} + igmp_group = {} + igmp_source = {} + igmp_config ={} + + if 'session_var' in kwargs: + igmp_session = tg.tg_emulation_igmp_config(handle=handle, **session_var) + st.log(igmp_session) + igmp_group_var['session_handle'] = igmp_session['host_handle'] + + if 'group_var' in kwargs: + igmp_group = tg.tg_emulation_multicast_group_config(**group_var) + st.log(igmp_group) + igmp_group_var['group_pool_handle'] = igmp_group['mul_group_handle'] + + if 'source_var' in kwargs: + source_var['active'] = '1' + igmp_source = tg.tg_emulation_multicast_source_config(**source_var) + st.log(igmp_source) + igmp_group_var['source_pool_handle'] = igmp_source['mul_source_handle'] + + if 'igmp_group_var' in kwargs: + igmp_config = tg.tg_emulation_igmp_group_config(**igmp_group_var) + st.log(igmp_config) + + ret['session'] = copy.deepcopy(igmp_session) + ret['group'] = copy.deepcopy(igmp_group) + ret['source'] = copy.deepcopy(igmp_source) + ret['config'] = copy.deepcopy(igmp_config) + st.log('Return Status : '+str(ret)) + + return ret + + +def get_traffic_stats(tg_obj, **kwargs): + """ + @author: Chaitanya Vella (chaitanya.vella-kumar@broadcom.com) + Common function to get the traffic stats + :param tg_obj: TG object + :param mode: Mode of the stats to be fetched + :param port_handle: Port handler + :return: stats: A Dictionary object with tx/rx packets/bytes stats + """ + stats = SpyTestDict() + stats.tx = SpyTestDict() + stats.rx = SpyTestDict() + if "port_handle" not in kwargs: + st.error("Please provide the port handler") + return False + port_handle = kwargs["port_handle"] + mode = kwargs["mode"] if "mode" in kwargs else "aggregate" + stats_tg = tg_obj.tg_traffic_stats(mode=mode, port_handle=port_handle) + if tg_obj.tg_type in ['ixia', 'scapy']: + stats.tx.total_packets = int(stats_tg[port_handle][mode]['tx']['total_pkts']) + stats.tx.total_bytes = int(stats_tg[port_handle][mode]['tx']['pkt_byte_count']) + stats.rx.total_packets = int(stats_tg[port_handle][mode]['rx']['total_pkts']) + stats.rx.total_bytes = int(stats_tg[port_handle][mode]['rx']['pkt_byte_count']) + stats.rx.oversize_count = 0 + if "oversize_count" in stats_tg[port_handle][mode]['rx']: + stats.rx.oversize_count = int(stats_tg[port_handle][mode]['rx']['oversize_count']) + elif tg_obj.tg_type == 'stc': + stats.tx.total_packets = int(stats_tg[port_handle][mode]['tx']['total_pkts']) + stats.tx.total_bytes = int(stats_tg[port_handle][mode]['tx']['pkt_byte_count']) + stats.rx.total_packets = int(stats_tg[port_handle][mode]['rx']['total_pkts']) + stats.rx.total_bytes = int(stats_tg[port_handle][mode]['rx']['pkt_byte_count']) + stats.rx.oversize_count = int(stats_tg[port_handle][mode]['rx']['oversize_count']) + st.log("*****TG STATS******") + st.log(stats) + st.log("***********") + return stats + +# combine the calls when the tg is same +def port_traffic_control(action, *args, **kwargs): + for arg in args: + tg, tg_ph = arg + if action == "reset_and_clear_stats": + tg.tg_traffic_control(action="reset", port_handle=tg_ph, **kwargs) + tg.tg_traffic_control(action="clear_stats", port_handle=tg_ph, **kwargs) + else: + tg.tg_traffic_control(action=action, port_handle=tg_ph, **kwargs) + diff --git a/spytest/spytest/tgen_api.py b/spytest/spytest/tgen_api.py new file mode 100644 index 00000000000..5a621707777 --- /dev/null +++ b/spytest/spytest/tgen_api.py @@ -0,0 +1,86 @@ +from spytest.framework import get_tgen_utils + +def get_counter_name(mode,tg_type,comp_type,direction): + return get_tgen_utils().get_counter_name(mode,tg_type,comp_type,direction) +def validate_tgen_traffic(**kwargs): + return get_tgen_utils().validate_tgen_traffic(**kwargs) +def validate_packet_capture(**kwargs): + return get_tgen_utils().validate_packet_capture(**kwargs) +def verify_ping(src_obj,port_handle,dev_handle,dst_ip,ping_count=5,exp_count=5): + return get_tgen_utils().verify_ping(src_obj,port_handle,dev_handle,dst_ip,ping_count=5,exp_count=5) +def tg_bgp_config(**kwargs): + return get_tgen_utils().tg_bgp_config(**kwargs) +def tg_igmp_config(**kwargs): + return get_tgen_utils().tg_igmp_config(**kwargs) +def get_traffic_stats(tg_obj, **kwargs): + return get_tgen_utils().get_traffic_stats(tg_obj, **kwargs) +def port_traffic_control(action, *args, **kwargs): + return get_tgen_utils().port_traffic_control(action, *args, **kwargs) + +def get_handle_byname(name, port=None, tg=None): + from spytest.framework import get_work_area + return get_work_area().get_tgen(name, port, tg) + +def get_handles_byname(*args): + rv = dict() + rv["tg_ph_list"] = [] + for i, name in enumerate(args, start=1): + tg, tg_ph = get_handle_byname(name) + rv["tg"] = tg + rv["tg{}".format(i)] = tg + rv["tg_ph_{}".format(i)] = tg_ph + rv["tg_ph_list"].append(tg_ph) + return rv + +def get_chassis(vars, index=0): + from spytest.tgen.tg import tgen_obj_dict + return tgen_obj_dict[vars['tgen_list'][index]] + +def get_handles(vars, tg_port_list=list()): + """ + @author: Chaitanya Vella (chaitanya.vella-kumar@broadcom.com) + This is the common function to get the port handlers based on tg_port_list + :param vars: This is the framework Spytest vars object. + :param tg_port_list: List of TG ports for which port handlers to be created. + :return: Dictionary with tg instance and port handlers + """ + from spytest import st + return_val = dict() + if not tg_port_list: + st.error("tg Port(s) not provided") + return None + tg = get_chassis(vars) + return_val["tg"] = tg + return_val["tg_ph_list"] = [] + for i, port in enumerate(tg_port_list, start=1): + tg_ph = tg.get_port_handle(port) + return_val["tg{}".format(i)] = tg + return_val["tg_ph_{}".format(i)] = tg_ph + return_val["tg_ph_list"].append(tg_ph) + return return_val + + +def traffic_action_control(tg_handler, actions=["reset", "clear_stats"]): + """ + @author: Prudvi Mangadu (prudvi.mangadu@broadcom.com) + @author: Chaitanya Vella (chaitanya.vella-kumar@broadcom.com) + Common function to apply the actions on TG port handlers + :param tg_handler: List of TG port handlers for which action to be performed. + :param actions: Default actions are reset and clear_stats, if users wants other this, they have to provided in list. + :return: Returns TG handler object. + """ + from spytest import st + if "tg" not in tg_handler: + st.error("tg object is not available") + return None + tg_port_handler = list() + for each_item in tg_handler: + if 'tg_ph_list' in each_item: + continue + if 'tg_ph_' in each_item: + tg_port_handler.append(tg_handler[each_item]) + st.debug("tg_port_handler {}".format(tg_port_handler)) + for action in actions: + tg_handler["tg"].tg_traffic_control(action=action, port_handle=tg_port_handler) + return tg_handler + diff --git a/spytest/spytest/uicli.py b/spytest/spytest/uicli.py new file mode 100644 index 00000000000..33f40f6bdf3 --- /dev/null +++ b/spytest/spytest/uicli.py @@ -0,0 +1,1208 @@ +import re +import string +import copy +import random +import time +from spytest.logger import Logger +from spytest.st_time import get_timenow + +class UICLI(object): + def __init__(self, logger=None, testbed_vars=None, scriptname=None): + self.logger = logger or Logger() + self.tb_vars = testbed_vars + self.script = scriptname + + def uicli_log(self, msg, width=120, delimiter="#", header=True, footer=True): + msg_list = [] + if header: + msg_list.append(delimiter * width) + if msg != None: + if isinstance(msg, list): + for eachline in msg: + if isinstance(eachline, list): + msg_list.append("{0} {1} {0}".format(delimiter, " : ".join(str(e) for e in eachline).center(width - 4))) + else: + msg_list.append("{0} {1} {0}".format(delimiter, str(eachline).center(width - 4))) + else: + msg_list.append("{0} {1} {0}".format(delimiter, msg.center(width - 4))) + if footer: + msg_list.append(delimiter * width) + for each_line in msg_list: + self.logger.info(each_line) + + def _uicli_get_config_mode_arg_values_dict(self, all_params, stepentry, replaced_mode_values): + try: + if "configs" in stepentry.keys(): + for config_step in stepentry["configs"]: + mode = config_step.get("mode", None) + + if len(mode) > 1: + (mode_name, mode_args) = mode + for key, value in mode_args.items(): + matched = re.match(r'\$(\S+)\$(\S+)\$', str(value).strip()) + if not matched: + replaced_mode_values[value] = value + continue + + param_data = matched.group(0) + param_name = matched.group(1) + param_type = matched.group(2) + + if param_data not in replaced_mode_values and param_type in all_params.keys(): + #param_dict = all_params[param_type] + #changed_value = self._uicli_get_random_value_for_param(param_name, param_type, param_dict, "argument") + changed_value = self._uicli_get_valueset_for_param(param_name, param_type, all_params, "argument") + replaced_mode_values[param_data] = changed_value + except: + pass + + #print("Updated modes - configs", replaced_mode_values) + #import pdb; pdb.set_trace() + return + + def _uicli_get_config_cmd_param_values_list(self, all_params, stepentry, replaced_cmd_params): + try: + if "configs" in stepentry.keys(): + for config_step in stepentry["configs"]: + cfg_section = config_step.get("config", None) + if cfg_section: + cfg_command = cfg_section.get("command", None) + + if cfg_section and cfg_command: + matched_list = re.findall(r'\$\S+\$\S+\$', str(cfg_command).strip()) + for matched in matched_list: + matched_values = re.match(r'\$(\S+)\$(\S+)\$', str(matched).strip()) + param_data = matched_values.group(0) + param_name = matched_values.group(1) + param_type = matched_values.group(2) + #param_data_esc = re.escape(param_data) + # print(param_data, param_name, param_type) + + #import pdb; pdb.set_trace() + if param_data not in replaced_cmd_params and param_type in all_params.keys(): + #param_dict = all_params[param_type] + #changed_value = self._uicli_get_random_value_for_param(param_name, param_type, param_dict, "parameter") + changed_value = self._uicli_get_valueset_for_param(param_name, param_type, all_params, "parameter") + if isinstance(changed_value, list): + replaced_cmd_params[param_data] = changed_value + else: + replaced_cmd_params[param_data] = str(changed_value) + except: + pass + + #print("Updated cmd params - configs", replaced_cmd_params) + #import pdb; pdb.set_trace() + return + + def _uicli_get_action_mode_arg_values_dict(self, all_params, stepentry, replaced_mode_values): + try: + if "actions" in stepentry.keys(): + for action_step in stepentry["actions"]: + mode = action_step.get("mode", None) + + if len(mode) > 1: + (mode_name, mode_args) = mode + for key, value in mode_args.items(): + matched = re.match(r'\$(\S+)\$(\S+)\$', str(value).strip()) + if not matched: + replaced_mode_values[value] = value + continue + + param_data = matched.group(0) + param_name = matched.group(1) + param_type = matched.group(2) + + if param_data not in replaced_mode_values and param_type in all_params.keys(): + #param_dict = all_params[param_type] + #changed_value = self._uicli_get_random_value_for_param(param_name, param_type, param_dict, "argument") + changed_value = self._uicli_get_valueset_for_param(param_name, param_type, all_params, "argument") + replaced_mode_values[param_data] = changed_value + except: + pass + + #print("Updated modes - actions", replaced_mode_values) + #import pdb; pdb.set_trace() + return + + def _uicli_get_action_cmd_param_values_list(self, all_params, stepentry, replaced_cmd_params): + try: + if "actions" in stepentry.keys(): + for action_step in stepentry["actions"]: + action_section = action_step.get("action", None) + if action_section: + action_command = action_section.get("command", None) + + if action_section and action_command: + matched_list = re.findall(r'\$\S+\$\S+\$', str(action_command).strip()) + for matched in matched_list: + matched_values = re.match(r'\$(\S+)\$(\S+)\$', str(matched).strip()) + param_data = matched_values.group(0) + param_name = matched_values.group(1) + param_type = matched_values.group(2) + #param_data_esc = re.escape(param_data) + # print(param_data, param_name, param_type) + + # import pdb; pdb.set_trace() + if param_data not in replaced_cmd_params and param_type in all_params.keys(): + #param_dict = all_params[param_type] + #changed_value = self._uicli_get_random_value_for_param(param_name, param_type, param_dict, "parameter") + changed_value = self._uicli_get_valueset_for_param(param_name, param_type, all_params, "parameter") + if isinstance(changed_value, list): + replaced_cmd_params[param_data] = changed_value + else: + replaced_cmd_params[param_data] = str(changed_value) + except: + pass + + #print("Updated cmd params - actions", replaced_cmd_params) + #import pdb; pdb.set_trace() + return + + def _uicli_get_preconfig_mode_arg_values_dict(self, all_params, stepentry, replaced_mode_values): + try: + if "pre-configs" in stepentry.keys(): + for config_step in stepentry["pre-configs"]: + mode = config_step.get("mode", None) + + if len(mode) > 1: + (mode_name, mode_args) = mode + for key, value in mode_args.items(): + matched = re.match(r'\$(\S+)\$(\S+)\$', str(value).strip()) + if not matched: + replaced_mode_values[value] = value + continue + + param_data = matched.group(0) + param_name = matched.group(1) + param_type = matched.group(2) + + if param_data not in replaced_mode_values and param_type in all_params.keys(): + #param_dict = all_params[param_type] + #changed_value = self._uicli_get_random_value_for_param(param_name, param_type, param_dict, "argument") + changed_value = self._uicli_get_valueset_for_param(param_name, param_type, all_params, "argument") + replaced_mode_values[param_data] = changed_value + except: + pass + + #print("Updated modes - pre-configs", replaced_mode_values) + #import pdb; pdb.set_trace() + return + + def _uicli_get_preconfig_cmd_param_values_list(self, all_params, stepentry, replaced_cmd_params): + try: + if "pre-configs" in stepentry.keys(): + for config_step in stepentry["pre-configs"]: + cfg_section = config_step.get("pre-config", None) + if cfg_section: + cfg_command = cfg_section.get("command", None) + + if cfg_section and cfg_command: + matched_list = re.findall(r'\$\S+\$\S+\$', str(cfg_command).strip()) + for matched in matched_list: + matched_values = re.match(r'\$(\S+)\$(\S+)\$', str(matched).strip()) + param_data = matched_values.group(0) + param_name = matched_values.group(1) + param_type = matched_values.group(2) + #param_data_esc = re.escape(param_data) + # print(param_data, param_name, param_type) + + #import pdb; pdb.set_trace() + if param_data not in replaced_cmd_params and param_type in all_params.keys(): + #param_dict = all_params[param_type] + #changed_value = self._uicli_get_random_value_for_param(param_name, param_type, param_dict, "parameter") + changed_value = self._uicli_get_valueset_for_param(param_name, param_type, all_params, "parameter") + if isinstance(changed_value, list): + replaced_cmd_params[param_data] = changed_value + else: + replaced_cmd_params[param_data] = str(changed_value) + except: + pass + + #print("Updated cmd params - pre-configs", replaced_cmd_params) + #import pdb; pdb.set_trace() + return + + def _uicli_substitute_args_params(self, all_params, stepentry, replaced_mode_values, replaced_cmd_params): + + changed_steps = [] + curr_cfg_cmds_list = [] + curr_precfg_cmds_list = [] + + minIndex = 0 + maxIndex = 5 + + for index in range(minIndex, maxIndex): + copied_step = copy.deepcopy(stepentry) + no_need_to_add = False + + try: + if "pre-configs" in copied_step.keys(): + for preconfig_step in copied_step["pre-configs"]: + cfg_mode = preconfig_step.get("mode", None) + cfg_section = preconfig_step.get("pre-config", None) + cfg_valid = preconfig_step.get("valid", 1) + if index > 2: + cfg_valid = int(not cfg_valid) + + if cfg_section: + cfg_command = cfg_section.get("command", None) + + # Replace the mode values in arg strings + if len(cfg_mode) > 1: + (mode_name, mode_args) = cfg_mode + for key, value in mode_args.items(): + if replaced_mode_values.get(value, None) is not None: + mode_args.update({key: replaced_mode_values.get(value)}) + + if cfg_section and cfg_command: + if not replaced_cmd_params: + if cfg_command not in curr_precfg_cmds_list: + curr_precfg_cmds_list.append(cfg_command) + for param_data in replaced_cmd_params.keys(): + param_data_esc = re.escape(param_data) + replace_with = replaced_cmd_params.get(param_data)[index] + if replace_with is not None: + replace_with = str(replace_with) + cfg_command = re.sub(param_data_esc, replace_with, cfg_command) + if cfg_command not in curr_precfg_cmds_list: + curr_precfg_cmds_list.append(cfg_command) + + # Update the step values with replaced data + preconfig_step.update({"mode": cfg_mode}) + cfg_section.update({"command": cfg_command}) + cfg_section.update({"valid": cfg_valid}) + + if "configs" in copied_step.keys(): + for config_step in copied_step["configs"]: + cfg_mode = config_step.get("mode", None) + cfg_section = config_step.get("config", None) + cfg_valid = config_step.get("valid", 1) + if index > 2: + cfg_valid = int(not cfg_valid) + + if cfg_section: + cfg_command = cfg_section.get("command", None) + + # Replace the mode values in arg strings + if len(cfg_mode) > 1: + (mode_name, mode_args) = cfg_mode + for key, value in mode_args.items(): + if replaced_mode_values.get(value, None) is not None: + mode_args.update({key: replaced_mode_values.get(value)}) + + if cfg_section and cfg_command: + for param_data in replaced_cmd_params.keys(): + param_data_esc = re.escape(param_data) + replace_with = replaced_cmd_params.get(param_data)[index] + if replace_with is not None: + replace_with = str(replace_with) + cfg_command = re.sub(param_data_esc, replace_with, cfg_command) + else: + no_need_to_add = True + if not replaced_cmd_params: + if cfg_command not in curr_cfg_cmds_list: + curr_cfg_cmds_list.append(cfg_command) + else: + no_need_to_add = True + elif not no_need_to_add: + if cfg_command not in curr_cfg_cmds_list: + curr_cfg_cmds_list.append(cfg_command) + else: + no_need_to_add = True + + # Update the step values with replaced data + config_step.update({"mode": cfg_mode}) + cfg_section.update({"command": cfg_command}) + cfg_section.update({"valid": cfg_valid}) + + if no_need_to_add: + continue + + if "actions" in copied_step.keys(): + for action_step in copied_step["actions"]: + action_mode = action_step.get("mode", None) + action_section = action_step.get("action", None) + action_valid = action_step.get("valid", 1) + if index > 2: + action_valid = int(not action_valid) + + if action_section: + action_command = action_section.get("command", None) + action_matches = action_section.get("match", None) + + # Replace the mode values in arg strings + if len(action_mode) > 1: + (mode_name, mode_args) = action_mode + for key, value in mode_args.items(): + if replaced_mode_values.get(value, None) is not None: + mode_args.update({key: replaced_mode_values.get(value)}) + + if action_section and action_command: + for param_data in replaced_cmd_params.keys(): + param_data_esc = re.escape(param_data) + replace_with = replaced_cmd_params.get(param_data)[index] + if replace_with is not None: + replace_with = str(replace_with) + action_command = re.sub(param_data_esc, replace_with, action_command) + else: + no_need_to_add = True + + # Substitute the param values in match values. + for match_dict in action_matches: + for key, value in match_dict.items(): + if value in replaced_mode_values: + changed_value = replaced_mode_values[value] + elif value in replaced_cmd_params: + changed_value = replaced_cmd_params[value][index] + else: + matched = re.match(r'\$(\S+)\$(\S+)\$', str(value).strip()) + if matched: + param_data = matched.group(0) + param_name = matched.group(1) + param_type = matched.group(2) + #param_dict = all_params[param_type] + changed_value = self._uicli_get_valueset_for_param(param_name, param_type, all_params, "match") + else: + changed_value = value + match_dict.update({key: str(changed_value)}) + + action_step.update({"mode": action_mode}) + action_section.update({"command": action_command}) + if action_matches: + action_section.update({"match": action_matches}) + action_section.update({"valid": action_valid}) + + if not no_need_to_add: + changed_steps.append(copied_step) + except: + pass + + #print("Changed Steps", changed_steps) + if not changed_steps: + changed_steps.append(copy.deepcopy(stepentry)) + + #import pdb; pdb.set_trace() + return changed_steps + + def _uicli_get_valueset_for_param(self, param_name, param_type, all_params, datatype): + retval = "TODO" + + param_dict = all_params[param_type] + method = param_dict.get("method", None) + pattern = param_dict.get("pattern", None) + ip_address_patterns = ["INT_OR_IP_ADDR", "IP_ADDR", "IP_ADDR_ANY", + "IP_ADDR_DHCP_SUBNET", "IP_ADDR_DHCP_SUBNET_IPV4IPV6", + "IP_ADDR_MASK", "IPADDR_NN", "IPV4_ADDR_ABC", + "IPV4_IPV6_NETWORK", "IPV4_OR_IPV6_ADDR", "INT32_OR_IP_ADDR", + "IPV4V6_ADDR", "IPV6_ADDR", "IPV6_ADDR_MASK", "DOTTED_QUAD", "AA_NN_IPADDR_NN", + "HOSTNAME_OR_IPADDR", "HOSTNAME_OR_IPV4_ADDR", "RD", "RT"] + + if param_type.startswith("SPYTEST_"): + if param_name == "bgp_instance": + retval = 1 + return retval + + if param_name == "bgp_vrf_name": + retval = "Vrf_test" + #minLen = 1 + #maxLen = 28 + #letters = string.ascii_letters + string.digits + #stringLength = random.randint(minLen, maxLen) + #retval = 'Vrf_' + ''.join(random.choice(letters) for i in range(stringLength)) + return retval + + if method in ["integer", "unsignedInteger"]: + (min, max) = re.match(r'(\d+)\.\.(\d+)', pattern).groups() + retval = random.randint(int(min), int(max)) + if method == "UINT" and "INTERFACE" in param_type: + if self.tb_vars.connected_ports: + retval = self.tb_vars.connected_ports[0] + else: + retval = self.tb_vars.free_ports[0] + if datatype not in ["argument", "match"]: + retval = re.sub("Ethernet", "", retval) + if method in ["select"]: + choices = re.findall(r"(\S+)\(\S+\)", pattern) + retval = random.choice(choices) + if method == "string": + while True: + letters = string.ascii_letters + string.digits + '_-' + minLen = 1 + maxLen = 512 + stringLength = random.randint(minLen, maxLen) + retval = ''.join(random.choice(letters) for i in range(stringLength)) + if re.match(pattern, retval): + break + if method == "ipaddress": + while True: + retval = '.'.join(str(random.randint(0, 255)) for _ in range(4)) + if re.match(pattern, retval): + break + elif method: + if method in ["integer", "unsignedInteger"]: + if param_name in ["as-num-dot", "asnum", "as-number"]: + if datatype in ["argument", "match"]: + retval = 1 + else: + retval = [1, 1, 1, None, None] + return retval + + (min, max) = re.match(r'([-+]?\d+)\.\.([-+]?\d+)', pattern).groups() + if datatype in ["argument", "match"]: + retval = random.randint(int(min), int(max)) + else: + if min == max: + retval = [int(min), None, None, int(min)-1, int(max)+1] + else: + #retval = random.randint(int(min), int(max)) + randNum = random.randint(int(min), int(max)) + retval = [int(min), int(max), randNum, int(min)-1, int(max)+1] + #retval = [int(min), int(max), random.randint(int(min), int(max))] + elif method in ["select"]: + if "(" in pattern: + choices = re.findall(r"(\S+)\(\S+\)", pattern) + else: + choices = re.findall(r"(\S+)", pattern) + if datatype in ["argument", "match"]: + retval = random.choice(choices) + else: + #retval = random.choice(choices) + retval = [choices[0], choices[-1], random.choice(choices), None, None] + #retval = [choices[0], choices[-1], random.choice(choices)] + elif method in ["regexp_select"]: + if param_type == "PHY_INTERFACE": + if self.tb_vars.connected_ports: + retval = self.tb_vars.connected_ports[0] + else: + retval = self.tb_vars.free_ports[0] + if datatype not in ["argument", "match"]: + retval = re.sub("Ethernet", "Ethernet ", retval) + retval = [retval, retval, retval, None, None] + elif param_type == "VLAN_INTERFACE": + vid_pattern = all_params["VLAN_ID"].get("pattern", None) + (min, max) = re.match(r'([-+]?\d+)\.\.([-+]?\d+)', vid_pattern).groups() + if datatype in ["argument", "match"]: + retval = "Vlan {}".format(random.randint(int(min), int(max))) + else: + randNum = random.randint(int(min), int(max)) + retval = [int(min), int(max), randNum, int(min) - 1, int(max) + 1] + retval = ["Vlan " + str(ele) for ele in retval] + else: + retval = "TODO"; # TODO + else: + retval = "TODO"; # TODO + else: + if param_type == "UINT": + if param_name.startswith("phy-if-") or param_name in ["if-id", "PLK", "ifnum", "ptp_port_number"]: + if self.tb_vars.connected_ports: + retval = self.tb_vars.connected_ports[0] + else: + retval = self.tb_vars.free_ports[0] + if datatype not in ["argument", "match"]: + retval = re.sub("Ethernet", "", retval) + retval = [retval, retval, retval, None, None] + elif param_name == "zone": + #retval = str(random.randint(0, 255)) + min = 0 + max = 3 + if datatype in ["argument", "match"]: + retval = str(random.randint(min, max)) + else: + #retval = str(random.randint(min, max)) + retval = [min, max, random.randint(min, max), min-1, max+1] + #retval = [min, max, random.randint(min, max)] + else: + #retval = str(random.randint(0, 65535)) + min = 0 + max = pow(2, 32) - 1 + if datatype in ["argument", "match"]: + retval = str(random.randint(min, max)) + else: + #retval = str(random.randint(min, max)) + retval = [min, max, random.randint(min, max), min-1, max+1] + #retval = [min, max, random.randint(min, max)] + elif param_type.startswith("STRING") or param_type.startswith("HOSTNAME_STR"): + if param_name.startswith("ifId"): + if self.tb_vars.connected_ports: + retval = self.tb_vars.connected_ports[0] + else: + retval = self.tb_vars.free_ports[0] + retval = re.sub("Ethernet", "", retval) + if datatype not in ["argument", "match"]: + retval = [retval, retval, retval, None, None] + return retval + + intf_names = ["phy-if-id", "interface", "interfacename", "interface-name", "intf-name", "mrouter-if-name", "grp-if-name", "donor-interface", "ifname", "ifName", "ifName1"] + if param_name in intf_names: + if self.tb_vars.connected_ports: + retval = self.tb_vars.connected_ports[0] + else: + retval = self.tb_vars.free_ports[0] + if datatype not in ["argument", "match"]: + retval = [retval, retval, retval, None, None] + return retval + + if "WITH_PIPE" in param_type and param_name == "cmd": + retval = random.choice(["ls", "whoami", "hostname"]) + if datatype not in ["argument", "match"]: + retval = [retval, None, None, None, None] + return retval + + if param_name == "rl": + choices = ["admin", "operator"] + if datatype in ["argument", "match"]: + retval = random.choice(choices) + else: + retval = [choices[0], choices[-1], random.choice(choices), None, None] + return retval + + if param_name == "date": + valid_val = get_timenow().strftime("%Y-%m-%dT%H:%M:%SZ") + invalid_val = get_timenow().strftime("%Y-%m-%dT%H:%M:%S") + time.sleep(1) + retval = valid_val + if datatype not in ["argument", "match"]: + retval = [valid_val, None, None, None, invalid_val] + return retval + + if param_name in ["vrf-name"] and param_type in ["STRING", "STRING_15"] and self.script in ["bgp"]: + if datatype in ["argument", "match"]: + retval = "Vrf_test" + else: + retval = ["Vrf_test", "Vrf_test", "Vrf_test", None, None] + return retval + + vrf_string_types = ["STRING", "STRING_15", "STRING_63"] + if param_name in ["vrfname"] and param_type in vrf_string_types: + minLen = 1 + maxLen = 28 + if param_type.startswith("STRING_"): + maxLen = int(re.sub("STRING_", "", param_type)) + letters = string.ascii_letters + string.digits + stringLength = random.randint(minLen, maxLen) + if datatype in ["argument", "match"]: + #retval = ''.join(random.choice(letters) for i in range(stringLength)) + retval = 'Vrf_' + ''.join(random.choice(letters) for i in range(stringLength)) + else: + minStr = 'Vrf_' + ''.join(random.choice(letters) for i in range(minLen)) + maxStr = 'Vrf_' + ''.join(random.choice(letters) for i in range(maxLen)) + randStr = 'Vrf_' + ''.join(random.choice(letters) for i in range(stringLength)) + invalidMaxStr = 'Vrf_' + ''.join(random.choice(letters) for i in range(maxLen + 1)) + invalidMinStr = ''.join(random.choice(letters) for i in range(maxLen + 1)) + retval = [minStr, maxStr, randStr, invalidMinStr, invalidMaxStr] + return retval + + if param_type.startswith("STRING"): + search_pattern = "{}_".format("STRING") + elif param_type.startswith("HOSTNAME_STR"): + search_pattern = "{}_".format("HOSTNAME_STR") + else: + search_pattern = "{}_".format("TODO") + + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + letters = string.ascii_letters + string.digits + '_-' + minLen = 1 + maxLen = 1 + if param_type.startswith(search_pattern): + lengths_part = re.sub(search_pattern, "", param_type) + if "_" in lengths_part: + min_part = lengths_part.split("_")[0] + max_part = lengths_part.split("_")[-1] + else: + min_part = "1" + max_part = lengths_part + try: + minLen = int(min_part) + maxLen = int(max_part) + except: + pass + else: + maxLen = 512 + stringLength = random.randint(minLen, maxLen) + if datatype in ["argument", "match"]: + retval = ''.join(random.choice(letters) for i in range(stringLength)) + else: + #retval = ''.join(random.choice(letters) for i in range(stringLength)) + minStr = ''.join(random.choice(letters) for i in range(minLen)) + maxStr = ''.join(random.choice(letters) for i in range(maxLen)) + randStr = ''.join(random.choice(letters) for i in range(stringLength)) + retval = [minStr, maxStr, randStr, None, None] + #retval = [minStr, maxStr, randStr] + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type.startswith("PASSWORD_STR"): + minLen = 1 + maxLen = 512 + letters = string.ascii_letters + string.digits + '_-' + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + stringLength = random.randint(minLen, maxLen) + if datatype in ["argument", "match"]: + retval = ''.join(random.choice(letters) for i in range(stringLength)) + else: + minStr = ''.join(random.choice(letters) for i in range(minLen)) + maxStr = ''.join(random.choice(letters) for i in range(maxLen)) + randStr = ''.join(random.choice(letters) for i in range(stringLength)) + retval = [minStr, maxStr, randStr, None, None] + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ip_address_patterns: + min_ipv4_mask = "1" + min_ipv6_mask = "1" + max_ipv4_mask = "32" + max_ipv6_mask = "128" + #min_ipv4_address = "0.0.0.0" + #min_ipv6_address = "0:0:0:0:0:0:0:0"; #"::" + #max_ipv4_address = "255.255.255.255" + #max_ipv6_address = "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF" + + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + rand_ipv4_mask = str(random.randint(int(min_ipv4_mask), int(max_ipv4_mask))) + rand_ipv6_mask = str(random.randint(int(min_ipv6_mask), int(max_ipv6_mask))) + rand_ipv4_address = '.'.join(str(random.randint(0,255)) for _ in range(4)) + rand_ipv6_address = ':'.join(''.join(random.choice(string.hexdigits).lower() for _ in range(4)) for _ in range(8)) + + if "MASK" in param_type or "SUBNET" in param_type or "NETWORK" in param_type: + # min_ipv4_address = "{}/{}".format(min_ipv4_address, min_ipv4_mask) + # max_ipv4_address = "{}/{}".format(max_ipv4_address, max_ipv4_mask) + # min_ipv6_address = "{}/{}".format(min_ipv6_address, min_ipv6_mask) + # max_ipv6_address = "{}/{}".format(max_ipv6_address, max_ipv6_mask) + rand_ipv4_address = "{}/{}".format(rand_ipv4_address, rand_ipv4_mask) + rand_ipv6_address = "{}/{}".format(rand_ipv6_address, rand_ipv6_mask) + + if "IPADDR_NN" in param_type or param_type in ["RD", "RT"]: + aa_nn_val = str(random.randint(0, 65535)) + rand_ipv4_address = "{}:{}".format(rand_ipv4_address, aa_nn_val) + rand_ipv6_address = "{}:{}".format(rand_ipv6_address, aa_nn_val) + + if datatype in ["argument", "match"]: + if "V6" in param_type: + retval = rand_ipv6_address + else: + retval = rand_ipv4_address + else: + if "V6" in param_type: + #retval = [min_ipv6_address, max_ipv6_address, rand_ipv6_address, None, None] + retval = [None, None, rand_ipv6_address, None, None] + else: + #retval = [min_ipv4_address, max_ipv4_address, rand_ipv4_address, None, None] + retval = [None, None, rand_ipv4_address, None, None] + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["MAC_ADDR"]: + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + rand_mac_address = ':'.join(''.join(random.choice(string.hexdigits).lower() for _ in range(2)) for _ in range(6)) + if datatype in ["argument", "match"]: + retval = rand_mac_address + else: + retval = [None, None, rand_mac_address, None, None] + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["HEX_TYPE"]: + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + min_hex_str = "0x0" + rand_hex_str = '0x' + ''.join(random.choice(string.hexdigits).lower() for _ in range(6)) + max_hex_str = "0xFFFFFF" + invalid_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(6)) + if datatype in ["argument", "match"]: + retval = rand_hex_str + else: + retval = [min_hex_str, max_hex_str, rand_hex_str, None, invalid_hex_str] + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["PTP_V6SCOPE_TYPE"]: + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + min_hex_str = "0x0" + rand_hex_str = '0x' + random.choice(string.hexdigits).lower() + max_hex_str = "0xF" + invalid_hex_str = random.choice(string.hexdigits).lower() + if datatype in ["argument", "match"]: + retval = rand_hex_str + else: + retval = [min_hex_str, max_hex_str, rand_hex_str, None, invalid_hex_str] + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["TACACS_KEY", "RADIUS_KEY"]: + minLen = 1 + maxLen = 65 + letters = string.ascii_letters + string.digits + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + stringLength = random.randint(minLen, maxLen) + if datatype in ["argument", "match"]: + retval = ''.join(random.choice(letters) for i in range(stringLength)) + else: + minStr = ''.join(random.choice(letters) for i in range(minLen)) + maxStr = ''.join(random.choice(letters) for i in range(maxLen)) + randStr = ''.join(random.choice(letters) for i in range(stringLength)) + invalidMaxStr = ''.join(random.choice(letters) for i in range(maxLen+1)) + retval = [minStr, maxStr, randStr, None, invalidMaxStr] + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["RADIUS_VRF"]: + minLen = 1 + maxLen = 28 + letters = string.ascii_letters + string.digits + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + stringLength = random.randint(minLen, maxLen) + if datatype in ["argument", "match"]: + #retval = ''.join(random.choice(letters) for i in range(stringLength)) + retval = 'Vrf_' + ''.join(random.choice(letters) for i in range(stringLength)) + else: + minStr = 'Vrf_' + ''.join(random.choice(letters) for i in range(minLen)) + maxStr = 'Vrf_' + ''.join(random.choice(letters) for i in range(maxLen)) + randStr = 'Vrf_' + ''.join(random.choice(letters) for i in range(stringLength)) + invalidMaxStr = 'Vrf_' + ''.join(random.choice(letters) for i in range(maxLen + 1)) + invalidMinStr = ''.join(random.choice(letters) for i in range(maxLen + 1)) + retval = [minStr, maxStr, randStr, invalidMinStr, invalidMaxStr] + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type == "FILE_TYPE": + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + letters = string.ascii_letters + randStr = "file://" + ''.join(random.choice(letters) for i in range(10)) + if datatype in ["argument", "match"]: + retval = randStr + else: + retval = [None, None, randStr, None, None] + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type == "AA_NN": + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + rand_aa_nn_val = "{}:{}".format(str(random.randint(0, 65535)), str(random.randint(0, 65535))) + if datatype in ["argument", "match"]: + retval = rand_aa_nn_val + else: + retval = [None, None, rand_aa_nn_val, None, None] + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type == "KDUMP_MEMORY": + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + rand_kdump_val = "0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M" + if datatype in ["argument", "match"]: + retval = rand_kdump_val + else: + retval = [None, None, rand_kdump_val, None, None] + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["AUTH_KEY_TYPE"]: + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + min_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(32)) + max_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(32)) + rand_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(32)) + invalid_hex_str1 = ''.join(random.choice(string.hexdigits).lower() for _ in range(30)) + invalid_hex_str2 = ''.join(random.choice(string.hexdigits).lower() for _ in range(34)) + if datatype in ["argument", "match"]: + retval = rand_hex_str + else: + retval = [min_hex_str, max_hex_str, rand_hex_str, invalid_hex_str1, invalid_hex_str2] + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["SHA_AUTH_KEY_TYPE"]: + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + min_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(40)) + max_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(40)) + rand_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(40)) + invalid_hex_str1 = ''.join(random.choice(string.hexdigits).lower() for _ in range(38)) + invalid_hex_str2 = ''.join(random.choice(string.hexdigits).lower() for _ in range(42)) + if datatype in ["argument", "match"]: + retval = rand_hex_str + else: + retval = [min_hex_str, max_hex_str, rand_hex_str, invalid_hex_str1, invalid_hex_str2] + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["ENGINE_ID_TYPE"]: + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + min_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(10)) + max_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(64)) + rand_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(random.randint(10, 64))) + invalid_hex_str1 = ''.join(random.choice(string.hexdigits).lower() for _ in range(9)) + invalid_hex_str2 = ''.join(random.choice(string.hexdigits).lower() for _ in range(65)) + if datatype in ["argument", "match"]: + retval = rand_hex_str + else: + retval = [min_hex_str, max_hex_str, rand_hex_str, invalid_hex_str1, invalid_hex_str2] + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["OID_IDENTIFIER"]: + minLen = 1 + maxLen = 255 + letters = string.digits + '.' + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + stringLength = random.randint(minLen, maxLen) + if datatype in ["argument", "match"]: + retval = ''.join(random.choice(letters) for i in range(stringLength)) + else: + minStr = ''.join(random.choice(letters) for i in range(minLen)) + maxStr = ''.join(random.choice(letters) for i in range(maxLen)) + randStr = ''.join(random.choice(letters) for i in range(stringLength)) + invalidStr1 = ''.join(random.choice(letters) for i in range(minLen - 1)) + invalidStr2 = ''.join(random.choice(letters) for i in range(maxLen + 1)) + retval = [minStr, maxStr, randStr, invalidStr1, invalidStr2] + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["SNMP_IDENTIFIER"]: + minLen = 1 + maxLen = 32 + letters = string.ascii_letters + string.digits + '_-' + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + stringLength = random.randint(minLen, maxLen) + if datatype in ["argument", "match"]: + retval = ''.join(random.choice(letters) for i in range(stringLength)) + else: + minStr = ''.join(random.choice(letters) for i in range(minLen)) + maxStr = ''.join(random.choice(letters) for i in range(maxLen)) + randStr = ''.join(random.choice(letters) for i in range(stringLength)) + invalidStr1 = ''.join(random.choice(letters) for i in range(minLen - 1)) + invalidStr2 = ''.join(random.choice(letters) for i in range(maxLen + 1)) + retval = [minStr, maxStr, randStr, invalidStr1, invalidStr2] + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["LINE"]: + minLen = 1 + maxLen = 512 + letters = string.ascii_letters + string.digits + '_-' + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + stringLength = random.randint(minLen, maxLen) + if datatype in ["argument", "match"]: + retval = ''.join(random.choice(letters) for i in range(stringLength)) + else: + minStr = ''.join(random.choice(letters) for i in range(minLen)) + maxStr = ''.join(random.choice(letters) for i in range(maxLen)) + randStr = ''.join(random.choice(letters) for i in range(stringLength)) + invalidStr1 = ''.join(random.choice(letters) for i in range(minLen - 1)) + invalidStr2 = ''.join(random.choice(letters) for i in range(maxLen + 1)) + retval = [minStr, maxStr, randStr, invalidStr1, invalidStr2] + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["VLAN_RANGE"]: + min = 1 + max = 4094 + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + randNum = random.randint(min, max) + if datatype in ["argument", "match"]: + retval = randNum + else: + retval = [min, max, randNum, min-1, max+1] + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, str(each_val)): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["STR_ASN_LST"]: + req_pattern = all_params["RANGE_1_4294967295"].get("pattern", None) + (min, max) = re.match(r'([-+]?\d+)\.\.([-+]?\d+)', req_pattern).groups() + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + randNum = random.randint(min, max) + if datatype in ["argument", "match"]: + retval = randNum + else: + retval = [min, max, randNum, min-1, max+1] + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, str(each_val)): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + + + # TODO: Need to do for other types such as IP, HOSTNAME , etc. + + #print("Random_value:", retval) + return retval + diff --git a/spytest/spytest/uignmi.py b/spytest/spytest/uignmi.py new file mode 100644 index 00000000000..31021a37563 --- /dev/null +++ b/spytest/spytest/uignmi.py @@ -0,0 +1,1074 @@ +import re +import string +import copy +import random +import json +from spytest.logger import Logger + +class UIGnmi(object): + def __init__(self, logger=None, testbed_vars=None): + self.logger = logger or Logger() + self.tb_vars = testbed_vars + + def uignmi_log(self, msg, width=120, delimiter="#", header=True, footer=True): + msg_list = [] + if header: + msg_list.append(delimiter * width) + if msg != None: + if isinstance(msg, list): + for eachline in msg: + if isinstance(eachline, list): + msg_list.append("{0} {1} {0}".format(delimiter, " : ".join(str(e) for e in eachline).center(width - 4))) + else: + msg_list.append("{0} {1} {0}".format(delimiter, str(eachline).center(width - 4))) + else: + msg_list.append("{0} {1} {0}".format(delimiter, msg.center(width - 4))) + if footer: + msg_list.append(delimiter * width) + for each_line in msg_list: + self.logger.info(each_line) + + def _uignmi_get_config_mode_path_values(self, all_path_args, all_params, stepentry, replaced_mode_values): + try: + if "configs" in stepentry.keys(): + for config_step in stepentry["configs"]: + path = config_step.get("path", None) + op = config_step.get("operation", None) + + if path and op: + path_plus_op = "{};{}".format(path, op) + if path_plus_op in all_path_args: + for key, value in all_path_args[path_plus_op].items(): + if key not in replaced_mode_values: + changed_value = self._uignmi_get_valueset_for_param(all_params, path_plus_op, key, value, "path") + replaced_mode_values[key] = changed_value + except: + pass + return + + def _uignmi_get_config_mode_data_values(self, all_data_args, all_params, stepentry, replaced_mode_values, replaced_cmd_params): + try: + if "configs" in stepentry.keys(): + for config_step in stepentry["configs"]: + path = config_step.get("path", None) + op = config_step.get("operation", None) + + if path and op: + path_plus_op = "{};{}".format(path, op) + if path_plus_op in all_data_args: + for key, value in all_data_args[path_plus_op].items(): + if key not in replaced_cmd_params: + trimmed_value = re.sub(r"\{\{", "", re.sub(r"\}\}", "", value)).split(" ")[0] + val_list = trimmed_value.split(";") + if len(val_list) > 2: + type_or_upd_value = val_list[-1].split("|") + else: + type_or_upd_value = val_list[1] + if val_list[0] in replaced_mode_values: + changed_value = replaced_mode_values[val_list[0]] + else: + changed_value = self._uignmi_get_valueset_for_param(all_params, path_plus_op, key, type_or_upd_value, "parameter") + if isinstance(changed_value, list): + replaced_cmd_params[key] = changed_value + else: + replaced_cmd_params[key] = changed_value + except: + pass + return + + def _uignmi_get_action_mode_arg_values(self, all_path_args, all_params, stepentry, replaced_mode_values): + try: + if "actions" in stepentry.keys(): + for action_step in stepentry["actions"]: + path = action_step.get("path", None) + op = action_step.get("operation", None) + + if path and op: + path_plus_op = "{};{}".format(path, op) + if path_plus_op in all_path_args: + for key, value in all_path_args[path_plus_op].items(): + if key not in replaced_mode_values: + changed_value = self._uignmi_get_valueset_for_param(all_params, path_plus_op, key, value, "path") + replaced_mode_values[key] = changed_value + except: + pass + return + + def _uignmi_get_action_cmd_param_values(self, all_data_args, all_params, stepentry, replaced_mode_values, replaced_cmd_params): + try: + if "actions" in stepentry.keys(): + for action_step in stepentry["actions"]: + path = action_step.get("path", None) + op = action_step.get("operation", None) + + if path and op: + path_plus_op = "{};{}".format(path, op) + if path_plus_op in all_data_args: + for key, value in all_data_args[path_plus_op].items(): + if key not in replaced_cmd_params: + trimmed_value = re.sub(r"\{\{", "", re.sub(r"\}\}", "", value)).split(" ")[0] + val_list = trimmed_value.split(";") + if len(val_list) > 2: + type_or_upd_value = val_list[-1].split("|") + else: + type_or_upd_value = val_list[1] + if val_list[0] in replaced_mode_values: + changed_value = replaced_mode_values[val_list[0]] + else: + changed_value = self._uignmi_get_valueset_for_param(all_params, path_plus_op, key, type_or_upd_value, "parameter") + if isinstance(changed_value, list): + replaced_cmd_params[key] = changed_value + else: + replaced_cmd_params[key] = changed_value + except: + pass + return + + def _uignmi_get_preconfig_mode_path_values(self, all_path_args, all_params, stepentry, replaced_mode_values): + try: + if "pre-configs" in stepentry.keys(): + for config_step in stepentry["pre-configs"]: + path = config_step.get("path", None) + op = config_step.get("operation", None) + + if path and op: + path_plus_op = "{};{}".format(path, op) + if path_plus_op in all_path_args: + for key, value in all_path_args[path_plus_op].items(): + if key not in replaced_mode_values: + changed_value = self._uignmi_get_valueset_for_param(all_params, path_plus_op, key, value, "path") + replaced_mode_values[key] = changed_value + except: + pass + return + + def _uignmi_get_preconfig_mode_data_values(self, all_data_args, all_params, stepentry, replaced_mode_values, replaced_cmd_params): + try: + if "pre-configs" in stepentry.keys(): + for config_step in stepentry["pre-configs"]: + path = config_step.get("path", None) + op = config_step.get("operation", None) + + if path and op: + path_plus_op = "{};{}".format(path, op) + if path_plus_op in all_data_args: + for key, value in all_data_args[path_plus_op].items(): + if key not in replaced_cmd_params: + trimmed_value = re.sub(r"\{\{", "", re.sub(r"\}\}", "", value)).split(" ")[0] + val_list = trimmed_value.split(";") + if len(val_list) > 2: + type_or_upd_value = val_list[-1].split("|") + else: + type_or_upd_value = val_list[1] + if val_list[0] in replaced_mode_values: + changed_value = replaced_mode_values[val_list[0]] + else: + changed_value = self._uignmi_get_valueset_for_param(all_params, path_plus_op, key, type_or_upd_value, "parameter") + if isinstance(changed_value, list): + replaced_cmd_params[key] = changed_value + else: + replaced_cmd_params[key] = changed_value + except: + pass + return + + def _uignmi_substitute_path_data_params(self, stepentry, all_data_args, all_params, replaced_mode_values, replaced_cmd_params): + changed_steps = [] + + minIndex = 0 + maxIndex = 1 + + for index in range(minIndex, maxIndex): + copied_step = copy.deepcopy(stepentry) + no_need_to_add = False + + try: + if "pre-configs" in copied_step.keys(): + for preconfig_step in copied_step["pre-configs"]: + path = preconfig_step.get("path", None) + op = preconfig_step.get("operation", None) + data = preconfig_step.get("data", None) + path_plus_op = "{};{}".format(path, op) + + cfg_valid = preconfig_step.get("valid", 1) + if index > 2: + cfg_valid = int(not cfg_valid) + + # Replace the arg values in path string + if path: + pattern_match = re.findall(r"{(\w+(-*\w+)+)}", path) + if pattern_match: + for key, value in replaced_mode_values.items(): + for attr in pattern_match: + # keyword = re.findall("\w+", attr) + if key == attr[0]: + path = path.replace("={" + key + "}", "[{}={}]".format(key, value)) + path = path.replace(",{" + key + "}", "[{}={}]".format(key, value)) + path = path.replace("/restconf/data", "") + if data: + datastr = json.dumps(data) + for key, value in replaced_cmd_params.items(): + if key in datastr: + arg_datatype_str = all_data_args[path_plus_op][key] + if "integer" in arg_datatype_str or "boolean" in arg_datatype_str or isinstance(value,int): + datastr = datastr.replace("\"{{" + key + "}}\"", str(value)) + else: + datastr = datastr.replace("{{" + key + "}}", str(value)) + data = json.loads(datastr) + + # Update the step values with replaced data + preconfig_step.update({"path": path}) + preconfig_step.update({"data": data}) + preconfig_step.update({"valid": cfg_valid}) + + if "configs" in copied_step.keys(): + for config_step in copied_step["configs"]: + path = config_step.get("path", None) + op = config_step.get("operation", None) + data = config_step.get("data", None) + path_plus_op = "{};{}".format(path, op) + + cfg_valid = config_step.get("valid", 1) + if index > 2: + cfg_valid = int(not cfg_valid) + + # Replace the arg values in path string + if path: + pattern_match = re.findall(r"{(\w+(-*\w+)+)}", path) + if pattern_match: + for key, value in replaced_mode_values.items(): + for attr in pattern_match: + # keyword = re.findall("\w+", attr) + if key == attr[0]: + path = path.replace("={" + key + "}", "[{}={}]".format(key, value)) + path = path.replace(",{" + key + "}", "[{}={}]".format(key, value)) + path = path.replace("/restconf/data", "") + + if data: + datastr = json.dumps(data) + for key, value in replaced_cmd_params.items(): + if key in datastr: + arg_datatype_str = all_data_args[path_plus_op][key] + if "integer" in arg_datatype_str or "boolean" in arg_datatype_str or isinstance(value, int): + datastr = datastr.replace("\"{{" + key + "}}\"", str(value)) + else: + datastr = datastr.replace("{{" + key + "}}", str(value)) + data = json.loads(datastr) + + # Update the step values with replaced data + config_step.update({"path": path}) + config_step.update({"data": data}) + config_step.update({"valid": cfg_valid}) + + if no_need_to_add: + continue + + if "actions" in copied_step.keys(): + for action_step in copied_step["actions"]: + path = action_step.get("path", None) + matches = action_step.get("match", None) + + action_valid = action_step.get("valid", 1) + if index > 2: + action_valid = int(not action_valid) + + # Replace the arg values in path string + if path: + pattern_match = re.findall(r"{(\w+(-*\w+)+)}", path) + if pattern_match: + for key, value in replaced_mode_values.items(): + for attr in pattern_match: + # keyword = re.findall("\w+", attr) + if key == attr[0]: + path = path.replace("={" + key + "}", "[{}={}]".format(key, value)) + path = path.replace(",{" + key + "}", "[{}={}]".format(key, value)) + path = path.replace("/restconf/data", "") + + if matches: + matches_str = json.dumps(matches) + for key, value in replaced_cmd_params.items(): + if key in matches_str: + arg_datatype_str = all_data_args[path_plus_op][key] + if "integer" in arg_datatype_str or "boolean" in arg_datatype_str or isinstance(value, int): + matches_str = matches_str.replace("\"{{" + key + "}}\"", str(value)) + else: + matches_str = matches_str.replace("{{" + key + "}}", str(value)) + matches = json.loads(matches_str) + + # Update the step values with replaced data + action_step.update({"path": path}) + if matches: + action_step.update({"match": matches}) + action_step.update({"valid": action_valid}) + + if not no_need_to_add: + changed_steps.append(copied_step) + except: + pass + + if not changed_steps: + changed_steps.append(copy.deepcopy(stepentry)) + + return changed_steps + + def _uignmi_get_valueset_for_param(self, all_params, path_plus_op, param_name, param_type, datatype): + retval = "TODO" + if isinstance(param_type, list): + if datatype in ["path", "match"]: + retval = random.choice(param_type) + else: + retval = random.choice(param_type) + elif param_type in ["integer", "number", "double", "string", "boolean"]: + if param_type in ["integer", "number", "double"]: + int_vals = range(0,65536) + if datatype in ["path", "match"]: + retval = random.choice(int_vals) + else: + retval = random.choice(int_vals) + elif param_type in ["string"]: + is_interface_in_path = "interface={"+param_name+"}" + is_ipaddress_in_path = "address={"+param_name+"}" + + mac_address_names = ["mac-address", "source-mac", "destination-mac"] + + if is_interface_in_path in path_plus_op: + if datatype in ["path", "match"]: + if self.tb_vars.connected_ports: + retval = self.tb_vars.connected_ports[0] + else: + retval = self.tb_vars.free_ports[0] + else: + if self.tb_vars.connected_ports: + retval = self.tb_vars.connected_ports[0] + else: + retval = self.tb_vars.free_ports[0] + elif param_name in mac_address_names: + retval = ':'.join(''.join(random.choice(string.hexdigits).lower() for _ in range(2)) for _ in range(6)) + elif is_ipaddress_in_path in path_plus_op: + if "v6" in path_plus_op or "V6" in path_plus_op: + retval = ':'.join(''.join(random.choice(string.hexdigits).lower() for _ in range(4)) for _ in range(8)) + else: + retval = '.'.join(str(random.randint(0, 255)) for _ in range(4)) + else: + letters = string.ascii_letters + string.digits + '_-' + minLen = 1 + maxLen = 64 + stringLength = random.randint(minLen, maxLen) + if datatype in ["path", "match"]: + retval = ''.join(random.choice(letters) for i in range(stringLength)) + else: + retval = ''.join(random.choice(letters) for i in range(stringLength)) + elif param_type in ["boolean"]: + bool_vals = [0,1] + if datatype in ["path", "match"]: + retval = random.choice(bool_vals) + else: + retval = random.choice(bool_vals) + elif param_type in all_params: + #param_dict = all_params[param_type] + retval = self._uignmi_get_valueset_for_param_from_clilist(param_name, param_type, all_params, datatype) + + + #print("Random_value:", retval) + return retval + + def _uignmi_get_valueset_for_param_from_clilist(self, param_name, param_type, all_params, datatype): + retval = "TODO" + + param_dict = all_params[param_type] + method = param_dict.get("method", None) + pattern = param_dict.get("pattern", None) + ip_address_patterns = ["INT_OR_IP_ADDR", "IP_ADDR", "IP_ADDR_ANY", + "IP_ADDR_DHCP_SUBNET", "IP_ADDR_DHCP_SUBNET_IPV4IPV6", + "IP_ADDR_MASK", "IPADDR_NN", "IPV4_ADDR_ABC", + "IPV4_IPV6_NETWORK", "IPV4_OR_IPV6_ADDR", "INT32_OR_IP_ADDR", + "IPV4V6_ADDR", "IPV6_ADDR", "IPV6_ADDR_MASK", "DOTTED_QUAD", "AA_NN_IPADDR_NN", + "HOSTNAME_OR_IPADDR", "HOSTNAME_OR_IPV4_ADDR", "RD", "RT"] + + if param_type.startswith("SPYTEST_"): + if method in ["integer", "unsignedInteger"]: + (min, max) = re.match(r'(\d+)\.\.(\d+)', pattern).groups() + retval = random.randint(int(min), int(max)) + if method == "UINT" and "INTERFACE" in param_type: + if self.tb_vars.connected_ports: + retval = self.tb_vars.connected_ports[0] + else: + retval = self.tb_vars.free_ports[0] + if datatype not in ["path", "match"]: + retval = re.sub("Ethernet", "", retval) + if method in ["select"]: + choices = re.findall(r"(\S+)\(\S+\)", pattern) + retval = random.choice(choices) + if method == "string": + while True: + letters = string.ascii_letters + string.digits + '_-' + minLen = 1 + maxLen = 63 + stringLength = random.randint(minLen, maxLen) + retval = ''.join(random.choice(letters) for i in range(stringLength)) + if re.match(pattern, retval): + break + if method == "ipaddress": + while True: + retval = '.'.join(str(random.randint(0, 255)) for _ in range(4)) + if re.match(pattern, retval): + break + elif method: + if method in ["integer", "unsignedInteger"]: + (min, max) = re.match(r'([-+]?\d+)\.\.([-+]?\d+)', pattern).groups() + if datatype in ["path", "match"]: + retval = random.randint(int(min), int(max)) + else: + #if min == max: + # retval = [int(min), None, None, int(min)-1, int(max)+1] + #else: + # randNum = random.randint(int(min), int(max)) + # retval = [int(min), int(max), randNum, int(min)-1, int(max)+1] + retval = random.randint(int(min), int(max)) + elif method in ["select"]: + if "(" in pattern: + choices = re.findall(r"(\S+)\(\S+\)", pattern) + else: + choices = re.findall(r"(\S+)", pattern) + if datatype in ["path", "match"]: + retval = random.choice(choices) + else: + #retval = [choices[0], choices[-1], random.choice(choices), None, None] + retval = random.choice(choices) + elif method in ["regexp_select"]: + if param_type == "PHY_INTERFACE": + if self.tb_vars.connected_ports: + retval = self.tb_vars.connected_ports[0] + else: + retval = self.tb_vars.free_ports[0] + elif param_type == "VLAN_INTERFACE": + vid_pattern = all_params["VLAN_ID"].get("pattern", None) + (min, max) = re.match(r'([-+]?\d+)\.\.([-+]?\d+)', vid_pattern).groups() + if datatype in ["argument", "match"]: + retval = "Vlan {}".format(random.randint(int(min), int(max))) + else: + retval = "Vlan {}".format(random.randint(int(min), int(max))) + else: + retval = "TODO"; # TODO + else: + retval = "TODO"; # TODO + else: + if param_type == "UINT": + if param_name.startswith("phy-if-"): + if self.tb_vars.connected_ports: + retval = self.tb_vars.connected_ports[0] + else: + retval = self.tb_vars.free_ports[0] + if datatype not in ["path", "match"]: + retval = re.sub("Ethernet", "", retval) + #retval = [retval, None, None, None, None] + elif param_name == "zone": + min = 0 + max = 3 + if datatype in ["path", "match"]: + retval = str(random.randint(min, max)) + else: + #retval = [min, max, random.randint(min, max), min-1, max+1] + retval = str(random.randint(min, max)) + else: + min = 0 + max = 65535 + if datatype in ["path", "match"]: + retval = str(random.randint(min, max)) + else: + #retval = [min, max, random.randint(min, max), min-1, max+1] + retval = str(random.randint(min, max)) + elif param_type.startswith("STRING") or param_type.startswith("HOSTNAME_STR"): + if param_type.startswith("STRING"): + search_pattern = "{}_".format("STRING") + elif param_type.startswith("HOSTNAME_STR"): + search_pattern = "{}_".format("HOSTNAME_STR") + else: + search_pattern = "{}_".format("TODO") + + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + letters = string.ascii_letters + string.digits + '_-' + minLen = 1 + maxLen = 1 + if param_type.startswith(search_pattern): + lengths_part = re.sub(search_pattern, "", param_type) + if "_" in lengths_part: + min_part = lengths_part.split("_")[0] + max_part = lengths_part.split("_")[-1] + else: + min_part = "1" + max_part = lengths_part + try: + minLen = int(min_part) + maxLen = int(max_part) + except: + pass + else: + maxLen = 64 + stringLength = random.randint(minLen, maxLen) + if datatype in ["path", "match"]: + retval = ''.join(random.choice(letters) for i in range(stringLength)) + else: + #minStr = ''.join(random.choice(letters) for i in range(minLen)) + #maxStr = ''.join(random.choice(letters) for i in range(maxLen)) + #randStr = ''.join(random.choice(letters) for i in range(stringLength)) + #retval = [minStr, maxStr, randStr, None, None] + retval = ''.join(random.choice(letters) for i in range(stringLength)) + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type.startswith("PASSWORD_STR"): + minLen = 1 + maxLen = 64 + letters = string.ascii_letters + string.digits + '_-' + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + stringLength = random.randint(minLen, maxLen) + if datatype in ["path", "match"]: + retval = ''.join(random.choice(letters) for i in range(stringLength)) + else: + #minStr = ''.join(random.choice(letters) for i in range(minLen)) + #maxStr = ''.join(random.choice(letters) for i in range(maxLen)) + #randStr = ''.join(random.choice(letters) for i in range(stringLength)) + #retval = [minStr, maxStr, randStr, None, None] + retval = ''.join(random.choice(letters) for i in range(stringLength)) + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ip_address_patterns: + min_ipv4_mask = "1" + min_ipv6_mask = "1" + max_ipv4_mask = "32" + max_ipv6_mask = "128" + + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + rand_ipv4_mask = str(random.randint(int(min_ipv4_mask), int(max_ipv4_mask))) + rand_ipv6_mask = str(random.randint(int(min_ipv6_mask), int(max_ipv6_mask))) + rand_ipv4_address = '.'.join(str(random.randint(0,255)) for _ in range(4)) + rand_ipv6_address = ':'.join(''.join(random.choice(string.hexdigits).lower() for _ in range(4)) for _ in range(8)) + + if "MASK" in param_type or "SUBNET" in param_type or "NETWORK" in param_type: + rand_ipv4_address = "{}/{}".format(rand_ipv4_address, rand_ipv4_mask) + rand_ipv6_address = "{}/{}".format(rand_ipv6_address, rand_ipv6_mask) + + if "IPADDR_NN" in param_type or param_type in ["RD", "RT"]: + aa_nn_val = str(random.randint(0, 65535)) + rand_ipv4_address = "{}:{}".format(rand_ipv4_address, aa_nn_val) + rand_ipv6_address = "{}:{}".format(rand_ipv6_address, aa_nn_val) + + if datatype in ["path", "match"]: + if "V6" in param_type: + retval = rand_ipv6_address + else: + retval = rand_ipv4_address + else: + if "V6" in param_type: + #retval = [None, None, rand_ipv6_address, None, None] + retval = rand_ipv6_address + else: + #retval = [None, None, rand_ipv4_address, None, None] + retval = rand_ipv4_address + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["MAC_ADDR"]: + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + rand_mac_address = ':'.join(''.join(random.choice(string.hexdigits).lower() for _ in range(2)) for _ in range(6)) + if datatype in ["path", "match"]: + retval = rand_mac_address + else: + #retval = [None, None, rand_mac_address, None, None] + retval = rand_mac_address + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["HEX_TYPE"]: + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + min_hex_str = "0x0" + rand_hex_str = '0x' + ''.join(random.choice(string.hexdigits).lower() for _ in range(6)) + max_hex_str = "0xFFFFFF" + invalid_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(6)) + if datatype in ["path", "match"]: + retval = rand_hex_str + else: + #retval = [min_hex_str, max_hex_str, rand_hex_str, None, invalid_hex_str] + retval = rand_hex_str + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["PTP_V6SCOPE_TYPE"]: + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + min_hex_str = "0x0" + rand_hex_str = '0x' + random.choice(string.hexdigits).lower() + max_hex_str = "0xF" + invalid_hex_str = random.choice(string.hexdigits).lower() + if datatype in ["path", "match"]: + retval = rand_hex_str + else: + #retval = [min_hex_str, max_hex_str, rand_hex_str, None, invalid_hex_str] + retval = rand_hex_str + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["TACACS_KEY", "RADIUS_KEY"]: + minLen = 1 + maxLen = 32 + letters = string.ascii_letters + string.digits + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + stringLength = random.randint(minLen, maxLen) + if datatype in ["path", "match"]: + retval = ''.join(random.choice(letters) for i in range(stringLength)) + else: + #minStr = ''.join(random.choice(letters) for i in range(minLen)) + #maxStr = ''.join(random.choice(letters) for i in range(maxLen)) + #randStr = ''.join(random.choice(letters) for i in range(stringLength)) + #invalidMaxStr = ''.join(random.choice(letters) for i in range(maxLen+1)) + #retval = [minStr, maxStr, randStr, None, invalidMaxStr] + retval = ''.join(random.choice(letters) for i in range(stringLength)) + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["RADIUS_VRF"]: + minLen = 1 + maxLen = 28 + letters = string.ascii_letters + string.digits + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + stringLength = random.randint(minLen, maxLen) + if datatype in ["path", "match"]: + #retval = ''.join(random.choice(letters) for i in range(stringLength)) + retval = 'Vrf_' + ''.join(random.choice(letters) for i in range(stringLength)) + else: + #minStr = 'Vrf_' + ''.join(random.choice(letters) for i in range(minLen)) + #maxStr = 'Vrf_' + ''.join(random.choice(letters) for i in range(maxLen)) + #randStr = 'Vrf_' + ''.join(random.choice(letters) for i in range(stringLength)) + #invalidMaxStr = 'Vrf_' + ''.join(random.choice(letters) for i in range(maxLen + 1)) + #invalidMinStr = ''.join(random.choice(letters) for i in range(maxLen + 1)) + #retval = [minStr, maxStr, randStr, invalidMinStr, invalidMaxStr] + retval = 'Vrf_' + ''.join(random.choice(letters) for i in range(stringLength)) + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type == "FILE_TYPE": + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + letters = string.ascii_letters + randStr = "file://" + ''.join(random.choice(letters) for i in range(10)) + if datatype in ["path", "match"]: + retval = randStr + else: + #retval = [None, None, randStr, None, None] + retval = randStr + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type == "AA_NN": + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + rand_aa_nn_val = "{}:{}".format(str(random.randint(0, 65535)), str(random.randint(0, 65535))) + if datatype in ["path", "match"]: + retval = rand_aa_nn_val + else: + #retval = [None, None, rand_aa_nn_val, None, None] + retval = rand_aa_nn_val + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type == "KDUMP_MEMORY": + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + rand_kdump_val = "0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M" + if datatype in ["path", "match"]: + retval = rand_kdump_val + else: + #retval = [None, None, rand_kdump_val, None, None] + retval = rand_kdump_val + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["AUTH_KEY_TYPE"]: + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + min_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(32)) + max_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(32)) + rand_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(32)) + invalid_hex_str1 = ''.join(random.choice(string.hexdigits).lower() for _ in range(30)) + invalid_hex_str2 = ''.join(random.choice(string.hexdigits).lower() for _ in range(34)) + if datatype in ["path", "match"]: + retval = rand_hex_str + else: + #retval = [min_hex_str, max_hex_str, rand_hex_str, invalid_hex_str1, invalid_hex_str2] + retval = rand_hex_str + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["SHA_AUTH_KEY_TYPE"]: + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + min_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(40)) + max_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(40)) + rand_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(40)) + invalid_hex_str1 = ''.join(random.choice(string.hexdigits).lower() for _ in range(38)) + invalid_hex_str2 = ''.join(random.choice(string.hexdigits).lower() for _ in range(42)) + if datatype in ["path", "match"]: + retval = rand_hex_str + else: + #retval = [min_hex_str, max_hex_str, rand_hex_str, invalid_hex_str1, invalid_hex_str2] + retval = rand_hex_str + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["ENGINE_ID_TYPE"]: + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + min_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(10)) + max_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(64)) + rand_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(random.randint(10, 64))) + invalid_hex_str1 = ''.join(random.choice(string.hexdigits).lower() for _ in range(9)) + invalid_hex_str2 = ''.join(random.choice(string.hexdigits).lower() for _ in range(65)) + if datatype in ["path", "match"]: + retval = rand_hex_str + else: + #retval = [min_hex_str, max_hex_str, rand_hex_str, invalid_hex_str1, invalid_hex_str2] + retval = rand_hex_str + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["OID_IDENTIFIER"]: + minLen = 1 + maxLen = 255 + letters = string.digits + '.' + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + stringLength = random.randint(minLen, maxLen) + if datatype in ["path", "match"]: + retval = ''.join(random.choice(letters) for i in range(stringLength)) + else: + minStr = ''.join(random.choice(letters) for i in range(minLen)) + maxStr = ''.join(random.choice(letters) for i in range(maxLen)) + randStr = ''.join(random.choice(letters) for i in range(stringLength)) + invalidStr1 = ''.join(random.choice(letters) for i in range(minLen - 1)) + invalidStr2 = ''.join(random.choice(letters) for i in range(maxLen + 1)) + #retval = [minStr, maxStr, randStr, invalidStr1, invalidStr2] + retval = ''.join(random.choice(letters) for i in range(stringLength)) + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["SNMP_IDENTIFIER"]: + minLen = 1 + maxLen = 32 + letters = string.ascii_letters + string.digits + '_-' + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + stringLength = random.randint(minLen, maxLen) + if datatype in ["path", "match"]: + retval = ''.join(random.choice(letters) for i in range(stringLength)) + else: + minStr = ''.join(random.choice(letters) for i in range(minLen)) + maxStr = ''.join(random.choice(letters) for i in range(maxLen)) + randStr = ''.join(random.choice(letters) for i in range(stringLength)) + invalidStr1 = ''.join(random.choice(letters) for i in range(minLen - 1)) + invalidStr2 = ''.join(random.choice(letters) for i in range(maxLen + 1)) + #retval = [minStr, maxStr, randStr, invalidStr1, invalidStr2] + retval = ''.join(random.choice(letters) for i in range(stringLength)) + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["LINE"]: + minLen = 1 + maxLen = 63 + letters = string.ascii_letters + string.digits + '_-' + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + stringLength = random.randint(minLen, maxLen) + if datatype in ["path", "match"]: + retval = ''.join(random.choice(letters) for i in range(stringLength)) + else: + minStr = ''.join(random.choice(letters) for i in range(minLen)) + maxStr = ''.join(random.choice(letters) for i in range(maxLen)) + randStr = ''.join(random.choice(letters) for i in range(stringLength)) + invalidStr1 = ''.join(random.choice(letters) for i in range(minLen - 1)) + invalidStr2 = ''.join(random.choice(letters) for i in range(maxLen + 1)) + #retval = [minStr, maxStr, randStr, invalidStr1, invalidStr2] + retval = ''.join(random.choice(letters) for i in range(stringLength)) + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["VLAN_RANGE"]: + min = 1 + max = 4094 + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + randNum = random.randint(min, max) + if datatype in ["path", "match"]: + retval = randNum + else: + #retval = [min, max, randNum, min-1, max+1] + retval = randNum + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, str(each_val)): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + + # TODO: Need to do for other types such as IP, HOSTNAME , etc. + + #print("Random_value_from_clilist:", param_name, param_type, retval) + return retval + diff --git a/spytest/spytest/uirest.py b/spytest/spytest/uirest.py new file mode 100644 index 00000000000..55e1879ae2c --- /dev/null +++ b/spytest/spytest/uirest.py @@ -0,0 +1,1096 @@ +import re +import string +import copy +import random +import json +from spytest.logger import Logger + +class UIRest(object): + def __init__(self, logger=None, testbed_vars=None): + self.logger = logger or Logger() + self.tb_vars = testbed_vars + + def uirest_log(self, msg, width=120, delimiter="#", header=True, footer=True): + msg_list = [] + if header: + msg_list.append(delimiter * width) + if msg != None: + if isinstance(msg, list): + for eachline in msg: + if isinstance(eachline, list): + msg_list.append("{0} {1} {0}".format(delimiter, " : ".join(str(e) for e in eachline).center(width - 4))) + else: + msg_list.append("{0} {1} {0}".format(delimiter, str(eachline).center(width - 4))) + else: + msg_list.append("{0} {1} {0}".format(delimiter, msg.center(width - 4))) + if footer: + msg_list.append(delimiter * width) + for each_line in msg_list: + self.logger.info(each_line) + + def _uirest_get_config_mode_path_values(self, all_path_args, all_params, stepentry, replaced_mode_values): + try: + if "configs" in stepentry.keys(): + for config_step in stepentry["configs"]: + path = config_step.get("path", None) + op = config_step.get("operation", None) + + if path and op: + path_plus_op = "{};{}".format(path, op) + if path_plus_op in all_path_args: + for key, value in all_path_args[path_plus_op].items(): + if key not in replaced_mode_values: + changed_value = self._uirest_get_valueset_for_param(all_params, path_plus_op, key, value, "path") + replaced_mode_values[key] = changed_value + except: + pass + + #print("Updated modes - configs", replaced_mode_values) + #import pdb; pdb.set_trace() + return + + def _uirest_get_config_mode_data_values(self, all_data_args, all_params, stepentry, replaced_mode_values, replaced_cmd_params): + try: + if "configs" in stepentry.keys(): + for config_step in stepentry["configs"]: + path = config_step.get("path", None) + op = config_step.get("operation", None) + + if path and op: + path_plus_op = "{};{}".format(path, op) + if path_plus_op in all_data_args: + for key, value in all_data_args[path_plus_op].items(): + if key not in replaced_cmd_params: + trimmed_value = re.sub(r"\{\{", "", re.sub(r"\}\}", "", value)).split(" ")[0] + val_list = trimmed_value.split(";") + if len(val_list) > 2: + type_or_upd_value = val_list[-1].split("|") + else: + type_or_upd_value = val_list[1] + if val_list[0] in replaced_mode_values: + changed_value = replaced_mode_values[val_list[0]] + else: + changed_value = self._uirest_get_valueset_for_param(all_params, path_plus_op, key, type_or_upd_value, "parameter") + if isinstance(changed_value, list): + replaced_cmd_params[key] = changed_value + else: + replaced_cmd_params[key] = changed_value + except: + pass + + #print("Updated cmd params - configs", replaced_cmd_params) + #import pdb; pdb.set_trace() + return + + def _uirest_get_action_mode_arg_values(self, all_path_args, all_params, stepentry, replaced_mode_values): + try: + if "actions" in stepentry.keys(): + for action_step in stepentry["actions"]: + path = action_step.get("path", None) + op = action_step.get("operation", None) + + if path and op: + path_plus_op = "{};{}".format(path, op) + if path_plus_op in all_path_args: + for key, value in all_path_args[path_plus_op].items(): + if key not in replaced_mode_values: + changed_value = self._uirest_get_valueset_for_param(all_params, path_plus_op, key, value, "path") + replaced_mode_values[key] = changed_value + except: + pass + + #print("Updated modes - actions", replaced_mode_values) + #import pdb; pdb.set_trace() + return + + def _uirest_get_action_cmd_param_values(self, all_data_args, all_params, stepentry, replaced_mode_values, replaced_cmd_params): + try: + if "actions" in stepentry.keys(): + for action_step in stepentry["actions"]: + path = action_step.get("path", None) + op = action_step.get("operation", None) + + if path and op: + path_plus_op = "{};{}".format(path, op) + if path_plus_op in all_data_args: + for key, value in all_data_args[path_plus_op].items(): + if key not in replaced_cmd_params: + trimmed_value = re.sub(r"\{\{", "", re.sub(r"\}\}", "", value)).split(" ")[0] + val_list = trimmed_value.split(";") + if len(val_list) > 2: + type_or_upd_value = val_list[-1].split("|") + else: + type_or_upd_value = val_list[1] + if val_list[0] in replaced_mode_values: + changed_value = replaced_mode_values[val_list[0]] + else: + changed_value = self._uirest_get_valueset_for_param(all_params, path_plus_op, key, type_or_upd_value, "parameter") + if isinstance(changed_value, list): + replaced_cmd_params[key] = changed_value + else: + replaced_cmd_params[key] = changed_value + except: + pass + + #print("Updated cmd params - actions", replaced_cmd_params) + #import pdb; pdb.set_trace() + return + + def _uirest_get_preconfig_mode_path_values(self, all_path_args, all_params, stepentry, replaced_mode_values): + try: + if "pre-configs" in stepentry.keys(): + for config_step in stepentry["pre-configs"]: + path = config_step.get("path", None) + op = config_step.get("operation", None) + + if path and op: + path_plus_op = "{};{}".format(path, op) + if path_plus_op in all_path_args: + for key, value in all_path_args[path_plus_op].items(): + if key not in replaced_mode_values: + changed_value = self._uirest_get_valueset_for_param(all_params, path_plus_op, key, value, "path") + replaced_mode_values[key] = changed_value + except: + pass + + #print("Updated modes - pre-configs", replaced_mode_values) + #import pdb; pdb.set_trace() + return + + def _uirest_get_preconfig_mode_data_values(self, all_data_args, all_params, stepentry, replaced_mode_values, replaced_cmd_params): + try: + if "pre-configs" in stepentry.keys(): + for config_step in stepentry["pre-configs"]: + path = config_step.get("path", None) + op = config_step.get("operation", None) + + if path and op: + path_plus_op = "{};{}".format(path, op) + if path_plus_op in all_data_args: + for key, value in all_data_args[path_plus_op].items(): + if key not in replaced_cmd_params: + trimmed_value = re.sub(r"\{\{", "", re.sub(r"\}\}", "", value)).split(" ")[0] + val_list = trimmed_value.split(";") + if len(val_list) > 2: + type_or_upd_value = val_list[-1].split("|") + else: + type_or_upd_value = val_list[1] + if val_list[0] in replaced_mode_values: + changed_value = replaced_mode_values[val_list[0]] + else: + changed_value = self._uirest_get_valueset_for_param(all_params, path_plus_op, key, type_or_upd_value, "parameter") + if isinstance(changed_value, list): + replaced_cmd_params[key] = changed_value + else: + replaced_cmd_params[key] = changed_value + except: + pass + + #print("Updated cmd params - pre-configs", replaced_cmd_params) + #import pdb; pdb.set_trace() + return + + def _uirest_substitute_path_data_params(self, stepentry, all_data_args, all_params, replaced_mode_values, replaced_cmd_params): + + changed_steps = [] + + minIndex = 0 + maxIndex = 1 + + for index in range(minIndex, maxIndex): + copied_step = copy.deepcopy(stepentry) + no_need_to_add = False + + try: + if "pre-configs" in copied_step.keys(): + for preconfig_step in copied_step["pre-configs"]: + path = preconfig_step.get("path", None) + op = preconfig_step.get("operation", None) + data = preconfig_step.get("data", None) + path_plus_op = "{};{}".format(path, op) + + cfg_valid = preconfig_step.get("valid", 1) + if index > 2: + cfg_valid = int(not cfg_valid) + + # Replace the arg values in path string + if path: + for key, value in replaced_mode_values.items(): + path = path.replace("{" + key + "}", str(value)) + + if data: + datastr = json.dumps(data) + for key, value in replaced_cmd_params.items(): + if key in datastr: + arg_datatype_str = all_data_args[path_plus_op][key] + if "boolean" in arg_datatype_str: + datastr = datastr.replace("\"{{" + key + "}}\"", value) + elif "integer" in arg_datatype_str or "number" in arg_datatype_str or isinstance(value, int): + datastr = datastr.replace("\"{{" + key + "}}\"", str(value)) + else: + datastr = datastr.replace("{{" + key + "}}", str(value)) + data = json.loads(datastr) + + # Update the step values with replaced data + preconfig_step.update({"path": path}) + preconfig_step.update({"data": data}) + preconfig_step.update({"valid": cfg_valid}) + + if "configs" in copied_step.keys(): + for config_step in copied_step["configs"]: + path = config_step.get("path", None) + op = config_step.get("operation", None) + data = config_step.get("data", None) + path_plus_op = "{};{}".format(path, op) + + cfg_valid = config_step.get("valid", 1) + if index > 2: + cfg_valid = int(not cfg_valid) + + # Replace the arg values in path string + if path: + for key, value in replaced_mode_values.items(): + path = path.replace("{" + key + "}", str(value)) + + if data: + datastr = json.dumps(data) + for key, value in replaced_cmd_params.items(): + if key in datastr: + arg_datatype_str = all_data_args[path_plus_op][key] + if "boolean" in arg_datatype_str: + datastr = datastr.replace("\"{{" + key + "}}\"", value) + elif "integer" in arg_datatype_str or "number" in arg_datatype_str or isinstance(value, int): + datastr = datastr.replace("\"{{" + key + "}}\"", str(value)) + else: + datastr = datastr.replace("{{" + key + "}}", str(value)) + data = json.loads(datastr) + + # Update the step values with replaced data + config_step.update({"path": path}) + config_step.update({"data": data}) + config_step.update({"valid": cfg_valid}) + + if no_need_to_add: + continue + + if "actions" in copied_step.keys(): + for action_step in copied_step["actions"]: + path = action_step.get("path", None) + matches = action_step.get("match", None) + + action_valid = action_step.get("valid", 1) + if index > 2: + action_valid = int(not action_valid) + + # Replace the arg values in path string + if path: + for key, value in replaced_mode_values.items(): + path = path.replace("{" + key + "}", value) + + if matches: + matches_str = json.dumps(matches) + for key, value in replaced_cmd_params.items(): + if key in matches_str: + arg_datatype_str = all_data_args[path_plus_op][key] + if "boolean" in arg_datatype_str: + datastr = datastr.replace("\"{{" + key + "}}\"", value) + elif "integer" in arg_datatype_str or "number" in arg_datatype_str or isinstance(value, int): + matches_str = matches_str.replace("\"{{" + key + "}}\"", str(value)) + else: + matches_str = matches_str.replace("{{" + key + "}}", str(value)) + matches = json.loads(matches_str) + + # Update the step values with replaced data + action_step.update({"path": path}) + if matches: + action_step.update({"match": matches}) + action_step.update({"valid": action_valid}) + + if not no_need_to_add: + changed_steps.append(copied_step) + except: + pass + + #print("Changed Steps", changed_steps) + if not changed_steps: + changed_steps.append(copy.deepcopy(stepentry)) + + #import pdb; pdb.set_trace() + return changed_steps + + def _uirest_get_valueset_for_param(self, all_params, path_plus_op, param_name, param_type, datatype): + retval = "TODO" + + #print("_uirest_get_valueset_for_param:", param_name, param_type) + if isinstance(param_type, list): + if datatype in ["path", "match"]: + retval = random.choice(param_type) + else: + retval = random.choice(param_type) + elif param_type in ["integer", "number", "double", "string", "boolean"]: + if param_type in ["integer", "number", "double"]: + int_vals = range(0,65536) + if datatype in ["path", "match"]: + retval = random.choice(int_vals) + else: + retval = random.choice(int_vals) + elif param_type in ["string"]: + is_interface_in_path = "interface={"+param_name+"}" + is_ipaddress_in_path = "address={"+param_name+"}" + + mac_address_names = ["mac-address", "source-mac", "destination-mac"] + + if is_interface_in_path in path_plus_op: + if datatype in ["path", "match"]: + if self.tb_vars.connected_ports: + retval = self.tb_vars.connected_ports[0] + else: + retval = self.tb_vars.free_ports[0] + else: + if self.tb_vars.connected_ports: + retval = self.tb_vars.connected_ports[0] + else: + retval = self.tb_vars.free_ports[0] + elif param_name in mac_address_names: + retval = ':'.join(''.join(random.choice(string.hexdigits).lower() for _ in range(2)) for _ in range(6)) + elif is_ipaddress_in_path in path_plus_op: + if "v6" in path_plus_op or "V6" in path_plus_op: + retval = ':'.join(''.join(random.choice(string.hexdigits).lower() for _ in range(4)) for _ in range(8)) + else: + retval = '.'.join(str(random.randint(0, 255)) for _ in range(4)) + else: + letters = string.ascii_letters + string.digits + '_-' + minLen = 1 + maxLen = 64 + stringLength = random.randint(minLen, maxLen) + if datatype in ["path", "match"]: + retval = ''.join(random.choice(letters) for i in range(stringLength)) + else: + retval = ''.join(random.choice(letters) for i in range(stringLength)) + elif param_type in ["boolean"]: + bool_vals = ["false", "true"] + if datatype in ["path", "match"]: + retval = random.choice(bool_vals) + else: + retval = random.choice(bool_vals) + elif param_type in all_params: + #param_dict = all_params[param_type] + retval = self._uirest_get_valueset_for_param_from_clilist(param_name, param_type, all_params, datatype) + + #print("Random_value:", retval) + return retval + + def _uirest_get_valueset_for_param_from_clilist(self, param_name, param_type, all_params, datatype): + retval = "TODO" + + param_dict = all_params[param_type] + method = param_dict.get("method", None) + pattern = param_dict.get("pattern", None) + ip_address_patterns = ["INT_OR_IP_ADDR", "IP_ADDR", "IP_ADDR_ANY", + "IP_ADDR_DHCP_SUBNET", "IP_ADDR_DHCP_SUBNET_IPV4IPV6", + "IP_ADDR_MASK", "IPADDR_NN", "IPV4_ADDR_ABC", + "IPV4_IPV6_NETWORK", "IPV4_OR_IPV6_ADDR", "INT32_OR_IP_ADDR", + "IPV4V6_ADDR", "IPV6_ADDR", "IPV6_ADDR_MASK", "DOTTED_QUAD", "AA_NN_IPADDR_NN", + "HOSTNAME_OR_IPADDR", "HOSTNAME_OR_IPV4_ADDR", "RD", "RT"] + + if param_type.startswith("SPYTEST_"): + if method in ["integer", "unsignedInteger"]: + (min, max) = re.match(r'(\d+)\.\.(\d+)', pattern).groups() + retval = random.randint(int(min), int(max)) + if method == "UINT" and "INTERFACE" in param_type: + if self.tb_vars.connected_ports: + retval = self.tb_vars.connected_ports[0] + else: + retval = self.tb_vars.free_ports[0] + if datatype not in ["path", "match"]: + retval = re.sub("Ethernet", "", retval) + if method in ["select"]: + choices = re.findall(r"(\S+)\(\S+\)", pattern) + retval = random.choice(choices) + if method == "string": + while True: + letters = string.ascii_letters + string.digits + '_-' + minLen = 1 + maxLen = 63 + stringLength = random.randint(minLen, maxLen) + retval = ''.join(random.choice(letters) for i in range(stringLength)) + if re.match(pattern, retval): + break + if method == "ipaddress": + while True: + retval = '.'.join(str(random.randint(0, 255)) for _ in range(4)) + if re.match(pattern, retval): + break + elif method: + if method in ["integer", "unsignedInteger"]: + (min, max) = re.match(r'([-+]?\d+)\.\.([-+]?\d+)', pattern).groups() + if datatype in ["path", "match"]: + retval = random.randint(int(min), int(max)) + else: + #if min == max: + # retval = [int(min), None, None, int(min)-1, int(max)+1] + #else: + # randNum = random.randint(int(min), int(max)) + # retval = [int(min), int(max), randNum, int(min)-1, int(max)+1] + retval = random.randint(int(min), int(max)) + elif method in ["select"]: + if "(" in pattern: + choices = re.findall(r"(\S+)\(\S+\)", pattern) + else: + choices = re.findall(r"(\S+)", pattern) + if datatype in ["path", "match"]: + retval = random.choice(choices) + else: + #retval = [choices[0], choices[-1], random.choice(choices), None, None] + retval = random.choice(choices) + elif method in ["regexp_select"]: + if param_type == "PHY_INTERFACE": + if self.tb_vars.connected_ports: + retval = self.tb_vars.connected_ports[0] + else: + retval = self.tb_vars.free_ports[0] + elif param_type == "VLAN_INTERFACE": + vid_pattern = all_params["VLAN_ID"].get("pattern", None) + (min, max) = re.match(r'([-+]?\d+)\.\.([-+]?\d+)', vid_pattern).groups() + if datatype in ["argument", "match"]: + retval = "Vlan {}".format(random.randint(int(min), int(max))) + else: + retval = "Vlan {}".format(random.randint(int(min), int(max))) + elif param_type == "PO_INTERFACE": + po_pattern = all_params["LAG_ID"].get("pattern", None) + (min, max) = re.match(r'([-+]?\d+)\.\.([-+]?\d+)', po_pattern).groups() + if datatype in ["argument", "match"]: + retval = "PortChannel{}".format(random.randint(int(min), int(max))) + else: + retval = "PortChannel{}".format(random.randint(int(min), int(max))) + elif param_type == "LOOPBACK_INTERFACE": + lb_pattern = all_params["LOOPBACK_NUM"].get("pattern", None) + (min, max) = re.match(r'([-+]?\d+)\.\.([-+]?\d+)', lb_pattern).groups() + if datatype in ["argument", "match"]: + retval = "Loopback{}".format(random.randint(int(min), int(max))) + else: + retval = "Loopback{}".format(random.randint(int(min), int(max))) + else: + retval = "TODO"; # TODO + else: + retval = "TODO"; # TODO + else: + if param_type == "UINT": + if param_name.startswith("phy-if-"): + if self.tb_vars.connected_ports: + retval = self.tb_vars.connected_ports[0] + else: + retval = self.tb_vars.free_ports[0] + if datatype not in ["path", "match"]: + retval = re.sub("Ethernet", "", retval) + #retval = [retval, None, None, None, None] + elif param_name == "zone": + min = 0 + max = 3 + if datatype in ["path", "match"]: + retval = str(random.randint(min, max)) + else: + #retval = [min, max, random.randint(min, max), min-1, max+1] + retval = str(random.randint(min, max)) + else: + min = 0 + max = 65535 + if datatype in ["path", "match"]: + retval = random.randint(min, max) + else: + #retval = [min, max, random.randint(min, max), min-1, max+1] + retval = random.randint(min, max) + elif param_type.startswith("STRING") or param_type.startswith("HOSTNAME_STR"): + if param_type.startswith("STRING"): + search_pattern = "{}_".format("STRING") + elif param_type.startswith("HOSTNAME_STR"): + search_pattern = "{}_".format("HOSTNAME_STR") + else: + search_pattern = "{}_".format("TODO") + + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + letters = string.ascii_letters + string.digits + '_-' + minLen = 1 + maxLen = 1 + if param_type.startswith(search_pattern): + lengths_part = re.sub(search_pattern, "", param_type) + if "_" in lengths_part: + min_part = lengths_part.split("_")[0] + max_part = lengths_part.split("_")[-1] + else: + min_part = "1" + max_part = lengths_part + try: + minLen = int(min_part) + maxLen = int(max_part) + except: + pass + else: + maxLen = 64 + stringLength = random.randint(minLen, maxLen) + if datatype in ["path", "match"]: + retval = ''.join(random.choice(letters) for i in range(stringLength)) + else: + #minStr = ''.join(random.choice(letters) for i in range(minLen)) + #maxStr = ''.join(random.choice(letters) for i in range(maxLen)) + #randStr = ''.join(random.choice(letters) for i in range(stringLength)) + #retval = [minStr, maxStr, randStr, None, None] + retval = ''.join(random.choice(letters) for i in range(stringLength)) + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type.startswith("PASSWORD_STR"): + minLen = 1 + maxLen = 64 + letters = string.ascii_letters + string.digits + '_-' + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + stringLength = random.randint(minLen, maxLen) + if datatype in ["path", "match"]: + retval = ''.join(random.choice(letters) for i in range(stringLength)) + else: + #minStr = ''.join(random.choice(letters) for i in range(minLen)) + #maxStr = ''.join(random.choice(letters) for i in range(maxLen)) + #randStr = ''.join(random.choice(letters) for i in range(stringLength)) + #retval = [minStr, maxStr, randStr, None, None] + retval = ''.join(random.choice(letters) for i in range(stringLength)) + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ip_address_patterns: + min_ipv4_mask = "1" + min_ipv6_mask = "1" + max_ipv4_mask = "32" + max_ipv6_mask = "128" + + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + rand_ipv4_mask = str(random.randint(int(min_ipv4_mask), int(max_ipv4_mask))) + rand_ipv6_mask = str(random.randint(int(min_ipv6_mask), int(max_ipv6_mask))) + rand_ipv4_address = '.'.join(str(random.randint(0,255)) for _ in range(4)) + rand_ipv6_address = ':'.join(''.join(random.choice(string.hexdigits).lower() for _ in range(4)) for _ in range(8)) + + if "MASK" in param_type or "SUBNET" in param_type or "NETWORK" in param_type: + rand_ipv4_address = "{}/{}".format(rand_ipv4_address, rand_ipv4_mask) + rand_ipv6_address = "{}/{}".format(rand_ipv6_address, rand_ipv6_mask) + + if "IPADDR_NN" in param_type or param_type in ["RD", "RT"]: + aa_nn_val = str(random.randint(0, 65535)) + rand_ipv4_address = "{}:{}".format(rand_ipv4_address, aa_nn_val) + rand_ipv6_address = "{}:{}".format(rand_ipv6_address, aa_nn_val) + + if datatype in ["path", "match"]: + if "V6" in param_type: + retval = rand_ipv6_address + else: + retval = rand_ipv4_address + else: + if "V6" in param_type: + #retval = [None, None, rand_ipv6_address, None, None] + retval = rand_ipv6_address + else: + #retval = [None, None, rand_ipv4_address, None, None] + retval = rand_ipv4_address + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["MAC_ADDR"]: + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + rand_mac_address = ':'.join(''.join(random.choice(string.hexdigits).lower() for _ in range(2)) for _ in range(6)) + if datatype in ["path", "match"]: + retval = rand_mac_address + else: + #retval = [None, None, rand_mac_address, None, None] + retval = rand_mac_address + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["HEX_TYPE"]: + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + min_hex_str = "0x0" + rand_hex_str = '0x' + ''.join(random.choice(string.hexdigits).lower() for _ in range(6)) + max_hex_str = "0xFFFFFF" + invalid_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(6)) + if datatype in ["path", "match"]: + retval = rand_hex_str + else: + #retval = [min_hex_str, max_hex_str, rand_hex_str, None, invalid_hex_str] + retval = rand_hex_str + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["PTP_V6SCOPE_TYPE"]: + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + min_hex_str = "0x0" + rand_hex_str = '0x' + random.choice(string.hexdigits).lower() + max_hex_str = "0xF" + invalid_hex_str = random.choice(string.hexdigits).lower() + if datatype in ["path", "match"]: + retval = rand_hex_str + else: + #retval = [min_hex_str, max_hex_str, rand_hex_str, None, invalid_hex_str] + retval = rand_hex_str + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["TACACS_KEY", "RADIUS_KEY"]: + minLen = 1 + maxLen = 32 + letters = string.ascii_letters + string.digits + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + stringLength = random.randint(minLen, maxLen) + if datatype in ["path", "match"]: + retval = ''.join(random.choice(letters) for i in range(stringLength)) + else: + #minStr = ''.join(random.choice(letters) for i in range(minLen)) + #maxStr = ''.join(random.choice(letters) for i in range(maxLen)) + #randStr = ''.join(random.choice(letters) for i in range(stringLength)) + #invalidMaxStr = ''.join(random.choice(letters) for i in range(maxLen+1)) + #retval = [minStr, maxStr, randStr, None, invalidMaxStr] + retval = ''.join(random.choice(letters) for i in range(stringLength)) + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["RADIUS_VRF"]: + minLen = 1 + maxLen = 28 + letters = string.ascii_letters + string.digits + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + stringLength = random.randint(minLen, maxLen) + if datatype in ["path", "match"]: + #retval = ''.join(random.choice(letters) for i in range(stringLength)) + retval = 'Vrf_' + ''.join(random.choice(letters) for i in range(stringLength)) + else: + #minStr = 'Vrf_' + ''.join(random.choice(letters) for i in range(minLen)) + #maxStr = 'Vrf_' + ''.join(random.choice(letters) for i in range(maxLen)) + #randStr = 'Vrf_' + ''.join(random.choice(letters) for i in range(stringLength)) + #invalidMaxStr = 'Vrf_' + ''.join(random.choice(letters) for i in range(maxLen + 1)) + #invalidMinStr = ''.join(random.choice(letters) for i in range(maxLen + 1)) + #retval = [minStr, maxStr, randStr, invalidMinStr, invalidMaxStr] + retval = 'Vrf_' + ''.join(random.choice(letters) for i in range(stringLength)) + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type == "FILE_TYPE": + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + letters = string.ascii_letters + randStr = "file://" + ''.join(random.choice(letters) for i in range(10)) + if datatype in ["path", "match"]: + retval = randStr + else: + #retval = [None, None, randStr, None, None] + retval = randStr + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type == "AA_NN": + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + rand_aa_nn_val = "{}:{}".format(str(random.randint(0, 65535)), str(random.randint(0, 65535))) + if datatype in ["path", "match"]: + retval = rand_aa_nn_val + else: + #retval = [None, None, rand_aa_nn_val, None, None] + retval = rand_aa_nn_val + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type == "KDUMP_MEMORY": + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + rand_kdump_val = "0M-2G:256M,2G-4G:320M,4G-8G:384M,8G-:448M" + if datatype in ["path", "match"]: + retval = rand_kdump_val + else: + #retval = [None, None, rand_kdump_val, None, None] + retval = rand_kdump_val + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["AUTH_KEY_TYPE"]: + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + min_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(32)) + max_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(32)) + rand_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(32)) + invalid_hex_str1 = ''.join(random.choice(string.hexdigits).lower() for _ in range(30)) + invalid_hex_str2 = ''.join(random.choice(string.hexdigits).lower() for _ in range(34)) + if datatype in ["path", "match"]: + retval = rand_hex_str + else: + #retval = [min_hex_str, max_hex_str, rand_hex_str, invalid_hex_str1, invalid_hex_str2] + retval = rand_hex_str + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["SHA_AUTH_KEY_TYPE"]: + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + min_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(40)) + max_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(40)) + rand_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(40)) + invalid_hex_str1 = ''.join(random.choice(string.hexdigits).lower() for _ in range(38)) + invalid_hex_str2 = ''.join(random.choice(string.hexdigits).lower() for _ in range(42)) + if datatype in ["path", "match"]: + retval = rand_hex_str + else: + #retval = [min_hex_str, max_hex_str, rand_hex_str, invalid_hex_str1, invalid_hex_str2] + retval = rand_hex_str + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["ENGINE_ID_TYPE"]: + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + min_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(10)) + max_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(64)) + rand_hex_str = ''.join(random.choice(string.hexdigits).lower() for _ in range(random.randint(10, 64))) + invalid_hex_str1 = ''.join(random.choice(string.hexdigits).lower() for _ in range(9)) + invalid_hex_str2 = ''.join(random.choice(string.hexdigits).lower() for _ in range(65)) + if datatype in ["path", "match"]: + retval = rand_hex_str + else: + #retval = [min_hex_str, max_hex_str, rand_hex_str, invalid_hex_str1, invalid_hex_str2] + retval = rand_hex_str + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["OID_IDENTIFIER"]: + minLen = 1 + maxLen = 255 + letters = string.digits + '.' + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + stringLength = random.randint(minLen, maxLen) + if datatype in ["path", "match"]: + retval = ''.join(random.choice(letters) for i in range(stringLength)) + else: + minStr = ''.join(random.choice(letters) for i in range(minLen)) + maxStr = ''.join(random.choice(letters) for i in range(maxLen)) + randStr = ''.join(random.choice(letters) for i in range(stringLength)) + invalidStr1 = ''.join(random.choice(letters) for i in range(minLen - 1)) + invalidStr2 = ''.join(random.choice(letters) for i in range(maxLen + 1)) + #retval = [minStr, maxStr, randStr, invalidStr1, invalidStr2] + retval = ''.join(random.choice(letters) for i in range(stringLength)) + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["SNMP_IDENTIFIER"]: + minLen = 1 + maxLen = 32 + letters = string.ascii_letters + string.digits + '_-' + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + stringLength = random.randint(minLen, maxLen) + if datatype in ["path", "match"]: + retval = ''.join(random.choice(letters) for i in range(stringLength)) + else: + minStr = ''.join(random.choice(letters) for i in range(minLen)) + maxStr = ''.join(random.choice(letters) for i in range(maxLen)) + randStr = ''.join(random.choice(letters) for i in range(stringLength)) + invalidStr1 = ''.join(random.choice(letters) for i in range(minLen - 1)) + invalidStr2 = ''.join(random.choice(letters) for i in range(maxLen + 1)) + #retval = [minStr, maxStr, randStr, invalidStr1, invalidStr2] + retval = ''.join(random.choice(letters) for i in range(stringLength)) + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["LINE"]: + minLen = 1 + maxLen = 63 + letters = string.ascii_letters + string.digits + '_-' + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + stringLength = random.randint(minLen, maxLen) + if datatype in ["path", "match"]: + retval = ''.join(random.choice(letters) for i in range(stringLength)) + else: + minStr = ''.join(random.choice(letters) for i in range(minLen)) + maxStr = ''.join(random.choice(letters) for i in range(maxLen)) + randStr = ''.join(random.choice(letters) for i in range(stringLength)) + invalidStr1 = ''.join(random.choice(letters) for i in range(minLen - 1)) + invalidStr2 = ''.join(random.choice(letters) for i in range(maxLen + 1)) + #retval = [minStr, maxStr, randStr, invalidStr1, invalidStr2] + retval = ''.join(random.choice(letters) for i in range(stringLength)) + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, each_val): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + elif param_type in ["VLAN_RANGE"]: + min = 1 + max = 4094 + iter_count = 0 + while True: + iter_count += 1 + if iter_count > 5: break + randNum = random.randint(min, max) + if datatype in ["path", "match"]: + retval = randNum + else: + #retval = [min, max, randNum, min-1, max+1] + retval = randNum + try: + if isinstance(retval, list): + all_correct_values = True + for each_val in retval[:-2]: + if each_val: + if not re.match(pattern, str(each_val)): + all_correct_values = False + break + if all_correct_values: + break + else: + if re.match(pattern, retval): + break + except: + break + + # TODO: Need to do for other types such as IP, HOSTNAME , etc. + + #print("Random_value_from_clilist:", param_name, param_type, retval) + return retval + diff --git a/spytest/spytest/utils.py b/spytest/spytest/utils.py new file mode 100644 index 00000000000..d7032e593d7 --- /dev/null +++ b/spytest/spytest/utils.py @@ -0,0 +1,13 @@ +from utilities import common as base + +def filter_and_select(output, select=None, match=None): + return base.filter_and_select(output, select, match) +def random_vlan_list(count=1, exclude=[]): + return base.random_vlan_list(count, exclude) +def exec_foreach (use_threads, items, func, *args, **kwargs): + return base.exec_foreach (use_threads, items, func, *args, **kwargs) +def exec_all(use_threads, entries): + return base.exec_all(use_threads, entries) +def poll_wait(method, timeout, *args, **kwargs): + return base.poll_wait(method, timeout, *args, **kwargs) + diff --git a/spytest/spytest/version.py b/spytest/spytest/version.py new file mode 100644 index 00000000000..8190c431de0 --- /dev/null +++ b/spytest/spytest/version.py @@ -0,0 +1,9 @@ + +def get_git_ver(): + try: + import git + repo = git.Repo(search_parent_directories=True) + return repo.head.object.hexsha + except: + return "UNKNOWN" + diff --git a/spytest/templates/bcmcmd_ipmc_table_show.tmpl b/spytest/templates/bcmcmd_ipmc_table_show.tmpl new file mode 100644 index 00000000000..42f223286be --- /dev/null +++ b/spytest/templates/bcmcmd_ipmc_table_show.tmpl @@ -0,0 +1,6 @@ +Value src_ip (\d+.\d+.\d+.\d+) +Value mc_ip (\d+.\d+.\d+.\d+) + +Start + ^\s*${src_ip}\s+${mc_ip} -> Record + diff --git a/spytest/templates/bcmcmd_l2_show.tmpl b/spytest/templates/bcmcmd_l2_show.tmpl new file mode 100644 index 00000000000..8cae7f067a9 --- /dev/null +++ b/spytest/templates/bcmcmd_l2_show.tmpl @@ -0,0 +1,12 @@ +Value mac (\S+) +Value vlan (\S+) +Value gport (\S+) +Value modid (\S+) +Value port (\S+) +Value type (\S+|\S+\s+\S+) + +Start + ^\s*mac=${mac}\s*vlan=${vlan}\s*GPORT=${gport}\s*modid=${modid}\s*port=${port}\s+${type}\s*$$ -> Record + ^\s*mac=${mac}\s*vlan=${vlan}\s*GPORT=${gport}\s*port=${port}\s+${type}\s*$$ -> Record + +EOF diff --git a/spytest/templates/bcmcmd_l3_defip_show.tmpl b/spytest/templates/bcmcmd_l3_defip_show.tmpl new file mode 100644 index 00000000000..007e975eaec --- /dev/null +++ b/spytest/templates/bcmcmd_l3_defip_show.tmpl @@ -0,0 +1,12 @@ +Value vrf (\d+) +Value route (\d+.\d+.\d+.\d+) +Value mask_len (\d+) +Value nhpmac (\d+:\d+:\d+:\d+:\d+:\d+) +Value intf (\d+) +Value port (\d+) +Value vlan (\d+) + +Start + ^\s*\d+\s+${vrf}\s+${route}\S${mask_len}\s+${nhpmac}\s+${intf}\s+\d+\s+${port}\s+\d+\s+\d+\s+\S+ -> Record + ^\s*\d+\s+${vrf}\s+${route}\S${mask_len}\s+${nhpmac}\s+${intf}\s+\d+\s+${port}\s+\d+\s+\d+\s+\S+\s+${vlan} -> Record + diff --git a/spytest/templates/bcmcmd_l3_intf_show.tmpl b/spytest/templates/bcmcmd_l3_intf_show.tmpl new file mode 100644 index 00000000000..1952dddaa28 --- /dev/null +++ b/spytest/templates/bcmcmd_l3_intf_show.tmpl @@ -0,0 +1,14 @@ +Value Unit (\d+) +Value Intf (\d+) +Value Vrf (\d+) +Value Group (\d+) +Value Vlan (\d+) +Value mac (\w+:\w+:\w+:\w+:\w+:\w+) +Value mtu (\d+) +Value ttl (\d+) +Value tunnel (\d+) +Value InnerVlan (\d+) +Value NatId (\d+) + +Start + ^\s*${Unit}\s+${Intf}\s+${Vrf}\s+${Group}\s+${Vlan}\s+${mac}\s+${mtu}\s+${ttl}\s+${tunnel}\s+${InnerVlan}\s+${NatId}\s*$$ -> Record diff --git a/spytest/templates/bcmcmd_l3_ip6host_show.tmpl b/spytest/templates/bcmcmd_l3_ip6host_show.tmpl new file mode 100644 index 00000000000..66c9fb34499 --- /dev/null +++ b/spytest/templates/bcmcmd_l3_ip6host_show.tmpl @@ -0,0 +1,9 @@ +Value VRF (\d+) +Value nhip (\d+:\d+:\d+:\d+:\d+:\d+:\d+:\d+) +Value nhpmac (\d+:\d+:\d+:\d+:\d+:\d+) +Value egr_intf (\d+) +Value port (\d+) + +Start + ^\s*\d+\s+${VRF}\s+${nhip}\s+${nhpmac}\s+${egr_intf}\s+\d+\s+${port}\s+\d+\s+\S+\s+\d+ -> Record + diff --git a/spytest/templates/bcmcmd_l3_ip6route_show.tmpl b/spytest/templates/bcmcmd_l3_ip6route_show.tmpl new file mode 100644 index 00000000000..bdbda8233e7 --- /dev/null +++ b/spytest/templates/bcmcmd_l3_ip6route_show.tmpl @@ -0,0 +1,12 @@ +Value vrf (\d+) +Value route (\d+:\d+:\d+:\d+:\d+:\d+:\d+:\d+) +Value mask_len (\d+) +Value nhpmac (\d+:\d+:\d+:\d+:\d+:\d+) +Value intf (\d+) +Value port (\d+) +Value vlan (\d+) + +Start + ^\s*\d+\s+${vrf}\s+${route}\S${mask_len}\s+${nhpmac}\s+${intf}\s+\d+\s+${port}\s+\d+\s+\d+\s+\S+ -> Record + ^\s*\d+\s+${vrf}\s+${route}\S${mask_len}\s+${nhpmac}\s+${intf}\s+\d+\s+${port}\s+\d+\s+\d+\s+\S+\s+${vlan} -> Record + diff --git a/spytest/templates/bcmcmd_l3_l3table_show.tmpl b/spytest/templates/bcmcmd_l3_l3table_show.tmpl new file mode 100644 index 00000000000..6b1b98e7b9b --- /dev/null +++ b/spytest/templates/bcmcmd_l3_l3table_show.tmpl @@ -0,0 +1,8 @@ +Value VRF (\d+) +Value nhip (\d+.\d+.\d+.\d+) +Value nhpmac (\d+:\d+:\d+:\d+:\d+:\d+) +Value egr_intf (\d+) +Value port (\d+) + +Start + ^\s*\d+\s+${VRF}\s+${nhip}\s+${nhpmac}\s+${egr_intf}\s+\d+\s+${port}\s+\d+\s+\S+\s+\d+ -> Record diff --git a/spytest/templates/bcmcmd_listmem_defip.tmpl b/spytest/templates/bcmcmd_listmem_defip.tmpl new file mode 100644 index 00000000000..499303f38dc --- /dev/null +++ b/spytest/templates/bcmcmd_listmem_defip.tmpl @@ -0,0 +1,5 @@ +Value names (\S+) +Value entries (\d+) + +Start + ^\s+\S+...${names}+\s+${entries} -> Record diff --git a/spytest/templates/bcmcmd_listmem_l3_entry.tmpl b/spytest/templates/bcmcmd_listmem_l3_entry.tmpl new file mode 100644 index 00000000000..d595b8605d5 --- /dev/null +++ b/spytest/templates/bcmcmd_listmem_l3_entry.tmpl @@ -0,0 +1,6 @@ +Value Names (\S+) +Value entries (\d+) + +Start + ^\s*\S+\s+${Names}\s+${entries} -> Record + ^\s*\S+\s+\*\s+${Names}\s+${entries} -> Record diff --git a/spytest/templates/bcmcmd_nat_entry_egress.tmpl b/spytest/templates/bcmcmd_nat_entry_egress.tmpl new file mode 100644 index 00000000000..c08090fe03d --- /dev/null +++ b/spytest/templates/bcmcmd_nat_entry_egress.tmpl @@ -0,0 +1,15 @@ +Value Filldown Type (\S+) +Value IDX (\d+) +Value TYPE (\S+) +Value ID (\d+) +Value IP (\d+.\d+.\d+.\d+) +Value MASK (\d+.\d+.\d+.\d+) +Value L4Port (\d+) +Value REFCOUNT (\d+) + +Start + ^\s*l3 nat_egress show\s*$$ + ^\s*IDX\s+TYPE\s+ID\s+IP\s+Mask\s+L4PORT\s+REFCOUNT\s*$$ + ^\s*${IDX}\s+${TYPE}\s+${ID}\s+${IP}\s+${MASK}\s+${L4Port}\s+${REFCOUNT}\s*$$ -> Record + +EOF diff --git a/spytest/templates/bcmcmd_nat_entry_ingress.tmpl b/spytest/templates/bcmcmd_nat_entry_ingress.tmpl new file mode 100644 index 00000000000..ff67a13d475 --- /dev/null +++ b/spytest/templates/bcmcmd_nat_entry_ingress.tmpl @@ -0,0 +1,14 @@ +Value Filldown Type (\S+) +Value IDX (\d+) +Value ID (\d+) +Value IP (\d+.\d+.\d+.\d+) +Value VRF (\d+) +Value L4Port (\d+) +Value IPPROTO (\d+) +Value NextHop (\d+) + +Start + ^\s*\*+${Type}\s+\S+ + ^\s*${IDX}\s+${ID}\s+${IP}\s+${VRF}\s+${L4Port}\s+${IPPROTO}\s+${NextHop}\s*$$ -> Record + +EOF diff --git a/spytest/templates/bcmcmd_pmap.tmpl b/spytest/templates/bcmcmd_pmap.tmpl new file mode 100644 index 00000000000..0de52bb1d9f --- /dev/null +++ b/spytest/templates/bcmcmd_pmap.tmpl @@ -0,0 +1,12 @@ +Value Interface (\S+) +Value Pipe (\S+) +Value Logical (\S+) +Value Physical (\S+) +Value Idb (\S+) +Value Mmu (\S+) +Value ucast_Qbase_Numq (\S+) +Value mcast_Qbase_Numq (\S+) + + +Start + ^\s*${Interface}\s+${Pipe}\s+${Logical}\s+${Physical}\s+${Idb}\s+${Mmu}\s+${ucast_Qbase_Numq}\s+${mcast_Qbase_Numq}\s*$$ -> Record \ No newline at end of file diff --git a/spytest/templates/bcmcmd_ps.tmpl b/spytest/templates/bcmcmd_ps.tmpl new file mode 100644 index 00000000000..3be9ebbe08d --- /dev/null +++ b/spytest/templates/bcmcmd_ps.tmpl @@ -0,0 +1,24 @@ +Value Port (\S+) +Value Lane_number (\d+) +Value Link (\S+) +Value Lanes (\d+) +Value Speed (\S+) +Value Duplex (\S+) +Value LinkScan (\S+) +Value AutoNeg (\S+) +Value STP_State (\S+) +Value Pause (\S+) +Value Discard (\S+) +Value Ops (\S+) +Value InterfaceType (\S+) +Value MaxFrames (\d+) +Value loopback (\S+) +Value CutThru (\S+|.) + + +Start + ^\s*${Port}\(\s*${Lane_number}\)\s+${Link}\s+${Lanes}\s+${Speed}\s+${Duplex}\s+${LinkScan}\s+${AutoNeg}\s+${STP_State}\s+${Pause}\s+${Discard}\s+${Ops}\s+${InterfaceType}\s+${MaxFrames}\s+${CutThru}\s+${loopback}\s*$$ -> Record + ^\s*${Port}\(\s*${Lane_number}\)\s+${Link}\s+${Lanes}\s+${Speed}\s+${Duplex}\s+${LinkScan}\s+${AutoNeg}\s+${STP_State}\s+${Pause}\s+${Discard}\s+${Ops}\s+${InterfaceType}\s+${MaxFrames}\s+${loopback}\s*$$ -> Record + ^\s*${Port}\(\s*${Lane_number}\)\s+${Link}\s+${Lanes}\s+${Speed}\s+${Duplex}\s+${LinkScan}\s+${AutoNeg}\s+${STP_State}\s+${Pause}\s+${Discard}\s+${Ops}\s+${InterfaceType}\s+${MaxFrames}\s*$$ -> Record + ^\s*${Port}\(\s*${Lane_number}\)\s+${Link}\s+${Lanes}\s+${Speed}\s+${Duplex}\s+${LinkScan}\s+${AutoNeg}\s+${STP_State}\s+${Discard}\s+${Ops}\s+${InterfaceType}\s+${MaxFrames}\s+${loopback}\s*$$ -> Record + ^\s*${Port}\(\s*${Lane_number}\)\s+${Link}\s+${Lanes}\s+${Speed}\s+${Duplex}\s+${LinkScan}\s+${AutoNeg}\s+${STP_State}\s+${Discard}\s+${Ops}\s+${InterfaceType}\s+${MaxFrames}\s*$$ -> Record \ No newline at end of file diff --git a/spytest/templates/bcmcmd_pvlan_show.tmpl b/spytest/templates/bcmcmd_pvlan_show.tmpl new file mode 100644 index 00000000000..e37655c2e16 --- /dev/null +++ b/spytest/templates/bcmcmd_pvlan_show.tmpl @@ -0,0 +1,7 @@ +Value port (\S+) +Value vlan (\S+) + +Start + ^\s*Port\s+${port}\s+default\s+VLAN\s+is\s+${vlan}\s*$$ -> Record + +EOF \ No newline at end of file diff --git a/spytest/templates/bcmcmd_trunk_show.tmpl b/spytest/templates/bcmcmd_trunk_show.tmpl new file mode 100644 index 00000000000..9b760b7d6c6 --- /dev/null +++ b/spytest/templates/bcmcmd_trunk_show.tmpl @@ -0,0 +1,12 @@ +Value trunk (\d+) +Value port_type (.*) +Value port_count (\d+) +Value ports (\S+) +Value dlf (\S+) +Value mc (\S+) +Value ipmc (\S+) +Value psc (\S+) +Value bit (\S+) + +Start + ^\s*trunk\s+${trunk}:\s+\(${port_type},\s+${port_count}\s+ports\)=${ports}\s+dlf=${dlf}\s+mc=${mc}\s+ipmc=${ipmc}\s+psc=${psc}\s+\(${bit}\)$$ -> Record diff --git a/spytest/templates/bcmcmd_vlan_show.tmpl b/spytest/templates/bcmcmd_vlan_show.tmpl new file mode 100644 index 00000000000..881ec3dd80b --- /dev/null +++ b/spytest/templates/bcmcmd_vlan_show.tmpl @@ -0,0 +1,9 @@ +Value vlan (\S+) +Value ports (\S+) +Value untagged (\S+) +Value filter (\S+) + +Start + ^\s*vlan\s*${vlan}\s*ports\s*${ports}\s*\(.*\),\s*untagged\s+${untagged}\s*\(.*\)\s*${filter}\s*$$ -> Record + +EOF \ No newline at end of file diff --git a/spytest/templates/box_service_version.tmpl b/spytest/templates/box_service_version.tmpl new file mode 100644 index 00000000000..bf97fa2ad6f --- /dev/null +++ b/spytest/templates/box_service_version.tmpl @@ -0,0 +1,7 @@ +Value service_type ((\S+\s*)*) +Value version (\d+|\d+\.\d+|\d+\.\d+.\d+) + +Start + ^${service_type}\s+${version}\s*$$ -> Record + +EOF \ No newline at end of file diff --git a/spytest/templates/clear_counters.tmpl b/spytest/templates/clear_counters.tmpl new file mode 100644 index 00000000000..0f44df047e7 --- /dev/null +++ b/spytest/templates/clear_counters.tmpl @@ -0,0 +1,5 @@ +Value Text (\S+\s+\S+) + +Start + ^${Text}$$ -> Record + ^$$ \ No newline at end of file diff --git a/spytest/templates/crm_show_resources.tmpl b/spytest/templates/crm_show_resources.tmpl new file mode 100644 index 00000000000..68562740df2 --- /dev/null +++ b/spytest/templates/crm_show_resources.tmpl @@ -0,0 +1,9 @@ +Value ResourceName (\S+) +Value UsedCount (\d+) +Value AvailableCount (\d+) + +Start + ^\s*Resource Name\s+Used Count\s+Available Count\s*$$ + ^(-+\s*)*$$ + ^\s*${ResourceName}\s+${UsedCount}\s+${AvailableCount}\s*$$ -> Record + ^$$ diff --git a/spytest/templates/crm_show_resources_acl_group.tmpl b/spytest/templates/crm_show_resources_acl_group.tmpl new file mode 100644 index 00000000000..560d6db7682 --- /dev/null +++ b/spytest/templates/crm_show_resources_acl_group.tmpl @@ -0,0 +1,11 @@ +Value Stage (\S+) +Value BindPoint (\S+) +Value ResourceName (\S+) +Value UsedCount (\d+) +Value AvailableCount (\d+) + +Start + ^\s*Stage\s+Bind Point\s+Resource Name\s+Used Count\s+Available Count\s*$$ + ^(-+\s*)*$$ + ^\s*${Stage}\s+${BindPoint}\s+${ResourceName}\s+${UsedCount}\s+${AvailableCount}\s*$$ -> Record + ^$$ \ No newline at end of file diff --git a/spytest/templates/crm_show_resources_acl_table.tmpl b/spytest/templates/crm_show_resources_acl_table.tmpl new file mode 100644 index 00000000000..3346ea5c939 --- /dev/null +++ b/spytest/templates/crm_show_resources_acl_table.tmpl @@ -0,0 +1,10 @@ +Value TableID (\S+) +Value ResourceName (\S+) +Value UsedCount (\d+) +Value AvailableCount (\d+) + +Start + ^\s*Table ID\s+Resource Name\s+Used Count\s+Available Count\s*$$ + ^(-+\s*)*$$ + ^\s*${TableID}\s+${ResourceName}\s+${UsedCount}\s+${AvailableCount}\s*$$ -> Record + ^$$ diff --git a/spytest/templates/crm_show_summary.tmpl b/spytest/templates/crm_show_summary.tmpl new file mode 100644 index 00000000000..53c6cca1e30 --- /dev/null +++ b/spytest/templates/crm_show_summary.tmpl @@ -0,0 +1,4 @@ +Value PollingInterval (\d+) + +Start + ^Polling Interval:\s+${PollingInterval}\s+second\(s\) -> Record \ No newline at end of file diff --git a/spytest/templates/crm_show_thresholds.tmpl b/spytest/templates/crm_show_thresholds.tmpl new file mode 100644 index 00000000000..2bb675a05c5 --- /dev/null +++ b/spytest/templates/crm_show_thresholds.tmpl @@ -0,0 +1,11 @@ +Value ResourceName (\S+) +Value ThresholdType (\S+) +Value LowThreshold (\d+) +Value HighThreshold (\d+) + +Start + ^\s*Resource Name\s+Threshold Type\s+Low Threshold\s+High Threshold\s*$$ + ^(-+\s*)*$$ + ^\s*${ResourceName}\s+${ThresholdType}\s+${LowThreshold}\s+${HighThreshold}\s*$$ -> Record + ^$$ + diff --git a/spytest/templates/docker_ps.tmpl b/spytest/templates/docker_ps.tmpl new file mode 100644 index 00000000000..c5fdbb7b4e9 --- /dev/null +++ b/spytest/templates/docker_ps.tmpl @@ -0,0 +1,14 @@ +Value container_id (\S+) +Value image (\S+) +Value command (\".*\") +Value created (\w+\s*\S+\s*\S+\s*\S+\s*\S+) +Value status (\w+\s*\w+\s*\S+\s*\S+) +Value ports (\S+|.) +Value names (\S+) + + +Start + ^\s*CONTAINER ID\s+IMAGE\s+COMMAND\s+CREATED\s+STATUS\s+PORTS\s+NAMES\s*$$ + ^\s*${container_id}\s+${image}\s+${command}\s+${created}\s+${status}\s+${names}\s*$$ -> Record + ^\s*${container_id}\s+${image}\s+${command}\s+${created}\s+${status}\s+${ports}\s+${names}\s*$$ -> Record + diff --git a/spytest/templates/ethtool_interface.tmpl b/spytest/templates/ethtool_interface.tmpl new file mode 100644 index 00000000000..ace60c2ccb4 --- /dev/null +++ b/spytest/templates/ethtool_interface.tmpl @@ -0,0 +1,37 @@ +Value INTERFACE (\S+) +Value SUPPORTED_PORTS (\S+) +Value SUPPORTED_LINK_MODES (\S+) +Value SUPPORTED_PAUSE_FRAME (\S+) +Value SUPPORTED_AUTO_NEGOTIATION (\S+) +Value ADVERTISED_PORTS (\S+) +Value ADVERTISED_LINK_MODES (\S+) +Value ADVERTISED_PAUSE_FRAME (\S+) +Value ADVERTISED_AUTO_NEGOTIATION (\S+) +Value SPEED (\S+) +Value DUPLEX (\S+) +Value PORT (\S+) +Value PHYAD (\S+) +Value TRANSCEIVER (\S+) +Value AUTO_NEGOTIATION (\S+) +Value MDIX (\S+) + + +Start + ^Settings\s+for\s+${INTERFACE}\: + ^\s*Supported\s+ports\:\s+${SUPPORTED_PORTS}$$ + ^\s*Supported\s+link\s+modes\:\s+${SUPPORTED_LINK_MODES}$$ + ^\s*Supported\s+pause\s+frame\s+use\:\s+${SUPPORTED_PAUSE_FRAME}$$ + ^\s*Supported\s+auto\-negotiation\:\s+${SUPPORTED_LINK_MODES}$$ + ^\s*Advertised\s+ports\:\s+${ADVERTISED_PORTS}$$ + ^\s*Advertised\s+link\s+modes\:\s+${ADVERTISED_LINK_MODES}$$ + ^\s*Advertised\s+pause\s+frame\s+use\:\s+${ADVERTISED_PAUSE_FRAME}$$ + ^\s*Advertised\s+auto\-negotiation\:\s+${ADVERTISED_AUTO_NEGOTIATION}$$ + ^\s*Speed\:\s+${SPEED}$$ + ^\s*Port\:\s+${PORT}$$ + ^\s*Duplex\:\s+${DUPLEX}$$ + ^\s*PHYAD\:\s+${PHYAD}$$ + ^\s*Transceiver\:\s+${TRANSCEIVER}$$ + ^\s*Auto\-negotiation\:\s+${AUTO_NEGOTIATION}$$ + ^\s*MDI\-X\:\s+${MDIX}$$ -> Record + +EOF \ No newline at end of file diff --git a/spytest/templates/id.tmpl b/spytest/templates/id.tmpl new file mode 100644 index 00000000000..7092c3df362 --- /dev/null +++ b/spytest/templates/id.tmpl @@ -0,0 +1,9 @@ +Value uid (\d+) +Value username (\S+) +Value gid (\d+) +Value group (\S+) +Value secondary_group (\S+) + + +Start + ^\s*uid=${uid}\(${username}\)\s+gid=${gid}\(${group}\)\s+groups=${secondary_group} \ No newline at end of file diff --git a/spytest/templates/index b/spytest/templates/index new file mode 100644 index 00000000000..292e933e75f --- /dev/null +++ b/spytest/templates/index @@ -0,0 +1,415 @@ +# First line is the header fields for columns and is mandatory. +# Regular expressions are supported in all fields except the first. +# Last field supports variable length command completion. +# abc[[xyz]] is expanded to abc(x(y(z)?)?)?, regexp inside [[]] is not supported +# +# Rules of Ordering: +# - OS in alphbetical order +# - Command in length other +# - When Length is the same, use alphabetical order +# - Keep space between OS's +# +Template, Hostname, Platform, Command +show_interfaces_status_qa.tmpl, .*, sonic, show interfaces status +show_interface_status.tmpl, .*, sonic, show interface status +unix_ifcfg.tmpl, .*,sonic, /sbin/ifconfig +linux/ip_route_list_dev.tmpl, .*,sonic, /sbin/ip route list dev eth0 +linux/ip_route_list_dev.tmpl, .*,sonic, ip route list dev eth0 +show_ip_bgp_summary.tmpl, .*, sonic, show ip bgp summary +show_ip_bgp_summary.tmpl, .*, sonic, show bgp ipv4 summary +show_ip_bgp_summary.tmpl, .*, sonic, show bgp ipv6 summary +show_ip_bgp_summary.tmpl, .*, sonic, show bgp vrf .* summary +show_ip_bgp_summary.tmpl, .*, sonic, show ip bgp vrf .* summary +show_ip_interfaces.tmpl, .*, sonic, show ip interface +show_ip_interfaces.tmpl, .*, sonic, show ipv6 interface +show_ip_ospf_lsdb.tmpl, .*, sonic, show ip ospf vrf .* database router +show_ip_ospf_lsdb.tmpl, .*, sonic, show ip ospf vrf .* database network +show_ip_ospf_lsdb.tmpl, .*, sonic, show ip ospf vrf .* database summary +show_ip_ospf_lsdb.tmpl, .*, sonic, show ip ospf vrf .* database asbr-summary +show_ip_ospf_lsdb.tmpl, .*, sonic, show ip ospf vrf .* database external +show_ip_ospf_lsdb.tmpl, .*, sonic, show ip ospf vrf .* database max-age +show_ip_ospf_traffic.tmpl, .*, sonic, show ip ospf vrf .* interface traffic +show_ip_ospf_traffic.tmpl, .*, sonic, show ip ospf interface traffic +show_ip_ospf_interface.tmpl, .*, sonic, show ip ospf interface +show_ip_ospf_interface.tmpl, .*, sonic, show ip ospf interface .* +show_ip_ospf_interface.tmpl, .*, sonic, show ip ospf vrf .* interface +show_ip_ospf_interface.tmpl, .*, sonic, show ip ospf vrf .* interface .* +show_ip_ospf_neighbor.tmpl, .*, sonic, show ip ospf neighbor +show_ip_ospf_neighbor.tmpl, .*, sonic, show ip ospf vrf .* neighbor +show_ip_ospf_router.tmpl, .*, sonic, show ip ospf +show_ip_ospf_router.tmpl, .*, sonic, show ip ospf vrf all +show_ip_ospf_router.tmpl, .*, sonic, show ip ospf vrf .* +show_ip_route_summary.tmpl, .*, sonic, show ip route summary +show_ip_route_summary.tmpl, .*, sonic, show ip route vrf .* summary +show_ip_route_summary.tmpl, .*, sonic, show ipv6 route vrf .* summary +show_ip_route.tmpl, .*, sonic, show ip route +show_ip_route.tmpl, .*, sonic, show ipv6 route +show_ip_route.tmpl, .*, sonic, show ip route vrf * ospf +show_vlan_config.tmpl, .*, sonic, show vlan config +show_platform_summary.tmpl, .*, sonic, show platform summary +show_interfaces_status.tmpl, .*, sonic, show interfaces status +show_bgp_ipv4_prefix.tmpl, .*, sonic, show bgp ipv4\s*([\d\.\/]+) +show_ip_bgp.tmpl, .*, sonic, show ip bgp +show_ip_bgp.tmpl, .*, sonic, show bgp ipv4 +show_bgp_ipv6.tmpl, .*, sonic, show bgp ipv6 +show_interfaces_portchannel_fallback.tmpl, .*, sonic, show interfaces portchannel \w+ fallback +show_interfaces_portchannel_qa.tmpl, .*, sonic, show interfaces portchannel +show_interfaces_loopback.tmpl, .*, sonic, show interfaces loopback +show_interfaces_counters_detailed.tmpl, .*, sonic, show interfaces counters detailed +show_interfaces_counters.tmpl, .*, sonic, show interfaces counters +crm_show_summary.tmpl, .*, sonic, crm show summary +crm_show_thresholds.tmpl, .*, sonic, crm show thresholds .* +crm_show_resources_acl_group.tmpl, .*, sonic, crm show resources acl group +crm_show_resources_acl_table.tmpl, .*, sonic, crm show resources acl table +crm_show_resources.tmpl, .*, sonic, crm show resources .* +show_aaa.tmpl, .*, sonic, show aaa +show_tacacs.tmpl, .*, sonic, show tacacs +show_acl_table_qa.tmpl, .*, sonic, show acl table +show_lldp_neighbors.tmpl, .*, sonic, show lldp neighbors +show_lldp_neighbors.tmpl, .*, sonic, show neighbors +show_acl_rule_qa.tmpl, .*, sonic, show acl rule +show_lldp_table.tmpl, .*, sonic, show lldp table +# Mirror output command for CLICK CLI +show_mirror_session.tmpl, .*, sonic, show mirror_session.* +show_arp.tmpl, .*, sonic, show arp +show_ntp.tmpl, .*, sonic, show ntp +show_ntpstat.tmpl, .*, sonic, ntpstat +show_ntpstat.tmpl, .*, sonic, sudo cgexec -g l3mdev:mgmt ntpstat +show_clock.tmpl, .*, sonic, show clock +show_timedatectl_status.tmpl, .*, sonic, timedatectl status +pfcwd_show_config.tmpl, .*, sonic, pfcwd show config +pfcwd_show_stats.tmpl, .*, sonic, pfcwd show stats +pfc_show_asymmetric.tmpl, .*, sonic, pfc show asymmetric +show_vlan_brief.tmpl, .*, sonic, show vlan brief +show_vlan_count.tmpl, .*, sonic, show vlan count +show_interfaces_description.tmpl, .*, sonic, show interfaces description +show_acl_rule.tmpl, .*, sonic, show acl rule +show_platform_psustatus.tmpl, .*, sonic, show platform psustatus +show_clock.tmpl, .*, sonic, show clock +show_users.tmpl, .*, sonic, show users +show_acl_table.tmpl, .*, sonic, show acl table +show_arp.tmpl, .*, sonic, show arp +show_mac_address_table_count.tmpl, .*, sonic, show mac address-table count$ +show_mac_address_table.tmpl, .*, sonic, show mac address-table +show_mac_count.tmpl, .*, sonic, show mac count +show_mac_aging_time.tmpl, .*, sonic, show mac aging_time +show_mac.tmpl, .*, sonic, show mac +show_interfaces_portchannel.tmpl, .*, sonic, show interfaces portchannel +show_interfaces_counters.tmpl, .*, sonic, show interfaces counters -a +clear_counters.tmpl, .*, sonic, show interfaces counters -c +show_arp.tmpl, .*, sonic,show arp +unix_ifcfg.tmpl, .*, sonic, /sbin/ifconfig .* +sonic_installer_list.tmpl, .*, sonic, sonic_installer list +sonic_acl_show.tmpl, .*, sonic, aclshow.* +sudo_sonic_installer_list.tmpl, .*, sonic, sudo sonic_installer list +show_bgp_neighbor.tmpl, .*, sonic, show bgp ipv4 neighbor.* +show_bgp_neighbor.tmpl, .*, sonic, show bgp neighbor.* +show_uptime.tmpl, .*, sonic, show uptime +show_ecn.tmpl, .*, sonic, show ecn +show_reboot-cause.tmpl, .*, sonic, show reboot-cause +show_pfc_counters.tmpl, .*, sonic, show pfc counters +show_spanning_tree_vlan_interface.tmpl, .*, sonic, show spanning-tree vlan interface +show_spanning_tree_statistics.tmpl, .*, sonic, show spanning_tree statistics(\s+vlan\s+\d+)? +ztp_status.tmpl, .*, sonic, sudo ztp status$ +top.tmpl, .*, sonic, top +docker_ps.tmpl, .*, sonic, docker ps +show_version.tmpl, .*, sonic, show version +bcmcmd_l2_show.tmpl, .* , sonic, bcmcmd "l2 show" +bcmcmd_vlan_show.tmpl, .* , sonic, bcmcmd "vlan show" +bcmcmd_pvlan_show.tmpl, .* , sonic, bcmcmd "pvlan show" +bcmcmd_trunk_show.tmpl, .* , sonic, bcmcmd "trunk show" +show_ndp.tmpl, .*, sonic, show ndp +pfcstat.tmpl, .*, sonic, pfcstat +show_c.tmpl, .*, sonic, bcmcmd "show c.*" +show_platform_syseeprom.tmpl, .*, sonic, show platform syseeprom +show_bgp_neighbor_detail.tmpl, .*, sonic, show bgp neighbors .* +show_bgp_neighbor.tmpl, .*, sonic, show bgp vrf.* +show_queue_counters.tmpl, .*, sonic, show queue counters .* +show_queue_threshold_multicast.tmpl, .*, sonic, show queue threshold multicast +show_queue_threshold_unicast.tmpl, .*, sonic, show queue threshold unicast +show_priority_group_threshold.tmpl, .*, sonic, show priority-group threshold .* +bcmcmd_pmap.tmpl, .*, sonic, bcmcmd "show pmap" +bcmcmd_ps.tmpl, .*, sonic, bcmcmd "ps" +show_bfd_peers_brief.tmpl, .* , sonic, show bfd peers brief +show_bfd_couters.tmpl, .*, sonic, show bfd peers counters +show_bfd_couters.tmpl, .*, sonic, show bfd peer .* counters +show_bfd_peer.tmpl, .*, sonic, show bfd peer* +show_bfd_peers_brief.tmpl, .* , sonic, show bfd vrf .* peers brief +show_bfd_couters.tmpl, .*, sonic, show bfd vrf .* peers counters +show_bfd_couters.tmpl, .*, sonic, show bfd vrf .* peer .* counters +show_bfd_peer.tmpl, .*, sonic, show bfd vrf .* peer* +show_nat_config_bindings.tmpl, .*, sonic, show nat config bindings +show_nat_config_globalvalues.tmpl, .*, sonic, show nat config globalvalues +show_nat_config_pool.tmpl, .*, sonic, show nat config pool +show_nat_config_static.tmpl, .*, sonic, show nat config static +show_warm_restart_config.tmpl, .*, sonic, show warm_restart config +show_warm_restart_state.tmpl, .*, sonic, show warm_restart state +show_nat_translations_count.tmpl, .*, sonic, show nat translations count +show_nat_translations.tmpl, .*, sonic, show nat translations +show_nat_statistics.tmpl, .*, sonic, show nat statistics +bcmcmd_nat_entry_ingress.tmpl, .*, sonic, bcmcmd 'l3 nat_ingress show' +bcmcmd_nat_entry_egress.tmpl, .*, sonic, bcmcmd 'l3 nat_egress show' +show_ssh_vrf.tmpl, .*, sonic, show ssh-server vrfs +show_ip_tables_s.tmpl, .*, sonic, sudo iptables -S +show_ip_tables_s.tmpl, .*, sonic, sudo ip6tables -S +show_ip_tables.tmpl, .*, sonic, sudo iptables -t nat -v -n -L +bcmcmd_l3_intf_show.tmpl, .*, sonic, bcmcmd 'l3 intf show' +show_conntrack_table.tmpl, .*, sonic, sudo conntrack -j -L +show_docker_ps.tmpl, .*, sonic, sudo docker ps +show_spanning_tree.tmpl, .*, sonic, show spanning_tree$ +show_vrf_management.tmpl, .*, sonic, show vrf management +show_vrf_verbose.tmpl,.*,sonic,show vrf --verbose +show_vrf.tmpl,.*,sonic,show vrf +show_ip_route.tmpl, .*, sonic, show ip route vrf .* +show_ip_route.tmpl, .*, sonic, show ipv6 route vrf .* +show_threshold_breaches.tmpl, .*, sonic, show threshold breaches +show_ip_route.tmpl, .*, sonic, show ipv6 route vrf .* +bcmcmd_l3_defip_show.tmpl, .*, sonic, bcmcmd 'l3 defip show' +bcmcmd_l3_ip6route_show.tmpl, .*, sonic, bcmcmd 'l3 ip6route show' +bcmcmd_l3_l3table_show.tmpl, .*, sonic, bcmcmd 'l3 l3table show' +bcmcmd_l3_ip6host_show.tmpl, .*, sonic, bcmcmd 'l3 ip6host show' +show_system_status.tmpl, .*, sonic, show system status +show_error_database.tmpl, .*, sonic, show error_database +bcmcmd_listmem_l3_entry.tmpl, .*, sonic, bcmcmd 'listmem l3_entry' +bcmcmd_listmem_defip.tmpl, .*, sonic, bcmcmd 'listmem DEFIP' +show_spanning_tree_vlan.tmpl, .*, sonic, show (spanning_tree|spanning-tree) vlan\s+\d+(\s+ interface\s+\w+)? +cloud_advanced/show_tam_device.tmpl, .*, sonic, show tam device +cloud_advanced/show_tam_collector.tmpl, .*, sonic, show tam collector +cloud_advanced/show_tam_int_ifa_flow.tmpl, .*, sonic, show tam-int-ifa flow +cloud_advanced/show_tam_int_ifa_flow_klish.tmpl, .*, sonic, show tam int-ifa flow +cloud_advanced/show_tam_int_ifa_status.tmpl, .*, sonic, show (tam-int-ifa|tam int-ifa) status +cloud_advanced/show_tam_int_ifa_statistics.tmpl, .*, sonic, show tam-int-ifa statistics +cloud_advanced/show_tam_int_ifa_statistics_klish.tmpl, .*, sonic, show tam int-ifa statistics +cloud_advanced/show_tam_int_ifa_supported.tmpl, .*, sonic, show (tam-int-ifa|tam int-ifa) supported +cloud_advanced/show_ifa_enabled.tmpl, .*, sonic, show ifa enabled +ztp_status_v.tmpl, .*, sonic, sudo ztp status -v +ps_aux.tmpl, .*, sonic, sudo ps aux.* +show_spanning_tree_root_guard.tmpl, .*, sonic, show spanning_tree root_guard +show_spanning_tree_bpdu_guard.tmpl, .*, sonic, show spanning_tree bpdu_guard +stpctl_port.tmpl, .*, sonic, stpctl\s+port\s+\d+\s+\w+ +route_n.tmpl, .*, sonic, sudo route -n +show_vrrpv3.tmpl,.*,sonic,show vrrp6 \w+ +show_vrrpv3_summary.tmpl,.*,sonic,show vrrp6 +show_vrrp.tmpl,.*,sonic,show vrrp \w+ +show_vrrp_summary.tmpl,.*,sonic,show vrrp +show_ip_dhcp_relay_statistics.tmpl, .*, sonic, show ip dhcp-relay statistics.* +spytest_infra_debug.tmpl, .*, sonic, spytest infra debug +show_interfaces_pktdrops_nonzero.tmpl, .*, sonic, show interfaces pktdrops nonzero +show_platform_psusummary.tmpl, .*, sonic, show platform psusummary +show_platform_fanstatus.tmpl, .*, sonic, show platform fanstatus +show_interfaces_counters.tmpl, .*, sonic, sudo show interfaces counters -i .* +show_bgp_l2vpn_evpn_vni.tmpl, .*, sonic, show bgp l2vpn evpn vni +show_bgp_l2vpn_evpn_vni_id.tmpl, .*, sonic, show bgp l2vpn evpn vni .* +show_bgp_l2vpn_evpn_summary.tmpl, .*, sonic, show bgp l2vpn evpn summary.* +show_bgp_l2vpn_evpn_rd.tmpl, .*, sonic, show bgp l2vpn evpn rd .* +show_bgp_l2vpn_evpn_route_type_multicast.tmpl, .*, sonic, show bgp l2vpn evpn route type multicast +show_bgp_l2vpn_evpn_route_type_prefix.tmpl, .*, sonic, show bgp l2vpn evpn route type prefix +show_bgp_l2vpn_evpn_route_type_macip.tmpl, .*, sonic, show bgp l2vpn evpn route type macip +show_bgp_l2vpn_evpn_route_detail_type_prefix.tmpl, .*, sonic, show bgp l2vpn evpn route detail type prefix +show_bgp_l2vpn_evpn_route.tmpl, .*, sonic, show bgp l2vpn evpn route +show_radius.tmpl, .*, sonic, show radius.* +show_sflow.tmpl, .*, sonic, show sflow$ +show_sflow_interface.tmpl, .*, sonic, show sflow interface.* +show_storm_control.tmpl, .*, sonic, show storm-control .* +show_vxlan_tunnel.tmpl, .*, sonic, show vxlan tunnel +show_vxlan_interface.tmpl, .*, sonic, show vxlan interface +telemetry_gnmi_get.tmpl, .*, sonic, docker exec -it telemetry bash -c .* +show_ip_igmp_snooping_groups.tmpl, .*, sonic, show ip igmp snooping groups +show_ip_igmp_snooping.tmpl, .*, sonic, show ip igmp snooping +psuutil_status.tmpl, .*, sonic, sudo psuutil status$ +box_service_version.tmpl, .*, sonic, sudo psuutil version +box_service_version.tmpl, .*, sonic, sudo sfputil version +box_service_version.tmpl, .*, sonic, sudo pddf_psuutil version +box_service_version.tmpl, .*, sonic, sudo pddf_fanutil version +box_service_version.tmpl, .*, sonic, sudo pddf_ledutil version +box_service_version.tmpl, .*, sonic, sudo pddf_thermalutil version +show_platform_syseeprom.tmpl, .*, sonic, sudo decode-syseeprom +show_interfaces_transceiver_presence.tmpl, .*, sonic, sudo sfputil show presence +show_interfaces_transceiver_presence.tmpl, .*, sonic, show interface transceiver presence +sfputil_show_lpmode.tmpl, .*, sonic, sudo sfputil show lpmode +sfputil_show_eeprom.tmpl, .*, sonic, sudo sfputil show eeprom +sfputil_show_eeprom.tmpl, .*, sonic, show interface transceiver eeprom +psuutil_status.tmpl, .*, sonic, sudo pddf_psuutil status +pddf_psuutil_mfrinfo.tmpl, .*, sonic, sudo pddf_psuutil mfrinfo +pddf_psuutil_seninfo.tmpl, .*, sonic, sudo pddf_psuutil seninfo +pddf_fanutil_direction.tmpl, .*, sonic, sudo pddf_fanutil direction +pddf_fanutil_getspeed.tmpl, .*, sonic, sudo pddf_fanutil getspeed +pddf_fanutil_status.tmpl, .*, sonic, sudo pddf_fanutil status +pddf_thermalutil_gettemp.tmpl, .*, sonic, sudo pddf_thermalutil gettemp +# Mirror output command for Klish CLI +show_mirror_session.tmpl, .*, sonic, show mirror-session.* +show_classifier.tmpl, .*, sonic, show classifier +show_policy.tmpl, .*, sonic, show policy +show_service-policy_summary.tmpl, .*, sonic, show service-policy summary +show_service-policy_policy.tmpl, .*, sonic, show service-policy policy +show_service-policy_interface.tmpl, .*, sonic, show service-policy interface +show_ip_mroute.tmpl,.*,sonic,show ip mroute .* +show_vxlan_evpn_remote_mac_all.tmpl, .*, sonic, show vxlan (evpn_remote_mac|remote mac) +show_vxlan_evpn_remote_vni_all.tmpl, .*, sonic, show vxlan (evpn_remote_vni|remote vni) +show_vxlan_vlanvnimap.tmpl, .*, sonic, show vxlan vlanvnimap +show_vxlan_vrfvnimap.tmpl, .*, sonic, show vxlan vrfvnimap +cloud_advanced/show_tam_drop_monitor_statistics.tmpl, .*, sonic, show tam drop-monitor statistics .* +cloud_advanced/show_tam_drop_monitor_aging_interval.tmpl, .*, sonic, show tam drop-monitor aging-interval +cloud_advanced/show_tam_drop_monitor_flow.tmpl, .*, sonic, show tam drop-monitor flow .* +cloud_advanced/show_sample.tmpl, .*, sonic, show sample .* +cloud_advanced/show_tam_drop_monitor_supported.tmpl, .*, sonic, show tam drop-monitor supported +show_ip_mroute.tmpl,.*,sonic,show ip mroute.* +show_ip_pim_nexthop_lookup.tmpl,.*,sonic,show ip pim nexthop-lookup .* +show_ip_pim_nexthop_lookup.tmpl,.*,sonic,show ip pim vrf \S+ nexthop-lookup .* +show_ip_pim_neighbor_detail.tmpl,.*,sonic,show ip pim neighbor \w+ +show_ip_pim_neighbor_detail.tmpl,.*,sonic,show ip pim vrf \S+ neighbor \w+ +show_ip_pim_neighbor.tmpl,.*,sonic,show ip pim neighbor +show_ip_pim_neighbor.tmpl,.*,sonic,show ip pim vrf \S+ neighbor +show_ip_pim_group_type.tmpl,.*,sonic,show ip pim group-type$ +show_ip_pim_group_type.tmpl,.*,sonic,show ip pim vrf \S+ group-type$ +show_ip_pim_group_type_groupid.tmpl,.*,sonic,show ip pim group-type \S+ +show_ip_pim_group_type_groupid.tmpl,.*,sonic,show ip pim vrf \S+ group-type \S+ +show_ip_pim_interface_traffic.tmpl,.*,sonic,show ip pim interface traffic +show_ip_pim_interface_traffic.tmpl,.*,sonic,show ip pim vrf \S+ interface traffic +show_ip_pim_interface_id.tmpl,.*,sonic,show ip pim interface \w+ +show_ip_pim_interface_id.tmpl,.*,sonic,show ip pim vrf \S+ interface \w+ +show_ip_pim_interface.tmpl,.*,sonic,show ip pim interface$ +show_ip_pim_interface.tmpl,.*,sonic,show ip pim vrf \S+ interface$ +show_ip_pim_nexthop.tmpl,.*,sonic,show ip pim nexthop +show_ip_pim_nexthop.tmpl,.*,sonic,show ip pim vrf \S+ nexthop +show_ip_pim_state.tmpl,.*,sonic,show ip pim state +show_ip_pim_state.tmpl,.*,sonic,show ip pim vrf \S+ state +show_ip_pim_assert_internal.tmpl,.*,sonic,show ip pim assert-internal +show_ip_pim_assert_internal.tmpl,.*,sonic,show ip pim vrf \S+ assert-internal +show_ip_pim_assert_metric.tmpl,.*,sonic,show ip pim assert-metric +show_ip_pim_assert_metric.tmpl,.*,sonic,show ip pim vrf \S+ assert-metric +show_ip_pim_assert_winner_metric.tmpl,.*,sonic,show ip pim assert-winner-metric +show_ip_pim_assert_winner_metric.tmpl,.*,sonic,show ip pim vrf \S+ assert-winner-metric +show_ip_pim_assert.tmpl,.*,sonic,show ip pim assert$ +show_ip_pim_assert.tmpl,.*,sonic,show ip pim vrf \S+ assert$ +show_ip_pim_upstream_join_desired.tmpl,.*,sonic,show ip pim upstream-join-desired +show_ip_pim_upstream_join_desired.tmpl,.*,sonic,show ip pim vrf \S+ upstream-join-desired +show_ip_pim_upstream_rpf.tmpl,.*,sonic,show ip pim upstream-rpf +show_ip_pim_upstream_rpf.tmpl,.*,sonic,show ip pim vrf \S+ upstream-rpf +show_ip_pim_upstream.tmpl,.*,sonic,show ip pim upstream$ +show_ip_pim_upstream.tmpl,.*,sonic,show ip pim vrf \S+ upstream$ +show_ip_pim_join.tmpl,.*,sonic,show ip pim join +show_ip_pim_join.tmpl,.*,sonic,show ip pim vrf \S+ join +show_ip_pim_secondary.tmpl,.*,sonic,show ip pim secondary +show_ip_pim_secondary.tmpl,.*,sonic,show ip pim vrf \S+ secondary +show_ip_pim_local_membership.tmpl,.*,sonic,show ip pim local-membership +show_ip_pim_local_membership.tmpl,.*,sonic,show ip pim vrf \S+ local-membership +show_ip_pim_rpf.tmpl,.*,sonic,show ip pim rpf +show_ip_pim_rpf.tmpl,.*,sonic,show ip pim vrf \S+ rpf +show_ip_multicast.tmpl,.*,sonic,show ip multicast +show_ip_multicast.tmpl,.*,sonic,show ip multicast vrf \S+ +bcmcmd_ipmc_table_show.tmpl, .*, sonic, bcmcmd 'ipmc table show' +show_debug_ipmcorchall.tmpl, .*, sonic, show debug ipmcorch all +show_ip_igmp_group_retransmission.tmpl,.*,sonic,show ip igmp groups retransmissions +show_ip_igmp_group_retransmission.tmpl,.*,sonic,show ip igmp vrf \S+ groups retransmissions +show_ip_igmp_source_retransmission.tmpl,.*,sonic,show ip igmp sources retransmissions +show_ip_igmp_source_retransmission.tmpl,.*,sonic,show ip igmp vrf \S+ sources retransmissions +show_ip_igmp_groups.tmpl,.*,sonic,show ip igmp groups +show_ip_igmp_groups.tmpl,.*,sonic,show ip igmp vrf \S+ groups +show_ip_igmp_sources.tmpl,.*,sonic,show ip igmp sources +show_ip_igmp_sources.tmpl,.*,sonic,show ip igmp vrf \S+ sources +show_ip_igmp_join.tmpl,.*,sonic,show ip igmp join +show_ip_igmp_join.tmpl,.*,sonic,show ip igmp vrf \S+ join +show_ip_igmp_statistics.tmpl,.*,sonic,show ip igmp statistics.* +show_ip_igmp_statistics.tmpl,.*,sonic,show ip igmp vrf \S+ statistics.* +show_ip_igmp_interface.tmpl,.*,sonic,show ip igmp interface.* +show_ip_igmp_interface.tmpl,.*,sonic,show ip igmp vrf \S+ interface.* +show_psample_stats.tmpl, .*, sonic, sudo cat /proc/bcm/knet-cb/psample/stats +cloud_advanced/show_tam_int_ifa_ts_flow.tmpl, .*, sonic, show tam int-ifa-ts flow +cloud_advanced/show_tam_int_ifa_ts_status.tmpl, .*, sonic, show tam int-ifa-ts status +cloud_advanced/show_tam_int_ifa_ts_statistics.tmpl, .*, sonic, show tam int-ifa-ts statistics +cloud_advanced/show_tam_int_ifa_ts_supported.tmpl, .*, sonic, show tam int-ifa-ts supported +show_Vlan.tmpl, .*, sonic, show Vlan.* +tcpdump.tmpl, .*, sonic, sudo cat /tmp/tcp.log +show_interface_status.tmpl, .*, sonic, show interface status +show_interface_counters.tmpl, .*, sonic, show interface counters +show_ptp.tmpl,.*,sonic,show ptp$ +show_ptp_clock.tmpl,.*,sonic,show ptp clock +show_ptp_time_property.tmpl,.*,sonic,show ptp time-property +show_ptp_parent.tmpl,.*,sonic,show ptp parent +show_ptp_port.tmpl,.*,sonic,show ptp port Ethernet \d+ +mclagdctl_dump_state.tmpl, .*, sonic, mclagdctl dump state +mclagdctl_dump_portlist_local.tmpl, .*, sonic, mclagdctl dump portlist local -i \d+ +mclagdctl_dump_portlist_peer.tmpl, .*, sonic, mclagdctl dump portlist peer -i \d+ +mclagdctl_dump_mac.tmpl, .*, sonic, mclagdctl dump mac -i \d+ +show_mclag_brief.tmpl, .*, sonic, show mclag brief +show_mclag_interface.tmpl, .*, sonic, show mclag interface \d+ \d+ +show_frr_config.tmpl,.*,sonic, write terminal +show_frr_config.tmpl,.*,sonic, write terminal ospfd +show_portchannel_kernel.tmpl, .*, sonic, ip link show dev .* +teamdctl_portchannel_state.tmpl, .*, sonic, teamdctl PortChannel.* state +teamdctl_portchannel_config.tmpl, .*, sonic, teamdctl PortChannel.* config dump +show_portchannel_summary.tmpl, .*, sonic, show PortChannel summary.* +show_intf_portchannel.tmpl, .*, sonic, show interface PortChannel(\s*|\s+\d+) +show_management_vrf.tmpl, .*, sonic, show mgmt-vrf +show_ip_vrf_management.tmpl, .*, sonic, show ip vrf management +show_ip_dhcp_relay_brief.tmpl, .*, sonic, show ip dhcp-relay brief.* +show_ip_dhcp_relay_brief.tmpl, .*, sonic, show ipv6 dhcp-relay brief.* +show_ipv6_dhcp_relay_statistics.tmpl, .*, sonic, show ipv6 dhcp-relay statistics.* +show_ip_dhcp_relay_detailed.tmpl, .*, sonic, show ip dhcp-relay detailed.* +show_ip_dhcp_relay_detailed.tmpl, .*, sonic, show ipv6 dhcp-relay detailed.* +show_ip_helper_address_config.tmpl, .*, sonic, show ip helper_address config +show_ip_forward_protocol_config.tmpl, .*, sonic, show ip forward_protocol config +show_ip_helper_address_statistics.tmpl, .*, sonic, show ip helper_address statistics +sonic_snmp_yml.tmpl, .*, sonic, cat /etc/sonic/snmp.yml +show_snmp_server_details.tmpl, .*, sonic, show snmp-server.* +show_portgroup.tmpl, .*, sonic, show portgroup +show_ip_static-anycast-gateway.tmpl, .*, sonic, show ip static-anycast-gateway +show_ipv6_static-anycast-gateway.tmpl, .*, sonic, show ipv6 static-anycast-gateway +id.tmpl, .*, sonic, id \S+ +show_hardware_access_list.tmpl, .*, sonic, show hardware access-list +show_docker_snmp_conf.tmpl, .*, sonic, sudo docker exec -ti snmp cat /etc/snmp/snmpd.conf.* + + +######################################################################################### +######################## NEED TO BE CAREFUL ABOUT ADDING REDIS-CLI PATTERNS +######################## ENSURE THAT THE WILDCARDS ARE BELOW SPECIFIC PATTERNS +######################################################################################### +show_stp_vlan_intf_flush.tmpl, .*, sonic, redis-cli -n 0 hgetall STP_VLAN_INTF_FLUSH.* +show_stp_vlan_intf.tmpl, .*, sonic, redis-cli -n 0 hgetall _STP_VLAN_INTF.* +show_stp_vlan_instance.tmpl, .*, sonic, redis-cli -n 0 hgetall STP_VLAN_INSTANCE.* +show_stp_port_state.tmpl, .*, sonic, redis-cli -n 0 hgetall STP_PORT_STATE.* +show_stp_intf.tmpl, .*, sonic, redis-cli -n 0 hgetall _STP_INTF.* +show_stp_vlan.tmpl, .*, sonic, redis-cli -n 0 hgetall _STP_VLAN.* +show_nat_apdb.tmpl, .*, sonic, redis-cli -n 0 hgetall NA.* +show_mcast_appdb.tmpl, .*, sonic, redis-cli -n 0 hgetall "IPMC_ROUTE_TABLE.*" +show_intf_mcast_mode_appdb.tmpl, .*, sonic, redis-cli -n 0 hgetall INTF_TABLE:.* +show_redis_cli_counters_map.tmpl, .*, sonic, redis-cli -n 2 hget .*MAP .* +show_redis_cli_vid_to_rid_map.tmpl, .*, sonic, redis-cli -n 1 hget VIDTORID .* +show_tam_int_ifa_ts_redis_flow.tmpl, .*, sonic, redis-cli -n 0 hgetall _TAM_INT_IFA_TS_FLOW.* +cloud_advanced/show_tam_int_ifa_ts_redis_flow.tmpl, .*, sonic, redis-cli -n 0 hgetall _TAM_INT_IFA_TS_FLOW.* +cloud_advanced/show_redis_tam_drop_monitor.tmpl, .*, sonic, sudo redis-cli -n 0 hgetall TAM_DROP_MONITOR_FLOW_TABLE:* +show_portchannel_configdb.tmpl, .*, sonic, redis-cli -n 4 hgetall 'PORTCHANNEL.* +show_portchannel_appdb.tmpl, .*, sonic, redis-cli -n 0 hgetall 'LAG_TABLE.* +show_portchannel_statedb.tmpl, .*, sonic, redis-cli -n 6 hgetall 'LAG_TABLE.* +show_redis_cli_stp_iccp.tmpl, .*, sonic, redis-cli -n 6 hgetall '_STP_ICCP.* +show_redis_cli_stp_iccp.tmpl, .*, sonic, redis-cli -n 6 hgetall 'STP_FASTAGEING_FLUSH.* +show_redis-cli_keys.tmpl, .*, sonic, redis-cli -n 8 keys ERROR.* +show_redis_error_db.tmpl, .*, sonic, redis-cli -n 8 hgetall .* +show_redis_cli_key_search.tmpl, .*, sonic, redis-cli HGETALL \S+ +show_redis_cli_key_search.tmpl, .*, sonic, redis-cli -n \d+ hgetall \S+:oid:\S+ +show_redis_cli_ip_db.tmpl, .*, sonic, redis-cli -n \d+ hgetall \S+ +show_redis_cli_key_search.tmpl, .*, sonic, redis-cli -n \d+ keys \S+ +show_redis_cli_key_search.tmpl, .*, sonic, sudo redis-cli -n \d+ keys \S+ +show_redis_cli_key_buffer_pool.tmpl, .*, sonic, redis-cli -n \d+ Hgetall \S+ +######################################################################################### +show_udld_global.tmpl,.*,sonic,show udld global +show_udld_neighbors.tmpl,.*,sonic,show udld neighbors +show_udld_interface.tmpl,.*,sonic,show udld interface \w+ +show_udld_statistics.tmpl,.*,sonic,show udld statistics +show_udld_statistics.tmpl,.*,sonic,show udld statistics interface \w+ +show_queue_watermark_multicast.tmpl, .*, sonic, show queue (watermark|persistent-watermark) multicast .* +show_queue_threshold_unicast.tmpl, .*, sonic, show queue (watermark|persistent-watermark) unicast .* +show_priority_group_threshold.tmpl, .*, sonic, show priority-group (watermark|persistent-watermark) (shared|headroom) .* +show_watermark_telemetry_interval.tmpl, .*, sonic, show watermark telemetry interval +show_watermark_interval.tmpl, .*, sonic, show watermark interval +show_buffer_pool_watermark.tmpl, .*, sonic, show buffer_pool (watermark|persistent-watermark) +show_linktrack_summary.tmpl, .*, sonic, show linktrack summary +show_linktrack_group.tmpl, .*, sonic, show linktrack group .* +show_link_state_tracking_group.tmpl, .*, sonic, show link state tracking \w+ +show_link_state_tracking.tmpl, .*, sonic, show link state tracking +show_ssh_server_vrfs.tmpl, .*, sonic, show ssh-server vrfs +show_neigh_suppress.tmpl, .*, sonic, show (neigh-suppress|NeighbourSuppressStatus).* +show_queue_watermark_cpu.tmpl, .*, sonic, show queue watermark cpu +show_buffer_pool_watermark_percentage.tmpl, .*, sonic, show buffer_pool (watermark|persistent-watermark) (-p|--percentage) +ethtool_interface.tmpl, .*, sonic, /sbin/ethtool .* \ No newline at end of file diff --git a/spytest/templates/linux/ip_route_list_dev.tmpl b/spytest/templates/linux/ip_route_list_dev.tmpl new file mode 100644 index 00000000000..5b387a2e769 --- /dev/null +++ b/spytest/templates/linux/ip_route_list_dev.tmpl @@ -0,0 +1,8 @@ +Value NETWORK (\d+\.\d+\.\d+\.\d+) +Value ADDRESS (\d+\.\d+\.\d+\.\d+) +Value PREFIX (\d+) +Value INTF (\S+) + +Start + ^${NETWORK}/${PREFIX}\s+proto\s+kernel\s+scope\s+link\s+src\s+${ADDRESS} -> Record + ^${NETWORK}/${PREFIX}\s+dev\s+${INTF}\s+proto\s+kernel\s+scope\s+link\s+src\s+${ADDRESS} -> Record diff --git a/spytest/templates/linux/ip_route_list_dev.txt b/spytest/templates/linux/ip_route_list_dev.txt new file mode 100644 index 00000000000..e37be33c793 --- /dev/null +++ b/spytest/templates/linux/ip_route_list_dev.txt @@ -0,0 +1,7 @@ +10.89.80.0/20 proto kernel scope link src 10.89.87.107 +169.254.0.0/16 scope link metric 1002 +default via 10.89.80.1 +Cannot find device "test" +10.89.80.0/20 dev eth0 proto kernel scope link src 10.89.87.107 +169.254.0.0/16 dev eth0 scope link metric 1002 +default via 10.89.80.1 dev eth0 diff --git a/spytest/templates/mclagdctl_dump_mac.tmpl b/spytest/templates/mclagdctl_dump_mac.tmpl new file mode 100644 index 00000000000..7b79f6106d4 --- /dev/null +++ b/spytest/templates/mclagdctl_dump_mac.tmpl @@ -0,0 +1,18 @@ +#################################################### +#admin@sonic:~$ mclagdctl -i 100 dump mac +#TYPE: S-STATIC, D-DYNAMIC; AGE: L-Local age, P-Peer age +#No. TYPE MAC VID DEV ORIGIN-DEV AGE +#1 D 00:1B:21:BA:DF:A8 1000 Ethernet4 PortChannel0001 L +#2 D 00:1B:21:BB:2F:DC 1000 Ethernet4 PortChannel0002 L +##################################################### +Value num (\d+) +Value type (\w) +Value vlan (\d+) +Value macaddress (\w\w:\w\w:\w\w:\w\w:\w\w:\w\w) +Value port (\w+) +Value origin_port (\w+) +Value age_flag (\w) + +Start + ^\s*${num}\s+${type}\s+${macaddress}\s+${vlan}\s+${port}\s+${origin_port}\s+${age_flag}\s*$$ -> Record + diff --git a/spytest/templates/mclagdctl_dump_portlist_local.tmpl b/spytest/templates/mclagdctl_dump_portlist_local.tmpl new file mode 100644 index 00000000000..daeafe42ba2 --- /dev/null +++ b/spytest/templates/mclagdctl_dump_portlist_local.tmpl @@ -0,0 +1,37 @@ +#################################################### +#admin@sonic:~$ mclagdctl -i 100 dump portlist local +#-------------------------------------------------- +#Ifindex: 7 +#Type: PortChannel +#PortName: {PortChannel0002} +#MAC: 6c:ec:5a:08:31:49 +#IPv4Address: {2.2.2.1} +#Prefixlen: {24} +#State: {Down} +#IsL3Interface: {Yes} +#IsPeerlink: No +#MemberPorts: Ethernet10 +#IsIsolateWithPeerlink: {No} +#IsTrafficDisable: {Yes} +#VlanList: Vlan10 +#--------------------------------------------------- +##################################################### +Value Key mclag_intf (\w+) +Value mclag_mac (\w\w:\w\w:\w\w:\w\w:\w\w:\w\w) +Value mclag_intf_ip (\d+\.\d+\.\d+\.\d+) +Value mclag_intf_mask (\d+) +Value mclag_intf_local_state (\w+) +Value mclag_intf_l3_status (\w+) +Value isolate_peer_link (\w+) +Value traffic_disable (\w+) + +Start + ^\s*PortName\s*:\s*${mclag_intf}$$ + ^\s*MAC\s*:\s*${mclag_mac}\s*$$ + ^\s*IPv4Address\s*:\s*${mclag_intf_ip}$$ + ^\s*Prefixlen\s*:\s*${mclag_intf_mask}$$ + ^\s*State\s*:\s*${mclag_intf_local_state}$$ + ^\s*IsL3Interface\s*:\s*${mclag_intf_l3_status}$$ + ^\s*IsIsolateWithPeerlink\s*:\s*${isolate_peer_link}$$ + ^\s*IsTrafficDisable\s*:\s*${traffic_disable}$$ -> Record + diff --git a/spytest/templates/mclagdctl_dump_portlist_peer.tmpl b/spytest/templates/mclagdctl_dump_portlist_peer.tmpl new file mode 100644 index 00000000000..0d7f7833397 --- /dev/null +++ b/spytest/templates/mclagdctl_dump_portlist_peer.tmpl @@ -0,0 +1,18 @@ +#################################################### +#admin@sonic:~$ mclagdctl -i 100 dump portlist peer +#-------------------------------------------------- +#Ifindex: 1 +#Type: PortChannel +#PortName: {PortChannel0001} +#MAC: 6c:ec:5a:08:31:49 +#State: {Up} +#--------------------------------------------------- +##################################################### +Value Required,Key mclag_intf (\w+) +Value mclag_intf_peer_state (\w+) + + +Start + ^\s*PortName\s*:\s*${mclag_intf}$$ + ^\s*State\s*:\s*${mclag_intf_peer_state}$$ -> Record + diff --git a/spytest/templates/mclagdctl_dump_state.tmpl b/spytest/templates/mclagdctl_dump_state.tmpl new file mode 100644 index 00000000000..4f73bf81bd5 --- /dev/null +++ b/spytest/templates/mclagdctl_dump_state.tmpl @@ -0,0 +1,37 @@ +#################################################### +#admin@sonic:~$ mclagdctl dump state -i 10 +#The MCLAG's keepalive is: ERROR +#MCLAG info sync is: incomplete +#Domain id: 10 +#Local Ip: 10.10.10.1 +#Peer Ip: 10.10.10.2 +#Peer Link Interface: Ethernet1 +#Keepalive time: 10 +#sesssion Timeout : 30 +#Peer Link Mac: 3c:2c:99:a7:0a:a0 +#Role: Active +#MCLAG Interface: PortChannel002,PortChannel001 +##################################################### +Value session_status (\w+) +Value domain_id (\d+) +Value local_ip (\d+\.\d+\.\d+\.\d+) +Value peer_ip (\d+\.\d+\.\d+\.\d+) +Value peer_link_inf (\w+) +Value keepalive_timer (\d+) +Value session_timer (\d+) +Value peer_link_mac (\w\w:\w\w:\w\w:\w\w:\w\w:\w\w) +Value node_role (\w+) +Value mclag_intfs ([\w+\,]+\w+) + +Start + ^\s*The\s*MCLAG\'s\s*keepalive\s*is\s*:\s*${session_status}$$ + ^\s*Domain\s*id\s*:\s*${domain_id}$$ + ^\s*Local\s*Ip\s*:\s*${local_ip}$$ + ^\s*Peer\s*Ip\s*:\s*${peer_ip}$$ + ^\s*Peer\s*Link\s*Interface\s*:\s*${peer_link_inf}$$ + ^\s*Keepalive\s*time\s*:\s*${keepalive_timer}$$ + ^\s*sesssion\s*Timeout\s*:\s*${session_timer}$$ + ^\s*Peer\s*Link\s*Mac\s*:\s*${peer_link_mac}$$ + ^\s*Role\s*:\s*${node_role}$$ + ^\s*MCLAG\s*Interface\s*:\s*${mclag_intfs}$$ -> Record + diff --git a/spytest/templates/pddf_fanutil_direction.tmpl b/spytest/templates/pddf_fanutil_direction.tmpl new file mode 100644 index 00000000000..7d78d491d01 --- /dev/null +++ b/spytest/templates/pddf_fanutil_direction.tmpl @@ -0,0 +1,6 @@ +Value fan (FAN\d_\d) +Value direction ([A-Z]+) + + +Start + ^\s*${fan}\s*${direction}\s*$$ -> Record \ No newline at end of file diff --git a/spytest/templates/pddf_fanutil_getspeed.tmpl b/spytest/templates/pddf_fanutil_getspeed.tmpl new file mode 100644 index 00000000000..40d3c126e2a --- /dev/null +++ b/spytest/templates/pddf_fanutil_getspeed.tmpl @@ -0,0 +1,6 @@ +Value fan (FAN\d_\d) +Value speed (\d{1,6}) + + +Start + ^\s*${fan}\s*${speed}\s*$$ -> Record \ No newline at end of file diff --git a/spytest/templates/pddf_fanutil_status.tmpl b/spytest/templates/pddf_fanutil_status.tmpl new file mode 100644 index 00000000000..cf7bc028898 --- /dev/null +++ b/spytest/templates/pddf_fanutil_status.tmpl @@ -0,0 +1,6 @@ +Value fan (FAN\d_\d) +Value status (OK|NOT\s+PRESENT) + + +Start + ^\s*${fan}\s*${status}\s*$$ -> Record \ No newline at end of file diff --git a/spytest/templates/pddf_psuutil_mfrinfo.tmpl b/spytest/templates/pddf_psuutil_mfrinfo.tmpl new file mode 100644 index 00000000000..24220dbbb4f --- /dev/null +++ b/spytest/templates/pddf_psuutil_mfrinfo.tmpl @@ -0,0 +1,14 @@ +Value psu_name (\S+) +Value psu_status (.*) +Value Manufacture (.*) +Value Model (\S+) +Value serial_number (\S+) +Value fan_direction (\S+) + + +Start + ^${psu_name}\s+is\s+${psu_status}\s*$$ + ^Manufacture\s+Id:\s+${Manufacture}\s*$$ + ^Model:\s+${Model}\s*$$ + ^Serial\s+Number:\s+${serial_number}\s*$$ + ^Fan\s+Direction:\s+${fan_direction}\s*$$ -> Record \ No newline at end of file diff --git a/spytest/templates/pddf_psuutil_seninfo.tmpl b/spytest/templates/pddf_psuutil_seninfo.tmpl new file mode 100644 index 00000000000..a1e14de9d50 --- /dev/null +++ b/spytest/templates/pddf_psuutil_seninfo.tmpl @@ -0,0 +1,16 @@ +Value psu_name (\S+) +Value psu_status (.*) +Value Voltage (\d+\.\d+) +Value current (\d+\.\d+) +Value power (\d+\.\d+) +Value fan_name (\S+) +Value fan_speed (\d+) + + +Start + ^${psu_name}\s+is\s+${psu_status}\s*$$ + ^Output\s+Voltage:\s+${Voltage}\s*mv\s*$$ + ^Output\s+Current:\s+${current}\s*ma\s*$$ + ^Output\s+Power:\s+${power}\s*mw\s*$$ + ^${fan_name}\s+Speed:\s+${fan_speed}\s*rpm\s*$$ -> Record + ^$$ diff --git a/spytest/templates/pddf_thermalutil_gettemp.tmpl b/spytest/templates/pddf_thermalutil_gettemp.tmpl new file mode 100644 index 00000000000..1431d2372ee --- /dev/null +++ b/spytest/templates/pddf_thermalutil_gettemp.tmpl @@ -0,0 +1,9 @@ +Value temp_sensor (\S+) +Value label (\S+) +Value value (.*) + + +Start + ^Temp\s+Sensor\s+Label\s+Value + ^(-+\s*)*$$ + ^${temp_sensor}\s+${label}\s+${value}\s*$$ -> Record diff --git a/spytest/templates/pfc_show_asymmetric.tmpl b/spytest/templates/pfc_show_asymmetric.tmpl new file mode 100644 index 00000000000..605022b770e --- /dev/null +++ b/spytest/templates/pfc_show_asymmetric.tmpl @@ -0,0 +1,7 @@ +Value Interface (\S+) +Value Asymmetric (\S+) + +Start + ^\s+Interface\s+Asymmetric\s+ + ^(-+\s*)*$$ + ^\s*${Interface}\s+${Asymmetric}\s*$$ -> Record \ No newline at end of file diff --git a/spytest/templates/pfcstat.tmpl b/spytest/templates/pfcstat.tmpl new file mode 100644 index 00000000000..166bba271ce --- /dev/null +++ b/spytest/templates/pfcstat.tmpl @@ -0,0 +1,23 @@ +Value Port (\S+) +Value Filldown Port_Mode (Port Tx|Port Rx) +Value PFC0 (\S+) +Value PFC1 (\S+) +Value PFC2 (\S+) +Value PFC3 (\S+) +Value PFC4 (\S+) +Value PFC5 (\S+) +Value PFC6 (\S+) +Value PFC7 (\S+) + + +Start + ^Last cached.*$$ + ^\s*${Port_Mode}\s+PFC0\s+PFC1\s+PFC2\s+PFC3\s+PFC4\s+PFC5\s+PFC6\s+PFC7\s*$$ + ^(-+\s*)*$$ + ^\s*${Port}\s+${PFC0}\s+${PFC1}\s+${PFC2}\s+${PFC3}\s+${PFC4}\s+${PFC5}\s+${PFC6}\s+${PFC7}\s*$$ -> Record + ^$$ + +EOF + + + diff --git a/spytest/templates/pfcwd_show_config.tmpl b/spytest/templates/pfcwd_show_config.tmpl new file mode 100644 index 00000000000..8d8884effec --- /dev/null +++ b/spytest/templates/pfcwd_show_config.tmpl @@ -0,0 +1,9 @@ +Value PORT (\S+) +Value ACTION (\S+) +Value DETECTIONTIME (\d+) +Value RESTORATIONTIME (\d+) + +Start + ^\s+PORT\s+ACTION\s+DETECTION\sTIME\s+RESTORATION\sTIME\s*$$ + ^(-+\s*)*$$ + ^\s*${PORT}\s+${ACTION}\s+${DETECTIONTIME}\s+${RESTORATIONTIME}\s*$$ -> Record \ No newline at end of file diff --git a/spytest/templates/pfcwd_show_stats.tmpl b/spytest/templates/pfcwd_show_stats.tmpl new file mode 100644 index 00000000000..fb6f686ab1c --- /dev/null +++ b/spytest/templates/pfcwd_show_stats.tmpl @@ -0,0 +1,12 @@ +Value QUEUE (\S+) +Value STATUS (\S+) +Value STORM (\S+) +Value TX (\S+) +Value RX (\S+) +Value TXLAST (\S+) +Value RXLAST (\S+) + +Start + ^\s+QUEUE\s+STATUS\s+STORM\s+DETECTED/RESTORED\s+TX\s+OK/DROP\s+RX\s+OK/DROP\s+TX\s+LAST\s+OK/DROP\s+RX\s+LAST\s+OK/DROP\s*$$ + ^(-+\s*)*$$ + ^\s*${QUEUE}\s+${STATUS}\s+${STORM}\s+${TX}\s+${RX}\s+${TXLAST}\s+${RXLAST}\s*$$ -> Record diff --git a/spytest/templates/ps_aux.tmpl b/spytest/templates/ps_aux.tmpl new file mode 100644 index 00000000000..b0576f565f4 --- /dev/null +++ b/spytest/templates/ps_aux.tmpl @@ -0,0 +1,14 @@ +Value user (\S+) +Value pid (\d+) +Value cpu (\S+) +Value mem (\S+) +Value vsz (\d+) +Value rss (\d+) +Value tty (\S+) +Value stat (\S+) +Value start (\S+) +Value time (\S+) +Value command ([\S+\s*]*) + +Start + ^\s*${user}\s+${pid}\s+${cpu}\s+${mem}\s+${vsz}\s+${rss}\s+${tty}\s+${stat}\s+${start}\s+${time}\s+${command}\s*$$ -> Record \ No newline at end of file diff --git a/spytest/templates/psuutil_status.tmpl b/spytest/templates/psuutil_status.tmpl new file mode 100644 index 00000000000..cbf814e85cd --- /dev/null +++ b/spytest/templates/psuutil_status.tmpl @@ -0,0 +1,7 @@ +Value psu (\w+) +Value status (OK|NOT\s{1}OK) + +Start + ^${psu}\s+${status}\s*$$ -> Record + +EOF \ No newline at end of file diff --git a/spytest/templates/route_n.tmpl b/spytest/templates/route_n.tmpl new file mode 100644 index 00000000000..9c7a678f81e --- /dev/null +++ b/spytest/templates/route_n.tmpl @@ -0,0 +1,13 @@ +Value destination (([0-9]{1,3}.){3}[0-9]{1,3}) +Value gateway (([0-9]{1,3}.){3}[0-9]{1,3}) +Value genmask (([0-9]{1,3}.){3}[0-9]{1,3}) +Value flags (\w+) +Value metric (\d+) +Value ref (\d+) +Value use (\d+) +Value iface (\w+) + +Start + ^${destination}\s+${gateway}\s+${genmask}\s+${flags}\s+${metric}\s+${ref}\s+${use}\s+${iface}$$ -> Record + +EOF diff --git a/spytest/templates/sfputil_show_eeprom.tmpl b/spytest/templates/sfputil_show_eeprom.tmpl new file mode 100644 index 00000000000..924cfee5831 --- /dev/null +++ b/spytest/templates/sfputil_show_eeprom.tmpl @@ -0,0 +1,33 @@ +Value port (Ethernet\d+) +Value eeprom_status (SFP\s+EEPROM\s+detected|SFP\s+EEPROM\s+Not\s+detected) +Value connector (.*) +Value encoding (\S+) +Value extended_identifier (.*) +Value rate_select (.*) +Value identifier (.*) +Value length (\d+) +Value nominal_bit_rate (\d+) +Value vendor_date (\d{4}\-\d{2}\-\d{2}) +Value vendor_name (\S+|\S+\s*\S+|\S+\s*\S+\s*|S+) +Value vendor_oui (\S+) +Value vendor_pn (\S+) +Value vendor_rev (\w{1,3}) +Value vendor_sn (\w+) +Value bit_rate_speed (\S+) + +Start + ^\s*${port}:\s+${eeprom_status}\s*$$ + ^\s*Connector:\s+${connector}\s*$$ + ^\s*Encoding:\s+${encoding}\s*$$ + ^\s*Extended Identifier:\s+${extended_identifier}\s*$$ + ^\s*Extended RateSelect Compliance:\s+${rate_select}\s*$$ + ^\s*Identifier:\s+${identifier}\s*$$ + ^\s*Length\s+Cable\s+Assembly\(m\)\s*:\s+${length}\s*$$ + ^\s*Nominal\s+Bit\s+Rate\(${bit_rate_speed}\)\s*:\s+${nominal_bit_rate}\s*$$ + ^\s*Vendor\s+Date\s+Code\(.*\)\s*:\s+${vendor_date}\s*$$ + ^\s*Vendor\s+Name:\s+${vendor_name}\s*$$ + ^\s*Vendor\s+OUI:\s+${vendor_oui}\s*$$ + ^\s*Vendor\s+PN:\s+${vendor_pn}\s*$$ + ^\s*Vendor\s+Rev:\s+${vendor_rev}\s*$$ + ^\s*Vendor\s+SN:\s+${vendor_sn}\s*$$ -> Record + diff --git a/spytest/templates/sfputil_show_lpmode.tmpl b/spytest/templates/sfputil_show_lpmode.tmpl new file mode 100644 index 00000000000..3ba43e29d92 --- /dev/null +++ b/spytest/templates/sfputil_show_lpmode.tmpl @@ -0,0 +1,6 @@ +Value Port (\S+) +Value lpmode (On|Off) + +Start + ^\s*${Port}\s+${lpmode} -> Record + ^\s*$$ \ No newline at end of file diff --git a/spytest/templates/show_Vlan.tmpl b/spytest/templates/show_Vlan.tmpl new file mode 100644 index 00000000000..f404e27ca4e --- /dev/null +++ b/spytest/templates/show_Vlan.tmpl @@ -0,0 +1,11 @@ +Value Filldown VID (\d+) +Value Filldown Status (\w+) +Value Member (\S+) +Value Mode (\S+) + +Start + ^\s*${VID}\s+${Status}\s+${Mode}\s+${Member}\s*$$ -> Record + ^\s*${VID}\s+${Status}\s*$$ -> Record + ^\s*${Mode}\s+${Member}\s*$$ -> Record + +EOF \ No newline at end of file diff --git a/spytest/templates/show_aaa.tmpl b/spytest/templates/show_aaa.tmpl new file mode 100644 index 00000000000..40b56b3374a --- /dev/null +++ b/spytest/templates/show_aaa.tmpl @@ -0,0 +1,9 @@ +Value Login (\S+\s*\(\S+\)|\S+) +Value Failthrough (\S+\s*\(\S+\)|\S+) +Value Fallback (\S+\s*\(\S+\)|\S+) + +Start + ^AAA\s+authentication\s+login\s+${Login}\s*$$ + ^AAA\s+authentication\s+failthrough\s+${Failthrough}\s*$$ + ^AAA\s+authentication\s+fallback\s+${Fallback}\s*$$ + ^$$ \ No newline at end of file diff --git a/spytest/templates/show_acl_rule.tmpl b/spytest/templates/show_acl_rule.tmpl new file mode 100644 index 00000000000..d31913c6bb2 --- /dev/null +++ b/spytest/templates/show_acl_rule.tmpl @@ -0,0 +1,12 @@ +Value Table (\S+) +Value Rule (\S+) +Value Priority (\S+) +Value Action (\S+) +Value Match (\S+:\s+\S+) + +Start + ^\s*Table\s+Rule\s+Priority\s+Action\s+Match\s*$$ + ^(-+\s*)*$$ + ^\s*${Table}\s+${Rule}\s+${Priority}\s+${Action}\s+${Match}\s*$$ -> Record + ^\s*\s+${Match}\s*$$ -> Record + ^$$ \ No newline at end of file diff --git a/spytest/templates/show_acl_rule_qa.tmpl b/spytest/templates/show_acl_rule_qa.tmpl new file mode 100644 index 00000000000..b437e7e0ca9 --- /dev/null +++ b/spytest/templates/show_acl_rule_qa.tmpl @@ -0,0 +1,14 @@ +Value Filldown Table (\S+) +Value Filldown Rule (\S+) +Value Filldown Priority (\d+) +Value Filldown Action (\S+) +Value Match (.+) + +Start + ^\s*Table\s+Rule\s+Priority\s+Action\s+Match\s*$$ + ^(-+\s*)*$$ + ^\s*${Table}\s+${Rule}\s+${Priority}\s+${Action}\s+${Match}\s*$$ -> Record + ^\s+${Match}\s*$$ -> Record + ^$$ + +EOF \ No newline at end of file diff --git a/spytest/templates/show_acl_table.tmpl b/spytest/templates/show_acl_table.tmpl new file mode 100644 index 00000000000..e201ab63f94 --- /dev/null +++ b/spytest/templates/show_acl_table.tmpl @@ -0,0 +1,11 @@ +Value Name (\S+) +Value Type (\S+) +Value Binding (\S+) +Value Description (\S+) + +Start + ^\s*Name\s+Type\s+Binding\s+Description\s*$$ + ^(-+\s*)*$$ + ^\s*${Name}\s+${Type}\s+${Binding}\s+${Description}\s*$$ -> Record + ^\s*\s+${Binding}\s*$$ -> Record + ^$$ \ No newline at end of file diff --git a/spytest/templates/show_acl_table_qa.tmpl b/spytest/templates/show_acl_table_qa.tmpl new file mode 100644 index 00000000000..1664c41e0b4 --- /dev/null +++ b/spytest/templates/show_acl_table_qa.tmpl @@ -0,0 +1,13 @@ +Value Filldown Name (\S+) +Value Filldown Type (\S+) +Value Binding (\S+) +Value Filldown Description (.+) + +Start + ^\s*Name\s+Type\s+Binding\s+Description\s*$$ + ^(-+\s*)*$$ + ^\s*${Name}\s+${Type}\s+${Binding}\s+${Description}\s*$$ -> Record + ^\s+${Binding}\s*$$ -> Record + ^$$ + +EOF \ No newline at end of file diff --git a/spytest/templates/show_arp.tmpl b/spytest/templates/show_arp.tmpl new file mode 100644 index 00000000000..5a3b93ed482 --- /dev/null +++ b/spytest/templates/show_arp.tmpl @@ -0,0 +1,11 @@ +Value Address (\S+) +Value MacAddress (..:..:..:..:..:..) +Value Iface (\S+) +Value Vlan (\S+) +Value Fillup count (\d+) + +Start + ^\s*Address\s+MacAddress\s+Iface\s+Vlan\s*$$ + ^(-+\s*)*$$ + ^\s*${Address}\s+${MacAddress}\s+${Iface}\s+${Vlan}\s*$$ -> Record + ^\s*Total\s+number\s+of\s+entries\s+${count} diff --git a/spytest/templates/show_bfd_couters.tmpl b/spytest/templates/show_bfd_couters.tmpl new file mode 100644 index 00000000000..b09cb022792 --- /dev/null +++ b/spytest/templates/show_bfd_couters.tmpl @@ -0,0 +1,31 @@ +Value Required peerAddress ([\:\.\dabcdefABCDEF]+) +Value vrfname (\S+) +Value localAddr ([\:\.\dabcdefABCDEF]+) +Value interface ([\w]+) +Value cntrlPktIn (\d+) +Value cntrlPktOut (\d+) +Value echoPktIn (\d+) +Value echoPktOut (\d+) +Value SessionUpEv (\d+) +Value SessionDownEv (\d+) +Value ZebraNotifys (\d+) + +Start + ^\s*peer\s*${peerAddress}$$ + ^\s*peer\s*${peerAddress}\s*vrf\s*${vrfname}$$ + ^\s*peer\s*${peerAddress}\s*vrf\s*${vrfname}\s*interface\s*${interface}$$ + ^\s*peer\s*${peerAddress}\s*interface\s*${interface}$$ + ^\s*peer\s*${peerAddress}\s*local-address\s*${localAddr}\s*vrf\s*${vrfname}\s*interface\s*${interface}$$ + ^\s*peer\s*${peerAddress}\s*local-address\s*${localAddr}\s*interface\s*${interface}$$ + ^\s*peer\s*${peerAddress}\s*local-address\s*${localAddr}\s*vrf\s*${vrfname}$$ + ^\s*peer\s*${peerAddress}\s*local-address\s*${localAddr}$$ + ^\s*peer\s*${peerAddress}\s*multihop\s*local-address\s*${localAddr}\s*vrf\s*${vrfname}$$ + ^\s*peer\s*${peerAddress}\s*multihop\s*local-address\s*${localAddr}$$ + ^\s*Control packet input:\s*${cntrlPktIn}\s*packets + ^\s*Control packet output:\s*${cntrlPktOut}\s*packets + ^\s*Echo packet input:\s*${echoPktIn}\s*packets + ^\s*Echo packet output:\s*${echoPktOut}\s*packets + ^\s*Session up events:\s*${SessionUpEv} + ^\s*Session down events:\s*${SessionDownEv} + ^\s*Zebra notifications:\s*${ZebraNotifys} -> Record + diff --git a/spytest/templates/show_bfd_peer.tmpl b/spytest/templates/show_bfd_peer.tmpl new file mode 100644 index 00000000000..4495b624b5d --- /dev/null +++ b/spytest/templates/show_bfd_peer.tmpl @@ -0,0 +1,77 @@ +Value peer ([\:\.\dabcdefABCDEF]+) +Value vrf_name (\S+) +Value local_addr ([\:\.\dabcdefABCDEF]+) +Value interface ([\w]+) +Value label ([\S]+) +Value local_id (\d+) +Value remote_id (\d+) +Value status (\w+) +Value upTimeDay (\d+) +Value upTimeHr (\d+) +Value upTimeMin (\d+) +Value upTimeSec (\d+) +Value downTimeDay (\d+) +Value downTimeHr (\d+) +Value downTimeMin (\d+) +Value downTimeSec (\d+) +Value diagnostics ([\w\s]+) +Value remote_diagnostics ([\w\s]+) +Value peer_type ([\w\s]+) +Value List multiplier (\d+) +Value List rx_interval (\d+) +Value List tx_interval (\d+) +Value List echo_tx_interval (\d+) +Value err ([\w\s]+) + +Start + ^\s*\%\s*${err}\s*\'peer ${peer}\'\s*$$ -> Record + ^\s*\%\s*${err}\s*\'peer ${peer}\s*vrf\s*${vrf_name}\'\s*$$ -> Record + ^\s*\%\s*${err}\s*\'peer ${peer}\s*local-address\s*${local_addr}\'\s*$$ -> Record + ^\s*\%\s*${err}\s*\'peer ${peer}\s*vrf\s*${vrf_name}\s*local-address\s*${local_addr}\'\s*$$ -> Record + ^\s*\%\s*${err}\s*\'peer ${peer}\s*local-address\s*${local_addr}\s*vrf\s*${vrf_name}\'\s*$$ -> Record + ^\s*\%\s*${err}\s*\'peer ${peer}\s*local-address\s*${local_addr}\s*interface\s*${interface}\'\s*$$ -> Record + ^\s*\%\s*${err}\s*\'peer ${peer}\s*local-address\s*${local_addr}\s*vrf\s*${vrf_name}\s*interface\s*${interface}\'\s*$$ -> Record + ^\s*\%\s*${err}\s*\'peer ${peer}\s*interface\s*${interface}\'\s*$$ -> Record + ^\s*\%\s*${err}\s*\'peer ${peer}\s*vrf\s*${vrf_name}\s*interface\s*${interface}\'\s*$$ -> Record + ^\s*\%\s*${err}\s*\'peer ${peer}\s*interface\s*${interface}\s*vrf\s*${vrf_name}\'\s*$$ -> Record + ^\s*\%\s*${err}\s*\'peer ${peer}\s*interface\s*${interface}\s*local-address\s*${local_addr}\'\s*$$ -> Record + ^\s*\%\s*${err}\s*\'peer ${peer}\s*interface\s*${interface}\s*local-address\s*${local_addr}\s*vrf\s*${vrf_name}\'\s*$$ -> Record + ^\s*\%\s*${err}\s*\'peer ${peer}\s*vrf\s*${vrf_name}\s*interface\s*${interface}\s*local-address\s*${local_addr}\'\s*$$ -> Record + ^\s*peer\s*(.*) -> Continue.Record + ^\s*peer\s*${peer}$$ + ^\s*peer\s*${peer}\s*vrf\s*${vrf_name}$$ + ^\s*peer\s*${peer}\s*local-address\s*${local_addr}$$ + ^\s*peer\s*${peer}\s*vrf\s*${vrf_name}\s*local-address\s*${local_addr}$$ + ^\s*peer\s*${peer}\s*local-address\s*${local_addr}\s*vrf\s*${vrf_name}$$ + ^\s*peer\s*${peer}\s*local-address\s*${local_addr}\s*interface\s*${interface}$$ + ^\s*peer\s*${peer}\s*local-address\s*${local_addr}\s*vrf\s*${vrf_name}\s*interface\s*${interface}$$ + ^\s*peer\s*${peer}\s*interface\s*${interface}$$ + ^\s*peer\s*${peer}\s*vrf\s*${vrf_name}\s*interface\s*${interface}$$ + ^\s*peer\s*${peer}\s*interface\s*${interface}\s*vrf\s*${vrf_name}$$i + ^\s*peer\s*${peer}\s*interface\s*${interface}\s*local-address\s*${local_addr}$$ + ^\s*peer\s*${peer}\s*vrf\s*${vrf_name}\s*local-address\s*${local_addr}\s*interface\s*${interface}$$ + ^\s*peer\s*${peer}\s*local-address\s*${local_addr}\s*interface\s*${interface}\s*vrf\s*${vrf_name}$$ + ^\s*peer\s*${peer}\s*multihop\s*local-address\s*${local_addr}$$ + ^\s*peer\s*${peer}\s*multihop\s*local-address\s*${local_addr}\s*vrf\s*${vrf_name}\s*interface\s*${interface}$$ + ^\s*peer\s*${peer}\s*multihop\s*local-address\s*${local_addr}\s*vrf\s*${vrf_name}$$ + ^\s*peer\s*${peer}\s*vrf\s*${vrf_name}\s*multihop\s*local-address\s*${local_addr}$$ + ^\s*label:\s*${label} + ^\s*ID:\s*${local_id} + ^\s*Remote ID:\s*${remote_id} + ^\s*Status:\s*${status} + ^\s*Uptime:\s*${upTimeDay}\s*day\(s\),\s*${upTimeHr}\s*hour\(s\),\s*${upTimeMin}\s*minute\(s\),\s*${upTimeSec}\s*second\(s\) + ^\s*Uptime:\s*${upTimeHr}\s*hour\(s\),\s*${upTimeMin}\s*minute\(s\),\s*${upTimeSec}\s*second\(s\) + ^\s*Uptime:\s*${upTimeMin}\s*minute\(s\),\s*${upTimeSec}\s*second\(s\) + ^\s*Uptime:\s*${upTimeSec}\s*second\(s\) + ^\s*Downtime:\s*${downTimeDay}\s*day\(s\),\s*${downTimeHr}\s*hour\(s\),\s*${downTimeMin}\s*minute\(s\),\s*${downTimeSec}\s*second\(s\) + ^\s*Downtime:\s*${downTimeHr}\s*hour\(s\),\s*${downTimeMin}\s*minute\(s\),\s*${downTimeSec}\s*second\(s\) + ^\s*Downtime:\s*${downTimeMin}\s*minute\(s\),\s*${downTimeSec}\s*second\(s\) + ^\s*Downtime:\s*${downTimeSec}\s*second\(s\) + ^\s*Diagnostics:\s*${diagnostics} + ^\s*Remote\s*diagnostics:\s*${remote_diagnostics} + ^\s*Peer\s*Type:\s*${peer_type} + ^\s*Detect-multiplier:\s*${multiplier} + ^\s*Receive\s*interval:\s*${rx_interval}ms + ^\s*Transmission\s*interval:\s*${tx_interval}ms + ^\s*Echo\s*transmission\s*interval:\s*${echo_tx_interval}ms + diff --git a/spytest/templates/show_bfd_peers_brief.tmpl b/spytest/templates/show_bfd_peers_brief.tmpl new file mode 100644 index 00000000000..22e506aca50 --- /dev/null +++ b/spytest/templates/show_bfd_peers_brief.tmpl @@ -0,0 +1,10 @@ +Value Filldown sCount (\d+) +Value Required sessionId (\d+) +Value ourAddress ((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])|([0-9a-fA-F]|:){1,4}(:([0-9a-fA-F]{0,4})*){1,7}) +Value peerAddress ((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])|([0-9a-fA-F]|:){1,4}(:([0-9a-fA-F]{0,4})*){1,7}) +Value status ([\w\-]*) + +Start + ^\s*Session count:\s*${sCount} + ^\s*${sessionId}\s*${ourAddress}\s*${peerAddress}\s*${status} -> Record + diff --git a/spytest/templates/show_bfd_peers_couters.tmpl b/spytest/templates/show_bfd_peers_couters.tmpl new file mode 100644 index 00000000000..cb17e675e38 --- /dev/null +++ b/spytest/templates/show_bfd_peers_couters.tmpl @@ -0,0 +1,24 @@ +Value Required peerAddress ([\:\.\d]+) +Value localAddr ([\:\.\d]+) +Value interface ([\w]+) +Value cntrlPktIn (\d+) +Value cntrlPktOut (\d+) +Value echoPktIn (\d+) +Value echoPktOut (\d+) +Value SessionUpEv (\d+) +Value SessionDownEv (\d+) +Value ZebraNotifys (\d+) + +Start + ^\s*peer\s*${peerAddress}\s*interface\s*${interface} + ^\s*peer\s*${peerAddress}\s*local-address\s*${localAddr}\s*interface\s*${interface} + ^\s*peer\s*${peerAddress}\s*local-address\s*${localAddr}$$ + ^\s*peer\s*${peerAddress}\s*multihop\s*local-address\s*${localAddr}\s*interface\s*${interface} + ^\s*Control packet input:\s*${cntrlPktIn}\s*packets + ^\s*Control packet output:\s*${cntrlPktOut}\s*packets + ^\s*Echo packet input:\s*${echoPktIn}\s*packets + ^\s*Echo packet output:\s*${echoPktOut}\s*packets + ^\s*Session up events:\s*${SessionUpEv} + ^\s*Session down events:\s*${SessionDownEv} + ^\s*Zebra notifications:\s*${ZebraNotifys} -> Record + diff --git a/spytest/templates/show_bgp_ipv4_prefix.tmpl b/spytest/templates/show_bgp_ipv4_prefix.tmpl new file mode 100644 index 00000000000..4018bda6844 --- /dev/null +++ b/spytest/templates/show_bgp_ipv4_prefix.tmpl @@ -0,0 +1,26 @@ +Value PREFIXIP ([\d\.]+) +Value PREFIXMASKLEN ([\d]+) +Value NOOFPATHS (\d+) +Value PEERIP ([(\d+\.\d+\.\d+\.\d)\s]+) +Value PEERASN ([\d+\s]+) +Value peernhtip1 ([\d\.]+) +Value peernhtip2 ([\d\.]+) +Value peerrtrid ([\d\.]+) +Value ORIGIN (\S+) +Value METRIC (\d+) +Value VALID (\S+) +Value EXTERNAL (\S+) +Value BEST (\S+) +Value COMMUNITY (.*?) + + +Start + ^\s*BGP\s*routing\s*table\s*entry\s*for\s*${PREFIXIP}\/${PREFIXMASKLEN}$$ + ^\s*Paths\:\s*\(${NOOFPATHS}\s*available,\s*best\s*\#([\d]+),\s*table\s*default\)$$ + ^\s*Advertised\s*to\s*non\s*peer-group\s*peers\:$$ + ^\s*${PEERASN}$$ + ^\s*${PEERIP}$$ + ^\s*${peernhtip1}\s*from\s*${peernhtip2}\s*\(${peerrtrid}\) + ^\s*Origin\s*${ORIGIN},\s*metric\s*${METRIC},\s*${VALID},\s*${EXTERNAL},\s*${BEST}$$ + ^\s*Community:\s*${COMMUNITY}\s*$$ + ^\s*Last update:\s*(.*)$$ -> Record diff --git a/spytest/templates/show_bgp_ipv6.tmpl b/spytest/templates/show_bgp_ipv6.tmpl new file mode 100644 index 00000000000..08bea7903ea --- /dev/null +++ b/spytest/templates/show_bgp_ipv6.tmpl @@ -0,0 +1,29 @@ +Value Filldown NETWORK ([.:\dabcdefABCDEF]+\/\d+) +Value NEXT_HOP ([.:\dabcdefABCDEF]+) +Value METRIC (\S{0,6}) +Value LOCAL_PREF (\S{0,6}) +Value WEIGHT (\S{1,6}) +Value AS_PATH (.*?) +Value VERSION (\d+) +Value ROUTER_ID (\S{0,19}) +Value STATUS_CODE ([sdhirSR*>=#]*) +Value INTERNAL (i?) + + +Start + ^BGP table version is ${VERSION}, local router ID is ${ROUTER_ID}\s*$$ + ^\s+Network\s+Next Hop\s+Metric\s+LocPrf\s+Weight\s+Path -> Bgp_table + ^Status codes:.*$$ -> Continue + ^\s+i internal.*$$ -> Continue + ^Origin codes:.*$$ -> Continue + +Bgp_table + ^${STATUS_CODE}\s+${INTERNAL}\s+${NEXT_HOP}\s*$$ -> Continue + ^\s{1,45}${METRIC}\s{0,9}${LOCAL_PREF}\s+${WEIGHT}\s+${AS_PATH}\s*$$ -> Record + ^${STATUS_CODE}\s{1,16}${INTERNAL}${NETWORK}\s+${NEXT_HOP}\s*$$ -> Continue + ^\s{1,45}${METRIC}\s{0,9}${LOCAL_PREF}\s+${WEIGHT}\s+${AS_PATH}\s*$$ -> Record + ^${STATUS_CODE}\s{1,16}${INTERNAL}${NETWORK}\s+${NEXT_HOP}\s+${METRIC}\s{0,9}${LOCAL_PREF}\s+${WEIGHT}\s+${AS_PATH}\s*$$ -> Record + ^${STATUS_CODE}\s{1,16}${INTERNAL}\s+${NEXT_HOP}\s+${METRIC}\s{0,9}${LOCAL_PREF}\s+${WEIGHT}\s+${AS_PATH}\s*$$ -> Record + ^${STATUS_CODE}\s+${INTERNAL}${NETWORK}\s+${NEXT_HOP}\s+\s+${WEIGHT}\s+${AS_PATH}\s*$$ -> Record + +EOF diff --git a/spytest/templates/show_bgp_l2vpn_evpn_rd.tmpl b/spytest/templates/show_bgp_l2vpn_evpn_rd.tmpl new file mode 100644 index 00000000000..7b01647c267 --- /dev/null +++ b/spytest/templates/show_bgp_l2vpn_evpn_rd.tmpl @@ -0,0 +1,18 @@ +Value Filldown RD_NAME (\S+) +Value Filldown RD (\d+\:\d+) +Value STATUS_CODE (\S+) +Value Required EVPN_TYPE_5_PREFIX (\s*\[5\]:\[\d+\]:\[\d+\]:\[\d+\.\d+\.\d+\.\d+\]) +Value NEXT_HOP (\d+\.\d+\.\d+\.\d) +Value METRIC (\s|\d) +Value WEIGHT (\d+|\s{1,4}\d+) +Value PATH ([\d+\s]+|\s{0}) +Value ORIGIN_CODE (i|e|\?) +Value Fillup DISPLAYED_PREFIXES (\d+) +Value Fillup TOTAL_PREFIXES (\d+) + +Start + ^\s*Route Distinguisher:\s${RD_NAME}\s${RD} + ^\s*${STATUS_CODE}\s*${EVPN_TYPE_5_PREFIX} + ^\s*${NEXT_HOP} \s{1,8}${METRIC} ${WEIGHT}\s${PATH}\s{0,1}${ORIGIN_CODE}$$ -> Continue.Record + ^\s*Displayed\s${DISPLAYED_PREFIXES}\sout of\s${TOTAL_PREFIXES}\stotal\sprefixes + diff --git a/spytest/templates/show_bgp_l2vpn_evpn_route.tmpl b/spytest/templates/show_bgp_l2vpn_evpn_route.tmpl new file mode 100644 index 00000000000..fd7d3cb790e --- /dev/null +++ b/spytest/templates/show_bgp_l2vpn_evpn_route.tmpl @@ -0,0 +1,26 @@ +Value Filldown BGP_VERSION (\d+) +Value Filldown ROUTER_ID (\d+\.\d+\.\d+\.\d) +Value Filldown RD (\d+\:\d+|\d+\.\d+\.\d+\.\d+) +Value Required STATUS_CODE (\S+) +Value EVPN_PREFIX (\s*\[5\]:\[\d+\]:\[\d+\]:\[\d+\.\d+\.\d+\.\d+\]|\s*\[3\]:\[\d+\]:\[\d+\]:\[\d+\.\d+\.\d+\.\d+\]|\s*\[2\]:\[\d+\]:\[\d+\]:\[([0-9a-f]{2}[:]){5}[0-9a-f]{2}\]:\[32\]:\[\d+\.\d+\.\d+\.\d+\]|\s*\[2\]:\[\d+\]:\[\d+\]:\[([0-9a-f]{2}[:]){5}[0-9a-f]{2}\]|\s*\[5\]:\[\d+\]:\[\d+\]:\[\S+\]) +Value NEXT_HOP (\d+\.\d+\.\d+\.\d) +Value METRIC (\s|\d) +Value WEIGHT (\d+|\s{1,4}\d+) +Value PATH ([\d+\s]+|\s{0}) +Value ORIGIN_CODE (i|e|\?) +Value RT (\d+\:\d+) +Value ET (\d+) +Value RMAC (([0-9a-f]{2}[:]){5}[0-9a-f]{2}) +Value Fillup DISPLAYED_PREFIXES (\d+) +Value Fillup NO_OF_PATHS (\d+) + +Start + ^\s*BGP table version is ${BGP_VERSION}, local router ID is ${ROUTER_ID} + ^\s*Route Distinguisher:\s${RD} + ^\s*${STATUS_CODE}\s*${EVPN_PREFIX} + ^\s*${NEXT_HOP} \s{1,8}${METRIC} ${WEIGHT}\s${PATH}\s{0,1}${ORIGIN_CODE}$$ + ^\s*RT:${RT}\sET:${ET}\sRmac:${RMAC} -> Continue.Record + ^\s*ET:${ET}\sRT:${RT}\sRmac:${RMAC} -> Continue.Record + ^\s*RT:${RT}\sET:${ET} -> Continue.Record + ^\s*ET:${ET}\sRT:${RT} -> Continue.Record + ^\s*Displayed\s${DISPLAYED_PREFIXES}\sprefixes\s\(${NO_OF_PATHS}\spaths\) diff --git a/spytest/templates/show_bgp_l2vpn_evpn_route_detail_type_prefix.tmpl b/spytest/templates/show_bgp_l2vpn_evpn_route_detail_type_prefix.tmpl new file mode 100644 index 00000000000..8f812ac7e30 --- /dev/null +++ b/spytest/templates/show_bgp_l2vpn_evpn_route_detail_type_prefix.tmpl @@ -0,0 +1,22 @@ +Value Filldown RD (\d+\:\d+|\d+\.\d+\.\d+\.\d+) +Value AS_PATH (\d+|\d+\s\d+) +Value VNI_ID (\d+) +Value PREFIX (\s*\[5\]:\[\d+\]:\[\d+\]:\[\d+\.\d+\.\d+\.\d+\]|\s*\[5\]:\[\d+\]:\[\d+\]:\[\S+\]) +Value RVTEP (\d+\.\d+\.\d+\.\d+) +Value BGP_PEER (\S+) +Value ORIGIN ([\w|\s|,]+) +Value RT (\d+\:\d+|\d+\.\d+\.\d+\.\d+:\d+) +Value ET (\d+) +Value RMAC (([0-9a-f]{2}[:]){5}[0-9a-f]{2}) +Value Fillup DPREFIXES (\d+) +Value Fillup NO_OF_PATHS (\d+) + +Start + ^\s*Route Distinguisher:\s${RD} -> Continue.Record + ^\s*Route ${PREFIX} VNI\s${VNI_ID} -> Continue.Record + ^\s*${AS_PATH}$$ + ^\s*${RVTEP}\sfrom\s${BGP_PEER}\s\( + ^\s*Origin\s${ORIGIN}$$ + ^\s*Extended Community:\sRT:${RT}\sET:${ET}\sRmac:${RMAC} + ^\s*Displayed\s${DPREFIXES}\sprefixes\s\(${NO_OF_PATHS}\spaths\) + diff --git a/spytest/templates/show_bgp_l2vpn_evpn_route_type_macip.tmpl b/spytest/templates/show_bgp_l2vpn_evpn_route_type_macip.tmpl new file mode 100644 index 00000000000..e60389bbb73 --- /dev/null +++ b/spytest/templates/show_bgp_l2vpn_evpn_route_type_macip.tmpl @@ -0,0 +1,20 @@ +Value Filldown BGP_VERSION (\d+) +Value Filldown ROUTER_ID (\d+\.\d+\.\d+\.\d) +Value Filldown RD (\d+\:\d+|\d+\.\d+\.\d+\.\d+) +Value STATUS_CODE (\S+) +Value Required EVPN_TYPE_2_PREFIX (\s*\[2\]:\[\d+\]:\[\d+\]:\[([0-9a-f]{2}[:]){5}[0-9a-f]{2}\]|\s*\[2\]:\[\d+\]:\[\d+\]:\[([0-9a-f]{2}[:]){5}[0-9a-f]{2}\]:\[32\]:\[\d+\.\d+\.\d+\.\d\]) +Value NEXT_HOP (\d+\.\d+\.\d+\.\d) +Value METRIC (\s|\d) +Value WEIGHT (\d+|\s{1,4}\d+) +Value PATH ([\d+\s]+|\s{0}) +Value ORIGIN_CODE (i|e|\?) +Value Fillup DISPLAYED_PREFIXES (\d+) +Value Fillup NO_OF_PATHS (\d+) + +Start + ^\s*BGP table version is ${BGP_VERSION}, local router ID is ${ROUTER_ID} + ^\s*Route Distinguisher:\s${RD} + ^\s*${STATUS_CODE}\s*${EVPN_TYPE_2_PREFIX}$$ + ^\s*${NEXT_HOP} \s{1,8}${METRIC} ${WEIGHT}\s${PATH}\s{0,1}${ORIGIN_CODE}$$ -> Continue.Record + ^\s*Displayed\s${DISPLAYED_PREFIXES}\sprefixes\s\(${NO_OF_PATHS}\spaths\) + diff --git a/spytest/templates/show_bgp_l2vpn_evpn_route_type_multicast.tmpl b/spytest/templates/show_bgp_l2vpn_evpn_route_type_multicast.tmpl new file mode 100644 index 00000000000..cc5d5cceabd --- /dev/null +++ b/spytest/templates/show_bgp_l2vpn_evpn_route_type_multicast.tmpl @@ -0,0 +1,23 @@ +Value Filldown BGP_VERSION (\d+) +Value Filldown ROUTER_ID (\d+\.\d+\.\d+\.\d) +Value Filldown RD (\d+\:\d+|\d+\.\d+\.\d+\.\d+) +Value Required STATUS_CODE (\S+) +Value EVPN_TYPE_3_PREFIX (\s*\[3\]:\[\d+\]:\[\d+\]:\[\d+\.\d+\.\d+\.\d+\]) +Value NEXT_HOP (\d+\.\d+\.\d+\.\d) +Value METRIC (\s|\d) +Value WEIGHT (\d+|\s{1,4}\d+) +Value PATH ([\d+\s]+|\s{0}) +Value ORIGIN_CODE (i|e|\?) +Value RT (\d+\:\d+) +Value ET (\d+) +Value Fillup DISPLAYED_PREFIXES (\d+) +Value Fillup NO_OF_PATHS (\d+) + +Start + ^\s*BGP table version is ${BGP_VERSION}, local router ID is ${ROUTER_ID} + ^\s*Route Distinguisher:\s${RD} + ^\s*${STATUS_CODE}\s*${EVPN_TYPE_3_PREFIX} + ^\s*${NEXT_HOP} \s{1,8}${METRIC} ${WEIGHT}\s${PATH}\s{0,1}${ORIGIN_CODE}$$ + ^\s*RT:${RT}\sET:${ET} -> Continue.Record + ^\s*ET:${ET}\sRT:${RT} -> Continue.Record + ^\s*Displayed\s${DISPLAYED_PREFIXES}\sprefixes\s\(${NO_OF_PATHS}\spaths\) diff --git a/spytest/templates/show_bgp_l2vpn_evpn_route_type_prefix.tmpl b/spytest/templates/show_bgp_l2vpn_evpn_route_type_prefix.tmpl new file mode 100644 index 00000000000..7ca20842de2 --- /dev/null +++ b/spytest/templates/show_bgp_l2vpn_evpn_route_type_prefix.tmpl @@ -0,0 +1,25 @@ +Value Filldown BGP_VERSION (\d+) +Value Filldown ROUTER_ID (\d+\.\d+\.\d+\.\d) +Value Filldown RD (\d+\:\d+|\d+\.\d+\.\d+\.\d+) +Value STATUS_CODE (\S+) +Value Required EVPN_TYPE_5_PREFIX (\s*\[5\]:\[\d+\]:\[\d+\]:\[\d+\.\d+\.\d+\.\d+\]|\s*\[5\]:\[\d+\]:\[\d+\]:\[\S+\]) +Value NEXT_HOP (\d+\.\d+\.\d+\.\d) +Value METRIC (\s|\d) +Value WEIGHT (\d+|\s{1,4}\d+) +Value PATH ([\d+\s]+|\s{0}) +Value ORIGIN_CODE (i|e|\?) +Value RT (\d+\:\d+|\d+\.\d+\.\d+\.\d+:\d+) +Value ET (\d+) +Value RMAC (([0-9a-f]{2}[:]){5}[0-9a-f]{2}) +Value Fillup DISPLAYED_PREFIXES (\d+) +Value Fillup NO_OF_PATHS (\d+) + +Start + ^\s*BGP table version is ${BGP_VERSION}, local router ID is ${ROUTER_ID} + ^\s*Route Distinguisher:\s${RD} + ^\s*${STATUS_CODE}\s*${EVPN_TYPE_5_PREFIX}$$ + ^\s*${NEXT_HOP} \s{1,8}${METRIC} ${WEIGHT}\s${PATH}\s{0,1}${ORIGIN_CODE}$$ + ^\s*RT:${RT}\sET:${ET}\sRmac:${RMAC} -> Continue.Record + ^\s*ET:${ET}\sRT:${RT}\sRmac:${RMAC} -> Continue.Record + ^\s*Displayed\s${DISPLAYED_PREFIXES}\sprefixes\s\(${NO_OF_PATHS}\spaths\) + diff --git a/spytest/templates/show_bgp_l2vpn_evpn_summary.tmpl b/spytest/templates/show_bgp_l2vpn_evpn_summary.tmpl new file mode 100644 index 00000000000..7b5ca283676 --- /dev/null +++ b/spytest/templates/show_bgp_l2vpn_evpn_summary.tmpl @@ -0,0 +1,25 @@ +Value Filldown IDENTIFIER (\d+\.\d+\.\d+\.\d) +Value Filldown LOCAL_AS (\d+) +Value Filldown VRF_ID (\d+) +Value Filldown RIB_ENTRIES (\d+) +Value Filldown NO_PEERS (\d+) +Value Required NEIGHBOR ([\.\d]+|[\d+:]+|\w+) +Value VERSION (\d+) +Value AS_NO (\d+) +Value MSGRCVD (\d+) +Value MSGSENT (\d+) +Value TBLVER (\d+) +Value INQ (\d+) +Value OUTQ (\d+) +Value UPDOWN (\S+) +Value PFXRCD (\S+) + + +Start + ^\s*BGP\srouter\sidentifier\s${IDENTIFIER},\slocal\sAS\snumber\s${LOCAL_AS}\svrf-id\s${VRF_ID} + ^\s*BGP\srouter\sidentifier\s${IDENTIFIER},\slocal\sAS\snumber\s${LOCAL_AS} + ^\s*RIB entries\s${RIB_ENTRIES},\s* + ^\s*Peers\s${NO_PEERS},\s* + ^\s*${NEIGHBOR}\s*${VERSION}\s*${AS_NO}\s*${MSGRCVD}\s*${MSGSENT}\s*${TBLVER}\s*${INQ}\s*${OUTQ}\s*${UPDOWN}\s*${PFXRCD}$$ -> Record + ^\s*${NEIGHBOR}\s*${VERSION}\s*${AS_NO}\s*${MSGRCVD}\s*${MSGSENT}\s*${INQ}\s*${OUTQ}\s*${UPDOWN}\s*${PFXRCD} -> Record + diff --git a/spytest/templates/show_bgp_l2vpn_evpn_vni.tmpl b/spytest/templates/show_bgp_l2vpn_evpn_vni.tmpl new file mode 100644 index 00000000000..b032da041fc --- /dev/null +++ b/spytest/templates/show_bgp_l2vpn_evpn_vni.tmpl @@ -0,0 +1,21 @@ +Value Filldown GW_MACIP (\S+) +Value Filldown ALL_VNI_FLAG (\S+) +Value Filldown BUM_FLOODING ([\S\s]+) +Value Filldown NO_L2VNI (\d+) +Value Filldown NO_L3VNI (\d+) +Value Required VNI (\d+) +Value TYPE (\S+) +Value RD (\d+\:\d+|\d+\.\d+\.\d+\.\d+\:\d+) +Value IMPORT_RT (\d+\:\d+) +Value EXPORT_RT (\d+\:\d+) +Value TENANT_VRF (\S+) + + +Start + ^\s*Advertise Gateway Macip:\s${GW_MACIP}$$ + ^\s*Advertise All VNI flag:\s${ALL_VNI_FLAG}$$ + ^\s*BUM flooding:\s${BUM_FLOODING}$$ + ^\s*Number of L2 VNIs:\s${NO_L2VNI}$$ + ^\s*Number of L3 VNIs:\s${NO_L3VNI}$$ + ^\s*${VNI}\s*${TYPE}\s*${RD}\s*${IMPORT_RT}\s*${EXPORT_RT}\s*${TENANT_VRF}\s* -> Record + ^\*\s${VNI}\s*${TYPE}\s*${RD}\s*${IMPORT_RT}\s*${EXPORT_RT}\s*${TENANT_VRF}\s* -> Record diff --git a/spytest/templates/show_bgp_l2vpn_evpn_vni_id.tmpl b/spytest/templates/show_bgp_l2vpn_evpn_vni_id.tmpl new file mode 100644 index 00000000000..6a152d3b32d --- /dev/null +++ b/spytest/templates/show_bgp_l2vpn_evpn_vni_id.tmpl @@ -0,0 +1,19 @@ +Value VNI (\d+) +Value TYPE (\S+) +Value VRFNAME (\S+) +Value RD (\d+\:\d+|\d+\.\d+\.\d+\.\d+) +Value ORIGINIP (\d+\.\d+\.\d+\.\d) +Value GWMAC (\S+) +Value List RT (\d+\:\d+) + + +Start + ^\s*VNI:\s${VNI} + ^\s*Type:\s${TYPE}$$ + ^\s*Tenant-Vrf:\s${VRFNAME}$$ + ^\s*RD:\s${RD} + ^\s*Originator IP:\s${ORIGINIP}$$ + ^\s*Advertise-gw-macip :\s${GWMAC}$$ + ^\s*${RT}$$ + + diff --git a/spytest/templates/show_bgp_neighbor.tmpl b/spytest/templates/show_bgp_neighbor.tmpl new file mode 100644 index 00000000000..da956e8fd32 --- /dev/null +++ b/spytest/templates/show_bgp_neighbor.tmpl @@ -0,0 +1,92 @@ +Value Required NEIGHBORIP ([\:\d\w\.]+) +Value REMOTEASN (\d+) +Value LOCALASN (\d+) +Value BGPVERSION (\d+) +Value REMROUTERID (\S+) +Value LOCALROUTERID (\S+) +Value STATE (\S+) +Value LASTREAD (\S+) +Value LASTWRITE (\S+) +Value HOLDTIME (\d+) +Value KEEPALIVE (\d+) +Value INQDEPTH (\d+) +Value OUTQDEPTH (\d+) +Value OPENSENT (\S+) +Value OPENRCVD (\S+) +Value NOTIFICATIONSENT (\S+) +Value NOTIFICATIONRCVD (\S+) +Value UPDATESENT (\S+) +Value UPDATERCVD (\S+) +Value KEEPALIVESENT (\S+) +Value KEEPALIVERCVD (\S+) +Value ROUTEREFRESHSENT (\S+) +Value ROUTEREFRESHRCVD (\S+) +Value CAPABLITYSENT (\S+) +Value CAPABILITYRCVD (\S+) +Value SENTTOTAL (\S+) +Value RCVDTOTAL (\S+) +Value PEERGROUP (\S+) +Value ASBYTE (\S+) +Value IPV4UCASTNCAP ([\S\s]+) +Value L2VPNEVPNNCAP ([\S\s]+) +Value ENDOFRIBSEND ([\S\s]+) +Value ENDOFRIBRCV ([\S\s]+) +Value GRCAPABILITY ([\S\s]+) +Value BGPDOWNREASON ([\S\s]+) +Value BGPLASTRESET ([\d\:]+) +Value BFDTYPE (\S+) +Value BFDMultiplier (\S+) +Value BFDRXINTR (\S+) +Value BFDTXINTR (\S+) +Value BFDSTATUS (\S+) +Value BFDLASTUPDATE (\S+) +Value List UPDTGRP (\d+) +Value List SUBGRP (\d+) +Value List PKTQL (\d+) +Value List ACCEPTPREFIX (\d+) + + +Start + ^\s*BGP\s*neighbor\s*is\s*(.*) -> Continue.Record + ^\s*BGP\s*neighbor\s*is\s*${NEIGHBORIP},\s*remote\s*AS\s*${REMOTEASN},\s*local\s*AS\s*${LOCALASN},\s*external\s*link + ^\s*BGP\s*neighbor\s*is\s*${NEIGHBORIP},\s*remote\s*AS\s*${REMOTEASN},\s*local\s*AS\s*${LOCALASN},\s*internal\s*link + ^\s*Member of peer-group ${PEERGROUP} for session parameters\s*$$ + ^\s*BGP\s+version\s+${BGPVERSION},\s+remote\s+router\s+ID\s+${REMROUTERID},\s+local\s+router\s+ID\s+${LOCALROUTERID}\s*$$ + ^\s*BGP\s+state\s+=\s+${STATE},\s*up\s*for\s*.* + ^\s*BGP\s+state\s+=\s+${STATE} + ^\s*Last\s+read\s+${LASTREAD},\s+Last\s+write\s+${LASTWRITE} + ^\s*Hold time is ${HOLDTIME}, keepalive interval is ${KEEPALIVE} seconds\s*$$ + ^\s*Neighbor capabilities: + ^\s*${ASBYTE} Byte AS: advertised + ^\s*Address Family IPv4 Unicast: ${IPV4UCASTNCAP} + ^\s*Address Family L2VPN EVPN: ${L2VPNEVPNNCAP} + ^\s*Graceful restart information: + ^\s*End-of-RIB send: ${ENDOFRIBSEND} + ^\s*End-of-RIB received: ${ENDOFRIBRCV} + ^\s*Graceful Restart Capabilty: ${GRCAPABILITY} + ^\s*Message statistics:\s*$$ + ^\s*Inq depth is ${INQDEPTH}\s*$$ + ^\s*Outq depth is ${OUTQDEPTH}\s*$$ + ^\s*Sent\s*Rcvd\s*$$ + ^\s*Opens:\s*${OPENSENT}\s*${OPENRCVD}\s*$$ + ^\s*Notifications:\s*${NOTIFICATIONSENT}\s*${NOTIFICATIONRCVD}\s*$$ + ^\s*Updates:\s*${UPDATESENT}\s*${UPDATERCVD}\s*$$ + ^\s*Keepalives:\s*${KEEPALIVESENT}\s*${KEEPALIVERCVD}\s*$$ + ^\s*Route Refresh:\s*${ROUTEREFRESHSENT}\s*${ROUTEREFRESHRCVD}\s*$$ + ^\s*Capability:\s*${CAPABLITYSENT}\s*${CAPABILITYRCVD}\s*$$ + ^\s*Total:\s*${SENTTOTAL}\s*${RCVDTOTAL}\s*$$ + ^\s*Last\s*reset\s*${BGPLASTRESET},\s*Notification\s*sent\s*\(\s*${BGPDOWNREASON}\) + ^\s*Last\s*reset\s*${BGPLASTRESET},\s*due\s*to\s*NOTIFICATION\s*sent\s*\(\s*${BGPDOWNREASON}\) + ^\s*Last\s*reset\s*${BGPLASTRESET},\s*due\s*to\s*NOTIFICATION\s*received\s*\(\s*${BGPDOWNREASON}\) + ^\s*Last\s*reset\s*${BGPLASTRESET},\s*due\s*to\s*${BGPDOWNREASON} + ^\s*Last\s*reset\s*${BGPLASTRESET},\s*${BGPDOWNREASON}$$ + ^\s*BFD: Type: ${BFDTYPE} hop + ^\s*Detect Multiplier: ${BFDMultiplier}, Min Rx interval: ${BFDRXINTR}, Min Tx interval: ${BFDTXINTR} + ^\s*Status: ${BFDSTATUS}, Last update: ${BFDLASTUPDATE}\s*$$ + ^\s*Update group ${UPDTGRP}, subgroup ${SUBGRP} + ^\s*Packet Queue length ${PKTQL} + ^\s*${ACCEPTPREFIX} accepted prefixes$$ + + + + diff --git a/spytest/templates/show_bgp_neighbor_detail.tmpl b/spytest/templates/show_bgp_neighbor_detail.tmpl new file mode 100644 index 00000000000..040c35c01b8 --- /dev/null +++ b/spytest/templates/show_bgp_neighbor_detail.tmpl @@ -0,0 +1,72 @@ +Value Required NEIGHBORIP ([\:\d\.]+) +Value REMOTEASN (\d+) +Value LOCALASN (\d+) +Value BGPVERSION (\d+) +Value REMROUTERID (\S+) +Value LOCALROUTERID (\S+) +Value STATE (\S+) +Value LASTREAD (\S+) +Value LASTWRITE (\S+) +Value HOLDTIME (\d+) +Value KEEPALIVE (\d+) +Value INQDEPTH (\d+) +Value OUTQDEPTH (\d+) +Value OPENSENT (\S+) +Value OPENRCVD (\S+) +Value NOTIFICATIONSENT (\S+) +Value NOTIFICATIONRCVD (\S+) +Value UPDATESENT (\S+) +Value UPDATERCVD (\S+) +Value KEEPALIVESENT (\S+) +Value KEEPALIVERCVD (\S+) +Value ROUTEREFRESHSENT (\S+) +Value ROUTEREFRESHRCVD (\S+) +Value CAPABLITYSENT (\S+) +Value CAPABILITYRCVD (\S+) +Value SENTTOTAL (\S+) +Value RCVDTOTAL (\S+) +Value PEERGROUP (\S+) +Value ASBYTE (\S+) +Value GRCAPABILITY (\S+) +Value BGPDOWNREASON ([\S\s]+) +Value BGPLASTRESET ([\d\:]+) +Value BFDTYPE (\S+) +Value BFDMultiplier (\S+) +Value BFDRXINTR (\S+) +Value BFDTXINTR (\S+) +Value BFDSTATUS (\S+) +Value BFDLASTUPDATE (\S+) + + +Start + ^\s*BGP\s*neighbor\s*is\s*${NEIGHBORIP},\s*remote\s*AS\s*${REMOTEASN},\s*local\s*AS\s*${LOCALASN},\s*external\s*link + ^\s*BGP\s*neighbor\s*is\s*${NEIGHBORIP},\s*remote\s*AS\s*${REMOTEASN},\s*local\s*AS\s*${LOCALASN},\s*internal\s*link + ^\s*Member of peer-group ${PEERGROUP} for session parameters\s*$$ + ^\s*BGP\s+version\s+${BGPVERSION},\s+remote\s+router\s+ID\s+${REMROUTERID},\s+local\s+router\s+ID\s+${LOCALROUTERID}\s*$$ + ^\s*BGP\s+state\s+=\s+${STATE} + ^\s*Last\s+read\s+${LASTREAD},\s+Last\s+write\s+${LASTWRITE} + ^\s*Hold time is ${HOLDTIME}, keepalive interval is ${KEEPALIVE} seconds\s*$$ + ^\s*Neighbor capabilities: + ^\s*${ASBYTE} Byte AS: advertised + ^\s*Graceful Restart Capabilty: ${GRCAPABILITY} + ^\s*Message statistics:\s*$$ + ^\s*Inq depth is ${INQDEPTH}\s*$$ + ^\s*Outq depth is ${OUTQDEPTH}\s*$$ + ^\s*Sent\s*Rcvd\s*$$ + ^\s*Opens:\s*${OPENSENT}\s*${OPENRCVD}\s*$$ + ^\s*Notifications:\s*${NOTIFICATIONSENT}\s*${NOTIFICATIONRCVD}\s*$$ + ^\s*Updates:\s*${UPDATESENT}\s*${UPDATERCVD}\s*$$ + ^\s*Keepalives:\s*${KEEPALIVESENT}\s*${KEEPALIVERCVD}\s*$$ + ^\s*Route Refresh:\s*${ROUTEREFRESHSENT}\s*${ROUTEREFRESHRCVD}\s*$$ + ^\s*Capability:\s*${CAPABLITYSENT}\s*${CAPABILITYRCVD}\s*$$ + ^\s*Total:\s*${SENTTOTAL}\s*${RCVDTOTAL}\s*$$ + ^\s*Last\s*reset\s*${BGPLASTRESET},\s*due\s*to\s*NOTIFICATION\s*sent\s*\(\s*${BGPDOWNREASON}\) + ^\s*Last\s*reset\s*${BGPLASTRESET},\s*due\s*to\s*NOTIFICATION\s*received\s*\(\s*${BGPDOWNREASON}\) + ^\s*Last\s*reset\s*${BGPLASTRESET},\s*due\s*to\s*${BGPDOWNREASON} + ^\s*BFD: Type: ${BFDTYPE} hop + ^\s*Detect Multiplier: ${BFDMultiplier}, Min Rx interval: ${BFDRXINTR}, Min Tx interval: ${BFDTXINTR} + ^\s*Status: ${BFDSTATUS}, Last update: ${BFDLASTUPDATE}\s*$$ -> Record + + + + diff --git a/spytest/templates/show_buffer_pool_watermark.tmpl b/spytest/templates/show_buffer_pool_watermark.tmpl new file mode 100644 index 00000000000..67fed359802 --- /dev/null +++ b/spytest/templates/show_buffer_pool_watermark.tmpl @@ -0,0 +1,7 @@ +Value Pool (\S+) +Value Bytes (\d+) + + +Start + ^\s*${Pool}\s+${Bytes}\s*$$ -> Record + diff --git a/spytest/templates/show_buffer_pool_watermark_percentage.tmpl b/spytest/templates/show_buffer_pool_watermark_percentage.tmpl new file mode 100644 index 00000000000..fbcc553cdc7 --- /dev/null +++ b/spytest/templates/show_buffer_pool_watermark_percentage.tmpl @@ -0,0 +1,6 @@ +Value Pool (\S+) +Value Percent (\d+) + + +Start + ^\s*${Pool}\s+${Percent}\s*$$ -> Record diff --git a/spytest/templates/show_c.tmpl b/spytest/templates/show_c.tmpl new file mode 100644 index 00000000000..746d02f0a92 --- /dev/null +++ b/spytest/templates/show_c.tmpl @@ -0,0 +1,10 @@ +Value KEY (\S+|\S+\(\d+\)\.\S+) +Value VALUE (\S+) +Value DIFF (\S+) +Value TIME (\S+|\s*) + +Start + ^\s*${KEY}\s+:\s+${VALUE}\s+${DIFF}\s+${TIME}\s*$$ -> Record + ^\s*${KEY}\s+:\s+${VALUE}\s+${DIFF}\s*$$ -> Record + +EOF diff --git a/spytest/templates/show_classifier.tmpl b/spytest/templates/show_classifier.tmpl new file mode 100644 index 00000000000..7d892b3214d --- /dev/null +++ b/spytest/templates/show_classifier.tmpl @@ -0,0 +1,38 @@ +Value class_name ([\w\S]+) +Value policy_name (\w+?) +Value acl_name (\S+) +Value desc_name (\w+) +Value match_type (\S+) +Value priority_val (\d+) +Value field_value (\d+) +Value src_port_val (\d+) +Value dst_port_val (\d+) +Value src_ip_val (\S+) +Value dst_ip_val (\S+) +Value src_mac_val (\S+) +Value dst_mac_val (\S+) +Value src_ipv6_val (\S+) +Value dst_ipv6_val (\S+) +Value tcp_flags_type (\S+.*) +Value ip_protocol_val (\d+) + + +Start + ^Classifier ${class_name} match-type ${match_type}\s*$$ + ^Description:.*$$ + ^\s*${desc_name}*$$ + ^Match:.*$$ + ^\s*match acl\s+${acl_name}\s*$$ + ^\s*ether-type ${field_value}\s*$$ + ^\s*ip-protocol ${ip_protocol_val}\s*$$ + ^\s*src-port ${src_port_val}\s*$$ + ^\s*dst-port ${dst_port_val}\s*$$ + ^\s*src-ip ${src_ip_val}\s*$$ + ^\s*dst-ip ${dst_ip_val}\s*$$ + ^\s*src-ipv6 ${src_ipv6_val}\s*$$ + ^\s*dst-ipv6 ${dst_ipv6_val}\s*$$ + ^\s*src-mac ${src_mac_val}\s*$$ + ^\s*dst-mac ${dst_mac_val}\s*$$ + ^\s*tcp-flags ${tcp_flags_type}*$$ + ^Referenced in flows:.*$$ + ^\s*policy\s+${policy_name} at priority ${priority_val}\s*$$ -> Record diff --git a/spytest/templates/show_clock.tmpl b/spytest/templates/show_clock.tmpl new file mode 100644 index 00000000000..bf536eaab8f --- /dev/null +++ b/spytest/templates/show_clock.tmpl @@ -0,0 +1,11 @@ +Value Day (\w+) +Value Month (\w+) +Value MonthDay (\d+) +Value Hours (\d+) +Value Minutes (\d+) +Value Seconds (\d+) +Value Timezone (\S+) +Value Year (\d+) + +Start + ^${Day}\s+${Month}\s+${MonthDay}\s+${Hours}:${Minutes}:${Seconds}\s+${Timezone}\s+${Year}$$ -> Record \ No newline at end of file diff --git a/spytest/templates/show_conntrack_table.tmpl b/spytest/templates/show_conntrack_table.tmpl new file mode 100644 index 00000000000..455db1e9552 --- /dev/null +++ b/spytest/templates/show_conntrack_table.tmpl @@ -0,0 +1,15 @@ +Value prot (\S+) +Value src_ip (\d+.\d+.\d+.\d+) +Value dst_ip (\d+.\d+.\d+.\d+) +Value src_port (\d+) +Value dst_port (\d+) +Value trns_src_ip (\d+.\d+.\d+.\d+) +Value trns_dst_ip (\d+.\d+.\d+.\d+) +Value trns_src_port (\d+) +Value trns_dst_port (\d+) +Value status (\S+) + +Start + ^\s*${prot}\s+\d+\s+\d+\s+\S+\s+src=${src_ip}\s+dst=${dst_ip}\s+sport=${src_port}\s+dport=${dst_port}\s+src=${trns_src_ip}\s+dst=${trns_dst_ip}\s+sport=${trns_src_port}\s+dport=${trns_dst_port}\s+${status} -> Record + ^\s*${prot}\s+\d+\s+\d+\s+src=${src_ip}\s+dst=${dst_ip}\s+sport=${src_port}\s+dport=${dst_port}\s+src=${trns_src_ip}\s+dst=${trns_dst_ip}\s+sport=${trns_src_port}\s+dport=${trns_dst_port}\s+${status} -> Record + diff --git a/spytest/templates/show_debug_ipmcorchall.tmpl b/spytest/templates/show_debug_ipmcorchall.tmpl new file mode 100644 index 00000000000..b36caf7c10d --- /dev/null +++ b/spytest/templates/show_debug_ipmcorchall.tmpl @@ -0,0 +1,20 @@ +Value vrf ([A-Za-z]+|[A-Za-z]+-[A-Za-z]+) +Value src_ip (\d+.\d+.\d+.\d+) +Value grp_ip (\d+.\d+.\d+.\d+) +Value IIF (\S+) +Value OIF (\S+) +Value total_ipmc_entries (\d+) +Value ipmc_gid (\S+) +Value ref_cnt (\d+) +Value ipmc_grp_mem (\S+) +Value total_ipmc_groups (\d+) +Value total_rpf_groups (\d+) + +Start + ^\s*VRF name\s+"${vrf}" -> Record + ^\s*${src_ip}\s+${grp_ip}\s+${IIF}\s+${OIF} -> Record + ^\s*Total number of IPMC entries\s+\S+\s+${total_ipmc_entries} -> Record + ^\s*${ipmc_gid}\s+${ref_cnt}\s+${ipmc_grp_mem} -> Record + ^\s*Total number of IPMC groups\s+\S+\s+${total_ipmc_groups} -> Record + ^\s*Total number of RPF groups\s+\S+\s+${total_rpf_groups} -> Record + diff --git a/spytest/templates/show_docker_ps.tmpl b/spytest/templates/show_docker_ps.tmpl new file mode 100644 index 00000000000..641895a0504 --- /dev/null +++ b/spytest/templates/show_docker_ps.tmpl @@ -0,0 +1,7 @@ +Value IMAGE (\S+) +Value STATUS (\S+) +Value NAMES (\S+) + +Start + ^\s*\w+\s+${IMAGE}\s+\S+\s+\d+\s+hours ago\s+${STATUS}\s+\d+\s+\S+\s+${NAMES}\s*$$ -> Record + ^$$ diff --git a/spytest/templates/show_docker_snmp_conf.tmpl b/spytest/templates/show_docker_snmp_conf.tmpl new file mode 100644 index 00000000000..32c13f1b566 --- /dev/null +++ b/spytest/templates/show_docker_snmp_conf.tmpl @@ -0,0 +1,12 @@ +Value rocommunity (\S+) +Value rocommunityv6 (\S+) +Value sysName (\S+) +Value sysContact (\S+) +Value sysLocation (\S+) + +Start + ^\s*rocommunity\s+${rocommunity}\s*$$ + ^\s*rocommunity6\s+${rocommunityv6}\s*$$ + ^\s*sysName\s+${sysName}\s*$$ + ^\s*sysContact\s+${sysContact}\s*$$ + ^\s*sysLocation\s+${sysLocation}\s*$$ -> Record \ No newline at end of file diff --git a/spytest/templates/show_ecn.tmpl b/spytest/templates/show_ecn.tmpl new file mode 100644 index 00000000000..badbbb4f6ff --- /dev/null +++ b/spytest/templates/show_ecn.tmpl @@ -0,0 +1,8 @@ +Value Filldown Profile (\S+) +Value data (.*\d+) + +Start + ^Profile:\s+${Profile}$$ + ^(-+\s*-+)*$$ + ^${data}$$ -> Record + ^(-+\s*-+)\s*$$ \ No newline at end of file diff --git a/spytest/templates/show_error_database.tmpl b/spytest/templates/show_error_database.tmpl new file mode 100644 index 00000000000..bd9364c0027 --- /dev/null +++ b/spytest/templates/show_error_database.tmpl @@ -0,0 +1,15 @@ +Value vrf (\S+) +Value route (\d+.\d+.\d+.\d+|\d+::|\d+::\d+) +Value subnet (\d+) +Value nexthop (\d+.\d+.\d+.\d+|\d+::\d+|\S+|\s) +Value interface (\S+) +Value failure (\S+) +Value operation (\S+) +Value mac (\w+:\w+:\w+:\w+:\w+:\w+|\s) + +Start + ^\s*${route}/${subnet}\s+${nexthop}\s+${interface}\s+${failure}\s+${operation}\s*$$ -> Record + ^\s*${vrf}\s+${route}/${subnet}\s+${nexthop}\s+${interface}\s+${failure}\s+${operation}\s*$$ -> Record + ^\s*${route}/${subnet}\s+${failure}\s+${operation}\s*$$ -> Record + ^\s*${vrf}\s+${route}/${subnet}\s+${failure}\s+${operation}\s*$$ -> Record + ^\s*${nexthop}\s+${mac}\s+${interface}\s+${failure}\s+${operation}\s*$$ -> Record diff --git a/spytest/templates/show_frr_config.tmpl b/spytest/templates/show_frr_config.tmpl new file mode 100644 index 00000000000..68ed01eb7c5 --- /dev/null +++ b/spytest/templates/show_frr_config.tmpl @@ -0,0 +1,13 @@ +Value Filldown ROUTEROSPF (\S+) +Value Filldown VRFNAME (\S+) +Value Filldown NETWORK (\S+) +Value ROUTERID (\S+) +Value Filldown INTERFACE (\S+) +Value IFAREA (\S+) + +Start + ^\s*router ospf${ROUTEROSPF}\s*$$ + ^\s*router ospf vrf\s+${VRFNAME}\s*$$ + ^\s*ospf router-id\s+${ROUTERID}\s*$$ -> Record + +EOF diff --git a/spytest/templates/show_hardware_access_list.tmpl b/spytest/templates/show_hardware_access_list.tmpl new file mode 100644 index 00000000000..336e65b1325 --- /dev/null +++ b/spytest/templates/show_hardware_access_list.tmpl @@ -0,0 +1,13 @@ +Value lookup_configured (Advanced|Legacy|Optimized) +Value lookup_active (Advanced|Legacy|Optimized) +Value counter_configured ((\w+-)+\w+) +Value counter_active ((\w+-)+\w+) + + +Start + ^\s*Lookup\s+mode:\s*$$ + ^\s+Configured:\s+${lookup_configured}\s*$$ + ^\s+Active:\s+${lookup_active}\s*$$ + ^\s*Counter\s+mode:\s*$$ + ^\s+Configured:\s+${counter_configured}\s*$$ + ^\s+Active:\s+${counter_active}\s*$$ \ No newline at end of file diff --git a/spytest/templates/show_interface_counters.tmpl b/spytest/templates/show_interface_counters.tmpl new file mode 100644 index 00000000000..c4258f623bb --- /dev/null +++ b/spytest/templates/show_interface_counters.tmpl @@ -0,0 +1,12 @@ +Value iface (\S+) +Value state (\w{1}) +Value rx_ok (\d+) +Value rx_err (\d+) +Value rx_drp (\d+) +Value tx_ok (\d+) +Value tx_err (\d+) +Value tx_drp (\d+) + +Start + ^\s*${iface}\s+${state}\s+${rx_ok}\s+${rx_err}\s+${rx_drp}\s+${tx_ok}\s+${tx_err}\s+${tx_drp}\s*$$ -> Record + diff --git a/spytest/templates/show_interface_status.tmpl b/spytest/templates/show_interface_status.tmpl new file mode 100644 index 00000000000..ee40aa02a5b --- /dev/null +++ b/spytest/templates/show_interface_status.tmpl @@ -0,0 +1,9 @@ +Value interface (\S+) +Value description (\S+) +Value admin (\S+) +Value oper (\S+) +Value speed (\S+) +Value mtu (\d+) + +Start + ^\s*${interface}\s*${description}\s*${admin}\s*${oper}\s*${speed}\s*${mtu}\s*$$ -> Record \ No newline at end of file diff --git a/spytest/templates/show_interfaces_counters.tmpl b/spytest/templates/show_interfaces_counters.tmpl new file mode 100644 index 00000000000..f9e271adeaa --- /dev/null +++ b/spytest/templates/show_interfaces_counters.tmpl @@ -0,0 +1,23 @@ +Value IFACE (\S+) +Value STATE (\S+) +Value RX_OK (\S+) +Value RX_BPS (\S+(\s\S+)?) +Value RX_PPS (\S+) +Value RX_UTIL (\S+) +Value RX_ERR (\S+) +Value RX_DRP (\S+) +Value RX_OVR (\S+) +Value TX_OK (\S+) +Value TX_BPS (\S+(\s\S+)?) +Value Tx_PPS (\S+) +Value TX_UTIL (\S+) +Value TX_ERR (\S+) +Value TX_DRP (\S+) +Value TX_OVR (\S+) + +Start + ^Last cached.*$$ -> Continue + ^\s*IFACE\s+STATE\s+RX_OK\s+RX_BPS\s+RX_PPS\s+RX_UTIL\s+RX_ERR\s+RX_DRP\s+RX_OVR\s+TX_OK\s+TX_BPS\s+Tx_PPS\s+TX_UTIL\s+TX_ERR\s+TX_DRP\s+TX_OVR + ^(-+\s*)*$$ + ^\s*${IFACE}\s+${STATE}\s+${RX_OK}\s+${RX_BPS}\s+${RX_PPS}\s+${RX_UTIL}\s+${RX_ERR}\s+${RX_DRP}\s+${RX_OVR}\s+${TX_OK}\s+${TX_BPS}\s+${Tx_PPS}\s+${TX_UTIL}\s+${TX_ERR}\s+${TX_DRP}\s+${TX_OVR} -> Record + ^$$ \ No newline at end of file diff --git a/spytest/templates/show_interfaces_counters_detailed.tmpl b/spytest/templates/show_interfaces_counters_detailed.tmpl new file mode 100644 index 00000000000..0954147cc01 --- /dev/null +++ b/spytest/templates/show_interfaces_counters_detailed.tmpl @@ -0,0 +1,68 @@ +Value pkt_rx_64_octets (\S+) +Value pkt_rx_65_127_octets (\S+) +Value pkt_rx_128_255_octets (\S+) +Value pkt_rx_256_511_octets (\S+) +Value pkt_rx_512_1023_octets (\S+) +Value pkt_rx_1024_1518_octets (\S+) +Value pkt_rx_1519_2047_octets (\S+) +Value pkt_rx_2048_4095_octets (\S+) +Value pkt_rx_4096_9216_octets (\S+) +Value pkt_rx_9217_16383_octets (\S+) +Value pkt_rx_without_errors (\S+) +Value pkt_rx_unicast (\S+) +Value pkt_rx_multicast (\S+) +Value pkt_rx_broadcast (\S+) +Value pkt_rx_jabbers (\S+) +Value pkt_rx_fragments (\S+) +Value pkt_rx_undersize (\S+) +Value pkt_rx_overruns (\S+) +Value pkt_tx_64_octets (\S+) +Value pkt_tx_65_127_octets (\S+) +Value pkt_tx_128_255_octets (\S+) +Value pkt_tx_256_511_octets (\S+) +Value pkt_tx_512_1023_octets (\S+) +Value pkt_tx_1024_1518_octets (\S+) +Value pkt_tx_1519_2047_octets (\S+) +Value pkt_tx_2048_4095_octets (\S+) +Value pkt_tx_4096_9216_octets (\S+) +Value pkt_tx_9217_16383_octets (\S+) +Value pkt_tx_successfully (\S+) +Value pkt_tx_unicast (\S+) +Value pkt_tx_multicast (\S+) +Value pkt_tx_broadcast (\S+) +Value time_since_counters_last_cleared (\S+) + +Start + ^\s*Packets\s+Received\s+64\s+Octets\.+\s+${pkt_rx_64_octets}$$ + ^\s*Packets\s+Received\s+65-127\s+Octets\.+\s+${pkt_rx_65_127_octets}$$ + ^\s*Packets\s+Received\s+128-255\s+Octets\.+\s+${pkt_rx_128_255_octets}$$ + ^\s*Packets\s+Received\s+256-511\s+Octets\.+\s+${pkt_rx_256_511_octets}$$ + ^\s*Packets\s+Received\s+512-1023\s+Octets\.+\s+${pkt_rx_512_1023_octets}$$ + ^\s*Packets\s+Received\s+1024-1518\s+Octets\.+\s+${pkt_rx_1024_1518_octets}$$ + ^\s*Packets\s+Received\s+1519-2047\s+Octets\.+\s+${pkt_rx_1519_2047_octets}$$ + ^\s*Packets\s+Received\s+2048-4095\s+Octets\.+\s+${pkt_rx_2048_4095_octets}$$ + ^\s*Packets\s+Received\s+4096-9216\s+Octets\.+\s+${pkt_rx_4096_9216_octets}$$ + ^\s*Packets\s+Received\s+9217-16383\s+Octets\.+\s+${pkt_rx_9217_16383_octets}$$ + ^\s*Total\s+Packets\s+Received\s+Without\s+Errors\.+\s+${pkt_rx_without_errors}$$ + ^\s*Unicast\s+Packets\s+Received\.+\s+${pkt_rx_unicast}$$ + ^\s*Multicast\s+Packets\s+Received\.+\s+${pkt_rx_multicast}$$ + ^\s*Broadcast\s+Packets\s+Received\.+\s+${pkt_rx_broadcast}$$ + ^\s*Jabbers\s+Received\.+\s+${pkt_rx_jabbers}$$ + ^\s*Fragments\s+Received\.+\s+${pkt_rx_fragments}$$ + ^\s*Undersize\s+Received\.+\s+${pkt_rx_undersize}$$ + ^\s*Overruns\s+Received\.+\s+${pkt_rx_overruns}$$ + ^\s*Packets\s+Transmitted\s+64\s+Octets\.+\s+${pkt_tx_64_octets}$$ + ^\s*Packets\s+Transmitted\s+65-127\s+Octets\.+\s+${pkt_tx_65_127_octets}$$ + ^\s*Packets\s+Transmitted\s+128-255\s+Octets\.+\s+${pkt_tx_128_255_octets}$$ + ^\s*Packets\s+Transmitted\s+256-511\s+Octets\.+\s+${pkt_tx_256_511_octets}$$ + ^\s*Packets\s+Transmitted\s+512-1023\s+Octets\.+\s+${pkt_tx_512_1023_octets}$$ + ^\s*Packets\s+Transmitted\s+1024-1518\s+Octets\.+\s+${pkt_tx_1024_1518_octets}$$ + ^\s*Packets\s+Transmitted\s+1519-2047\s+Octets\.+\s+${pkt_tx_1519_2047_octets}$$ + ^\s*Packets\s+Transmitted\s+2048-4095\s+Octets\.+\s+${pkt_tx_2048_4095_octets}$$ + ^\s*Packets\s+Transmitted\s+4096-9216\s+Octets\.+\s+${pkt_tx_4096_9216_octets}$$ + ^\s*Packets\s+Transmitted\s+9217-16383\s+Octets\.+\s+${pkt_tx_9217_16383_octets}$$ + ^\s*Total\s+Packets\s+Transmitted\s+Successfully\.+\s+${pkt_tx_successfully}$$ + ^\s*Unicast\s+Packets\s+Transmitted\.+\s+${pkt_tx_unicast}$$ + ^\s*Multicast\s+Packets\s+Transmitted\.+\s+${pkt_tx_multicast}$$ + ^\s*Broadcast\s+Packets\s+Transmitted\.+\s+${pkt_tx_broadcast}$$ + ^\s*Time\s+Since\s+Counters\s+Last\s+Cleared\.+\s+${time_since_counters_last_cleared}$$ -> Record diff --git a/spytest/templates/show_interfaces_description.tmpl b/spytest/templates/show_interfaces_description.tmpl new file mode 100644 index 00000000000..402896a548a --- /dev/null +++ b/spytest/templates/show_interfaces_description.tmpl @@ -0,0 +1,11 @@ +Value Interface (\S+) +Value Oper (\S+) +Value Admin (\S+) +Value Alias (\S+) +Value Description (\S+) + +Start + ^\s*Interface\s+Oper\s+Admin\s+Alias\s+Description\s*$$ + ^(-+\s*)*$$ + ^\s*${Interface}\s+${Oper}\s+${Admin}\s+${Alias}\s+${Description}\s*$$ -> Record + ^$$ diff --git a/spytest/templates/show_interfaces_loopback.tmpl b/spytest/templates/show_interfaces_loopback.tmpl new file mode 100644 index 00000000000..225f576d86b --- /dev/null +++ b/spytest/templates/show_interfaces_loopback.tmpl @@ -0,0 +1,12 @@ +Value IfName (Loopback\S+) +Value IfVrf (\S+) + +Start + ^(-+\s*)*$$ + ^\s*Name\s+VRF\s*$$ -> Data + +Data + ^(-+\s*)*$$ + ^\s*${IfName}\s+${IfVrf}\s*$$ -> Record + ^\s*${IfName}\s*$$ -> Record + ^.*$$ diff --git a/spytest/templates/show_interfaces_pktdrops_nonzero.tmpl b/spytest/templates/show_interfaces_pktdrops_nonzero.tmpl new file mode 100644 index 00000000000..ca1c667e4e2 --- /dev/null +++ b/spytest/templates/show_interfaces_pktdrops_nonzero.tmpl @@ -0,0 +1,12 @@ +Value INTF (\S+) +Value COUNTNAME (\S+) +Value TOTVAL (\S+) +Value DIFFVAL (\S+) + +Start + ^\s*IFACE\s+COUNTER\s+COUNT\s+CHANGE + ^(-+\s*)*$$ + ^\s*${INTF}\s+${COUNTNAME}\s+${TOTVAL}\s+${DIFFVAL} -> Record + ^$$ + +EOF diff --git a/spytest/templates/show_interfaces_portchannel.tmpl b/spytest/templates/show_interfaces_portchannel.tmpl new file mode 100644 index 00000000000..edb5374bfcb --- /dev/null +++ b/spytest/templates/show_interfaces_portchannel.tmpl @@ -0,0 +1,11 @@ +Value No (\d+) +Value TeamDev (\S+) +Value Protocol (\S+) +Value Ports (\S+[\S ]+) + +Start + ^Flags:.*$$ -> Continue + ^-+\s*$$ -> Continue + ^\s*${No}\s+${TeamDev}\s+${Protocol}\s+${Ports}+\s*$$ -> Record + + diff --git a/spytest/templates/show_interfaces_portchannel_fallback.tmpl b/spytest/templates/show_interfaces_portchannel_fallback.tmpl new file mode 100644 index 00000000000..da66780d5e3 --- /dev/null +++ b/spytest/templates/show_interfaces_portchannel_fallback.tmpl @@ -0,0 +1,15 @@ +################################################################################# +#admin@sonic:~$ show interfaces portchannel PortChannel3 fallback +#Port Channel : PortChannel3 +#Fallback Configured Value : Enabled +#Fallback Operational Status : Disabled +#admin@sonic:~$ +################################################################################# +Value port_channel_name (\w+) +Value fallback_config (\w+) +Value fallback_oper_status (\w+) + +Start + ^\s*Port\s*Channel\s*:\s*${port_channel_name}$$ + ^\s*Fallback\s*Configured\s*Value\s*:\s*${fallback_config}$$ + ^\s*Fallback\s*Operational\s*Status\s*:\s*${fallback_oper_status}$$ -> Record \ No newline at end of file diff --git a/spytest/templates/show_interfaces_portchannel_qa.tmpl b/spytest/templates/show_interfaces_portchannel_qa.tmpl new file mode 100644 index 00000000000..68f9fbfa313 --- /dev/null +++ b/spytest/templates/show_interfaces_portchannel_qa.tmpl @@ -0,0 +1,11 @@ +Value No (\d+) +Value TeamDev (\S+) +Value Protocol (\S+) +Value Ports (\S+[\S ]+) + +Start + ^Flags:\s+A\s+-\sactive,\s+I\s+-\s+inactive,\s+Up\s+-\s+up,\s+Dw\s+-\s+Down,\s+N/A\s+-\s+not\s+available,\s+S\s+-\s+selected,\s+D\s+-\sdeselected -> Continue + ^\s*No.\s+Team Dev\s+Protocol\s+Ports\s*$$ + ^(-+\s*)*$$ + ^\s*${No}\s+${TeamDev}\s+${Protocol}\s+${Ports}\s*$$ -> Record + ^\s*${No}\s+${TeamDev}\s+${Protocol}\s*$$ -> Record diff --git a/spytest/templates/show_interfaces_status_qa.tmpl b/spytest/templates/show_interfaces_status_qa.tmpl new file mode 100644 index 00000000000..25a1faaadf2 --- /dev/null +++ b/spytest/templates/show_interfaces_status_qa.tmpl @@ -0,0 +1,27 @@ +Value Interface (\S+) +Value Lanes (\S+) +Value Speed (\S+) +Value MTU (\S+) +Value Alias (\S+) +Value Filldown Vlan (\S+) +Value Oper (\S+) +Value Admin (\S+) +Value Type (\S+[\S ]+) +Value AsymPFC (\S+) + +Start + ^\s*Interface\s+Lanes\s+Speed\s+MTU\s+Alias\s+Vlan\s+Oper\s+Admin\s+Type\s+Asym PFC\s*$$ -> Continue + ^\s*Interface\s+Lanes\s+Speed\s+MTU\s+Alias\s+Oper\s+Admin\s+Type\s+Asym PFC\s*$$ -> TYPE1 + ^\s*Interface\s+Lanes\s+Speed\s+MTU\s+Alias\s+Vlan\s+Oper\s+Admin\s+Type\s+Asym PFC\s*$$ -> TYPE2 + +TYPE1 + ^(-+\s*)*$$ + ^\s*${Interface}\s+${Lanes}\s+${Speed}\s+${MTU}\s+${Alias}\s+${Oper}\s+${Admin}\s+${Type}\s+${AsymPFC}\s*$$ -> Record + ^$$ + +TYPE2 + ^(-+\s*)*$$ + ^\s*${Interface}\s+${Lanes}\s+${Speed}\s+${MTU}\s+${Alias}\s+${Vlan}\s+${Oper}\s+${Admin}\s+${Type}\s+${AsymPFC}\s*$$ -> Record + ^$$ + +EOF diff --git a/spytest/templates/show_interfaces_transceiver_presence.tmpl b/spytest/templates/show_interfaces_transceiver_presence.tmpl new file mode 100644 index 00000000000..aad8be0ab00 --- /dev/null +++ b/spytest/templates/show_interfaces_transceiver_presence.tmpl @@ -0,0 +1,8 @@ +Value Port (\S+) +Value Presence (Not present|Present) + +Start + ^\s*Port\s+Presence\s+ + ^(-+\s+-+)*$$ + ^\s*${Port}\s+${Presence} -> Record + ^\s*$$ \ No newline at end of file diff --git a/spytest/templates/show_intf_mcast_mode_appdb.tmpl b/spytest/templates/show_intf_mcast_mode_appdb.tmpl new file mode 100644 index 00000000000..ba04f06c879 --- /dev/null +++ b/spytest/templates/show_intf_mcast_mode_appdb.tmpl @@ -0,0 +1,5 @@ +Value mcast_forwarding (\S+) + +Start + ^\s*\d+\S+\s+"mcast_forwarding"\s*$$ -> Continue + ^\s*4\)\s+"${mcast_forwarding}"\s*$$ diff --git a/spytest/templates/show_intf_portchannel.tmpl b/spytest/templates/show_intf_portchannel.tmpl new file mode 100644 index 00000000000..52bd9ee3ba4 --- /dev/null +++ b/spytest/templates/show_intf_portchannel.tmpl @@ -0,0 +1,44 @@ +Value channel_number (\d+) +Value state (up|down) +Value protocol_state (up|down) +Value mode (LACP|NONE) +Value fallback (Enabled|Disabled) +Value mtu (\d+) +Value ip_mtu (\d+) +Value min_links (\d+) +Value LACP_mode (active|inactive) +Value priority (\d+) +Value pc_mac_address (..:..:..:..:..:..) +Value members (.*) +Value is_selected (True|False) +Value actor_port (\d+) +Value actor_mac (..:..:..:..:..:..) +Value partner_port (\d+) +Value partner_mac (..:..:..:..:..:..) +Value pkts (\d+) +Value octets (\d+) +Value multicasts (\d+) +Value broadcasts (\d+) +Value unicasts (\d+) +Value errors (\d+) +Value discards (\d+) + +Start + ^\s*PortChannel\s*${channel_number}\s+is\s+${state},\s+line\s+protocol\s+is\s+${protocol_state},\s+mode\s+${mode}\s*$$ + ^\s*Fallback:\s+${fallback}\s*$$ + ^\s*MTU\s+${mtu}\s+bytes,\s+IP\s+MTU\s+${ip_mtu}\s+bytes\s*$$ + ^\s*Minimum\s+number\s+of\s+links\s+to\s+bring\s+PortChannel\s+up\s+is\s+${min_links}\s*$$ + ^\s*LACP\s+mode\s+${LACP_mode},\s+interval\s+slow,\s+priority\s+${priority},\s+address\s+${pc_mac_address}\s*$$ + ^\s*Members\s+in\s+this\s+channel:\s+${members}\s*$$ + ^\s*selected\s+${is_selected}\s*$$ + ^\s*LACP\s+Actor\s+port\s+${actor_port}\s+address\s+${actor_mac}\s+key\s+\d+\s*$$ + ^\s*LACP\s+Partner\s+port\s+${partner_port}\s+address\s+${partner_mac}\s+key\s+\d+\s*$$ -> Record + ^\s*\S+\s+statistics: -> counters + +counters + ^\s+${pkts}\s+packets,\s+${octets}\s+octets\s*$$ + ^\s+${multicasts}\s+Multicasts,\s+${broadcasts}\s+Broadcasts,\s+${unicasts}\s+Unicasts\s*$$ + ^\s+${errors}\s+error,\s+${discards}\s+discarded\s*$$ -> Record + + + diff --git a/spytest/templates/show_ip_bgp.tmpl b/spytest/templates/show_ip_bgp.tmpl new file mode 100644 index 00000000000..14b9eb6896e --- /dev/null +++ b/spytest/templates/show_ip_bgp.tmpl @@ -0,0 +1,27 @@ +Value Filldown NETWORK ([.:\dabcdefABCDEF]+\/\d+) +Value NEXT_HOP ([.:\dabcdefABCDEF]+) +Value METRIC (\S{1,6}) +Value LOCAL_PREF (\S{0,6}) +Value WEIGHT (\S{1,6}) +Value AS_PATH (.*?) +Value VERSION (\d+) +Value ROUTER_ID (\S{0,19}) +Value STATUS_CODE ([sdhirSR*>=#]*) +Value INTERNAL (i?) + + +Start + ^BGP table version is ${VERSION}, local router ID is ${ROUTER_ID}\s*$$ + ^\s+Network\s+Next Hop\s+Metric\s+LocPrf\s+Weight\s+Path -> Bgp_table + ^Status codes:.*$$ -> Continue + ^\s+i internal.*$$ -> Continue + ^Origin codes:.*$$ -> Continue + +Bgp_table + ^${STATUS_CODE}\s{1,16}${INTERNAL}${NETWORK}\s+${NEXT_HOP}\s*$$ -> Continue + ^\s{20}\s+${METRIC}\s{0,9}${LOCAL_PREF}\s+${WEIGHT}\s+${AS_PATH}\s*$$ -> Record + ^${STATUS_CODE}\s{1,16}${INTERNAL}${NETWORK}\s+${NEXT_HOP}\s{1,18}${METRIC}\s{0,9}${LOCAL_PREF}\s+${WEIGHT}\s+${AS_PATH}\s*$$ -> Record + ^${STATUS_CODE}\s{1,16}${INTERNAL}\s+${NEXT_HOP}\s+${METRIC}\s{0,9}${LOCAL_PREF}\s+${WEIGHT}\s+${AS_PATH}\s*$$ -> Record + ^${STATUS_CODE}\s+${INTERNAL}${NETWORK}\s+${NEXT_HOP}\s+\s+${WEIGHT}\s+${AS_PATH}\s*$$ -> Record + +EOF diff --git a/spytest/templates/show_ip_bgp_summary.tmpl b/spytest/templates/show_ip_bgp_summary.tmpl new file mode 100644 index 00000000000..de406f67079 --- /dev/null +++ b/spytest/templates/show_ip_bgp_summary.tmpl @@ -0,0 +1,50 @@ +Value NEIGHBOR (\S+) +Value VERSION (\d+) +Value ASN (\d+|\s+) +Value MSGRCVD (\d+) +Value MSGSENT (\d+) +Value TBLVER (\d+) +Value INQ (\d+) +Value OUTQ (\d+) +Value UPDOWN (\S+|\d+\-\d+\-\d+\s+\d+\:\d+\:\d+|\d+\:\d+\:\d+) +Value STATE (\S+) +Value Filldown ROUTERID (\S+) +Value Filldown LOCALASNNUMBER (\d+) +Value Filldown TABLEVERSION (\d+) +Value Filldown RIBENTRIES (\d+) +Value Filldown RIBMEMORYINBYTES (\d+) +Value Filldown PEERS (\d+) +Value Filldown PEERSMEMORYINKBYTES (\d+) +Value Filldown VRFID (\d+) +Value Filldown TOTALENTRIES (\d+) +Value DYNNBR (\d+) +Value DYNLIMIT (\d+) + +Start + ^\s*IPv\d{1} Unicast Summary:.*$$ -> VTYSH + ^\s*BGP router identifier ${ROUTERID}, local AS number ${LOCALASNNUMBER}\s*$$ -> KLISH + +VTYSH + ^BGP router identifier ${ROUTERID}, local AS number ${LOCALASNNUMBER} vrf-id ${VRFID}\s*$$ + ^BGP table version ${VERSION}\s*$$ + ^RIB entries ${RIBENTRIES}, using ${RIBMEMORYINBYTES} (\S+) of memory\s*$$ + ^Peers ${PEERS}, using ${PEERSMEMORYINKBYTES} KiB of memory\s*$$ + ^(\s*)*$$ -> Continue + ^\s+Neighbor\s+V\s+AS\s+MsgRcvd\s+MsgSent\s+TblVer\s+InQ\s+OutQ\s+Up\/Down\s+State\/PfxRcd\s+ -> Continue + ^\s*${NEIGHBOR}\s+${VERSION}\s+${ASN}\s+${MSGRCVD}\s+${MSGSENT}\s+${TBLVER}\s+${INQ}\s+${OUTQ}\s+${UPDOWN}\s+${STATE}\s*$$ -> Record + ^(\s*)*$$ -> Continue + ^Total number of neighbors ${TOTALENTRIES} + ^${DYNNBR} dynamic neighbor\(s\), limit ${DYNLIMIT}$$ -> Record + +KLISH + ^\s*BGP router identifier ${ROUTERID}, local AS number ${LOCALASNNUMBER}\s*$$ + ^\s*Neighbor\s+V\s+AS\s+MsgRcvd\s+MsgSent\s+TblVer\s+InQ\s+OutQ\s+Up\/Down\s+State\/PfxRcd\s+ -> Continue + ^\s*${NEIGHBOR}\s+${VERSION}\s+${ASN}\s+${MSGRCVD}\s+${MSGSENT}\s+${TBLVER}\s+${INQ}\s+${OUTQ}\s+${UPDOWN}\s+${STATE}\s*$$ -> Record + ^(\s*)*$$ -> Continue + ^\s*Total number of neighbors ${TOTALENTRIES}\s*$$ -> Record + +EOF + + + + diff --git a/spytest/templates/show_ip_dhcp_relay_brief.tmpl b/spytest/templates/show_ip_dhcp_relay_brief.tmpl new file mode 100644 index 00000000000..e81f27bd604 --- /dev/null +++ b/spytest/templates/show_ip_dhcp_relay_brief.tmpl @@ -0,0 +1,10 @@ +Value Filldown intf (Ethernet\d+|Vlan\d+|PortChannel\d+|Loopback\d+) +Value dhcprelay_addr (\S+) + +Start + ^\s*${intf}\s+${dhcprelay_addr}\s* -> Record + ^\|\s+${intf}\s+\|\s+(${dhcprelay_addr}|.)\s+\| -> Record + ^\|\s+\|\s+(${dhcprelay_addr}|.)\s+\| -> Record + ^\+\S+ + +EOF \ No newline at end of file diff --git a/spytest/templates/show_ip_dhcp_relay_detailed.tmpl b/spytest/templates/show_ip_dhcp_relay_detailed.tmpl new file mode 100644 index 00000000000..4a94c2b429c --- /dev/null +++ b/spytest/templates/show_ip_dhcp_relay_detailed.tmpl @@ -0,0 +1,12 @@ +Value Filldown server_addr (^$|\S+|\S+, \S+|\S+, \S+, \S+|\S+, \S+, \S+, \S+) +Value Filldown src_interface (\S+) +Value Filldown link_select (\S+) +Value Filldown max_hop_count (\d+) + +Start + ^\s*Server Address:\s*${server_addr}\s*$$ -> Record + ^\s*Source Interface:\s*${src_interface}\s*$$ -> Record + ^\s*Link Select:\s*${link_select}\s*$$ -> Record + ^\s*Max Hop Count:\s*${max_hop_count}\s*$$ -> Record + +EOF \ No newline at end of file diff --git a/spytest/templates/show_ip_dhcp_relay_statistics.tmpl b/spytest/templates/show_ip_dhcp_relay_statistics.tmpl new file mode 100644 index 00000000000..4daaa083f96 --- /dev/null +++ b/spytest/templates/show_ip_dhcp_relay_statistics.tmpl @@ -0,0 +1,61 @@ +Value Packets_relayed_from_client_to_server (\S+) +Value Packets_relayed_from_server_to_client (\S+) +Value Errors_sending_packets_to_clients (\S+) +Value Errors_sending_packets_to_servers (\S+) +Value Packets_dropped_with_bogus_GIADDR (\S+) +Value Packets_dropped_due_to_bad_relay_info (\S+) +Value Packets_dropped_due_to_missing_relay_info (\S+) +Value Packets_dropped_due_to_invalid_hdr_length (\S+) +Value Packets_dropped_on_interface_with_no_IP (\S+) +Value Replies_dropped_on_downstream_interface (\S+) +Value Requests_dropped_on_upstream_interface (\S+) +Value Packets_dropped_due_to_invalid_drop_opcode (\S+) +Value Packets_dropped_due_to_invalid_options (\S+) +Value Packets_dropped_on_exceeding_the_max_hop_count (\S+) +Value Total_number_of_DHCPV4_packets_dropped (\S+) +Value BOOTP_packets_received_from_client (\S+) +Value DHCPv4_DISCOVER_packets_received_from_client (\S+) +Value DHCPv4_REQUEST_packets_received_from_client (\S+) +Value DHCPv4_INFORM_packets_received_from_client (\S+) +Value DHCPv4_RELEASE_packets_received_from_client (\S+) +Value DHCPv4_DECLINE_packets_received_from_client (\S+) +Value DHCPv4_OFFER_packets_received_from_server (\S+) +Value DHCPv4_ACK_packets_received_from_server (\S+) +Value DHCPv4_NACK_packets_received_from_server (\S+) +Value DHCPv4_OFFER_packets_relayed_to_client (\S+) +Value DHCPv4_ACK_packets_relayed_to_client (\S+) +Value DHCPv4_NACK_packets_relayed_to_client (\S+) +Value relay_interface (\S+) + + +Start + ^\s*Relay\s+Interface:\s+${relay_interface} + ^\s*Packets\s+relayed\s+from\s+client\s+to\s+server\:+\s+${Packets_relayed_from_client_to_server}$$ + ^\s*Packets\s+relayed\s+from\s+server\s+to\s+client\:+\s+${Packets_relayed_from_server_to_client}$$ + ^\s*Errors\s+(sending|relaying)\s+packets\s+(to|from)\s+clients\:+\s+${Errors_sending_packets_to_clients}$$ + ^\s*Errors\s+(sending|relaying)\s+packets\s+(to|from)\s+servers\:+\s+${Errors_sending_packets_to_servers}$$ + ^\s*Packets\s+dropped\s+with\s+bogus\s+GIADDR\:+\s+${Packets_dropped_with_bogus_GIADDR}$$ + ^\s*Packets\s+dropped\s+due\s+to\s+bad\s+relay\s+info\:+\s+${Packets_dropped_due_to_bad_relay_info}$$ + ^\s*Packets\s+dropped\s+due\s+to\s+missing\s+relay\s+info\:+\s+${Packets_dropped_due_to_missing_relay_info}$$ + ^\s*Packets\s+dropped\s+due\s+to\s+invalid\s+hdr\s+length\:+\s+${Packets_dropped_due_to_invalid_hdr_length}$$ + ^\s*Packets\s+dropped\s+on\s+interface\s+with\s+no\s+IP\:+\s+${Packets_dropped_on_interface_with_no_IP}$$ + ^\s*Replies\s+dropped\s+on\s+downstream\s+interface\:+\s+${Replies_dropped_on_downstream_interface}$$ + ^\s*Requests\s+dropped\s+on\s+upstream\s+interface\:+\s+${Requests_dropped_on_upstream_interface}$$ + ^\s*Packets\s+dropped\s+due\s+to\s+invalid\s+opcode:\s+${Packets_dropped_due_to_invalid_drop_opcode}$$ + ^\s*Packets\s+dropped\s+due\s+to\s+invalid\s+options:\s+${Packets_dropped_due_to_invalid_options}$$ + ^\s*Packets\s+dropped\s+on\s+exceeding\s+the\s+max\s+hop\s+count:\s+${Packets_dropped_on_exceeding_the_max_hop_count}$$ + ^\s*Total\s+number\s+of\s+DHCPv4\s+packets\s+dropped:\s+${Total_number_of_DHCPV4_packets_dropped}$$ + ^\s*BOOTP\s+packets\s+received\s+from\s+client:\s+${BOOTP_packets_received_from_client}$$ + ^\s*DHCPv4\s+DISCOVER\s+packets\s+received\s+from\s+client:\s+${DHCPv4_DISCOVER_packets_received_from_client}$$ + ^\s*DHCPv4\s+REQUEST\s+packets\s+received\s+from\s+client:\s+${DHCPv4_REQUEST_packets_received_from_client}$$ + ^\s*DHCPv4\s+INFORM\s+packets\s+received\s+from\s+client:\s+${DHCPv4_INFORM_packets_received_from_client}$$ + ^\s*DHCPv4\s+RELEASE\s+packets\s+received\s+from\s+client:\s+${DHCPv4_RELEASE_packets_received_from_client}$$ + ^\s*DHCPv4\s+DECLINE\s+packets\s+received\s+from\s+client:\s+${DHCPv4_DECLINE_packets_received_from_client}$$ + ^\s*DHCPv4\s+OFFER\s+packets\s+received\s+from\s+server:\s+${DHCPv4_OFFER_packets_received_from_server}$$ + ^\s*DHCPv4\s+ACK\s+packets\s+received\s+from\s+server:\s+${DHCPv4_ACK_packets_received_from_server}$$ + ^\s*DHCPv4\s+NACK\s+packets\s+received\s+from\s+server:\s+${DHCPv4_NACK_packets_received_from_server}$$ + ^\s*DHCPv4\s+OFFER\s+packets\s+relayed\s+to\s+client:\s+${DHCPv4_OFFER_packets_relayed_to_client}$$ + ^\s*DHCPv4\s+ACK\s+packets\s+relayed\s+to\s+client:\s+${DHCPv4_ACK_packets_relayed_to_client}$$ + ^\s*DHCPv4\s+NACK\s+packets\s+relayed\s+to\s+client:\s+${DHCPv4_NACK_packets_relayed_to_client}$$ -> Record + +EOF \ No newline at end of file diff --git a/spytest/templates/show_ip_forward_protocol_config.tmpl b/spytest/templates/show_ip_forward_protocol_config.tmpl new file mode 100644 index 00000000000..39a41634107 --- /dev/null +++ b/spytest/templates/show_ip_forward_protocol_config.tmpl @@ -0,0 +1,11 @@ +Value forwarding (\S+) +Value rate_limit (\d+) +Value enable_ports ([\S+ ,]+|\S+) +Value disable_ports ([\S+ ,]+|\S+) + + +Start + ^\s*UDP\s+forwarding\s+:\s*${forwarding}\s*$$ + ^\s*UDP\s+rate\s+limit\s*:\s+${rate_limit}\s*pps\s*$$ + ^\s*UDP forwarding enabled on the ports:\s+${enable_ports}\s*$$ + ^\s*UDP forwarding disabled on the ports:\s+${disable_ports}\s*$$ diff --git a/spytest/templates/show_ip_helper_address_config.tmpl b/spytest/templates/show_ip_helper_address_config.tmpl new file mode 100644 index 00000000000..8ebbb4bcf33 --- /dev/null +++ b/spytest/templates/show_ip_helper_address_config.tmpl @@ -0,0 +1,12 @@ +Value Filldown interface (Ethernet\d+|PortChannel\d+|Vlan\d+) +Value vrf (\S+) +Value relay_address (\d+\.\d+\.\d+\.\d+) + + +Start + ^\s*${interface}\s+${vrf}\s+${relay_address}\s*$$ -> Record + ^\s*${interface}\s+${relay_address}\s*$$ -> Record + ^\s*${vrf}\s+${relay_address}\s*$$ -> Record + ^\s*${relay_address}\s*$$ -> Record + +EOF \ No newline at end of file diff --git a/spytest/templates/show_ip_helper_address_statistics.tmpl b/spytest/templates/show_ip_helper_address_statistics.tmpl new file mode 100644 index 00000000000..9ae1cfe929c --- /dev/null +++ b/spytest/templates/show_ip_helper_address_statistics.tmpl @@ -0,0 +1,16 @@ +Value packets_received (\d+) +Value packets_relayed (\d+) +Value packets_dropped (\d+) +Value invalid_ttl_packets (\d+) +Value all_ones_broadcast_packets_received (\d+) +Value net_directed_broadcast_packets_received (\d+) + + + +Start + ^\s*Packets\s+received\s+:\s+${packets_received}\s*$$ + ^\s*Packets\s+relayed\s+:\s+${packets_relayed}\s*$$ + ^\s*Packets\s+dropped\s+:\s+${packets_dropped}\s*$$ + ^\s*Invalid\s+TTL\s+packets\s+:\s+${invalid_ttl_packets}\s*$$ + ^\s*All\s+ones\s+broadcast\s+packets\s+received\s+:\s+${all_ones_broadcast_packets_received}\s*$$ + ^\s*Net\s+directed\s+broadcast\s+packets\s+received\s+:\s+${net_directed_broadcast_packets_received}\s*$$ diff --git a/spytest/templates/show_ip_igmp_group_retransmission.tmpl b/spytest/templates/show_ip_igmp_group_retransmission.tmpl new file mode 100644 index 00000000000..41c57e16669 --- /dev/null +++ b/spytest/templates/show_ip_igmp_group_retransmission.tmpl @@ -0,0 +1,14 @@ +######################################################### +#sonic# show ip igmp groups retransmissions +#Interface Address Group RetTimer Counter RetSrcs +#Ethernet46 20.20.20.1 231.1.1.1 --:-- 0 0 +######################################################## +Value interface (\w+) +Value address ([\w\.]+) +Value group ([\w\.]+) +Value ret_timer ([\d\:\-]*) +Value counter (\d+) +Value ret_sources (\d+) + +Start + ^\s*${interface}\s*${address}\s*${group}\s*${ret_timer}\s*${counter}\s*${ret_sources}$$ -> Record \ No newline at end of file diff --git a/spytest/templates/show_ip_igmp_groups.tmpl b/spytest/templates/show_ip_igmp_groups.tmpl new file mode 100644 index 00000000000..94c90afd311 --- /dev/null +++ b/spytest/templates/show_ip_igmp_groups.tmpl @@ -0,0 +1,17 @@ +################################################################## +#sonic# show ip igmp groups +#Interface Address Group Mode Timer Srcs V Uptime +#Ethernet46 20.20.20.1 231.1.1.1 INCL --:--:-- 1 3 00:00:05 +#sonic# +################################################################# +Value interface (\w+) +Value address ([\w\.]+) +Value group ([\w\.]+) +Value mode (\w+|\-+) +Value timer ([\d\:\-]*) +Value source_count (\d+) +Value version (\w+) +Value uptime ([\d\:]+) + +Start + ^\s*${interface}\s*${address}\s*${group}\s*${mode}\s*${timer}\s*${source_count}\s*${version}\s*${uptime}$$ -> Record diff --git a/spytest/templates/show_ip_igmp_interface.tmpl b/spytest/templates/show_ip_igmp_interface.tmpl new file mode 100644 index 00000000000..3ad37b4fa94 --- /dev/null +++ b/spytest/templates/show_ip_igmp_interface.tmpl @@ -0,0 +1,92 @@ +################################################## +#sonic# show ip igmp interface Ethernet46 +#Interface : Ethernet46 +#State : up +#Address : 20.20.20.1 +#Uptime : 83:19:17 +#Version : 3 +# +# +#Querier +#------- +#Querier : local +#Start Count : 0 +#Query Timer : 00:01:32 +#Other Timer : --:--:-- +# +# +#Timers +#------ +#Group Membership Interval : 260s +#Last Member Query Count : 4 +#Last Member Query Time : 20s +#Older Host Present Interval : 260s +#Other Querier Present Interval : 255s +#Query Interval : 125s +#Query Response Interval : 10s +#Robustness Variable : 2 +#Startup Query Interval : 31s +# +# +#Flags +#----- +#All Multicast : no +#Broadcast : yes +#Deleted : no +#Interface Index : 479 +#Multicast : yes +#Multicast Loop : 0 +#Promiscuous : no +################################################## +Value interface (\w+) +Value state (\w+) +Value address ([\d\.]+) +Value uptime ([\d\:\-]*) +Value version (\d+) +Value querier (\w+) +Value start_count (\d+) +Value query_timer ([\d\:]+) +Value other_timer ([\d\:\-]*) +Value gmi (\d+) +Value last_member_query_count (\d+) +Value last_member_query_time (\d+) +Value old_host_present_interval (\d+) +Value other_querier_present_interval (\d+) +Value query_interval (\d+) +Value query_response_interval (\d+) +Value robustness (\d+) +Value startup_query_interval (\d+) +Value all_multicast (\w+) +Value broadcast (\w+) +Value deleted (\w+) +Value ifindex (\w+) +Value multicast (\w+) +Value multicast_loop (\d+) +Value promiscuous (\w+) + +Start + ^\s*Interface\s*:\s*${interface}$$ + ^\s*State\s*:\s*${state}$$ + ^\s*Address\s*:\s*${address}$$ + ^\s*Uptime\s*:\s*${uptime}$$ + ^\s*Version\s*:\s*${version}$$ + ^\s*Querier\s*:\s*${querier}$$ + ^\s*Start\s*Count\s*:\s*${start_count}$$ + ^\s*Query\s*Timer\s*:\s*${query_timer}$$ + ^\s*Other\s*Timer\s*:\s*${other_timer}$$ + ^\s*Group\s*Membership\s*Interval\s*:\s*${gmi}s$$ + ^\s*Last\s*Member\s*Query\s*Count\s*:\s*${last_member_query_count}$$ + ^\s*Last\s*Member\s*Query\s*Time\s*:\s*${last_member_query_time}s$$ + ^\s*Older\s*Host\s*Present\s*Interval\s*:\s*${old_host_present_interval}s$$ + ^\s*Other\s*Querier\s*Present\s*Interval\s*:\s*${other_querier_present_interval}s$$ + ^\s*Query\s*Interval\s*:\s*${query_interval}s$$ + ^\s*Query\s*Response\s*Interval\s*:\s*${query_response_interval}s$$ + ^\s*Robustness\s*Variable\s*:\s*${robustness}$$ + ^\s*Startup\s*Query\s*Interval\s*:\s*${startup_query_interval}s$$ + ^\s*All\s*Multicast\s*:\s*${all_multicast}$$ + ^\s*Broadcast\s*:\s*${broadcast}$$ + ^\s*Deleted\s*:\s*${deleted}$$ + ^\s*Interface\s*Index\s*:\s*${ifindex}$$ + ^\s*Multicast\s*:\s*${multicast}$$ + ^\s*Multicast\s*Loop\s*:\s*${multicast_loop}$$ + ^\s*Promiscuous\s*:\s*${promiscuous}$$ -> Record \ No newline at end of file diff --git a/spytest/templates/show_ip_igmp_join.tmpl b/spytest/templates/show_ip_igmp_join.tmpl new file mode 100644 index 00000000000..3c3c8232c3f --- /dev/null +++ b/spytest/templates/show_ip_igmp_join.tmpl @@ -0,0 +1,15 @@ +####################################################### +#sonic# show ip igmp join +#Interface Address Source Group Socket Uptime +#Ethernet46 20.20.20.1 10.10.10.2 231.1.1.1 20 00:00:28 +#sonic# +###################################################### +Value interface (\w+) +Value address ([\w\.]+) +Value source ([\w\.]+) +Value group ([\w\.]+) +Value socket (\d+) +Value uptime ([\d\:]+) + +Start + ^\s*${interface}\s*${address}\s*${source}\s*${group}\s*${socket}\s*${uptime}$$ -> Record diff --git a/spytest/templates/show_ip_igmp_snooping.tmpl b/spytest/templates/show_ip_igmp_snooping.tmpl new file mode 100644 index 00000000000..c0c3ccdbfb5 --- /dev/null +++ b/spytest/templates/show_ip_igmp_snooping.tmpl @@ -0,0 +1,18 @@ +Value vlan (\d+) +Value mrouter_interface (.*) +Value querier (\S+) +Value igmp_operation_mode (\S+) +Value fast_leave (\S+) +Value query_max_response_time (\d+) +Value last_member_query_interval (\d+) +Value query_interval (\d+) + +Start + ^\s*Vlan ID:\s+${vlan} + ^\s*Multicast\s+Router\s+ports:\s+${mrouter_interface} + ^\s*Querier\s+-\s+${querier} + ^\s*IGMP\s+Operation\s+mode:\s+${igmp_operation_mode} + ^\s*Is Fast-Leave\s+Enabled\s+:\s+${fast_leave} + ^\s*Max\s+Response\s+time\s+=\s+${query_max_response_time} + ^\s*Last\s+Member\s+Query\s+Interval\s+=\s+${last_member_query_interval} + ^\s*Query\s+interval\s+=\s+${query_interval} \ No newline at end of file diff --git a/spytest/templates/show_ip_igmp_snooping_groups.tmpl b/spytest/templates/show_ip_igmp_snooping_groups.tmpl new file mode 100644 index 00000000000..786628c9b06 --- /dev/null +++ b/spytest/templates/show_ip_igmp_snooping_groups.tmpl @@ -0,0 +1,14 @@ +Value Filldown vlan (\d+) +Value source_address (\*|\S+) +Value group_address (\S+) +Value outgoing_ports (.*) +Value Fillup number_of_entries (\d+) + +Start + ^\s*Vlan\s*ID\s*:\s*${vlan} + ^(-+\s*)*$$ + ^\s*\d+\s*\(${source_address}\s*,\s*${group_address}\s*\) + ^\s*Outgoing\s*Ports:\s*${outgoing_ports} -> Record + ^\s*Total number of entries:\s*${number_of_entries} -> Record + +EOF \ No newline at end of file diff --git a/spytest/templates/show_ip_igmp_source_retransmission.tmpl b/spytest/templates/show_ip_igmp_source_retransmission.tmpl new file mode 100644 index 00000000000..e27fbc6a33e --- /dev/null +++ b/spytest/templates/show_ip_igmp_source_retransmission.tmpl @@ -0,0 +1,13 @@ +######################################################### +#sonic# show ip igmp source retransmissions +#Interface Address Group Source Counter +#Ethernet46 20.20.20.1 231.1.1.1 10.10.10.2 0 +########################################################## +Value interface (\w+) +Value address ([\w\.]+) +Value group ([\w\.]+) +Value source ([\w\.]+) +Value counter (\d+) + +Start + ^\s*${interface}\s*${address}\s*${group}\s*${source}\s*${counter}$$ -> Record \ No newline at end of file diff --git a/spytest/templates/show_ip_igmp_sources.tmpl b/spytest/templates/show_ip_igmp_sources.tmpl new file mode 100644 index 00000000000..72ae4e982f5 --- /dev/null +++ b/spytest/templates/show_ip_igmp_sources.tmpl @@ -0,0 +1,16 @@ +####################################################### +#sonic# show ip igmp sources +#Interface Address Group Source Timer Fwd Uptime +#Ethernet46 20.20.20.1 231.1.1.1 10.10.10.2 04:01 Y 00:00:19 +#sonic# +###################################################### +Value interface (\w+) +Value address ([\w\.]+) +Value source ([\w\.]+|\*) +Value group ([\w\.]+) +Value timer ([\d\:\-]+) +Value fwd (\w+) +Value uptime ([\d\:]+) + +Start + ^\s*${interface}\s*${address}\s*${group}\s*${source}\s*${timer}\s*${fwd}\s*${uptime}$$ -> Record diff --git a/spytest/templates/show_ip_igmp_statistics.tmpl b/spytest/templates/show_ip_igmp_statistics.tmpl new file mode 100644 index 00000000000..c882ce77856 --- /dev/null +++ b/spytest/templates/show_ip_igmp_statistics.tmpl @@ -0,0 +1,39 @@ +######################################## +#sonic# show ip igmp statistics +#IGMP RX statistics +#Interface : global +#V1 query : 0 +#V2 query : 0 +#V3 query : 2401 +#V2 leave : 0 +#V1 report : 0 +#V2 report : 0 +#V3 report : 2404 +#mtrace response : 0 +#mtrace request : 0 +#unsupported : 0 +######################################## +Value interface (\w+) +Value query_v1 (\d+) +Value query_v2 (\d+) +Value query_v3 (\d+) +Value leave_v2 (\d+) +Value report_v1 (\d+) +Value report_v2 (\d+) +Value report_v3 (\d+) +Value mtrace_response (\d+) +Value mtrace_request (\d+) +Value unsupported (\d+) + +Start + ^\s*Interface\s*:\s*${interface}$$ + ^\s*V1\s*query\s*:\s*${query_v1}$$ + ^\s*V2\s*query\s*:\s*${query_v2}$$ + ^\s*V3\s*query\s*:\s*${query_v3}$$ + ^\s*V2\s*leave\s*:\s*${leave_v2}$$ + ^\s*V1\s*report\s*:\s*${report_v1}$$ + ^\s*V2\s*report\s*:\s*${report_v2}$$ + ^\s*V3\s*report\s*:\s*${report_v3}$$ + ^\s*mtrace\s*response\s*:\s*${mtrace_response}$$ + ^\s*mtrace\s*request\s*:\s*${mtrace_request}$$ + ^\s*unsupported\s*:\s*${unsupported}$$ -> Record \ No newline at end of file diff --git a/spytest/templates/show_ip_interfaces.tmpl b/spytest/templates/show_ip_interfaces.tmpl new file mode 100644 index 00000000000..71ae66c1ec3 --- /dev/null +++ b/spytest/templates/show_ip_interfaces.tmpl @@ -0,0 +1,34 @@ +Value Filldown INTERFACE (\w+) +Value Filldown IPADDR (\S+) +Value VRF (\S+) +Value Filldown STATUS (\S+) +Value Filldown Neighbor (\S+) +Value Filldown NeighborIP (\S+) +Value Flags (U) + +Start + ^\s*Interface\s+IPv(4|6) address/mask\s+Master\s+Admin/Oper\s+Flags\s*$$ -> NOBGPINFO + ^\s*Interface\s+IPv(4|6) address/mask\s+Master\s+Admin/Oper\s*$$ -> NOBGPINFO + ^\s*Interface\s+IPv(4|6)\s+address/mask\s+Master\s+Admin/Oper\s+BGP\s+Neighbor\s+Neighbor\s+IP\s+Flags\s*$$ -> BGPINFO + ^\s*Interface\s+IPv(4|6)\s+address/mask\s+Master\s+Admin/Oper\s+BGP\s+Neighbor\s+Neighbor\s+IP\s*$$ -> BGPINFO + ^\s*Interface\s+Master\s+IPv(4|6)\s+address/mask\s+Admin/Oper\s+BGP\s+Neighbor\s+Neighbor\s+IP\s*$$ -> BGPINFO + ^(-+\s*)*$$ + +NOBGPINFO + ^\s*${INTERFACE}\s+${IPADDR}\s+${VRF}\s+${STATUS}\s+${Flags}\s*$$ -> Record + ^\s*${INTERFACE}\s+${IPADDR}\s+${STATUS}\s+${Flags}\s*$$ -> Record + ^\s*${INTERFACE}\s+${IPADDR}\s+${VRF}\s+${STATUS}\s*$$ -> Record + ^\s*${INTERFACE}\s+${IPADDR}\s+\s+${STATUS}\s*$$ -> Record + ^\s*${IPADDR}\s*$$ -> Record + ^$$ + +BGPINFO + ^\s*${INTERFACE}\s+${IPADDR}\s+${VRF}\s+${STATUS}\s+${Neighbor}\s+${NeighborIP}\s+${Flags}\s*$$ -> Record + ^\s*${INTERFACE}\s+${IPADDR}\s+${STATUS}\s+${Neighbor}\s+${NeighborIP}\s*$$ -> Record + ^\s*${INTERFACE}\s+${IPADDR}\s+${STATUS}\s+${Neighbor}\s+${NeighborIP}\s+${Flags}\s*$$ -> Record + ^\s*${INTERFACE}\s+${IPADDR}\s+${VRF}\s+${STATUS}\s+${Neighbor}\s+${NeighborIP}\s*$$ -> Record + ^\s*${INTERFACE}\s+${IPADDR}\s+\s+${STATUS}\s+${Neighbor}\s+${NeighborIP}\s*$$ -> Record + ^\s*${IPADDR}\s*$$ -> Record + ^$$ + +EOF diff --git a/spytest/templates/show_ip_mroute.tmpl b/spytest/templates/show_ip_mroute.tmpl new file mode 100644 index 00000000000..9112d76a71c --- /dev/null +++ b/spytest/templates/show_ip_mroute.tmpl @@ -0,0 +1,23 @@ +################################################################################## +#Source Group Proto Input Output TTL Uptime +#90.0.0.2 232.1.1.1 PIM Ethernet46 Ethernet12 1 00:00:13 +# PIM Vlan101 1 00:00:13 +#90.0.0.2 232.1.1.2 PIM Ethernet46 Ethernet12 1 00:00:11 +# PIM Vlan101 1 00:00:11 +################################################################################### +Value installed ([\*]*) +Value Filldown source ([\d\.]+|\*) +Value Filldown group ([\w\.]+) +Value Filldown iif (\w+) +Value proto (\w+) +Value oif (\w+) +Value ttl (\d+) +Value uptime ([\d\:\-]+) +Value vrf (\w+) + +Start + ^\s+${source}\s*${group}\s*\<*${proto}\>*\s*\<*\s*${iif}\s*\>*\s*\<*\s*${oif}\s*\>*\s*${ttl}\s*${uptime}.*$$ -> Record + ^\s*${installed}\s*${source}\s*${group}\s*\<*${proto}\>*\s*\<*\s*${iif}\s*\>*\s*\<*\s*${oif}\s*\>*\s*${ttl}\s*${uptime}.*$$ -> Record + ^\s*${installed}\s+${proto}\s+${oif}\s*${ttl}\s*${uptime}$$ -> Record + +EOF diff --git a/spytest/templates/show_ip_multicast.tmpl b/spytest/templates/show_ip_multicast.tmpl new file mode 100644 index 00000000000..02fe32cad8b --- /dev/null +++ b/spytest/templates/show_ip_multicast.tmpl @@ -0,0 +1,130 @@ +#################################################### +#VRF: default +#Router MLAG Role: NONE +#Mroute socket descriptor: 7(default) +#Mroute socket uptime: 00:34:04 +# +#Zclient update socket: 11 failures=0 +#Zclient lookup socket: 12 failures=0 +# +#Maximum highest VifIndex: 31 +# +#Total Dynamic Multicast routes in VRF default: 0 +#Total Dynamic Uninstalled Multicast routes in VRF default: 0 +#Total Static Multicast routes in VRF default: 0 +#Total Static Uninstalled Multicast routes in VRF default: 0 +#Total Uninstalled Multicast routes in VRF default: 0 +#Total Multicast routes in VRF default: 0 +# +#Total Dynamic Multicast routes across all VRFs: 0 +#Total Dynamic Uninstalled Multicast routes across all VRFs: 0 +#Total Static Multicast routes across all VRFs: 0 +#Total Static Uninstalled Multicast routes across all VRFs: 0 +#Total Uninstalled Multicast routes across all VRFs: 0 +#Total Multicast routes across all VRFs: 0 +# +#Upstream Join Timer: 60 secs +#Join/Prune Holdtime: 210 secs +#PIM ECMP: Disable +#PIM ECMP Rebalance: Disable +# +#RPF Cache Refresh Delay: 50 msecs +#RPF Cache Refresh Timer: 0 msecs +#RPF Cache Refresh Requests: 12 +#RPF Cache Refresh Events: 3 +#RPF Cache Refresh Last: 00:04:16 +#Nexthop Lookups: 1 +#Nexthop Lookups Avoided: 0 +# +#Scan OIL - Last: 00:04:16 Events: 13 +#MFC Add - Last: 00:28:12 Events: 6 +#MFC Del - Last: 00:24:29 Events: 4 +# +#Interface Address ifi Vif PktsIn PktsOut BytesIn BytesOut +#Ethernet12 14.14.1.1 1029 3 0 0 0 0 +##################################################### +Value vrf (\w+) +Value mlag_role (\w+) +Value mroute_sock_desc (\d+) +Value mroute_sock_uptime ([\d\:]+) +Value zclient_update_sock (\d+) +Value zclient_lookup_sock (\d+) +Value zclient_update_sock_fail (\d+) +Value zclient_lookup_sock_fail (\d+) +Value max_vif_index (\d+) +Value tot_dyn_mcast_routes (\d+) +Value tot_dyn_uninst_mcast_routes (\d+) +Value tot_static_mcast_routes (\d+) +Value tot_static_uninst_mcast_routes (\d+) +Value tot_uninst_mcast_routes (\d+) +Value tot_mcast_routes (\d+) +Value tot_dyn_mcast_routes_ac (\d+) +Value tot_dyn_uninst_mcast_routes_ac (\d+) +Value tot_static_mcast_routes_ac (\d+) +Value tot_static_uninst_mcast_routes_ac (\d+) +Value tot_uninst_mcast_routes_ac (\d+) +Value tot_mcast_routes_ac (\d+) +Value upstream_join_timer (\d+) +Value join_prune_holdtime (\d+) +Value pim_ecmp (\w+) +Value pim_ecmp_rebalance (\w+) +Value rpf_cache_ref_delay (\d+) +Value rpf_cache_ref_timer (\d+) +Value rpf_cache_ref_requests (\d+) +Value rpf_cache_ref_events (\d+) +Value rpf_cache_ref_last ([\d\:]+) +Value nxthop_lookups (\d+) +Value nxthop_lookups_avoided (\d+) +Value scan_oil_last_time ([\d\:]+) +Value mfc_add_last_time ([\d\:]+) +Value mfc_del_last_time ([\d\:]+) +Value scan_oil_last_event (\d+) +Value mfc_add_last_event (\d+) +Value mfc_del_last_event (\d+) +Value interface (\w+) +Value address ([\w\.]+) +Value ifi (\d+) +Value vif (\d+) +Value pkts_in (\d+) +Value pkts_out (\d+) +Value bytes_in (\d+) +Value bytes_out (\d+) + +Start + ^\s*VRF:\s*${vrf}$$ + ^\s*Router MLAG Role:\s*${mlag_role}$$ + ^\s*Mroute socket descriptor:\s*${mroute_sock_desc}(\s*${vrf})$$ + ^\s*Mroute socket uptime:\s*${mroute_sock_uptime}$$ + ^\s*Zclient update socket:\s*${zclient_update_sock} failures=\s*${zclient_update_sock_fail}$$ + ^\s*Zclient lookup socket:\s*${zclient_lookup_sock} failures=\s*${zclient_lookup_sock_fail}$$ + ^\s*Maximum highest VifIndex:\s*${max_vif_index}$$ + ^\s*Total Dynamic Multicast routes in VRF default:\s*${tot_dyn_mcast_routes}$$ + ^\s*Total Dynamic Uninstalled Multicast routes in VRF default:\s*${tot_dyn_uninst_mcast_routes}$$ + ^\s*Total Static Multicast routes in VRF default:\s*${tot_static_mcast_routes}$$ + ^\s*Total Static Uninstalled Multicast routes in VRF default:\s*${tot_static_uninst_mcast_routes}$$ + ^\s*Total Uninstalled Multicast routes in VRF default:\s*${tot_uninst_mcast_routes}$$ + ^\s*Total Multicast routes in VRF default:\s*${tot_mcast_routes}$$ + ^\s*Total Dynamic Multicast routes across all VRFs:\s*${tot_dyn_mcast_routes_ac}$$ + ^\s*Total Dynamic Uninstalled Multicast routes across all VRFs:\s*${tot_dyn_uninst_mcast_routes_ac}$$ + ^\s*Total Static Multicast routes across all VRFs:\s*${tot_static_mcast_routes_ac}$$ + ^\s*Total Static Uninstalled Multicast routes across all VRFs:\s*${tot_static_uninst_mcast_routes_ac}$$ + ^\s*Total Uninstalled Multicast routes across all VRFs:\s*${tot_uninst_mcast_routes_ac}$$ + ^\s*Total Multicast routes across all VRFs:\s*${tot_mcast_routes_ac}$$ + ^\s*Upstream Join Timer:\s*${upstream_join_timer} secs + ^\s*Join/Prune Holdtime:\s*${join_prune_holdtime} secs + ^\s*PIM ECMP:\s*${pim_ecmp}$$ + ^\s*PIM ECMP Rebalance:\s*${pim_ecmp_rebalance}$$ + ^\s*RPF Cache Refresh Delay:\s*${rpf_cache_ref_delay} msecs + ^\s*RPF Cache Refresh Timer:\s*${rpf_cache_ref_timer} msecs + ^\s*RPF Cache Refresh Requests:\s*${rpf_cache_ref_requests}$$ + ^\s*RPF Cache Refresh Events:\s*${rpf_cache_ref_events}$$ + ^\s*RPF Cache Refresh Last:\s*${rpf_cache_ref_last}$$ + ^\s*Nexthop Lookups:\s*${nxthop_lookups}$$ + ^\s*Nexthop Lookups Avoided:\s*${nxthop_lookups_avoided}$$ + ^\s*Scan OIL - Last:\s*${scan_oil_last_time}\s*Events:\s*${scan_oil_last_event}$$ + ^\s*MFC Add - Last\s*${mfc_add_last_time}\s*Events:\s*${mfc_add_last_event}$$ + ^\s*MFC Del - Last:\s*${mfc_del_last_time}\s*Events:\s*${mfc_del_last_event}$$ + ^\s*Interface\s*Address\s*ifi\s*Vif\s*PktsIn\s*PktsOut\s*BytesIn\s*BytesOut -> header + +header + ^\s*${interface}\s*${address}\s*${ifi}\s*${vif}\s*${pkts_in}\s*${pkts_out}\s*${bytes_in}\s*${bytes_out}$$ -> Record diff --git a/spytest/templates/show_ip_ospf_interface.tmpl b/spytest/templates/show_ip_ospf_interface.tmpl new file mode 100644 index 00000000000..42f6ad7cf78 --- /dev/null +++ b/spytest/templates/show_ip_ospf_interface.tmpl @@ -0,0 +1,53 @@ +Value Filldown VRFNAME (\S+) +Value Filldown NAME (\S+) +Value Filldown STATE (\S+) +Value Filldown INDEX (\d+) +Value Filldown MTU (\d+) +Value Filldown BW (\d+) +Value Filldown LINESTATE (\S+) +Value Filldown IPV4 (\S+) +Value Filldown PEERADDR (\S+) +Value Filldown AREA (\S+) +Value Filldown MTUMISSMATCH (\S+) +Value Filldown RTRID (\S+) +Value Filldown NWTYPE (\S+) +Value Filldown COST (\d+) +Value Filldown TXDELAY (\d+) +Value Filldown DR (\S+) +Value Filldown BDR (\S+) +Value Filldown NBDR (\S+) +Value Filldown BDRIFIP (\S+) +Value Filldown OTHER (\S+) +Value Filldown NBRSTATE (\S+) +Value Filldown PRIORITY (\d+) +Value Filldown LSASEQ (\S+) +Value Filldown MCASTMEM (\S+) +Value Filldown HELLOTMR (\d+) +Value Filldown DEADTMR (\d+) +Value Filldown WAITTMR (\d+) +Value Filldown RTXTTMR (\d+) +Value Filldown HELLODUE (\S+) +Value Filldown PASSIVE (\S+) +Value NBRCNT (\d+) +Value ADJCNT (\d+) + +Start + ^VRF Name:\s+${VRFNAME}\s*$$ + ^${NAME}\s+is\s+${STATE}\s*$$ + ^\s+ifindex\s+${INDEX},\s+MTU\s+${MTU}\s+bytes, BW\s+${BW}\s+Mbit\s+${LINESTATE}\s*$$ + ^\s+Internet Address\s+${IPV4}, Area\s+${AREA}\s*$$ + ^\s+Internet Address\s+${IPV4}, Peer\s+${PEERADDR}, Area\s+${AREA}\s*$$ + ^\s+MTU mismatch detection:\s+${MTUMISSMATCH}\s*$$ + ^\s+Router ID\s+${RTRID}, Network Type\s+${NWTYPE}, Cost:\s+${COST}\s*$$ + ^\s+Transmit Delay is\s+${TXDELAY}\s+sec, State\s+${NBRSTATE}, Priority\s+${PRIORITY}\s*$$ + ^\s+Backup Designated Router \(ID\)\s+${BDR}, Interface Address\s+${BDRIFIP}\s*$$ + ^\s+No\s+${NBDR}\s+designated router on this network\s*$$ + ^\s+Saved Network-LSA sequence number 0x${LSASEQ}\s*$$ + ^\s+Multicast group memberships:\s+${MCASTMEM}\s*$$ + ^\s+Timer intervals configured, Hello\s+${HELLOTMR}s, Dead\s+${DEADTMR}s, Wait\s+${WAITTMR}s, Retransmit\s+${RTXTTMR}\s*$$ + ^\s+Hello due in\s+${HELLODUE}s\s*$$ + ^\s+No Hellos \(${PASSIVE}\s+interface\)\s*$$ + ^\s+Neighbor Count is\s+${NBRCNT}, Adjacent neighbor count is\s+${ADJCNT}\s*$$ -> Record + +EOF + diff --git a/spytest/templates/show_ip_ospf_lsdb.tmpl b/spytest/templates/show_ip_ospf_lsdb.tmpl new file mode 100644 index 00000000000..b8338970f69 --- /dev/null +++ b/spytest/templates/show_ip_ospf_lsdb.tmpl @@ -0,0 +1,230 @@ +# Author: naveen.suvarna@broadcom.com +Value Filldown VRFNAME (\S+) +Value Filldown ROUTERID (\S+) +Value Filldown AREAID (\S+) +Value Filldown LSTYPE (\S+) +Value Filldown LSAGE (\S+) +Value Filldown LSOPTIONS (\S+) +Value Filldown LSFLAGS (\S+) +Value Filldown STATEFLG (\S+) +Value Filldown LINKSTATEID (\S+) +Value Filldown ADVROUTER (\S+) +Value Filldown LSSEQNUM (\S+) +Value Filldown LSCHECKSUM (\S+) +Value Filldown LSLENGTH (\S+) +Value Filldown STATE (\S+\s+\S+) +Value LINKCOUNT (\d+) +Value TOSCOUNT (\S+) +Value LINKTYPE (\S+) +Value LINKID (\S+) +Value LINKDATA (\S+) +Value NWMASK (\S+) +Value ATTACHEDROUTER (\S+) +Value TOS (\d+) +Value TOSMETRIC (\d+) +Value METRICTYPE (\S+) +Value METRICVALUE (\S+) +Value FWDADDR (\S+) +Value ROUTETAG (\S+) + + +Start + ^\s*$$ -> Next + ^\s*VRF Name:\s+${VRFNAME}\s*$$ + ^\s*$$ -> Next + ^\s*OSPF Router with ID\s*\(${ROUTERID}\)\s*$$ -> Routerid_record + +Emptyline_record + ^\s*$$ -> Next + ^\s*$$ -> Emptyline_record + +Routerid_record + ^\s*$$ -> Next + ^\s*OSPF Router with ID\s*\(${ROUTERID}\)\s*$$ + ^\s*Router Link States\s*\(Area\s*${AREAID}\)\s*$$ -> Routerlsa_area_record + ^\s*Net Link States\s*\(Area\s*${AREAID}\)\s*$$ -> Networklsa_area_record + ^\s*Summary Link States\s*\(Area\s*${AREAID}\)\s*$$ -> Summarylsa_area_record + ^\s*ASBR-Summary Link States\s*\(Area\s*${AREAID}\)\s*$$ -> Asbrsummarylsa_area_record + ^\s*AS External Link States\s*$$ -> Externallsa_area_record + +Routerlsa_area_record + ^\s*$$ -> Next + ^\s*LS age: ${LSAGE}\s*$$ -> Routerlsa_record + ^\s*VRF Name:\s+${VRFNAME}\s*$$ -> Start + ^\s*OSPF Router with ID\s*\(${ROUTERID}\)\s*$$ -> Routerid_record + +Routerlsa_record + ^\s*Options: 0x${LSOPTIONS}\s*:\.*$$ + ^\s*LS Flags: 0x${LSFLAGS}\.*$$ + ^\s*Flags: 0x${STATEFLG}\s*:\s*ABR\s${STATE}\s*$$ + ^\s*LS Type: ${LSTYPE}\s*$$ + ^\s*Link State ID: ${LINKSTATEID}\s*$$ + ^\s*Advertising Router: ${ADVROUTER}\s*$$ + ^\s*LS Seq Number: ${LSSEQNUM}\s*$$ + ^\s*Checksum: 0x${LSCHECKSUM}\s*$$ + ^\s*Length: ${LSLENGTH}\s*$$ + ^\s*$$ -> Next + ^\s*Number of Links: ${LINKCOUNT}\s*$$ + ^\s*$$ -> Next + ^\s*Link connected to:\s*a\s*${LINKTYPE}\s*Network\s*$$ -> Routerlsa_link_record + ^\s*Link connected to:\s*${LINKTYPE}\s*Network\s*$$ -> Routerlsa_link_record + ^\s*LS age: ${LSAGE}\s*$$ -> Routerlsa_record + +Routerlsa_link_record + ^\s*\(Link ID\) Designated Router address: ${LINKID}\s*$$ + ^\s*\(Link Data\) Router Interface address: ${LINKDATA}\s*$$ + ^\s*Number of TOS metrics: ${TOSCOUNT}\s*$$ -> Routerlsa_tos_record + +Routerlsa_tos_record + ^\s*TOS\s*${TOS}\s*Metric:\s*${TOSMETRIC}\s*$$ -> Record + ^\s*$$ -> Next + ^\s*$$ -> Next + ^\s*Link connected to:\s*a\s*${LINKTYPE}\s*Network\s*$$ -> Routerlsa_link_record + ^\s*Link connected to:\s*${LINKTYPE}\s*Network\s*$$ -> Routerlsa_link_record + ^\s*LS age: ${LSAGE}\s*$$ -> Routerlsa_record + ^\s*Router Link States\s*\(Area\s*${AREAID}\)\s*$$ -> Routerlsa_area_record + ^\s*Net Link States\s*\(Area\s*${AREAID}\)\s*$$ -> Networklsa_area_record + ^\s*Summary Link States\s*\(Area\s*${AREAID}\)\s*$$ -> Summarylsa_area_record + ^\s*ASBR-Summary Link States\s*\(Area\s*${AREAID}\)\s*$$ -> Asbrsummarylsa_area_record + ^\s*AS External Link States\s*$$ -> Externallsa_area_record + ^\s*OSPF Router with ID\s*\(${ROUTERID}\)\s*$$ -> Routerid_record + ^\s*VRF Name:\s+${VRFNAME}\s*$$ -> Start + +Networklsa_area_record + ^\s*$$ -> Next + ^\s*LS age: ${LSAGE}\s*$$ -> Networklsa_record + ^\s*OSPF Router with ID\s*\(${ROUTERID}\)\s*$$ -> Routerid_record + ^\s*VRF Name:\s+${VRFNAME}\s*$$ -> Start + +Networklsa_record + ^\s*LS age: ${LSAGE}\s*$$ + ^\s*Options: 0x${LSOPTIONS}\s*:\.*$$ + ^\s*LS Flags: 0x${LSFLAGS}\s*$$ + ^\s*LS Type: ${LSTYPE}\s*$$ + ^\s*Link State ID:\s*${LINKSTATEID}\s*\(address of Designated Router\)\s*$$ + ^\s*Advertising Router: ${ADVROUTER}\s*$$ + ^\s*LS Seq Number: ${LSSEQNUM}\s*$$ + ^\s*Checksum: 0x${LSCHECKSUM}\s*$$ + ^\s*Length: ${LSLENGTH}\s*$$ + ^\s*$$ -> Next + ^\s*Network Mask:\s*/${NWMASK}\s*$$ -> Networklsa_router_record + ^\s*LS age: ${LSAGE}\s*$$ -> Networklsa_record + +Networklsa_router_record + ^\s*Attached Router:\s*${ATTACHEDROUTER}\s*$$ -> Record + ^\s*$$ -> Next + ^\s*Network Mask:\s*/${NWMASK}\s*$$ -> Networklsa_router_record + ^\s*LS age: ${LSAGE}\s*$$ -> Networklsa_record + ^\s*Router Link States\s*\(Area\s*${AREAID}\)\s*$$ -> Routerlsa_area_record + ^\s*Net Link States\s*\(Area\s*${AREAID}\)\s*$$ -> Networklsa_area_record + ^\s*Summary Link States\s*\(Area\s*${AREAID}\)\s*$$ -> Summarylsa_area_record + ^\s*ASBR-Summary Link States\s*\(Area\s*${AREAID}\)\s*$$ -> Asbrsummarylsa_area_record + ^\s*AS External Link States\s*$$ -> Externallsa_area_record + ^\s*OSPF Router with ID\s*\(${ROUTERID}\)\s*$$ -> Routerid_record + ^\s*VRF Name:\s+${VRFNAME}\s*$$ -> Start + + +Summarylsa_area_record + ^\s*$$ -> Next + ^\s*LS age: ${LSAGE}\s*$$ -> Summarylsa_record + ^\s*OSPF Router with ID\s*\(${ROUTERID}\)\s*$$ -> Routerid_record + ^\s*VRF Name:\s+${VRFNAME}\s*$$ -> Start + +Summarylsa_record + ^\s*LS age: ${LSAGE}\s*$$ + ^\s*Options: 0x${LSOPTIONS}\s*:\.*$$ + ^\s*LS Flags: 0x${LSFLAGS}\s*$$ + ^\s*LS Type:\s*${LSTYPE}\s*$$ + ^\s*Link State ID: ${LINKSTATEID}\s*\(summary Network Number\)\s*$$ + ^\s*Advertising Router: ${ADVROUTER}\s*$$ + ^\s*LS Seq Number: ${LSSEQNUM}\s*$$ + ^\s*Checksum: 0x${LSCHECKSUM}\s*$$ + ^\s*Length:\s*${LSLENGTH}\s*$$ + ^\s*$$ -> Next + ^\s*Network Mask:\s*/${NWMASK}\s*$$ -> Summarylsa_tos_record + ^\s*LS age: ${LSAGE}\s*$$ -> Summarylsa_record + +Summarylsa_tos_record + ^\s*TOS:\s*${TOS}\s*Metric:\s*${TOSMETRIC}\s*$$ -> Record + ^\s*$$ -> Next + ^\s*Network Mask:\s*/${NWMASK}\s*$$ -> Summarylsa_tos_record + ^\s*LS age: ${LSAGE}\s*$$ -> Summarylsa_record + ^\s*Router Link States\s*\(Area\s*${AREAID}\)\s*$$ -> Routerlsa_area_record + ^\s*Net Link States\s*\(Area\s*${AREAID}\)\s*$$ -> Networklsa_area_record + ^\s*Summary Link States\s*\(Area\s*${AREAID}\)\s*$$ -> Summarylsa_area_record + ^\s*ASBR-Summary Link States\s*\(Area\s*${AREAID}\)\s*$$ -> Asbrsummarylsa_area_record + ^\s*AS External Link States\s*$$ -> Externallsa_area_record + ^\s*OSPF Router with ID\s*\(${ROUTERID}\)\s*$$ -> Routerid_record + ^\s*VRF Name:\s+${VRFNAME}\s*$$ -> Start + + +Asbrsummarylsa_area_record + ^\s*$$ -> Next + ^\s*LS age: ${LSAGE}\s*$$ -> Asbrsummarylsa_record + ^VRF Name:\s+${VRFNAME}\s*$$ -> Start + +Asbrsummarylsa_record + ^\s*LS age: ${LSAGE}\s*$$ + ^\s*Options: 0x${LSOPTIONS}\s*:\.*$$ + ^\s*LS Flags: 0x${LSFLAGS}\s*$$ + ^\s*LS Type:\s*${LSTYPE}\s*$$ + ^\s*Link State ID: ${LINKSTATEID}\s*\(AS Boundary Router address\)\s*$$ + ^\s*Advertising Router: ${ADVROUTER}\s*$$ + ^\s*LS Seq Number: ${LSSEQNUM}\s*$$ + ^\s*Checksum: 0x${LSCHECKSUM}\s*$$ + ^\s*Length:\s*${LSLENGTH}\s*$$ + ^\s*$$ -> Next + ^\s*Network Mask:\s*/${NWMASK}\s*$$ -> Asbrsummarylsa_nw_record + ^\s*LS age: ${LSAGE}\s*$$ -> Asbrsummarylsa_record + +Asbrsummarylsa_nw_record + ^\s*TOS:\s*${TOS}\s*Metric:\s*${TOSMETRIC}\s*$$ -> Record + ^\s*$$ -> Next + ^\s*Network Mask:\s*/${NWMASK}\s*$$ -> Asbrsummarylsa_nw_record + ^\s*LS age: ${LSAGE}\s*$$ -> Asbrsummarylsa_record + ^\s*Router Link States\s*\(Area\s*${AREAID}\)\s*$$ -> Routerlsa_area_record + ^\s*Net Link States\s*\(Area\s*${AREAID}\)\s*$$ -> Networklsa_area_record + ^\s*Summary Link States\s*\(Area\s*${AREAID}\)\s*$$ -> Asbrsummarylsa_area_record + ^\s*AS External Link States\s*\(Area\s*${AREAID}\)\s*$$ -> Externallsa_area_record + ^\s*ASBR-Summary Link States\s*\(Area\s*${AREAID}\)\s*$$ -> Asbrsummarylsa_area_record + ^\s*OSPF Router with ID\s*\(${ROUTERID}\)\s*$$ -> Start + + +Externallsa_area_record + ^\s*$$ -> Next + ^\s*LS age: ${LSAGE}\s*$$ -> Externallsa_record + ^VRF Name:\s+${VRFNAME}\s*$$ -> Start + +Externallsa_record + ^\s*LS age: ${LSAGE}\s*$$ + ^\s*Options: 0x${LSOPTIONS}\s*:\.*$$ + ^\s*LS Flags: 0x${LSFLAGS}\s*$$ + ^\s*LS Type:\s*${LSTYPE}\s*$$ + ^\s*Link State ID: ${LINKSTATEID}\s*\(External Network Number\)\s*$$ + ^\s*Advertising Router: ${ADVROUTER}\s*$$ + ^\s*LS Seq Number: ${LSSEQNUM}\s*$$ + ^\s*Checksum: 0x${LSCHECKSUM}\s*$$ + ^\s*Length:\s*${LSLENGTH}\s*$$ + ^\s*$$ -> Next + ^\s*Network Mask:\s*/${NWMASK}\s*$$ -> Externallsa_nw_record + ^\s*LS age: ${LSAGE}\s*$$ -> Externallsa_record + +Externallsa_nw_record + ^\s*Metric Type:\s*${METRICTYPE}\s*\(Larger than any link state path\)\s*$$ + ^\s*TOS:\s*${TOS}\s*$$ + ^\s*Metric:\s*${METRICVALUE}\s*$$ + ^\s*Forward Address:\s*${FWDADDR}\s*$$ + ^\s*External Route Tag:\s*${ROUTETAG}\s*$$ -> Record + ^\s*$$ -> Next + ^\s*Network Mask:\s*/${NWMASK}\s*$$ -> Externallsa_nw_record + ^\s*LS age: ${LSAGE}\s*$$ -> Externallsa_record + ^\s*Router Link States\s*\(Area\s*${AREAID}\)\s*$$ -> Routerlsa_area_record + ^\s*Net Link States\s*\(Area\s*${AREAID}\)\s*$$ -> Networklsa_area_record + ^\s*Summary Link States\s*\(Area\s*${AREAID}\)\s*$$ -> Summarylsa_area_record + ^\s*ASBR-Summary Link States\s*\(Area\s*${AREAID}\)\s*$$ -> Asbrsummarylsa_area_record + ^\s*AS External Link States\s*$$ -> Externallsa_area_record + ^\s*OSPF Router with ID\s*\(${ROUTERID}\)\s*$$ -> Start + + +EOF + diff --git a/spytest/templates/show_ip_ospf_neighbor.tmpl b/spytest/templates/show_ip_ospf_neighbor.tmpl new file mode 100644 index 00000000000..7c1b24e3e5e --- /dev/null +++ b/spytest/templates/show_ip_ospf_neighbor.tmpl @@ -0,0 +1,25 @@ +Value Filldown VRFNAME (\S+) +Value NEIGHBHORID (\S+) +Value PRIORITY (\d+) +Value STATE (\S+) +Value ROLE (\S*) +Value DEADTIME (\S+) +Value NEIGHBORADDR (\S+) +Value IFNAME (\S+) +Value IFIP (\S+) +Value RXMTL (\d+) +Value RQSTL (\d+) +Value DBSML (\d+) + +Start + ^VRF Name:\s+${VRFNAME}\s*$$ + ^Neighbor ID\s+Pri\s+State\s+Dead Time\s+Address\s+Interface\s+RXmtL\s+RqstL\s+DBsmL\s*$$ + ^${NEIGHBHORID}\s+${PRIORITY}\s+${STATE}[/]${ROLE}\s+${DEADTIME}\s+${NEIGHBORADDR}\s+${IFNAME}:${IFIP}\s+${RXMTL}\s+${RQSTL}\s+${DBSML}\s*$$ -> Record + ^${NEIGHBHORID}\s+${PRIORITY}\s+${STATE}[/]${ROLE}\s+${DEADTIME}\s+${NEIGHBORADDR}\s+${IFNAME}\s+${RXMTL}\s+${RQSTL}\s+${DBSML}\s*$$ -> Record + ^${NEIGHBHORID}\s+${PRIORITY}\s+${STATE}[/]${ROLE}\s+${DEADTIME}\s*usecs\s+${NEIGHBORADDR}\s+${IFNAME}:${IFIP}\s+${RXMTL}\s+${RQSTL}\s+${DBSML}\s*$$ -> Record + ^${NEIGHBHORID}\s+${PRIORITY}\s+${STATE}[/]${ROLE}\s+${DEADTIME}\s*usecs\s+${NEIGHBORADDR}\s+${IFNAME}\s+${RXMTL}\s+${RQSTL}\s+${DBSML}\s*$$ -> Record + ^${NEIGHBHORID}\s+${PRIORITY}\s+${STATE}\s+${DEADTIME}\s+${NEIGHBORADDR}\s+${IFNAME}:${IFIP}\s+${RXMTL}\s+${RQSTL}\s+${DBSML}\s*$$ -> Record + ^${NEIGHBHORID}\s+${PRIORITY}\s+${STATE}\s+${DEADTIME}\s*usecs\s+${NEIGHBORADDR}\s+${IFNAME}:${IFIP}\s+${RXMTL}\s+${RQSTL}\s+${DBSML}\s*$$ -> Record + ^${NEIGHBHORID}\s+${PRIORITY}\s+${STATE}\s+${DEADTIME}\s*usecs\s+${NEIGHBORADDR}\s+${IFNAME}\s+${RXMTL}\s+${RQSTL}\s+${DBSML}\s*$$ -> Record + +EOF diff --git a/spytest/templates/show_ip_ospf_router.tmpl b/spytest/templates/show_ip_ospf_router.tmpl new file mode 100644 index 00000000000..00451521474 --- /dev/null +++ b/spytest/templates/show_ip_ospf_router.tmpl @@ -0,0 +1,58 @@ +Value Filldown VRFNAME (\S+) +Value Filldown ROUTERID (\S+) +Value Filldown TOS (\S+) +Value Filldown TOSLIST (\S+) +Value Filldown RFC (\S+) +Value Filldown RFC1583 (\S+) +Value Filldown OPAQCAPABILITY (\S+) +Value Filldown MMTMR (\S+) +Value Filldown MMTYPE (\S+) +Value Filldown SPFDELAY (\d+) +Value Filldown SPFMINHOLD (\d+) +Value Filldown SPFMAXHOLD (\d+) +Value Filldown SPFHOLDMULTIPLIER (\d+) +Value Filldown SPFLASTEXEC (\S+) +Value Filldown SPFLASTDURATION (\S+) +Value Filldown SPFRUNCOUNT (\d+) +Value Filldown SPFTMRSTATE (\S+) +Value Filldown LSAMININTERVAL (\d+) +Value Filldown LSAMINARRIVAL (\d+) +Value Filldown LSAMAXINTERVAL (\d+) +Value Filldown WRITEMULTIPLIER (\d+) +Value Filldown REFRESHTIMER (\d+) +Value Filldown EXTLSACOUNT (\d+) +Value Filldown EXTLSACHKSUM (\S+) +Value Filldown OPQLSACOUNT (\d+) +Value Filldown ADJLOGGED (\S+) +Value Filldown OPQLSACHKSUM (\S+) +Value AREACOUNT (\d+) + +Start + ^VRF Name:\s+${VRFNAME}\s*$$ + ^\s*OSPF Routing Process, Router ID:\s*${ROUTERID}\s*$$ + ^\s*Supports only\s+${TOS}\s+TOS (${TOSLIST}) routes\s*$$ + ^\s*This implementation conforms to\s+${RFC}\s*$$ + ^\s*RFC1583Compatibility flag is\s+${RFC1583}\s*$$ + ^\s*OpaqueCapability flag is\s+${OPAQCAPABILITY}\s*$$ + ^\s*Stub router advertisement is configured\s*$$ + ^\s*Enabled for\s*${MMTMR}s prior to full\s*${MMTYPE}\s*$$ + ^\s*Enabled for\s*${MMTMR}s after\s*${MMTYPE}\s*$$ + ^\s*${MMTYPE}istratively activated \(${MMTMR}\)\s*$$ + ^\s*Initial SPF scheduling delay\s+${SPFDELAY}\s+millisec\(s\)\s*$$ + ^\s*Minimum hold time between consecutive SPFs\s+${SPFMINHOLD}\s+millisec\(s\)\s*$$ + ^\s*Maximum hold time between consecutive SPFs\s+${SPFMAXHOLD}\s+millisec\(s\)\s*$$ + ^\s*Hold time multiplier is currently\s+${SPFHOLDMULTIPLIER}\s*$$ + ^\s*SPF algorithm last executed\s+${SPFLASTEXEC}s ago\s*$$ + ^\s*Last SPF duration\s+${SPFLASTDURATION}\s+usecs\s*$$ + ^\s*SPF algorithm has not been run\s*$$ + ^\s*SPF timer is\s+${SPFTMRSTATE}\s*$$ + ^\s*LSA minimum interval\s+${LSAMININTERVAL}\s+msecs\s*$$ + ^\s*LSA minimum arrival\s+${LSAMINARRIVAL}\s+msecs\s*$$ + ^\s*Write Multiplier set to\s+${WRITEMULTIPLIER}\s*$$ + ^\s*Refresh timer\s+${REFRESHTIMER}\s+secs\s*$$ + ^\s*Number of external LSA\s+${EXTLSACOUNT}\. Checksum Sum 0x${EXTLSACHKSUM}\s*$$ + ^\s*Number of opaque AS LSA\s+${OPQLSACOUNT}\. Checksum Sum 0x${OPQLSACHKSUM}\s*$$ + ^\s*Adjacency changes are\s+${ADJLOGGED}\s*$$ + ^\s*Number of areas attached to this router:\s+${AREACOUNT}\s*$$ -> Record + +EOF diff --git a/spytest/templates/show_ip_ospf_traffic.tmpl b/spytest/templates/show_ip_ospf_traffic.tmpl new file mode 100644 index 00000000000..9eb638c4590 --- /dev/null +++ b/spytest/templates/show_ip_ospf_traffic.tmpl @@ -0,0 +1,18 @@ +Value Filldown VRFNAME (\S+) +Value interface (\S+) +Value hello_rx (\S+) +Value hello_tx (\S+) +Value dbd_rx (\S+) +Value dbd_tx (\S+) +Value lsr_rx (\S+) +Value lsr_tx (\S+) +Value lsu_rx (\S+) +Value lsu_tx (\S+) +Value lsa_rx (\S+) +Value lsa_tx (\S+) + +Start + ^VRF Name:\s+${VRFNAME}\s*$$ + ^${interface}\s*${hello_rx}/${hello_tx}\s*${dbd_rx}/${dbd_tx}\s*${lsr_rx}/${lsr_tx}\s*${lsu_rx}/${lsu_tx}\s*${lsa_rx}/${lsa_tx}\s*$$ -> Record + +EOF \ No newline at end of file diff --git a/spytest/templates/show_ip_pim_assert.tmpl b/spytest/templates/show_ip_pim_assert.tmpl new file mode 100644 index 00000000000..e48e80775b5 --- /dev/null +++ b/spytest/templates/show_ip_pim_assert.tmpl @@ -0,0 +1,21 @@ +############################################################### +#sonic# show ip pim assert +#Interface Address Source Group State Winner Uptime Timer +#Ethernet46 10.1.1.2 10.2.1.20 232.0.0.20 NOINFO * 00:08:49 --:-- +# +############################################################### +Value interface (\w+) +Value address ([\w\.]+) +Value source ([\w\.]+) +Value group ([\w\.]+) +Value state (\w+) +Value winner ([\w\.]+|\**) +Value uptime ([\d\:]+) +Value timer ([\d\:\-]+) +Value vrf (\w+) + +Start + ^\s*VRF:\s*${vrf}$$ + ^\s*${interface}\s*${address}\s*${source}\s*${group}\s*${state}\s*${winner}\s*${uptime}\s*${timer} -> Record + +EOF diff --git a/spytest/templates/show_ip_pim_assert_internal.tmpl b/spytest/templates/show_ip_pim_assert_internal.tmpl new file mode 100644 index 00000000000..9b4c4cec9f6 --- /dev/null +++ b/spytest/templates/show_ip_pim_assert_internal.tmpl @@ -0,0 +1,27 @@ +############################################################### +#sonic# show ip pim assert-internal +#CA: CouldAssert +#ECA: Evaluate CouldAssert +#ATD: AssertTrackingDesired +#eATD: Evaluate AssertTrackingDesired +# +#Interface Address Source Group CA eCA ATD eATD +#Ethernet46 10.1.1.2 10.2.1.20 232.0.0.20 no no no no +# +############################################################### +Value interface (\w+) +Value address ([\w\.]+) +Value source ([\w\.]+) +Value group ([\w\.]+) +Value ca (\w+) +Value eca (\w+) +Value atd (\w+) +Value eatd (\w+) +Value vrf (\w+) + +Start + ^\s*CA:\s*CouldAssert + ^\s*ECA:\s*Evaluate CouldAssert + ^\s*ATD:\s*AssertTrackingDesired + ^\s*eATD:\s*Evaluate AssertTrackingDesired + ^\s*${interface}\s*${address}\s*${source}\s*${group}\s*${ca}\s*${eca}\s*${atd}\s*${eatd}$$ -> Record diff --git a/spytest/templates/show_ip_pim_assert_metric.tmpl b/spytest/templates/show_ip_pim_assert_metric.tmpl new file mode 100644 index 00000000000..6fccb0e1c63 --- /dev/null +++ b/spytest/templates/show_ip_pim_assert_metric.tmpl @@ -0,0 +1,19 @@ +############################################################### +#sonic# show ip pim assert-metric +#Interface Address Source Group RPT Pref Metric Address +#Ethernet46 10.1.1.2 10.2.1.20 232.0.0.20 no 0 0 10.1.1.2 +# +############################################################### +Value interface (\w+) +Value address ([\w\.]+) +Value source ([\w\.]+) +Value group ([\w\.]+) +Value rpt (\w+) +Value pref (\d+) +Value metric (\d+) +Value address2 ([\w\.]+) +Value vrf (\w+) + +Start + ^\s*VRF:\s*${vrf}$$ + ^\s*${interface}\s*${address}\s*${source}\s*${group}\s*${rpt}\s*${pref}\s*${metric}\s*${address2}$$ -> Record diff --git a/spytest/templates/show_ip_pim_assert_winner_metric.tmpl b/spytest/templates/show_ip_pim_assert_winner_metric.tmpl new file mode 100644 index 00000000000..fed40fb8ae5 --- /dev/null +++ b/spytest/templates/show_ip_pim_assert_winner_metric.tmpl @@ -0,0 +1,19 @@ +############################################################### +#sonic# show ip pim assert-winner-metric +#Interface Address Source Group RPT Pref Metric Address +#Ethernet46 10.1.1.2 10.2.1.20 232.0.0.20 yes INFI INFI * +# +############################################################### +Value interface (\w+) +Value address ([\w\.]+) +Value source ([\w\.]+) +Value group ([\w\.]+) +Value rpt (\w+) +Value pref (\w+) +Value metric (\w+) +Value address2 ([\w\.\*]+) +Value vrf (\w+) + +Start + ^\s*VRF:\s*${vrf}$$ + ^\s*${interface}\s*${address}\s*${source}\s*${group}\s*${rpt}\s*${pref}\s*${metric}\s*${address2}$$ -> Record diff --git a/spytest/templates/show_ip_pim_group_type.tmpl b/spytest/templates/show_ip_pim_group_type.tmpl new file mode 100644 index 00000000000..7e5ee2777fb --- /dev/null +++ b/spytest/templates/show_ip_pim_group_type.tmpl @@ -0,0 +1,8 @@ +#################################### +#sonic# show ip pim group-type +#SSM group range : 232.0.0.0/8 +#################################### +Value ssm_group_range ([\w\.\/]+) + +Start + ^\s*SSM\s*group\s*range\s*:\s*${ssm_group_range}$$ -> Record diff --git a/spytest/templates/show_ip_pim_group_type_groupid.tmpl b/spytest/templates/show_ip_pim_group_type_groupid.tmpl new file mode 100644 index 00000000000..58ea1c7b986 --- /dev/null +++ b/spytest/templates/show_ip_pim_group_type_groupid.tmpl @@ -0,0 +1,8 @@ +################################################# +#sonic# show ip pim group-type 225.1.1.1 +#Group type : ASM +################################################ +Value group_type (\w+) + +Start + ^\s*Group type\s*:\s*${group_type}$$ -> Record diff --git a/spytest/templates/show_ip_pim_interface.tmpl b/spytest/templates/show_ip_pim_interface.tmpl new file mode 100644 index 00000000000..8a5c1be3ef0 --- /dev/null +++ b/spytest/templates/show_ip_pim_interface.tmpl @@ -0,0 +1,23 @@ +##################################################### +#sonic# show ip pim interface +#Interface State Address PIM Nbrs PIM DR FHR IfChannels +#Ethernet7 up 10.10.10.1 0 local 0 0 +#pimreg up 0.0.0.0 0 local 0 0 +####################################################### +Value vrf (\w+) +Value interface (\w+) +Value state (\w+) +Value address ([\w\.]+) +Value nbr_count (\d+) +Value dr ([\w\.]+) +Value fhr (\w+) +Value if_channels (\d+) + +Start + ^\s*VRF:\s*${vrf}$$ + ^.*Interface\s*State\s*Address\s*PIM\s*Nbrs\s*PIM\s*DR\s*FHR\s*IfChannels -> header + +header + ^\s*${interface}\s*${state}\s*${address}\s*${nbr_count}\s*${dr}\s*${fhr}\s*${if_channels}$$ -> Record + + diff --git a/spytest/templates/show_ip_pim_interface_id.tmpl b/spytest/templates/show_ip_pim_interface_id.tmpl new file mode 100644 index 00000000000..326b8ef70df --- /dev/null +++ b/spytest/templates/show_ip_pim_interface_id.tmpl @@ -0,0 +1,137 @@ +############################################################################ +#sonic# show ip pim interface Ethernet7 +#Interface : Ethernet7 +#State : up +#Address : 10.10.10.1 (primary) +# fe80::3e2c:99ff:fea6:fba0/64 +# +#Designated Router +#----------------- +#Address : 10.10.10.1 +#Local DR Priority : 1 +#Neighbors that didn't +# advertise DR Priority : 0 +# +#Uptime : --:--:-- +#Elections : 2 +#Changes : 0 +# +# +#Hellos +#------ +#Period : 30 +#Timer : 00:00:04 +#StatStart : 00:01:25 +#Receive : 0 +#Receive Failed : 0 +#Send : 3 +#Send Failed : 0 +#Generation ID : 2e7ca6f0 +# +# +#Flags +#----- +#All Multicast : no +#Broadcast : yes +#Deleted : no +#Interface Index : 91 +#Multicast : yes +#Multicast Loop : 0 +#Promiscuous : no +# +# +#Join Prune Interval +#------------------- +#LAN Delay : yes +#Effective Propagation Delay : 0 msec +#Effective Override Interval : 0 msec +#Join Prune Override Interval : 0 msec +# +# +#LAN Prune Delay +#--------------- +#Propagation Delay : 500 msec +#Propagation Delay (Highest) : 0 msec +#Override Interval : 2500 msec +#Override Interval (Highest) : 0 msec +########################################################################################## +Value interface (\w+) +Value state (\w+) +Value primary_addr ([\w\.\:]*) +Value List secondary_addr ([\w\.\:\/]+) +Value pim_nbr ([\w\.]+) +Value nbr_state (\w+) +Value nbr_uptime (.*) +Value nbr_expiry_timer (.*) +Value dr_addr ([\w\.]+) +Value dr_priority_local (.*) +Value nbrs_no_advertise (\d+) +Value dr_uptime (.*) +Value dr_elections (\d+) +Value dr_changes (\d+) +Value period (\d+) +Value timer (.*) +Value stat_start (.*) +Value receive (\d+) +Value receive_failed (\d+) +Value send (\d+) +Value send_failed (\d+) +Value gen_id (.*) +Value all_multicast (\w+) +Value broadcast (\w+) +Value deleted (\w+) +Value ifindex (\w+) +Value multicast (\w+) +Value mulicast_loop (\w+) +Value promiscuous (\w+) +Value lan_delay (\w+) +Value eff_propogation_delay (\d+) +Value eff_override_interval (\d+) +Value join_prune_override_interval (\d+) +Value propogation_delay (\d+) +Value propogation_delay_high (\d+) +Value override_interval (\d+) +Value override_interval_high (\d+) + +Start + ^\s*Interface\s*:\s*${interface}$$ + ^\s*State\s*:\s*${state}$$ + ^\s*Address\s*:\s*${primary_addr}\s*\(primary\)$$ + ^\s*${secondary_addr}$$ + ^.*------- -> header + +header + ^\s*${pim_nbr}\s*\:\s*${nbr_state}\s*for${nbr_uptime}\s*,\s*holdtime\s*expires\s*in\s*${nbr_expiry_timer}$$ + ^\s*Address\s*\:\s*${dr_addr}$$ + ^\s*Local\s*DR\s*Priority\s*\:\s*${dr_priority_local}$$ + ^\s*advertise\s*DR\s*Priority\s*:\s*${nbrs_no_advertise}$$ + ^\s*Uptime\s*\:\s*${dr_uptime}$$ + ^\s*Elections\s*:\s*${dr_elections}$$ + ^\s*Changes\s*:\s*${dr_changes}$$ + ^\s*Period\s*:\s*${period}$$ + ^\s*Timer\s*:\s*${timer}$$ + ^\s*StatStart\s*:\s*${stat_start}$$ + ^\s*Receive\s*:\s*${receive}$$ + ^\s*Receive\s*Failed\s*:\s*${receive_failed}$$ + ^\s*Send\s*:\s*${send}$$ + ^\s*Send\s*Failed\s*:\s*${send_failed}$$ + ^\s*Generation\s*ID\s*:\s*${gen_id}$$ + ^\s*All\s*Multicast\s*:\s*${all_multicast}$$ + ^\s*Broadcast\s*:\s*${broadcast}$$ + ^\s*Deleted\s*:\s*${deleted}$$ + ^\s*Interface\s*Index\s*:\s*${ifindex}$$ + ^\s*Multicast\s*:\s*${multicast}$$ + ^\s*Multicast\s*Loop\s*:\s*${mulicast_loop}$$ + ^\s*Promiscuous\s*:\s*${promiscuous}$$ + ^\s*LAN\s*Delay\s*:\s*${lan_delay}$$ + ^\s*Effective\s*Propagation\s*Delay\s*:\s*${eff_propogation_delay}\s*msec$$ + ^\s*Effective\s*Override\s*Interval\s*:\s*${eff_override_interval}\s*msec$$ + ^\s*Join\s*Prune\s*Override\s*Interval\s*:\s*${join_prune_override_interval}\s*msec$$ + ^\s*Propagation\s*Delay\s*:\s*${propogation_delay}\s*msec$$ + ^\s*Propagation\s*Delay\s*\(Highest\)\s*:\s*${propogation_delay_high}\s*msec$$ + ^\s*Override\s*Interval\s*:\s*${override_interval}\s*msec$$ + ^\s*Override\s*Interval\s*\(Highest\)\s*:\s*${override_interval_high}\s*msec -> Record Start + + + + diff --git a/spytest/templates/show_ip_pim_interface_traffic.tmpl b/spytest/templates/show_ip_pim_interface_traffic.tmpl new file mode 100644 index 00000000000..5bb27cb990f --- /dev/null +++ b/spytest/templates/show_ip_pim_interface_traffic.tmpl @@ -0,0 +1,29 @@ +####################################################################################### +#show ip pim interface traffic +# +#Interface HELLO JOIN PRUNE REGISTER REGISTER-STOP ASSERT +# Rx/Tx Rx/Tx Rx/Tx Rx/Tx Rx/Tx Rx/Tx +#--------------------------------------------------------------------------------------------------------------- +#Ethernet24 2781/2780 0/0 0/0 0/0 0/0 0/0 +#Vlan100 1234/1233 1/2 2/3 1/2 2/1 1/1 +############################################################################################### +Value interface (\w+) +Value hello_rx (\d+) +Value hello_tx (\d+) +Value join_rx (\d+) +Value join_tx (\d+) +Value prune_rx (\d+) +Value prune_tx (\d+) +Value register_rx (\d+) +Value register_tx (\d+) +Value register_stop_rx (\d+) +Value register_stop_tx (\d+) +Value assert_rx (\d+) +Value assert_tx (\d+) + +Start + ^.*------- -> header + +header + ^\s*${interface}\s*${hello_rx}/${hello_tx}\s*${join_rx}/${join_tx}\s*${prune_rx}/${prune_tx}\s*${register_rx}/${register_tx}\s*${register_stop_rx}/${register_stop_tx}\s*${assert_rx}/${assert_tx}\s* -> Record + diff --git a/spytest/templates/show_ip_pim_join.tmpl b/spytest/templates/show_ip_pim_join.tmpl new file mode 100644 index 00000000000..9c254605629 --- /dev/null +++ b/spytest/templates/show_ip_pim_join.tmpl @@ -0,0 +1,19 @@ +############################################################### +#sonic# show ip pim join +#Interface Address Source Group State Uptime Expire Prune +#Ethernet46 10.1.1.2 10.2.1.20 232.0.0.20 NOINFO --:--:-- --:-- --:-- +# +############################################################### +Value interface (\w+) +Value address ([\w\.]+) +Value source ([\w\.]+) +Value group ([\w\.]+) +Value state (\w+) +Value uptime ([\d\:\-]+) +Value expire ([\d\:\-]+) +Value prune ([\d\:\-]+) +Value vrf (\w+) + +Start + ^\s*VRF:\s*${vrf}$$ + ^\s*${interface}\s*${address}\s*${source}\s*${group}\s*${state}\s*${uptime}\s*${expire}\s*${prune}$$ -> Record diff --git a/spytest/templates/show_ip_pim_local_membership.tmpl b/spytest/templates/show_ip_pim_local_membership.tmpl new file mode 100644 index 00000000000..e68ca28b868 --- /dev/null +++ b/spytest/templates/show_ip_pim_local_membership.tmpl @@ -0,0 +1,16 @@ +############################################################### +#sonic# show ip pim local-membership +#Interface Address Source Group Membership +#Ethernet46 10.1.1.2 10.2.1.20 232.0.0.20 INCLUDE +# +############################################################### +Value interface (\w+) +Value address ([\w\.]+) +Value source ([\w\.]+) +Value group ([\w\.]+) +Value membership (\w+) +Value vrf (\w+) + +Start + ^\s*VRF:\s*${vrf}$$ + ^\s*${interface}\s*${address}\s*${source}\s*${group}\s*${membership}$$ -> Record diff --git a/spytest/templates/show_ip_pim_neighbor.tmpl b/spytest/templates/show_ip_pim_neighbor.tmpl new file mode 100644 index 00000000000..6d8c503c837 --- /dev/null +++ b/spytest/templates/show_ip_pim_neighbor.tmpl @@ -0,0 +1,23 @@ +############################################################### +#sonic# show ip pim neighbor +#Interface Neighbor Uptime Holdtime DR Pri +#Ethernet24 10.10.10.2 00:00:07 00:01:37 1 +# +#sonic# show ip pim vrf all neighbor +#VRF: default +#Interface Neighbor Uptime Holdtime DR Pri +#Ethernet24 10.10.10.2 00:17:29 00:01:15 1 +#VRF: RED +#Interface Neighbor Uptime Holdtime DR Pri +#Ethernet26 10.10.10.2 00:17:29 00:01:15 1 +############################################################### +Value Required interface (\w+) +Value neighbor ([\w\.]+) +Value uptime ([\d\:]+) +Value holdtime ([\d\:]+) +Value dr_priority (\d+) +Value vrf (\w+) + +Start + ^\s*VRF:\s*${vrf}$$ + ^\s*${interface}\s*${neighbor}\s*${uptime}\s*${holdtime}\s*${dr_priority}$$ -> Record \ No newline at end of file diff --git a/spytest/templates/show_ip_pim_neighbor_detail.tmpl b/spytest/templates/show_ip_pim_neighbor_detail.tmpl new file mode 100644 index 00000000000..07222265def --- /dev/null +++ b/spytest/templates/show_ip_pim_neighbor_detail.tmpl @@ -0,0 +1,62 @@ +#################################################### +#sonic# show ip pim neighbor detail +#VRF: default +#Interface : Ethernet24 +#Neighbor : 10.10.10.2 +# Uptime : 00:02:41 +# Holdtime : 00:01:33 +# DR Priority : 1 +# Generation ID : 1e3e7da5 +# Override Interval (msec) : 2500 +# Propagation Delay (msec) : 500 +# Hello Option - Address List : yes +# Hello Option - DR Priority : yes +# Hello Option - Generation ID : yes +# Hello Option - Holdtime : yes +# Hello Option - LAN Prune Delay : yes +# Hello Option - T-bit : no +# BFD: Type: single hop +# Detect Multiplier: 3, Min Rx interval: 300, Min Tx interval: 300 +# Status: Up, Last update: 0:00:00:05 +##################################################### +Value vrf (\w+) +Value Required interface (\w+) +Value neighbor ([\w\.]+) +Value uptime ([\d\:]+) +Value holdtime ([\d\:]+) +Value dr_priority (\d+) +Value gen_id (.*) +Value override_interval (\d+) +Value propogation_delay (\d+) +Value hello_addr_list (\w+) +Value hello_dr_priority (\w+) +Value hello_gen_id (\w+) +Value hello_holdtime (\w+) +Value hello_lan_prune_delay (\w+) +Value hello_t_bit (\w+) +Value bfd_type ([\w\s]+) +Value bfd_multiplier (\d+) +Value bfd_rx (\d+) +Value bfd_tx (\d+) +Value bfd_status (\w+) +Value bfd_last_update ([\w\:]+) + +Start + ^\s*VRF:\s*${vrf}$$ + ^\s*Interface\s*:\s*${interface}$$ + ^\s*Neighbor\s*:\s*${neighbor}$$ + ^\s*Uptime\s*:\s*${uptime}$$ + ^\s*Holdtime\s*:\s*${holdtime}$$ + ^\s*DR\s*Priority\s*:\s*${dr_priority}$$ + ^\s*Generation\s*ID\s*:\s*${gen_id}$$ + ^\s*Override\s*Interval\s*\(msec\)\s*:\s*${override_interval}$$ + ^\s*Propagation\s*Delay\s*\(msec\)\s*:\s*${propogation_delay}$$ + ^\s*Hello\s*Option\s*\-\s*Address\s*List\s*:\s*${hello_addr_list}$$ + ^\s*Hello\s*Option\s*\-\s*DR\s*Priority\s*:\s*${hello_dr_priority}$$ + ^\s*Hello\s*Option\s*\-\s*Generation\s*ID\s*:\s*${hello_gen_id}$$ + ^\s*Hello\s*Option\s*\-\s*Holdtime\s*:\s*${hello_holdtime}$$ + ^\s*Hello\s*Option\s*\-\s*LAN\s*Prune\s*Delay\s*:\s*${hello_lan_prune_delay}$$ + ^\s*Hello\s*Option\s*\-\s*T\-bit\s*:\s*${hello_t_bit}$$ + ^\s*BFD:\s*Type:\s*${bfd_type}$$ + ^\s*Detect\s*Multiplier:\s*${bfd_multiplier},\s*Min\s*Rx\s*interval:\s*${bfd_rx},\s*Min\s*Tx\s*interval:\s*${bfd_tx}$$ + ^\s*Status:\s*${bfd_status},\s*Last\s*update:\s*${bfd_last_update}$$ -> Record diff --git a/spytest/templates/show_ip_pim_nexthop.tmpl b/spytest/templates/show_ip_pim_nexthop.tmpl new file mode 100644 index 00000000000..06f89aa2649 --- /dev/null +++ b/spytest/templates/show_ip_pim_nexthop.tmpl @@ -0,0 +1,23 @@ +################################################## +#sonic# show ip pim nexthop +#Number of registered addresses: 1 +#Address Interface Nexthop +#------------------------------------------- +#10.10.20.2 Ethernet24 10.10.10.2 +#20.20.20.3 Ethernet24 10.10.10.2 +################################################## +Value Filldown vrf (\w+) +Value Filldown registered_count (\d+) +Value source ([\d\.]+) +Value interface (\w+) +Value nexthop ([\d\.]+) + +Start + ^\s*VRF:\s*${vrf}$$ + ^\s*Number\s*of\s*registered\s*addresses:\s*${registered_count} + ^.*----- -> header + +header + ^\s*${source}\s*${interface}\s*${nexthop} -> Record + +EOF \ No newline at end of file diff --git a/spytest/templates/show_ip_pim_nexthop_lookup.tmpl b/spytest/templates/show_ip_pim_nexthop_lookup.tmpl new file mode 100644 index 00000000000..783bcabd2b6 --- /dev/null +++ b/spytest/templates/show_ip_pim_nexthop_lookup.tmpl @@ -0,0 +1,12 @@ +################################################# +#sonic# show ip pim nexthop-lookup 10.10.10.2 232.1.1.1 +#Group 232.1.1.1 --- Nexthop 10.10.10.2 Interface Ethernet24 +################################################## +Value group ([\w\.]+) +Value source ([\w\.]+) +Value interface (\w+) + +Start + ^\s*Group\s*${group}\s*---\s*Nexthop\s*${source}\s*Interface\s*${interface} -> Record + + diff --git a/spytest/templates/show_ip_pim_rpf.tmpl b/spytest/templates/show_ip_pim_rpf.tmpl new file mode 100644 index 00000000000..5bbbe00f8ab --- /dev/null +++ b/spytest/templates/show_ip_pim_rpf.tmpl @@ -0,0 +1,40 @@ +############################################################### +#sonic# show ip pim rpf +#RPF Cache Refresh Delay: 50 msecs +#RPF Cache Refresh Timer: 0 msecs +#RPF Cache Refresh Requests: 3 +#RPF Cache Refresh Events: 3 +#RPF Cache Refresh Last: 18:41:25 +#Nexthop Lookups: 1 +#Nexthop Lookups Avoided: 0 +# +#Source Group RpfIface RpfAddress RibNextHop Metric Pref +#10.2.1.20 232.0.0.20 Ethernet47 10.2.1.20 10.2.1.20 0 0 +# +############################################################### +Value Filldown cache_ref_delay (\d+) +Value Filldown cache_ref_timer (\d+) +Value Filldown cache_ref_reqs (\d+) +Value Filldown cache_ref_events (\d+) +Value Filldown cache_ref_last ([\d\:]+) +Value Filldown nexthop_lookup (\d+) +Value Filldown nexthop_lookup_avoid (\d+) +Value source ([\d\.]+) +Value group ([\d\.]+) +Value rpfiface (\w+) +Value rpfaddress ([\d\.]+) +Value ribnexthop ([\d\.]+) +Value metric ([\-\d]*) +Value pref ([\-\d]*) + +Start + ^\s*RPF Cache Refresh Delay:\s*${cache_ref_delay}\s*msecs$$ + ^\s*RPF Cache Refresh Timer:\s*${cache_ref_timer}\s*msecs$$ + ^\s*RPF Cache Refresh Requests:\s*${cache_ref_reqs}$$ + ^\s*RPF Cache Refresh Events:\s*${cache_ref_events}$$ + ^\s*RPF Cache Refresh Last:\s*${cache_ref_last}$$ + ^\s*Nexthop Lookups:\s*${nexthop_lookup}$$ + ^\s*Nexthop\s*Lookups\s*Avoided:\s*${nexthop_lookup_avoid}$$ + ^\s*${source}\s*${group}\s*\<*${rpfiface}\>*\s*${rpfaddress}\s*${ribnexthop}\s*${metric}\s*${pref} -> Record + +EOF diff --git a/spytest/templates/show_ip_pim_secondary.tmpl b/spytest/templates/show_ip_pim_secondary.tmpl new file mode 100644 index 00000000000..96837b54aad --- /dev/null +++ b/spytest/templates/show_ip_pim_secondary.tmpl @@ -0,0 +1,16 @@ +################################################## +#sonic# show ip pim secondary +#Interface Address Neighbor Secondary +#Ethernet24 20.20.20.3 10.10.10.2 10.10.11.2 +################################################## +Value interface (\w+) +Value address ([\d\.]+) +Value neighbor ([\d\.]+) +Value secondary ([\d\.]+) +Value vrf (\w+) + +Start + ^\s*VRF:\s*${vrf}$$ + ^\s*${interface}\s*${address}\s*${neighbor}\s*${secondary}$$ -> Record + +EOF diff --git a/spytest/templates/show_ip_pim_state.tmpl b/spytest/templates/show_ip_pim_state.tmpl new file mode 100644 index 00000000000..a701bd8df54 --- /dev/null +++ b/spytest/templates/show_ip_pim_state.tmpl @@ -0,0 +1,18 @@ +################################################################################## +#sonic# show ip pim state +#Codes: J -> Pim Join, I -> IGMP Report, S -> Source, * -> Inherited from (*,G) +#Installed Source Group IIF OIL +#1 10.10.10.2 232.1.1.1 Ethernet24 Ethernet12( J ), Vlan101( J ) +################################################################################# +Value vrf (\w+) +Value installed (\d+) +Value source ([\w\.]+) +Value group ([\w\.]+) +Value iif (\w*) +Value oif (.*) + +Start + ^.*Installed\s*Source\s*Group\s*IIF\s*OIL$$ -> header + +header + ^\s*${installed}\s*${source}\s*${group}\s*${iif}\s*${oif}$$ -> Record diff --git a/spytest/templates/show_ip_pim_upstream.tmpl b/spytest/templates/show_ip_pim_upstream.tmpl new file mode 100644 index 00000000000..d1edcbe4cba --- /dev/null +++ b/spytest/templates/show_ip_pim_upstream.tmpl @@ -0,0 +1,20 @@ +############################################################### +#sonic# show ip pim upstream +#Iif Source Group State Uptime JoinTimer RSTimer KATimer RefCnt +#br0 172.16.5.105 239.1.1.1 Prune 00:07:40 --:--:-- 00:00:36 00:02:50 1 +# +############################################################### +Value iif (\w+) +Value source ([\w\.]+) +Value group ([\w\.]+) +Value state (\w+) +Value uptime ([\d\:\-]+) +Value jointimer ([\d\:\-]+) +Value rstimer ([\d\:\-]+) +Value katimer ([\d\:\-]+) +Value refcnt (\d+) +Value vrf (\w+) + +Start + ^\s*VRF:\s*${vrf}$$ + ^\s*${iif}\s*${source}\s*${group}\s*${state}\s*${uptime}\s*${jointimer}\s*${rstimer}\s*${katimer}\s*${refcnt}$$ -> Record diff --git a/spytest/templates/show_ip_pim_upstream_join_desired.tmpl b/spytest/templates/show_ip_pim_upstream_join_desired.tmpl new file mode 100644 index 00000000000..30f2455955f --- /dev/null +++ b/spytest/templates/show_ip_pim_upstream_join_desired.tmpl @@ -0,0 +1,19 @@ +############################################################### +#sonic# show ip pim upstream-join-desired +#Interface Source Group LostAssert Joins PimInclude JoinDesired EvalJD +#Ethernet46 10.2.1.20 232.0.0.20 no no no no no +# +############################################################### +Value interface (\w+) +Value source ([\w\.]+) +Value group ([\w\.]+) +Value lostassert (\w+) +Value joins (\w+) +Value piminclude (\w+) +Value joindesired (\w+) +Value evaljd (\w+) +Value vrf (\w+) + +Start + ^\s*VRF:\s*${vrf}$$ + ^\s*${interface}\s*${source}\s*${group}\s*${lostassert}\s*${joins}\s*${piminclude}\s*${joindesired}\s*${evaljd}$$ -> Record diff --git a/spytest/templates/show_ip_pim_upstream_rpf.tmpl b/spytest/templates/show_ip_pim_upstream_rpf.tmpl new file mode 100644 index 00000000000..da889f4f05c --- /dev/null +++ b/spytest/templates/show_ip_pim_upstream_rpf.tmpl @@ -0,0 +1,16 @@ +############################################################### +#sonic# show ip pim upstream-rpf +#Source Group RpfIface RibNextHop RpfAddress +#10.2.1.20 232.0.0.20 Ethernet47 10.2.1.20 10.2.1.20 +# +############################################################### +Value source ([\w\.]+) +Value group ([\w\.]+) +Value rpfiface (\w+) +Value ribnexthop ([\w\.]+) +Value rpfaddress ([\w\.]+) +Value vrf (\w+) + +Start + ^\s*VRF:\s*${vrf}$$ + ^\s*${source}\s*${group}\s*${rpfiface}\s*${ribnexthop}\s*${rpfaddress}$$ -> Record diff --git a/spytest/templates/show_ip_route.tmpl b/spytest/templates/show_ip_route.tmpl new file mode 100644 index 00000000000..1f4470c5212 --- /dev/null +++ b/spytest/templates/show_ip_route.tmpl @@ -0,0 +1,33 @@ +Value Filldown TYPE (\w) +Value Filldown SELECTED (\S|\s) +Value Filldown FIB (\S|\s) +Value Filldown NOT_INSTALLED (\S|\s) +Value Filldown IP_ADDRESS (\S+) +Value INTERFACE (\S+) +Value Filldown DURATION (\S+) +Value NEXTHOP (\S+) +Value NH_TYPE (\S+) +Value Filldown DISTANCE (\d+) +Value Filldown COST (\d+) +Value Filldown vrf_name (\S+) +Value dest_vrf_name (\S+) + +Start + ^\s*VRF ${vrf_name}: + ^\s*${TYPE}${SELECTED}${FIB}\s*${IP_ADDRESS}\s*is directly connected,\s*${INTERFACE},\s*${DURATION}\s*$$ -> Record + ^\s*${TYPE}${SELECTED}${FIB}\s*${IP_ADDRESS}\s*\[${DISTANCE}\/${COST}\]\s*is directly connected,\s*${INTERFACE}\(vrf\s${dest_vrf_name}\),\s*${DURATION}\s*$$ -> Record + ^\s*${TYPE}${SELECTED}${FIB}\s*${IP_ADDRESS}\s*\[${DISTANCE}\/${COST}\]\s*is directly connected,\s*${INTERFACE},\s*${DURATION}\s*$$ -> Record + ^\s*${TYPE}${SELECTED}${FIB}\s*${IP_ADDRESS}\s*\[${DISTANCE}\/${COST}\]\s*via\s*${NEXTHOP},\s*${INTERFACE},\s*${DURATION}\s*$$ -> Record + ^\s*${FIB}\s*via\s*${NEXTHOP},\s*${INTERFACE},\s*${DURATION}\s*$$ -> Record + ^\s*${TYPE}${SELECTED}${FIB}\s*${IP_ADDRESS}\s*\[${DISTANCE}\/${COST}\]\s*via\s*${NEXTHOP},\s*${INTERFACE}\s*onlink,\s*${DURATION}\s*$$ -> Record + ^\s*${TYPE}\s*${IP_ADDRESS}\s*\[${DISTANCE}\/${COST}\]\s*via\s*${NEXTHOP},\s*${INTERFACE}\s*inactive\s*onlink,\s*${DURATION}\s*$$ -> Record + ^\s*${TYPE}${SELECTED}${FIB}\s*${IP_ADDRESS}\s*\[${DISTANCE}\/${COST}\]\s*${NEXTHOP}\s*\(${INTERFACE}\),\s*${DURATION}\s*$$ -> Record + ^\s*${TYPE}${SELECTED}${FIB}${NOT_INSTALLED}\s*${IP_ADDRESS}\s*\[${DISTANCE}\/${COST}\]\s*via\s*${NEXTHOP},\s*${INTERFACE},\s*${DURATION}\s*$$ -> Record + ^\s*${TYPE}${SELECTED}\s*${IP_ADDRESS}\s*\[${DISTANCE}\/${COST}\]\s*via\s*${NEXTHOP}\s*${INTERFACE},\s*${DURATION}\s*$$ -> Record + ^\s*${TYPE}${SELECTED}${FIB}${NOT_INSTALLED}\s*${IP_ADDRESS}\s*\[${DISTANCE}\/${COST}\]\s*via\s*${NEXTHOP},\s*${INTERFACE}(\(vrf\s*${dest_vrf_name}\))*\s*${NH_TYPE},\s*${DURATION}\s*$$ -> Record + ^\s*Codes:.* -> Clearall + ^\s*${TYPE}${SELECTED}${FIB}\s*${IP_ADDRESS}\s*via\s*${NEXTHOP}\s*${DISTANCE}\/${COST}\s*${DURATION}\s* -> Record + ^\s*${TYPE}${SELECTED}${FIB}\s*${IP_ADDRESS}\s*Direct\s*${DISTANCE}\/${COST}\s*${DURATION}\s* -> Record + ^\s*via\s*${NEXTHOP}$$ -> Record + ^$$ + diff --git a/spytest/templates/show_ip_route_summary.tmpl b/spytest/templates/show_ip_route_summary.tmpl new file mode 100644 index 00000000000..50581ce1e06 --- /dev/null +++ b/spytest/templates/show_ip_route_summary.tmpl @@ -0,0 +1,24 @@ +Value VRF (\S+) +Value CONNECTED (\d+) +Value EBGP (\d+) +Value IBGP (\d+) +Value STATIC (\d+) +Value OSPF (\d+) +Value TOTAL (\d+) +Value FIB_CONNECTED (\d+) +Value FIB_EBGP (\d+) +Value FIB_IBGP (\d+) +Value FIB_STATIC (\d+) +Value FIB_OSPF (\d+) +Value FIB_TOTAL (\d+) + +Start + ^\s*Route\s*Source\s*Routes\s*FIB.*\(vrf ${VRF}\).* + ^\s*connected\s*${CONNECTED}\s*${FIB_CONNECTED} + ^\s*static\s*${STATIC}\s*${FIB_STATIC} + ^\s*ospf\s*${OSPF}\s*${FIB_OSPF} + ^\s*ebgp\s*${EBGP}\s*${FIB_EBGP} + ^\s*ibgp\s*${IBGP}\s*${FIB_IBGP} + ^\s*---* + ^\s*Totals\s*${TOTAL}\s*${FIB_TOTAL} -> Record + diff --git a/spytest/templates/show_ip_static-anycast-gateway.tmpl b/spytest/templates/show_ip_static-anycast-gateway.tmpl new file mode 100644 index 00000000000..d51323114c1 --- /dev/null +++ b/spytest/templates/show_ip_static-anycast-gateway.tmpl @@ -0,0 +1,39 @@ +######################################################### +# admin@sonic:~$ show ip static-anycast-gateway +# Configured Anycast Gateway MAC address: 00:00:00:01:02:03 +# IPv4 Anycast Gateway MAC address: enable +# Total number of gateway: 3 +# Total number of gateway admin UP: 3 +# Total number of gateway oper UP: 3 +# Interfaces Gateway Address Master Admin/Oper +# ------------ ----------------- -------- ------------ +# Vlan2 2.0.101.100/24 up/up +# Vlan200 200.0.0.10/24 Vrf1 up/up +# 200.1.0.10/24 up/up +######################################################### +Value MAC (\S+) +Value STATUS (\S+) +Value TOTAL (\d+) +Value TOTAL_ADMIN (\d+) +Value TOTAL_OPER (\d+) +Value Filldown INTERFACE (\S+) +Value Filldown GATEWAY (\S+) +Value Filldown MASK (\d+) +Value Filldown VRF (\S+) +Value Filldown ADMIN (\S+) +Value Filldown OPER (\S+) + +Start + ^\s*Configured Anycast Gateway MAC address: ${MAC}\s*$$ + ^\s*Configured Anycast Gateway MAC address: ${MAC} Configured\s*$$ + ^\s*IPv4 Anycast Gateway MAC address: ${STATUS}\s*$$ + ^\s*Total number of gateway: ${TOTAL}\s*$$ + ^\s*Total number of gateway admin UP: ${TOTAL_ADMIN}\s*$$ + ^\s*Total number of gateway oper UP: ${TOTAL_OPER}\s*$$ -> Record + ^\s*${INTERFACE}\s+${GATEWAY}/${MASK}\s+${VRF}\s+${ADMIN}/${OPER}\s*$$ -> Record + ^\s*${INTERFACE}\s+${GATEWAY}/${MASK}\s+\s+${ADMIN}/${OPER}\s*$$ -> Continue.Clearall + ^\s*${INTERFACE}\s+${GATEWAY}/${MASK}\s+\s+${ADMIN}/${OPER}\s*$$ -> Record + ^\s*${GATEWAY}/${MASK}\s+\s+${ADMIN}/${OPER}\s*$$ -> Record + ^$$ + +EOF diff --git a/spytest/templates/show_ip_tables.tmpl b/spytest/templates/show_ip_tables.tmpl new file mode 100644 index 00000000000..edfe1421753 --- /dev/null +++ b/spytest/templates/show_ip_tables.tmpl @@ -0,0 +1,19 @@ +Value pkts (\d+) +Value bytes (\d+) +Value target (\S+) +Value prot (\S+) +Value opt (\S+) +Value source (\S+) +Value destination (\S+) +#Value localip (\S+:\s+\S+) +Value port (\d+) +Value trans_ip (\d+.\d+.\d+.\d+) +Value trans_port (\d+) +Value global_ip_range (\S+) +Value global_port_range (\d+-\d+) + +Start + ^\s+${pkts}\s+${bytes}\s+${target}\s+${prot}\s+${opt}\s+\S+\s+\S+\s+${source}\s+${destination}\s*\S+\s*\S+\s*\S+\s*\S+\s*\S+:${port}\s*to:${trans_ip}:${trans_port} -> Record + ^\s+${pkts}\s+${bytes}\s+${target}\s+${prot}\s+${opt}\s+\S+\s+\S+\s+${source}\s+${destination}\s*\S+\s*\S+:${port}\s*to:${trans_ip}:${trans_port} -> Record + ^\s+${pkts}\s+${bytes}\s+${target}\s+${prot}\s+${opt}\s+\S+\s+\S+\s+${source}\s+${destination}\s*\S+\s*\S+\s*\S+\s*to:${global_ip_range}:${global_port_range} -> Record + ^\s+${pkts}\s+${bytes}\s+${target}\s+${prot}\s+${opt}\s+\S+\s+\S+\s+${source}\s+${destination}\s*\S+\s*\S+\s*\S+\s*to:${trans_ip} -> Record diff --git a/spytest/templates/show_ip_tables_s.tmpl b/spytest/templates/show_ip_tables_s.tmpl new file mode 100644 index 00000000000..86dc4004be3 --- /dev/null +++ b/spytest/templates/show_ip_tables_s.tmpl @@ -0,0 +1,15 @@ +#admin ttyS1 2019-03-25 20:31 +#admin ttyS1 2019-03-25 20:31 (100.127.20.23) +#admin pts/9 May 11 22:32 (100.127.20.23) +#-P INPUT ACCEPT +#-P FORWARD ACCEPT +#-P OUTPUT ACCEPT +#-A INPUT -i Vrf10 -p tcp -m tcp --dport 22 -j ACCEPT +#-A INPUT -i VrfTest -p tcp -m tcp --dport 22 -j ACCEPT +#-A INPUT -i mgmt+ -p tcp -m tcp --dport 22 -j REJECT --reject-with icmp-port-unreachable +#-A INPUT -i Vrf+ -p tcp -m tcp --dport 22 -j REJECT --reject-with icmp-port-unreachable +#-A INPUT -s 127.0.0.1/32 -i lo -j ACCEPT +Value vrf_name (\S+) + +Start + ^-A INPUT -i\s+${vrf_name}.* -> Record diff --git a/spytest/templates/show_ip_vrf_management.tmpl b/spytest/templates/show_ip_vrf_management.tmpl new file mode 100644 index 00000000000..7a3ad7e9725 --- /dev/null +++ b/spytest/templates/show_ip_vrf_management.tmpl @@ -0,0 +1,7 @@ +Value VrfName (\S+) +Value Iface (\S+) + +Start + ^\s*VRF-NAME\s+INTERFACES\s*$$ + ^(-+\s*)*$$ + ^\s*${VrfName}\s+${Iface}\s*$$ -> Record \ No newline at end of file diff --git a/spytest/templates/show_ipv6_dhcp_relay_statistics.tmpl b/spytest/templates/show_ipv6_dhcp_relay_statistics.tmpl new file mode 100644 index 00000000000..94d90806cab --- /dev/null +++ b/spytest/templates/show_ipv6_dhcp_relay_statistics.tmpl @@ -0,0 +1,16 @@ +Value Packets_relayed_from_client_to_server (\S+) +Value Packets_relayed_from_server_to_client (\S+) +Value Errors_relaying_packets_from_clients (\S+) +Value Errors_relaying_packets_from_servers (\S+) +Value Packets_wrong_type_drop_on_downstream (\S+) +Value Packets_wrong_type_drop_on_upstream (\S+) + +Start + ^\s*Packets\s+relayed\s+from\s+client\s+to\s+server\:+\s+${Packets_relayed_from_client_to_server}$$ + ^\s*Packets\s+relayed\s+from\s+server\s+to\s+client\:+\s+${Packets_relayed_from_server_to_client}$$ + ^\s*Errors\s+relaying\s+packets\s+from\s+clients\:+\s+${Errors_relaying_packets_from_clients}$$ + ^\s*Errors\s+relaying\s+packets\s+from\s+servers\:+\s+${Errors_relaying_packets_from_servers}$$ + ^\s*Packets\s+with\s+wrong\s+message\s+type\s+dropped\s+on\s+downstream\s+interface\:+\s+${Packets_wrong_type_drop_on_downstream}$$ + ^\s*Packets\s+with\s+wrong\s+message\s+type\s+dropped\s+on\s+upstream\s+interface\:+\s+${Packets_wrong_type_drop_on_upstream}$$ -> Record + +EOF diff --git a/spytest/templates/show_ipv6_interface.tmpl b/spytest/templates/show_ipv6_interface.tmpl new file mode 100644 index 00000000000..e69de29bb2d diff --git a/spytest/templates/show_ipv6_static-anycast-gateway.tmpl b/spytest/templates/show_ipv6_static-anycast-gateway.tmpl new file mode 100644 index 00000000000..fe4a69f9848 --- /dev/null +++ b/spytest/templates/show_ipv6_static-anycast-gateway.tmpl @@ -0,0 +1,39 @@ +######################################################### +# admin@sonic:~$ show ipv6 static-anycast-gateway +# Configured Anycast Gateway MAC address: 00:00:00:01:02:03 +# IPv6 Anycast Gateway MAC address: enable +# Total number of gateway: 3 +# Total number of gateway admin UP: 3 +# Total number of gateway oper UP: 3 +# Interfaces Gateway Address Master Admin/Oper +# ------------ ----------------- -------- ------------ +# Vlan2 2001::100/64 up/up +# Vlan200 2001:1::10/64 Vrf1 up/up +# 2001:1::12/64 up/up +######################################################### +Value MAC (\S+) +Value STATUS (\S+) +Value TOTAL (\d+) +Value TOTAL_ADMIN (\d+) +Value TOTAL_OPER (\d+) +Value Filldown INTERFACE (\S+) +Value Filldown GATEWAY (\S+) +Value Filldown MASK (\d+) +Value Filldown VRF (\S+) +Value Filldown ADMIN (\S+) +Value Filldown OPER (\S+) + +Start + ^\s*Configured Anycast Gateway MAC address: ${MAC}\s*$$ + ^\s*Configured Anycast Gateway MAC address: ${MAC} Configured\s*$$ + ^\s*IPv6 Anycast Gateway MAC address: ${STATUS}\s*$$ + ^\s*Total number of gateway: ${TOTAL}\s*$$ + ^\s*Total number of gateway admin UP: ${TOTAL_ADMIN}\s*$$ + ^\s*Total number of gateway oper UP: ${TOTAL_OPER}\s*$$ -> Record + ^\s*${INTERFACE}\s+${GATEWAY}/${MASK}\s+${VRF}\s+${ADMIN}/${OPER}\s*$$ -> Record + ^\s*${INTERFACE}\s+${GATEWAY}/${MASK}\s+\s+${ADMIN}/${OPER}\s*$$ -> Continue.Clearall + ^\s*${INTERFACE}\s+${GATEWAY}/${MASK}\s+\s+${ADMIN}/${OPER}\s*$$ -> Record + ^\s*${GATEWAY}/${MASK}\s+\s+${ADMIN}/${OPER}\s*$$ -> Record + ^$$ + +EOF diff --git a/spytest/templates/show_link_state_tracking.tmpl b/spytest/templates/show_link_state_tracking.tmpl new file mode 100644 index 00000000000..d71e6eb1c55 --- /dev/null +++ b/spytest/templates/show_link_state_tracking.tmpl @@ -0,0 +1,9 @@ +Value name (\S+) +Value description (\S+) +Value timeout (\d+) + +Start + ^\s*Name:\s*${name} + ^\s*Description:\s*${description} + ^\s*Timeout:\s*${timeout} + diff --git a/spytest/templates/show_link_state_tracking_group.tmpl b/spytest/templates/show_link_state_tracking_group.tmpl new file mode 100644 index 00000000000..f697fed114f --- /dev/null +++ b/spytest/templates/show_link_state_tracking_group.tmpl @@ -0,0 +1,15 @@ +Value Filldown name (\S+) +Value Filldown description (\S+) +Value Filldown timeout (\d+) +Value Filldown direction (\S+) +Value interface (\w+) +Value direction_state (\S+) + +Start + ^\s*Name:\s*${name} + ^\s*Description:\s*${description} + ^\s*Timeout:\s*${timeout} + ^\s*${direction}:\s* + ^\s*${interface}\s\(${direction_state}\) -> Record + +EOF diff --git a/spytest/templates/show_linktrack_group.tmpl b/spytest/templates/show_linktrack_group.tmpl new file mode 100644 index 00000000000..62695e571a0 --- /dev/null +++ b/spytest/templates/show_linktrack_group.tmpl @@ -0,0 +1,15 @@ +Value Filldown name (\S+) +Value Filldown description (\S+) +Value Filldown timeout (\d+) +Value Filldown direction (\S+) +Value interface (\w+) +Value direction_state (\S+) + +Start + ^\s*Name:\s*${name} + ^\s*Description:\s*${description} + ^\s*Timeout:\s*${timeout}\s*seconds + ^\s*${direction}:\s* + ^\s*${interface}\s\(${direction_state}\) -> Record + +EOF diff --git a/spytest/templates/show_linktrack_summary.tmpl b/spytest/templates/show_linktrack_summary.tmpl new file mode 100644 index 00000000000..1ba82f82f5a --- /dev/null +++ b/spytest/templates/show_linktrack_summary.tmpl @@ -0,0 +1,9 @@ +Value name (\S+) +Value description (\S+) +Value timeout (\d+) + +Start + ^\s*Name:\s*${name} + ^\s*Description:\s*${description} + ^\s*Timeout:\s*${timeout}\s*seconds + diff --git a/spytest/templates/show_lldp_neighbors.tmpl b/spytest/templates/show_lldp_neighbors.tmpl new file mode 100644 index 00000000000..32e32750105 --- /dev/null +++ b/spytest/templates/show_lldp_neighbors.tmpl @@ -0,0 +1,44 @@ +Value Interface (\S+) +Value Via (\S+) +Value RID (\d+) +Value Time (.*) +Value Chassis_ID_type (\S+) +Value Chassis_ID_value (.*) +Value Chassis_name (.*) +Value Chassis_descr (.*) +Value Chassis_ttl (\d+) +Value Chassis_mgmt_ip (\d+.\d+.\d+.\d+) +Value Chassis_mgmt_ipv6 (.*) +Value Chassis_Capability_Bridge (\S+) +Value Chassis_Capability_Router (\S+) +Value Chassis_Capability_Wlan (\S+) +Value Chassis_Capability_Station (\S+) +Value PortID_type (\S+) +Value PortID_value (.*) +Value PortDescr (\S+) + + +Start + ^(-+\s*)*$$ + ^\s*LLDP\s*neighbors: -> LLDP_details + +LLDP_details + ^(-+\s*)*$$ -> Record + ^\s*Interface:\s*${Interface},\s*via:\s*${Via},\s*RID:\s*${RID},\s*Time:\s*${Time}\s*$$ + ^\s*Chassis:\s*$$ + ^\s*ChassisID:\s*${Chassis_ID_type}\s*${Chassis_ID_value}\s*$$ + ^\s*SysName:\s*${Chassis_name}\s*$$ + ^\s*SysDescr:\s*${Chassis_descr}\s*$$ + ^\s*TTL:\s*${Chassis_ttl}\s*$$ + ^\s*MgmtIP:\s*${Chassis_mgmt_ip}\s*$$ + ^\s*MgmtIP:\s*${Chassis_mgmt_ipv6}\s*$$ + ^\s*Capability:\s*Bridge,\s*${Chassis_Capability_Bridge}\s*$$ + ^\s*Capability:\s*Router,\s*${Chassis_Capability_Router}\s*$$ + ^\s*Capability:\s*Wlan,\s*${Chassis_Capability_Wlan}\s*$$ + ^\s*Capability:\s*Station,\s*${Chassis_Capability_Station}\s*$$ + ^\s*Port:\s*$$ + ^\s*PortID:\s*${PortID_type}\s*${PortID_value}\s*$$ + ^\s*PortDescr:\s*${PortDescr}\s*$$ + ^(-+\s*)*$$ + ^$$ + diff --git a/spytest/templates/show_lldp_table.tmpl b/spytest/templates/show_lldp_table.tmpl new file mode 100644 index 00000000000..d89d7c0171e --- /dev/null +++ b/spytest/templates/show_lldp_table.tmpl @@ -0,0 +1,15 @@ +Value LocalPort (\S+) +Value RemoteDevice (\S+) +Value RemotePortID (\S+) +Value Capability (\S+) +Value RemotePortDescr (\S+) + +Start + ^\s*Capability\s*codes:\s*(R)\s*Router,\s*(B)\s*Bridge,\s*(O)\s*Other + ^\s*LocalPort\s+RemoteDevice\s+RemotePortID\s+Capability\s+RemotePortDescr\s*$$ + ^(-+\s*)*$$ + ^\s*${LocalPort}\s+${RemoteDevice}\s+${RemotePortID}\s+${Capability}\s+${RemotePortDescr}\s*$$ -> Record + ^\s*${LocalPort}\s+${RemotePortID}\s*$$ -> Record + ^(-+\s*)*$$ + ^\s*Total\s*entries\s*displayed:\s*\d+\s*$$ + ^$$ diff --git a/spytest/templates/show_mac.tmpl b/spytest/templates/show_mac.tmpl new file mode 100644 index 00000000000..9fa17fecc56 --- /dev/null +++ b/spytest/templates/show_mac.tmpl @@ -0,0 +1,16 @@ +Value SERIALNO (\s*\d+) +Value VLAN (\S+) +Value MACADDRESS (..:..:..:..:..:..) +Value PORT (\S+) +Value DEST_IP (\d+\.\d+\.\d+\.\d+) +Value TYPE (\S+) +Value Fillup total (\d+) + +Start + ^No\.\s+Vlan\s+MacAddress\s+Port\s+Type\s*$$ + ^(-+\s*)*$$ + ^${SERIALNO}\s+${VLAN}\s+${MACADDRESS}\s+${PORT}\s+${TYPE}\s*$$ -> Record + ^${SERIALNO}\s+${VLAN}\s+${MACADDRESS}\s+VxLAN\sDIP:\s${DEST_IP}\s+${TYPE}\s*$$ -> Record + ^\s*Total number of entries\s+${total} + ^$$ + diff --git a/spytest/templates/show_mac_address_table.tmpl b/spytest/templates/show_mac_address_table.tmpl new file mode 100644 index 00000000000..469359ed404 --- /dev/null +++ b/spytest/templates/show_mac_address_table.tmpl @@ -0,0 +1,10 @@ +Value VLAN (\d+) +Value MACADDRESS (..:..:..:..:..:..) +Value TYPE (\S+) +Value PORT (\S+) +Value DEST_IP (\d+\.\d+\.\d+\.\d+) + +Start + ^\s*${VLAN}\s+${MACADDRESS}\s+${TYPE}\s+VxLAN\sDIP:\s${DEST_IP}\s*$$ -> Record + ^\s*${VLAN}\s+${MACADDRESS}\s+${TYPE}\s+${PORT}\s*$$ -> Record + ^$$ diff --git a/spytest/templates/show_mac_address_table_count.tmpl b/spytest/templates/show_mac_address_table_count.tmpl new file mode 100644 index 00000000000..cadfea643d0 --- /dev/null +++ b/spytest/templates/show_mac_address_table_count.tmpl @@ -0,0 +1,10 @@ +Value dynamic_cnt (\d+) +Value static_cnt (\d+) +Value count (\d+) + + +Start + ^\s*Dynamic\s+Address\s+Count\s*:\s+${dynamic_cnt}\s*$$ -> Record + ^\s*Static\s+Address\s+\(User-defined\)\s+Count\s*:\s+${static_cnt}\s*$$ -> Record + ^\s*Total\s+MAC\s+Addresses\s+in\s+Use\s*:\s+${count}\s*$$ -> Record + ^$$ \ No newline at end of file diff --git a/spytest/templates/show_mac_aging_time.tmpl b/spytest/templates/show_mac_aging_time.tmpl new file mode 100644 index 00000000000..5132b921e2a --- /dev/null +++ b/spytest/templates/show_mac_aging_time.tmpl @@ -0,0 +1,5 @@ +Value aging_time (\S+) + +Start + ^\s*Mac\s*Aging-Time\s*:\s*${aging_time}\s*seconds\s*$$ + ^\s*$$ -> Record \ No newline at end of file diff --git a/spytest/templates/show_mac_count.tmpl b/spytest/templates/show_mac_count.tmpl new file mode 100644 index 00000000000..f9cdf9a73d4 --- /dev/null +++ b/spytest/templates/show_mac_count.tmpl @@ -0,0 +1,4 @@ +Value mac_count (\d+) + +Start + ^\s*Total\s*MAC\s*count:${mac_count}\s*$$ -> Record diff --git a/spytest/templates/show_mac_qa.tmpl b/spytest/templates/show_mac_qa.tmpl new file mode 100644 index 00000000000..97a0b5e6664 --- /dev/null +++ b/spytest/templates/show_mac_qa.tmpl @@ -0,0 +1,10 @@ +Value No (\d+) +Value Vlan (\d+) +Value MacAddress (\S+) +Value Port (\S+) +Value Type (\S+) + +Start + ^No.\s+Vlan\s+MacAddress\s+Port\s+Type*$$ + ^(-+\s*)*$$ + ^\s*${No}\s+${Vlan}\s+${MacAddress}\s+${Port}\s+${Type}\s*$$ -> Record \ No newline at end of file diff --git a/spytest/templates/show_management_vrf.tmpl b/spytest/templates/show_management_vrf.tmpl new file mode 100644 index 00000000000..1938987df47 --- /dev/null +++ b/spytest/templates/show_management_vrf.tmpl @@ -0,0 +1,15 @@ +Value mvrfstate (\w+) +Value mvrf_interface (\S+) +Value intf_state (\S+,\S+,\S+,\S+) +Value vrf_val (\S+) + +Start + ^\s*ManagementVRF\s+:\s+${mvrfstate}\s*$$ -> Record + ^\s*Management\s*VRF\s+interfaces\s+in\s+Linux: -> MVRF + +MVRF + ^\s*\d+:\s+${mvrf_interface}:\s+<${intf_state}>\s+.*\s*$$ -> Record + ^\s*vrf\s+table\s+${vrf_val}\s+\w+\s+\w+(\s+\w+\s+\d+\s*)*$$ -> Record + + + diff --git a/spytest/templates/show_mcast_appdb.tmpl b/spytest/templates/show_mcast_appdb.tmpl new file mode 100644 index 00000000000..7ae81913af6 --- /dev/null +++ b/spytest/templates/show_mcast_appdb.tmpl @@ -0,0 +1,8 @@ +Value in_intf (Ethernet\d+|Vlan\d+) +Value out_intf_list (^$|Ethernet\d+|Ethernet\d+,Ethernet\d+|Vlan\d+|Vlan\d+,Vlan\d+) + +Start + ^\s*\d+\S+\s+in_interface\s*$$ -> Continue + ^\s*2\)\s+"${in_intf}"\s*$$ -> Continue + ^\s*\d+\S+\s+out_interfaces\s*$$ -> Continue + ^\s*4\)\s+"${out_intf_list}"\s*$$ \ No newline at end of file diff --git a/spytest/templates/show_mclag_brief.tmpl b/spytest/templates/show_mclag_brief.tmpl new file mode 100644 index 00000000000..c3bc0d628f0 --- /dev/null +++ b/spytest/templates/show_mclag_brief.tmpl @@ -0,0 +1,57 @@ +#################################################### +#sonic# show mclag brief +# +#Domain ID : 2 +#Role : standby +#Session Status : up +#Peer Link Status : up +#Source Address : 12.1.1.2 +#Peer Address : 12.1.1.1 +#Peer Link : PortChannel0002 +#Keepalive Interval : 1 secs +#Session Timeout : 15 secs +#System Mac : 3c:2c:99:53:c7:c0 +# +# +#Number of MLAG Interfaces:4 +#----------------------------------------------------------- +#MLAG Interface Local/Remote Status +#----------------------------------------------------------- +#PortChannel10 down/down +#PortChannel20 down/down +#PortChannel30 down/down +#PortChannel40 down/down +#sonic# +# +#Output: +#['session_status', 'domain_id', 'local_ip', 'peer_ip', 'peer_link_inf', 'keepalive_timer', 'session_timer', 'peer_link_mac', 'node_role', 'num_mclag_intfs', 'mclag_intfs', 'local_status', 'remote_status'] +#[['up', '2', '12.1.1.2', '12.1.1.1', 'PortChannel0002', '1', '15', '3c:2c:99:53:c7:c0', 'standby', '4', [], [], []], ['', '', '', '', '', '', '', '', '', '', ['PortChannel10', 'PortChannel20', 'PortChannel30', 'PortChannel40'], ['down', 'down', 'down', 'down'], ['down', 'down', 'down', 'down']]] +# +##################################################### +Value session_status (\w+) +Value domain_id (\d+) +Value local_ip (\d+\.\d+\.\d+\.\d+) +Value peer_ip (\d+\.\d+\.\d+\.\d+) +Value peer_link_inf (\w+) +Value keepalive_timer (\d+) +Value session_timer (\d+) +Value peer_link_mac (\w\w:\w\w:\w\w:\w\w:\w\w:\w\w) +Value node_role (\w+) +Value num_mclag_intfs (\d+) +Value List mclag_intfs (\w+) +Value List local_status (\w+) +Value List remote_status (\w+) + +Start + ^\s*Session Status\s*: ${session_status}$$ + ^\s*Domain ID\s*: ${domain_id}$$ + ^\s*Source Address\s*: ${local_ip}$$ + ^\s*Peer Address\s*: ${peer_ip}$$ + ^\s*Peer Link\s*: ${peer_link_inf}$$ + ^\s*Keepalive Interval\s*: ${keepalive_timer} secs + ^\s*Session Timeout\s*: ${session_timer} secs + ^\s*System Mac\s*: ${peer_link_mac}$$ + ^\s*Role\s*: ${node_role}$$ + ^\s*Number of MLAG Interfaces:${num_mclag_intfs}$$ -> Record + ^\s*${mclag_intfs}\s*${local_status}/${remote_status}\s*$$ + ^\s*-> Record diff --git a/spytest/templates/show_mclag_interface.tmpl b/spytest/templates/show_mclag_interface.tmpl new file mode 100644 index 00000000000..51fe9e75505 --- /dev/null +++ b/spytest/templates/show_mclag_interface.tmpl @@ -0,0 +1,18 @@ +#################################################### +#A1# show mclag interface 10 2 +# +#Local/Remote Status : up/up +#TrafficDisable : No +#IsolateWithPeerLink : Yes +#A1# +##################################################### +Value mclag_intf_local_state (\w+) +Value mclag_intf_peer_state (\w+) +Value traffic_disable (\w+) +Value isolate_peer_link (\w+) + +Start + ^\s*Local/Remote Status\s*: ${mclag_intf_local_state}/${mclag_intf_peer_state}$$ + ^\s*TrafficDisable\s*: ${traffic_disable}$$ + ^\s*IsolateWithPeerLink\s*: ${isolate_peer_link}$$ -> Record + diff --git a/spytest/templates/show_mirror_session.tmpl b/spytest/templates/show_mirror_session.tmpl new file mode 100644 index 00000000000..5d7b9011690 --- /dev/null +++ b/spytest/templates/show_mirror_session.tmpl @@ -0,0 +1,28 @@ +Value Name (\S+) +Value Status ([a-z]+) +Value SRC_IP (\d+.\d+.\d+.\d+) +Value DST_IP (\d+.\d+.\d+.\d+) +Value GRE_TYPE (\S+) +Value DSCP (\d{1,4}) +Value TTL (\d{1,4}) +Value Queue (\d{1,2}) +Value Policer (\S+) +Value Monitor_port (\S+) +Value erspan_src_port (\S+) +Value erspan_direction (rx|tx|both) +Value span_name (\S+) +Value span_status ([a-z]+) +Value destination_port (\S+) +Value source_port (\S+) +Value direction (rx|tx|both) + +Start + ^\s*${Name}\s*${Status}\s*${SRC_IP}\s*${DST_IP}\s*${GRE_TYPE}\s*${DSCP}\s*${TTL}\s*${Queue}\s*${Policer}\s*${Monitor_port}\s*${erspan_src_port}\s*${erspan_direction}\s*$$ + ^\s*${Name}\s+${Status}\s+${SRC_IP}\s+${DST_IP}\s+${GRE_TYPE}\s+${DSCP}\s+${TTL}\s+${Queue}\s*${Monitor_port}\s*$$ + ^\s*${Name}\s+${Status}\s+${SRC_IP}\s+${DST_IP}\s+${GRE_TYPE}\s+${DSCP}\s+${TTL}\s+${Queue}\s*$$ + ^\s*${Name}\s+${Status}\s+${SRC_IP}\s+${DST_IP}\s+${GRE_TYPE}\s+${DSCP}\s+${TTL}\s*$$ + ^\s*${Name}\s+${Status}\s+${SRC_IP}\s+${DST_IP}\s+${GRE_TYPE}\s+${DSCP}\s*$$ + ^\s*${Name}\s+${Status}\s+${SRC_IP}\s+${DST_IP}\s*$$ + ^\s*${span_name}\s+${span_status}\s+${destination_port}\s+${source_port}\s+${direction}\s*$$ + ^\s*${span_name}\s+${span_status}\s+${destination_port}\s+${direction}\s*$$ + ^\s*${span_name}\s+${span_status}\s+${destination_port}\s*$$ diff --git a/spytest/templates/show_nat_apdb.tmpl b/spytest/templates/show_nat_apdb.tmpl new file mode 100644 index 00000000000..9657b0869b8 --- /dev/null +++ b/spytest/templates/show_nat_apdb.tmpl @@ -0,0 +1,15 @@ +Value translated_ip (\d+.\d+.\d+.\d+) +Value translated_l4_port (\d+) +Value nat_type (\S+at) +Value entry_type (\S+) + +Start + ^\s*\d+\S+\s+"translated_ip"\s*$$ + ^\s*\d+\S+\s+"${translated_ip}"\s*$$ + ^\s*\d+\S+\s+"translated_l4_port"\s*$$ + ^\s*\d+\S+\s+"${translated_l4_port}"\s*$$ + ^\s*\d+\S+\s+"nat_type"\s*$$ + ^\s*\d+\S+\s+"${nat_type}"\s*$$ + ^\s*\d+\S+\s+"entry_type"\s*$$ + ^\s*\d+\S+\s+"${entry_type}"\s*$$ -> Record + diff --git a/spytest/templates/show_nat_config_bindings.tmpl b/spytest/templates/show_nat_config_bindings.tmpl new file mode 100644 index 00000000000..6d84df3faac --- /dev/null +++ b/spytest/templates/show_nat_config_bindings.tmpl @@ -0,0 +1,8 @@ +Value binding_name (\S+) +Value pool_name (\S+) +Value acl_name (\S+|\s) +Value nat_type (\S+) +Value twice_nat_id (\d+|---) + +Start + ^\s*${binding_name}\s+${pool_name}\s+${acl_name}\s+${nat_type}\s+${twice_nat_id}\s*$$ -> Record diff --git a/spytest/templates/show_nat_config_globalvalues.tmpl b/spytest/templates/show_nat_config_globalvalues.tmpl new file mode 100644 index 00000000000..80cd1e609fa --- /dev/null +++ b/spytest/templates/show_nat_config_globalvalues.tmpl @@ -0,0 +1,11 @@ +Value admin_mode (\S+) +Value global_timeout (\d+) +Value tcp_timeout (\d+) +Value udp_timeout (\d+) + + +Start + ^\s*Admin\s+Mode\s+:\s+${admin_mode}\s*$$ + ^\s*Global\s+Timeout\s+:\s+${global_timeout}\s+secs\s*$$ + ^\s*TCP\s+Timeout\s+:\s+${tcp_timeout}\s+secs\s*$$ + ^\s*UDP\s+Timeout\s+:\s+${udp_timeout}\s+secs\s*$$ -> Record \ No newline at end of file diff --git a/spytest/templates/show_nat_config_pool.tmpl b/spytest/templates/show_nat_config_pool.tmpl new file mode 100644 index 00000000000..1f0f04bca06 --- /dev/null +++ b/spytest/templates/show_nat_config_pool.tmpl @@ -0,0 +1,6 @@ +Value pool_name (\S+) +Value global_port_range (\S+) +Value global_ip_range (\S+) + +Start + ^\s*${pool_name}\s+${global_ip_range}\s+${global_port_range}\s*$$ -> Record diff --git a/spytest/templates/show_nat_config_static.tmpl b/spytest/templates/show_nat_config_static.tmpl new file mode 100644 index 00000000000..8511652a8cd --- /dev/null +++ b/spytest/templates/show_nat_config_static.tmpl @@ -0,0 +1,10 @@ +Value nat_type (\S+) +Value protocol (\S+) +Value global_ip (\d+.\d+.\d+.\d+) +Value global_port (\d+|---) +Value local_ip (\d+.\d+.\d+.\d+) +Value local_port (\d+|---) +Value twice_nat_id (\d+|---) + +Start + ^\s*${nat_type}\s+${protocol}\s+${global_ip}\s+${global_port}\s+${local_ip}\s+${local_port}\s+${twice_nat_id}\s*$$ -> Record diff --git a/spytest/templates/show_nat_statistics.tmpl b/spytest/templates/show_nat_statistics.tmpl new file mode 100644 index 00000000000..33bb067ea65 --- /dev/null +++ b/spytest/templates/show_nat_statistics.tmpl @@ -0,0 +1,15 @@ +Value protocol (\S+) +Value src_ip (\d+.\d+.\d+.\d+|---) +Value src_ip_port (\d+| ) +Value dst_ip (\d+.\d+.\d+.\d+|---) +Value dst_ip_port (\d+| ) +Value packets (\d+) +Value bytes (\d+) + +Start + ^\s*Protocol\s+Source\s+Destination\s+Packets\s+Bytes\s*$$ + ^(-+\s*)*$$ + ^\s*${protocol}\s+${src_ip}:${src_ip_port}\s+${dst_ip}:${dst_ip_port}\s+${packets}\s+${bytes}\s*$$ -> Record + ^\s*${protocol}\s+${src_ip}\s+${dst_ip}:${dst_ip_port}\s+${packets}\s+${bytes}\s*$$ -> Record + ^\s*${protocol}\s+${src_ip}:${src_ip_port}\s+${dst_ip}\s+${packets}\s+${bytes}\s*$$ -> Record + ^\s*${protocol}\s+${src_ip}\s+${dst_ip}\s+${packets}\s+${bytes}\s*$$ -> Record diff --git a/spytest/templates/show_nat_translations.tmpl b/spytest/templates/show_nat_translations.tmpl new file mode 100644 index 00000000000..a59a2cf1558 --- /dev/null +++ b/spytest/templates/show_nat_translations.tmpl @@ -0,0 +1,18 @@ +Value protocol (\S+) +Value src_ip (\d+.\d+.\d+.\d+|-+) +Value src_ip_port (\d+|\s+) +Value dst_ip (\d+.\d+.\d+.\d+|-+) +Value dst_ip_port (\d+|\s+) +Value trn_src_ip (\d+.\d+.\d+.\d+|-+) +Value trn_src_ip_port (\d+|\s+) +Value trn_dst_ip (\d+.\d+.\d+.\d+|-+) +Value trn_dst_ip_port (\d+|\s+) + +Start + ^\s*Protocol\s+Source IP\s+Destination IP\s+Translated Source\s+Translated Destination\s*$$ + ^(-+\s*)*$$ + ^\s*${protocol}\s+${src_ip}:${src_ip_port}\s+${dst_ip}:${dst_ip_port}\s+${trn_src_ip}:${trn_src_ip_port}\s+${trn_dst_ip}:${trn_dst_ip_port}\s*$$ -> Record + ^\s*${protocol}\s+${src_ip}:${src_ip_port}\s+${dst_ip}\s+${trn_src_ip}:${trn_src_ip_port}\s+${trn_dst_ip}\s*$$ -> Record + ^\s*${protocol}\s+${src_ip}\s+${dst_ip}:${dst_ip_port}\s+${trn_src_ip}\s+${trn_dst_ip}:${trn_dst_ip_port}\s*$$ -> Record + ^\s*${protocol}\s+${src_ip}\s+${dst_ip}\s+${trn_src_ip}\s+${trn_dst_ip}\s*$$ -> Record + diff --git a/spytest/templates/show_nat_translations_count.tmpl b/spytest/templates/show_nat_translations_count.tmpl new file mode 100644 index 00000000000..c6d92781300 --- /dev/null +++ b/spytest/templates/show_nat_translations_count.tmpl @@ -0,0 +1,14 @@ +Value static_nat_entries (\d+) +Value static_napt_entries (\d+) +Value dynamic_nat_entries (\d+) +Value dynamic_napt_entries (\d+) +Value total_entries (\d+) + + + +Start + ^\s*Static NAT Entries\s*\.+\s+${static_nat_entries}\s*$$ + ^\s*Static NAPT Entries\s*\.+\s+${static_napt_entries}\s*$$ + ^\s*Dynamic NAT Entries\s*\.+\s+${dynamic_nat_entries}\s*$$ + ^\s*Dynamic NAPT Entries\s*\.+\s+${dynamic_napt_entries}\s*$$ + ^\s*Total Entries\s*\.+\s+${total_entries}\s*$$ diff --git a/spytest/templates/show_ndp.tmpl b/spytest/templates/show_ndp.tmpl new file mode 100644 index 00000000000..fcd33122a2d --- /dev/null +++ b/spytest/templates/show_ndp.tmpl @@ -0,0 +1,12 @@ +Value ADDRESS (\S+) +Value MACADDRESS (\S+) +Value INTERFACE (\S+) +Value VLAN (\d+|-) +Value STATUS (\S+) +Value Fillup count (\d+) + +Start + ^\s*Address\s+MacAddress\s+Iface\s+Vlan\s+Status\s*$$ + ^(-+\s*)*$$ + ^\s*${ADDRESS}\s+${MACADDRESS}\s+${INTERFACE}\s+${VLAN}\s+${STATUS}\s*$$ -> Record + ^\s*Total\s+number\s+of\s+entries\s+${count} diff --git a/spytest/templates/show_ntp.tmpl b/spytest/templates/show_ntp.tmpl new file mode 100644 index 00000000000..4342decb667 --- /dev/null +++ b/spytest/templates/show_ntp.tmpl @@ -0,0 +1,15 @@ +Value remote (\S+) +Value refid (\S+) +Value st (\d+) +Value t (\S+) +Value when (\S+) +Value poll (\S+) +Value reach (\S+) +Value delay (\S+) +Value offset (\S+) +Value jitter (\S+) + +Start + ^\s*remote\s+refid\s+st\s+t\s+when\s+poll\s+reach\s+delay\s+offset\s+jitter\s*$$ + ^(=+\s*)*$$ + ^\s*${remote}\s+${refid}\s+${st}\s+${t}\s+${when}\s+${poll}\s+${reach}\s+${delay}\s+${offset}\s+${jitter}\s*$$ -> Record \ No newline at end of file diff --git a/spytest/templates/show_ntpstat.tmpl b/spytest/templates/show_ntpstat.tmpl new file mode 100644 index 00000000000..d68a4e84f59 --- /dev/null +++ b/spytest/templates/show_ntpstat.tmpl @@ -0,0 +1,10 @@ +Value server (.\d+.\d+.\d+.\d+.) +Value stratum (\d+) +Value time (\d+) +Value poll (\d+) + +Start + ^.*\s+${server}\s+.*${stratum} + ^.*\s+${time}\s+ms + ^.*\s+${poll}\s+s + ^.*$$ -> Record \ No newline at end of file diff --git a/spytest/templates/show_pfc_counters.tmpl b/spytest/templates/show_pfc_counters.tmpl new file mode 100644 index 00000000000..6f3885027c9 --- /dev/null +++ b/spytest/templates/show_pfc_counters.tmpl @@ -0,0 +1,20 @@ +Value Port (\S+) +Value Filldown Port_Mode (Port Tx|Port Rx) +Value PFC0 (\d+(,\d+)+|\d+) +Value PFC1 (\d+(,\d+)+|\d+) +Value PFC2 (\d+(,\d+)+|\d+) +Value PFC3 (\d+(,\d+)+|\d+) +Value PFC4 (\d+(,\d+)+|\d+) +Value PFC5 (\d+(,\d+)+|\d+) +Value PFC6 (\d+(,\d+)+|\d+) +Value PFC7 (\d+(,\d+)+|\d+) + + +Start + ^Last cached.*$$ + ^\s*${Port_Mode}\s+PFC0\s+PFC1\s+PFC2\s+PFC3\s+PFC4\s+PFC5\s+PFC6\s+PFC7\s*$$ + ^(-+\s*)*$$ + ^\s*${Port}\s+${PFC0}\s+${PFC1}\s+${PFC2}\s+${PFC3}\s+${PFC4}\s+${PFC5}\s+${PFC6}\s+${PFC7}\s*$$ -> Record + ^$$ + +EOF \ No newline at end of file diff --git a/spytest/templates/show_platform_fanstatus.tmpl b/spytest/templates/show_platform_fanstatus.tmpl new file mode 100644 index 00000000000..bd23c0bbf9a --- /dev/null +++ b/spytest/templates/show_platform_fanstatus.tmpl @@ -0,0 +1,7 @@ +Value fan (\S+) +Value status (\S+) +Value speed (\d+|N/A) +Value direction (\S+|N/A) + +Start + ^\s*${fan}\s+${status}\s+${speed}\s+${direction}\s*$$ -> Record diff --git a/spytest/templates/show_platform_psustatus.tmpl b/spytest/templates/show_platform_psustatus.tmpl new file mode 100644 index 00000000000..5f4e9cdf0dc --- /dev/null +++ b/spytest/templates/show_platform_psustatus.tmpl @@ -0,0 +1,9 @@ +Value PSU (\S+\s+\d+) +Value Status (\S+) + +Start + ^\s*PSU\s+Status\s*$$ + ^(-+\s*)*$$ + ^\s*${PSU}\s+${Status}\s*$$ -> Record + ^$$ + ^Error.* -> Error "Error: fail to get psu status from state DB" diff --git a/spytest/templates/show_platform_psusummary.tmpl b/spytest/templates/show_platform_psusummary.tmpl new file mode 100644 index 00000000000..0710f4580d2 --- /dev/null +++ b/spytest/templates/show_platform_psusummary.tmpl @@ -0,0 +1,22 @@ +Value psu (\w+\s\d+) +Value psu_status (\S+\s*\S+|\S+) +Value manufacturer_id (\S+\s*\S+|\S+) +Value model (\S+) +Value serial_number (\S+) +Value output_voltage (\S+) +Value output_current (\S+) +Value output_power (\S+) +Value fan_direction (\S+) +Value fan_speed (\d+) + +Start + ^\s*${psu}:\s+${psu_status}\s* + ^\s*Manufacturer\s*Id:\s*${manufacturer_id}\s*$$ + ^\s*Model:\s*${model}\s*$$ + ^\s*Serial\s*Number:\s*${serial_number}\s*$$ + ^\s*Output\s*Voltage\s*\(mV\):\s*${output_voltage}\s*$$ + ^\s*Output\s*Current\s*\(mA\):\s*${output_current}\s*$$ + ^\s*Output\s*Power\s*\(mW\):\s*${output_power}\s*$$ + ^\s*Fan\s*Direction:\s*${fan_direction}\s*$$ + ^\s*Fan\s*Speed\s*\(RPM\):\s*${fan_speed}\s*$$ + ^$$ -> Record diff --git a/spytest/templates/show_platform_summary.tmpl b/spytest/templates/show_platform_summary.tmpl new file mode 100644 index 00000000000..f20f781bbc9 --- /dev/null +++ b/spytest/templates/show_platform_summary.tmpl @@ -0,0 +1,11 @@ +#Platform: x86_64-accton_as5712_54x-r0 +#HwSKU: Accton-AS5712-54X +#ASIC: broadcom +Value Platform (\S+) +Value HwSKU (\S+) +Value ASIC (\S+) + +Start + ^Platform:\s+${Platform} -> Continue + ^HwSKU:\s+${HwSKU} -> Continue + ^ASIC:\s+${ASIC} diff --git a/spytest/templates/show_platform_syseeprom.tmpl b/spytest/templates/show_platform_syseeprom.tmpl new file mode 100644 index 00000000000..b03c445a5dd --- /dev/null +++ b/spytest/templates/show_platform_syseeprom.tmpl @@ -0,0 +1,14 @@ +Value tlv_name (\S+\s+\S+|\S+|\S+\s+\S+\s+\S+) +Value code (\S+) +Value len (\d+) +Value value (\S+|\S+\s+\S+) + +Start + ^\s*TlvInfo\s+Header: + ^\s*Id\s+String:\s+TlvInfo + ^\s*Version:\s+\d+ + ^\s*Total\s+Length:\s+\d+ + ^\s*TLV Name\s+Code\s+Len\s+Value\s*$$ + ^(-+\s*)*$$ + ^\s*${tlv_name}\s+${code}\s+${len}\s+${value}\s*$$ -> Record + ^\s*${tlv_name}\s+${code}\s+${len}\s*$$ -> Record diff --git a/spytest/templates/show_policy.tmpl b/spytest/templates/show_policy.tmpl new file mode 100644 index 00000000000..33dc2370396 --- /dev/null +++ b/spytest/templates/show_policy.tmpl @@ -0,0 +1,21 @@ +Value policy_name (\w+?) +Value class_name ([\w\S]+) +Value priority_val (\d+) +Value stage (\w+) +Value dscp_val (\d+) +Value pcp_val (\d+) +Value interface (\w+) +Value desc_name (\w+) + +Start + ^Policy\s+${policy_name}\s+Type\s+qos\s*$$ + ^\s*Description:.*$$ + ^\s*${desc_name}*$$ + ^\s*Flow\s+${class_name}\s+at\s+priority\s+${priority_val}\s*$$ + ^\s*Description:.*$$ + ^\s*${desc_name}*$$ + ^\s*set-dscp\s+${dscp_val}\s*$$ + ^\s*set-pcp\s+${pcp_val}\s*$$ + ^\s*Applied to:.*$$ + ^\s*${interface}\s+at\s+${stage}\s*$$ -> Record + ^$$ diff --git a/spytest/templates/show_portchannel_appdb.tmpl b/spytest/templates/show_portchannel_appdb.tmpl new file mode 100644 index 00000000000..0b15701dae6 --- /dev/null +++ b/spytest/templates/show_portchannel_appdb.tmpl @@ -0,0 +1,12 @@ +Value admin_status (\S+) +Value oper_status (\S+) +Value mtu (\d+) +Value active (\S+) +Value name (\S+) + +Start + ^\s*2\)\s*\"${admin_status}\"\s*$$ + ^\s*4\)\s*\"${oper_status}\"\s*$$ + ^\s*6\)\s*\"${mtu}\"\s*$$ + ^\s*8\)\s*\"${active}\"\s*$$ + ^\s*10\)\s*\"${name}\"\s*$$ -> Record diff --git a/spytest/templates/show_portchannel_configdb.tmpl b/spytest/templates/show_portchannel_configdb.tmpl new file mode 100644 index 00000000000..98bf0845f77 --- /dev/null +++ b/spytest/templates/show_portchannel_configdb.tmpl @@ -0,0 +1,8 @@ +Value admin_status (\S+) +Value static (\S+) +Value mtu (\d+) + +Start + ^\s*2\)\s*\"${admin_status}\"\s*$$ + ^\s*4\)\s*\"${static}\"\s*$$ + ^\s*6\)\s*\"${mtu}\"\s*$$ -> Record diff --git a/spytest/templates/show_portchannel_kernel.tmpl b/spytest/templates/show_portchannel_kernel.tmpl new file mode 100644 index 00000000000..a8fce151751 --- /dev/null +++ b/spytest/templates/show_portchannel_kernel.tmpl @@ -0,0 +1,14 @@ +Value portchannel (\S+) +Value desc (\S+) +Value mtu (\d+) +Value qdisc (\S+) +Value state (\S+) +Value mode (\S+) +Value group (\S+) +Value qlen (\d+) +Value mac (\S+) +Value brd (\S+) + +Start + ^\s*\d+:\s*${portchannel}:\s*<${desc}>\s+mtu\s+${mtu}\s+qdisc\s+${qdisc}\s+state\s+${state}\s+mode\s+${mode}\s+group\s+${group}\s+qlen\s+${qlen}$$ + ^\s*link/ether\s+${mac}\s+brd\s+${brd}\s*$$ -> Record diff --git a/spytest/templates/show_portchannel_statedb.tmpl b/spytest/templates/show_portchannel_statedb.tmpl new file mode 100644 index 00000000000..9c43fa9d159 --- /dev/null +++ b/spytest/templates/show_portchannel_statedb.tmpl @@ -0,0 +1,4 @@ +Value state (\S+) + +Start + ^\s*2\)\s*\"${state}\"\s*$$ -> Record diff --git a/spytest/templates/show_portchannel_summary.tmpl b/spytest/templates/show_portchannel_summary.tmpl new file mode 100644 index 00000000000..52bb5a4bdff --- /dev/null +++ b/spytest/templates/show_portchannel_summary.tmpl @@ -0,0 +1,15 @@ +Value Filldown group (\d+) +Value Filldown name (\S+) +Value Filldown state (D|U) +Value Filldown type (\w+) +Value Filldown protocol (LACP|NONE) +Value port (\w+) +Value port_state (D|U) + +Start + ^\s*${group}\s+${name}\s+\(${state}\)\s+${type}\s+${protocol}\s+${port}\(${port_state}\)\s*$$ -> Record + ^\s*${group}\s+${name}\s+\(${state}\)\s+${type}\s+${protocol}\s*$$ -> Record + ^\s*${port}\(${port_state}\)\s*$$ -> Record + ^$$ + +EOF diff --git a/spytest/templates/show_portgroup.tmpl b/spytest/templates/show_portgroup.tmpl new file mode 100644 index 00000000000..cafcad6e2d5 --- /dev/null +++ b/spytest/templates/show_portgroup.tmpl @@ -0,0 +1,6 @@ +Value portgroup (\d+) +Value ports (Ethernet\d+-\d+) +Value valid_speeds ((\d+,?)+) + +Start + ^\s*${portgroup}\s+${ports}\s+${valid_speeds}\s*$$ -> Record \ No newline at end of file diff --git a/spytest/templates/show_priority_group_threshold.tmpl b/spytest/templates/show_priority_group_threshold.tmpl new file mode 100644 index 00000000000..b908510f013 --- /dev/null +++ b/spytest/templates/show_priority_group_threshold.tmpl @@ -0,0 +1,13 @@ +Value port (\S+) +Value pg0 (\d+) +Value pg1 (\d+) +Value pg2 (\d+) +Value pg3 (\d+) +Value pg4 (\d+) +Value pg5 (\d+) +Value pg6 (\d+) +Value pg7 (\d+) + +Start + ^\s*${port}\s+${pg0}\s+${pg1}\s+${pg2}\s+${pg3}\s+${pg4}\s+${pg5}\s+${pg6}\s+${pg7}\s*$$ -> Record + diff --git a/spytest/templates/show_psample_stats.tmpl b/spytest/templates/show_psample_stats.tmpl new file mode 100644 index 00000000000..6e4a33c5ef5 --- /dev/null +++ b/spytest/templates/show_psample_stats.tmpl @@ -0,0 +1,27 @@ +Value dcb_type (\d+) +Value psample_cb (\d+) +Value psample_module (\d+) +Value psample (\d+) +Value pass_through (\d+) +Value drop_no_psample (\d+) +Value drop_sampling (\d+) +Value drop_no_skb (\d+) +Value drop_psample_not_ready (\d+) +Value drop_metadeta (\d+) +Value invalid_src_port (\d+) +Value invalid_dst_port (\d+) + +Start + ^\s*DCB\s+type\s+${dcb_type}\s* + ^\s*pkts\s+filter\s+psample\s+cb\s+${psample_cb}\s* + ^\s*pkts\s+sent\s+to\s+psample\s+module\s+${psample_module}\s* + ^\s*pkts\s+handled\s+by\s+psample\s+${psample}\s* + ^\s*pkts\s+pass\s+through\s+${pass_through}\s* + ^\s*pkts\s+drop\s+no\s+psample\s+group\s+${drop_no_psample}\s* + ^\s*pkts\s+drop\s+sampling\s+disabled\s+${drop_sampling}\s* + ^\s*pkts\s+drop\s+no\s+skb\s+${drop_no_skb}\s* + ^\s*pkts\s+drop\s+psample\s+not\s+ready\s+${drop_psample_not_ready}\s* + ^\s*pkts\s+drop\s+metadata\s+parse\s+error\s+${drop_metadeta}\s* + ^\s*pkts\s+with\s+invalid\s+src\s+port\s+${invalid_src_port}\s* + ^\s*pkts\s+with\s+invalid\s+dst\s+port\s+${invalid_dst_port}\s* + ^$$ -> Record diff --git a/spytest/templates/show_ptp.tmpl b/spytest/templates/show_ptp.tmpl new file mode 100644 index 00000000000..e42aada2a3d --- /dev/null +++ b/spytest/templates/show_ptp.tmpl @@ -0,0 +1,7 @@ +Value port (\w+) +Value mode (\w+) + +Start + ^\s*${port}\s+${mode}\s*$$ -> Record + +EOF diff --git a/spytest/templates/show_ptp_clock.tmpl b/spytest/templates/show_ptp_clock.tmpl new file mode 100644 index 00000000000..b4edc5c9a80 --- /dev/null +++ b/spytest/templates/show_ptp_clock.tmpl @@ -0,0 +1,34 @@ +Value mode (\w+) +Value domain_profile ([0-9A-Za-z._]+) +Value network_transport (\w+\s+\w+) +Value domain_number (\d+) +Value clock_id (\w+\.\w+\.\w+) +Value priority1 (\d+) +Value priority2 (\d+) +Value two_step (\w+) +Value slave_only (\w+) +Value number_ports (\d+) +Value clock_class (\d+) +Value clock_accuracy (\d+) +Value ofst_log_var (\d+) +Value mean_path_delay (\d+) +Value steps_removed (\d+) +Value offset_from_master ([0-9\-]+) + +Start + ^\s*Mode\s+${mode}\s*$$ + ^\s*Domain\s+Profile\s+${domain_profile}\s*$$ + ^\s*Network\s+Transport\s+${network_transport}\s*$$ + ^\s*Domain\s+Number\s+${domain_number}\s*$$ + ^\s*Clock\s+Identity\s+${clock_id}\s*$$ + ^\s*Priority1\s+${priority1}\s*$$ + ^\s*Priority2\s+${priority2}\s*$$ + ^\s*Two\s+Step\s+${two_step}\s*$$ + ^\s*Slave\s+Only\s+${slave_only}\s*$$ + ^\s*Number\s+Ports\s+${number_ports}\s*$$ + ^\s*Clock\s+Class\s+${clock_class}\s*$$ + ^\s*Clock\s+Accuracy\s+${clock_accuracy}\s*$$ + ^\s*Ofst\s+Scaled\s+Log\s+Var\s+${ofst_log_var}\s*$$ + ^\s*Mean\s+Path\s+Delay\s+${mean_path_delay}\s*$$ + ^\s*Steps\s+Removed\s+${steps_removed}\s*$$ + ^\s*Offset\s+from\s+master\s+${offset_from_master}\s*$$ -> Record diff --git a/spytest/templates/show_ptp_parent.tmpl b/spytest/templates/show_ptp_parent.tmpl new file mode 100644 index 00000000000..22f2d095677 --- /dev/null +++ b/spytest/templates/show_ptp_parent.tmpl @@ -0,0 +1,26 @@ +Value parent_clock_id (\w+\.\w+\.\w+) +Value port_number (\d+) +Value gm_clock_class (\d+) +Value gm_off_scale_log_var (\d+) +Value gm_clock_accuracy (\d+) +Value gm_id (\w+\.\w+\.\w+) +Value gm_priority1 (\d+) +Value gm_priority2 (\d+) +Value stats_valid (\w+) +Value observed_off_scale_log_var (\d+) +Value observed_clock_phase_change_rate (\d+) + +Start + ^\s*Parent\s+Clock\s+Identity\s+${parent_clock_id}\s*$$ + ^\s*Port\s+Number\s+${port_number}\s*$$ + ^\s*Grandmaster\s+Clock\s+Class\s+${gm_clock_class}\s*$$ + ^\s*Grandmaster\s+Off\s+Scaled\s+Log\s+Var\s+${gm_off_scale_log_var}\s*$$ + ^\s*Grandmaster\s+Clock\s+Accuracy\s+${gm_clock_accuracy}\s*$$ + ^\s*Grandmaster\s+Identity\s+${gm_id}\s*$$ + ^\s*Grandmaster\s+Priority1\s+${gm_priority1}\s*$$ + ^\s*Grandmaster\s+Priority2\s+${gm_priority2}\s*$$ + ^\s*Stats\s+Valid\s+${stats_valid}\s*$$ + ^\s*Observed\s+Off\s+Scaled\s+Log\s+Var\s+${observed_off_scale_log_var}\s*$$ + ^\s*Observed\s+Clock\s+Phase\s+Chg\s+Rate\s+${observed_clock_phase_change_rate}\s*$$ -> Record + +EOF diff --git a/spytest/templates/show_ptp_port.tmpl b/spytest/templates/show_ptp_port.tmpl new file mode 100644 index 00000000000..0bd2b181e4c --- /dev/null +++ b/spytest/templates/show_ptp_port.tmpl @@ -0,0 +1,20 @@ +Value log_sync_interval (\d+) +Value delay_mechanism (\w+) +Value port_state (\w+) +Value peer_mean_path_delay (\d+) +Value port_number (\d+) +Value version_number (\d+) +Value log_announce_interval (\d+) +Value log_min_pdelay_req_interval (\d+) + +Start + ^\s*Log\s+Sync\s+Interval\s+${log_sync_interval}\s*$$ + ^\s*Delay\s+Mechanism\s+${delay_mechanism}\s*$$ + ^\s*Port\s+State\s+${port_state}\s*$$ + ^\s*Peer\s+Mean\s+Path\s+delay\s+${peer_mean_path_delay}\s*$$ + ^\s*Port\s+Number\s+${port_number}\s*$$ + ^\s*Version\s+Number\s+${version_number}\s*$$ + ^\s*Log\s+Announce\s+Interval\s+${log_announce_interval}\s*$$ + ^\s*Log\s+Min\s+PDelay\s+Req\s+Interval\s+${log_min_pdelay_req_interval}\s*$$ -> Record + +EOF diff --git a/spytest/templates/show_ptp_time_property.tmpl b/spytest/templates/show_ptp_time_property.tmpl new file mode 100644 index 00000000000..9b3a6bc24e4 --- /dev/null +++ b/spytest/templates/show_ptp_time_property.tmpl @@ -0,0 +1,18 @@ +Value cur_utc_offset_valid (\S+) +Value cur_utc_offset (\d+) +Value leap59 (\S+) +Value leap61 (\S+) +Value time_traceable (\S+) +Value freq_traceable (\S+) +Value ptp_timescale (\S+) + +Start + ^\s*Curr\s+UTC\s+Offset\s+Vld\s+${cur_utc_offset_valid}\s*$$ + ^\s*Curr\s+UTC\s+Offset\s+${cur_utc_offset}\s*$$ + ^\s*Leap59\s+${leap59}\s*$$ + ^\s*Leap61\s+${leap61}\s*$$ + ^\s*Time\s+Traceable\s+${time_traceable}\s*$$ + ^\s*Freq\s+Traceable\s+${freq_traceable}\s*$$ + ^\s*PTP\s+Timescale\s+${ptp_timescale}\s*$$ -> Record + +EOF diff --git a/spytest/templates/show_queue_counters.tmpl b/spytest/templates/show_queue_counters.tmpl new file mode 100644 index 00000000000..fe83708bb7e --- /dev/null +++ b/spytest/templates/show_queue_counters.tmpl @@ -0,0 +1,13 @@ +Value PORT (\S+) +Value TXQ (\S+) +Value PKTS_COUNT (\S+) +Value BYTE_COUNT (\S+) +Value PKTS_DROP (\S+) +Value BYTE_DROP (\S+) + +Start + ^\s*Port\s+TxQ\s+Counter/pkts\s+Counter/bytes\s+Drop/pkts\s+Drop/bytes + ^(-+\s*)*$$ + ^\s*${PORT}\s+${TXQ}\s+${PKTS_COUNT}\s+${BYTE_COUNT}\s+${PKTS_DROP}\s+${BYTE_DROP} -> Record + +EOF diff --git a/spytest/templates/show_queue_threshold_multicast.tmpl b/spytest/templates/show_queue_threshold_multicast.tmpl new file mode 100644 index 00000000000..81306dd0ea1 --- /dev/null +++ b/spytest/templates/show_queue_threshold_multicast.tmpl @@ -0,0 +1,13 @@ +Value port (\S+) +Value mc0 (\d+) +Value mc1 (\d+) +Value mc2 (\d+) +Value mc3 (\d+) +Value mc4 (\d+) +Value mc5 (\d+) +Value mc6 (\d+) +Value mc7 (\d+) + +Start + ^\s*${port}\s+${mc0}\s+${mc1}\s+${mc2}\s+${mc3}\s+${mc4}\s+${mc5}\s+${mc6}\s+${mc7}\s*$$ -> Record + diff --git a/spytest/templates/show_queue_threshold_unicast.tmpl b/spytest/templates/show_queue_threshold_unicast.tmpl new file mode 100644 index 00000000000..bf119d32b0e --- /dev/null +++ b/spytest/templates/show_queue_threshold_unicast.tmpl @@ -0,0 +1,13 @@ +Value port (\S+) +Value uc0 (\d+) +Value uc1 (\d+) +Value uc2 (\d+) +Value uc3 (\d+) +Value uc4 (\d+) +Value uc5 (\d+) +Value uc6 (\d+) +Value uc7 (\d+) + +Start + ^\s*${port}\s+${uc0}\s+${uc1}\s+${uc2}\s+${uc3}\s+${uc4}\s+${uc5}\s+${uc6}\s+${uc7}\s*$$ -> Record + diff --git a/spytest/templates/show_queue_watermark_cpu.tmpl b/spytest/templates/show_queue_watermark_cpu.tmpl new file mode 100644 index 00000000000..d162d15dd39 --- /dev/null +++ b/spytest/templates/show_queue_watermark_cpu.tmpl @@ -0,0 +1,7 @@ +Value Queue (\S+) +Value Bytes (\d+) + + +Start + ^\s*${Queue}\s+${Bytes}\s*$$ -> Record + diff --git a/spytest/templates/show_queue_watermark_multicast.tmpl b/spytest/templates/show_queue_watermark_multicast.tmpl new file mode 100644 index 00000000000..0352793e32b --- /dev/null +++ b/spytest/templates/show_queue_watermark_multicast.tmpl @@ -0,0 +1,13 @@ +Value port (\S+) +Value mc8 (\d+) +Value mc9 (\d+) +Value mc10 (\d+) +Value mc11 (\d+) +Value mc12 (\d+) +Value mc13 (\d+) +Value mc14 (\d+) +Value mc15 (\d+) + +Start + ^\s*${port}\s+${mc8}\s+${mc9}\s+${mc10}\s+${mc11}\s+${mc12}\s+${mc13}\s+${mc14}\s+${mc15}\s*$$ -> Record + diff --git a/spytest/templates/show_radius.tmpl b/spytest/templates/show_radius.tmpl new file mode 100644 index 00000000000..726ec01c10d --- /dev/null +++ b/spytest/templates/show_radius.tmpl @@ -0,0 +1,44 @@ +Value Global_Auth_type (\S+\s*\(\S+\)|\S+) +Value Global_Retransmit (\d+) +Value Global_Timeout (\d+\s*\(\S+\)|\d+) +Value Global_Passkey (\S+\s*\(\S+\)|\S+) +Value Global_Source_Ip (\S+) +Value Address (\S+) +Value Priority (\d+) +Value Retransmit (\d+|-) +Value Auth_Port (\d+) +Value Auth_type (\S+) +Value Passkey (\S+) +Value Timeout (\d+|-) +Value vrf_mgmt (\S+) + +Start + ^\s*RADIUS\s+global\s+auth_type\s+${Global_Auth_type}\s*$$ -> CLICK + ^\s*RADIUS\s+Global\s+Configuration\s*$$ -> KLISH + ^\s*HOST\s+AUTH-TYPE\s+KEY\s+AUTH-PORT\s+PRIORITY\s+TIMEOUT\s+RTSMT\s+VRF\s*$$ -> KLISH + +CLICK + ^RADIUS\s+global\s+auth_type\s+${Global_Auth_type}\s*$$ + ^RADIUS\s+global\s+retransmit\s+${Global_Retransmit}\s*$$ + ^RADIUS\s+global\s+timeout\s+${Global_Timeout}\s*$$ + ^RADIUS\s+global\s+passkey\s+${Global_Passkey}\s*$$ + ^RADIUS\s+global\s+source_ip\s+${Global_Source_Ip}\s*$$ + ^RADIUS_SERVER\s+address\s+${Address}\s*$$ + ^\s*priority\s+${Priority}\s*$$ + ^\s*auth_port\s+${Auth_Port}\s*$$ + ^\s*retransmit\s+${Retransmit}\s*$$ + ^\s*auth_type\s+${Auth_type}\s*$$ + ^\s*timeout\s+${Timeout}\s*$$ + ^\s*passkey\s+${Passkey}\s*$$ + ^\s*vrf\s+${vrf_mgmt}\s*$$ + ^$$ -> Record + +KLISH + ^\s*source-ip\s*:\s*${Global_Source_Ip}\s*$$ + ^\s*timeout\s*:\s*${Global_Timeout}\s*$$ + ^\s*auth-type\s*:\s*${Global_Auth_type}\s*$$ + ^\s*key\s*:\s*${Global_Passkey}\s*$$ + ^\s*retransmit\s*:\s*${Global_Retransmit}\s*$$ -> Record + ^\s*${Address}\s+${Auth_type}\s+${Passkey}\s+${Auth_Port}\s+${Priority}\s+${Timeout}\s+${Retransmit}\s+${vrf_mgmt}\s*$$ -> Record + ^\s*${Address}\s+${Auth_type}\s+${Passkey}\s+${Auth_Port}\s+${Priority}\s*$$ -> Record + ^\s*${Address}\s+${Passkey}\s+${Auth_Port}\s+${Priority}\s*$$ -> Record diff --git a/spytest/templates/show_reboot-cause.tmpl b/spytest/templates/show_reboot-cause.tmpl new file mode 100644 index 00000000000..e8a61f84641 --- /dev/null +++ b/spytest/templates/show_reboot-cause.tmpl @@ -0,0 +1,9 @@ +Value Message (.*) +Value Username (\S+) +Value Time (.*) + +Start + ^${Message}\[User:\s*${Username},\s*Time:\s*${Time}\] -> Record + ^${Message} -> Record + +EOF \ No newline at end of file diff --git a/spytest/templates/show_redis-cli_keys.tmpl b/spytest/templates/show_redis-cli_keys.tmpl new file mode 100644 index 00000000000..72e42ed590b --- /dev/null +++ b/spytest/templates/show_redis-cli_keys.tmpl @@ -0,0 +1,10 @@ +Value TABLE (\w+) +Value PROT (\w+) +Value IP (\d+.\d+.\d+.\d+) +Value PORT (\d+) +Value Route (\S+) + +Start + ^\s*\s*\d+\S+\s*"${TABLE}:${PROT}:${IP}:${PORT} -> Record + ^\s*\s*\d+\S+\s*"${TABLE}:${Route} -> Record + ^\s*\s*\d+\S+\s*"${TABLE}:${IP} -> Record diff --git a/spytest/templates/show_redis_cli_counters_map.tmpl b/spytest/templates/show_redis_cli_counters_map.tmpl new file mode 100644 index 00000000000..04cb9875f00 --- /dev/null +++ b/spytest/templates/show_redis_cli_counters_map.tmpl @@ -0,0 +1,5 @@ +Value oid (\S+) + +Start + ^\s*${oid}\s*$$ -> Record + diff --git a/spytest/templates/show_redis_cli_ip_db.tmpl b/spytest/templates/show_redis_cli_ip_db.tmpl new file mode 100644 index 00000000000..9d0519555c4 --- /dev/null +++ b/spytest/templates/show_redis_cli_ip_db.tmpl @@ -0,0 +1,5 @@ +Value donor_intf (\S+) + +Start + ^\s*\d+\S+\s+"unnumbered"\s*$$ + ^\s*\d+\S+\s+"${donor_intf}"\s*$$ -> Record diff --git a/spytest/templates/show_redis_cli_key_buffer_pool.tmpl b/spytest/templates/show_redis_cli_key_buffer_pool.tmpl new file mode 100644 index 00000000000..bceab9ee3c1 --- /dev/null +++ b/spytest/templates/show_redis_cli_key_buffer_pool.tmpl @@ -0,0 +1,8 @@ +Value id (\d+) +Value name (\S+) + +Start + ^\s*${name}\s*$$ -> Record + ^\s*${id}\)\s*"\s*${name}\s*"\s*$$ -> Record + ^\s*${id}\)\s*"\s*${name}\s*"\s*$$ -> Record + diff --git a/spytest/templates/show_redis_cli_key_search.tmpl b/spytest/templates/show_redis_cli_key_search.tmpl new file mode 100644 index 00000000000..cae22ed2d5a --- /dev/null +++ b/spytest/templates/show_redis_cli_key_search.tmpl @@ -0,0 +1,8 @@ +Value id (\d+) +Value name (\S+) + +Start + ^\s*${name}\s*$$ -> Record + ^\s*${id}\)\s*"\s*${name}\s*"\s*$$ -> Record + ^\s*${id}\)\s*${name}\s*$$ -> Record + diff --git a/spytest/templates/show_redis_cli_stp_iccp.tmpl b/spytest/templates/show_redis_cli_stp_iccp.tmpl new file mode 100644 index 00000000000..718f0027439 --- /dev/null +++ b/spytest/templates/show_redis_cli_stp_iccp.tmpl @@ -0,0 +1,7 @@ +Value id (\d+) +Value name (\S+) + +Start + ^\s*${id}\)\s*"\s*${name}\s*"\s*$$ -> Record + ^\s*${id}\)\s*${name}\s*$$ -> Record + diff --git a/spytest/templates/show_redis_cli_vid_to_rid_map.tmpl b/spytest/templates/show_redis_cli_vid_to_rid_map.tmpl new file mode 100644 index 00000000000..ffe1e386405 --- /dev/null +++ b/spytest/templates/show_redis_cli_vid_to_rid_map.tmpl @@ -0,0 +1,4 @@ +Value oid (\S+) + +Start + ^\s*${oid}\s*$$ -> Record diff --git a/spytest/templates/show_redis_error_db.tmpl b/spytest/templates/show_redis_error_db.tmpl new file mode 100644 index 00000000000..ac58237c553 --- /dev/null +++ b/spytest/templates/show_redis_error_db.tmpl @@ -0,0 +1,15 @@ +Value ifname (Ethernet\d+|Ethernet\d+,Ethernet\d+) +Value opcode (\S+te|\S+ve) +Value nhp (\d+.\d+.\d+.\d+|\d+::\d+|\d+.\d+.\d+.\d+,\d+.\d+.\d+.\d+) +Value rc (\S+_\S+_\S+|\S+_\S+_\S+_\S+) + +Start + ^\s*\d+\S+\s+"nexthop"\s*$$ + ^\s*\d+\S+\s+"${nhp}"\s*$$ + ^\s*\d+\S+\s+"ifname"\s*$$ + ^\s*\d+\S+\s+"${ifname}"\s*$$ + ^\s*\d+\S+\s+"rc"\s*$$ + ^\s*\d+\S+\s+"${rc}"\s*$$ + ^\s*\d+\S+\s+"operation"\s*$$ + ^\s*\d+\S+\s+"${opcode}"\s*$$ -> Record + diff --git a/spytest/templates/show_service-policy_interface.tmpl b/spytest/templates/show_service-policy_interface.tmpl new file mode 100644 index 00000000000..33a049f33f4 --- /dev/null +++ b/spytest/templates/show_service-policy_interface.tmpl @@ -0,0 +1,65 @@ +Value interface_name (\w+) +Value policy_name (\S+) +Value stage (\w+) +Value policy_desc_name (\w+) +Value class_desc_name (\w+) +Value class_name ([\w\S]+) +Value priority_val (\d+) +Value flow_state ([\w\S]+) +Value dscp_val (\d+) +Value pcp_val (\d+) +Value acl_name (\S+) +Value field_value (\d+) +Value ip_protocol_val (\d+) +Value src_port_val (\d+) +Value dst_port_val (\d+) +Value src_ip_val (\S+) +Value dst_ip_val (\S+) +Value src_ipv6_val (\S+) +Value dst_ipv6_val (\S+) +Value src_mac_val (\S+) +Value dst_mac_val (\S+) +Value cir_val (\d+) +Value cbs_val (\d+) +Value pir_val (\d+) +Value pbs_val (\d+) +Value policy_flow_state ([\w\S]+) +Value green_pkts_val (\d+) +Value green_bytes_val (\d+) +Value yellow_pkts_val (\d+) +Value yellow_bytes_val (\d+) +Value red_pkts_val (\d+) +Value red_bytes_val (\d+) +Value match_pkts_val (\d+) +Value match_bytes_val (\d+) +Value match_type (\S+) +Value tcp_flags_type (\S+.*) + +Start + ^${interface_name} + ^\s*Policy ${policy_name} Type qos at ${stage}\s*$$ + ^\s*Description:.*$$ + ^\s*${policy_desc_name}*$$ + ^\s*Flow\s+${class_name}\s+at\s+priority\s+${priority_val}\s+${flow_state}\s*$$ + ^\s*Description:.*$$ + ^\s*${class_desc_name}*$$ + ^\s*set-pcp\s+${pcp_val}\s*$$ + ^\s*set-dscp\s+${dscp_val}\s*$$ + ^\s*match acl\s+${acl_name}\s*$$ + ^\s*ether-type ${field_value}\s*$$ + ^\s*ip-protocol ${ip_protocol_val}\s*$$ + ^\s*src-port ${src_port_val}\s*$$ + ^\s*dst-port ${dst_port_val}\s*$$ + ^\s*src-ip ${src_ip_val}\s*$$ + ^\s*dst-ip ${dst_ip_val}\s*$$ + ^\s*src-ipv6 ${src_ipv6_val}\s*$$ + ^\s*dst-ipv6 ${dst_ipv6_val}\s*$$ + ^\s*src-mac ${src_mac_val}\s*$$ + ^\s*dst-mac ${dst_mac_val}\s*$$ + ^\s*tcp-flags ${tcp_flags_type}*$$ + ^\s*police:\s+cir\s+${cir_val}\s+cbs\s+${cbs_val}\s+pir\s+${pir_val}\s+pbs\s+${pbs_val}\s+${policy_flow_state}\s*$$ + ^\s*operational cir ${cir_val} cbs ${cbs_val} pir ${pir_val} pbs ${pbs_val}\s.*$$ + ^\s*green ${green_pkts_val} packets ${green_bytes_val} bytes action forward\s.*$$ + ^\s*yellow ${yellow_pkts_val} packets ${yellow_bytes_val} bytes action forward\s*$$ + ^\s*red ${red_pkts_val} packets ${red_bytes_val} bytes action drop\s*$$ + ^\s*Packet matches:\s+${match_pkts_val}\s+frames\s+${match_bytes_val}\s+bytes\s*$$ diff --git a/spytest/templates/show_service-policy_policy.tmpl b/spytest/templates/show_service-policy_policy.tmpl new file mode 100644 index 00000000000..43cbbc6709c --- /dev/null +++ b/spytest/templates/show_service-policy_policy.tmpl @@ -0,0 +1,12 @@ +Value policy_name (\S+) +Value class_name (\S+) +Value priority_val (\d+) +Value stage (\w+) +Value interface_name (\w+) + + +Start + ^${interface_name} + ^\s*policy ${policy_name} type qos at ${stage}\s*$$ + ^\s*flow ${class_name} at priority ${priority_val}.*$$ + ^$$ -> Record \ No newline at end of file diff --git a/spytest/templates/show_service-policy_summary.tmpl b/spytest/templates/show_service-policy_summary.tmpl new file mode 100644 index 00000000000..733af50628f --- /dev/null +++ b/spytest/templates/show_service-policy_summary.tmpl @@ -0,0 +1,8 @@ +Value policy_name (\S+) +Value stage (\w+) +Value interface_name (\w+) + +Start + ^${interface_name} + ^\s*qos policy ${policy_name} at ${stage}\s*$$ -> Record + ^$$ -> Record \ No newline at end of file diff --git a/spytest/templates/show_sflow.tmpl b/spytest/templates/show_sflow.tmpl new file mode 100644 index 00000000000..ccfc468cedb --- /dev/null +++ b/spytest/templates/show_sflow.tmpl @@ -0,0 +1,28 @@ +Value State (\S+) +Value Polling_Interval (\S+) +Value Collectors_Cnt (\d+) +Value Collector_Name (\S+) +Value Collector_IP (\S+) +Value Collector_Port (\d+) +Value Agent_ID (\S+) + + +Start + ^\s*sFlow\s+Global\s+Information: -> CLICK + ^\s*Global\s+sFlow\s+Information -> KLISH + +CLICK + ^\s*sFlow\s+Admin\s+State:\s+${State}\s*$$ -> Record + ^\s*(sFlow|SFlow)\s+Polling\s+Interval:\s+${Polling_Interval}\s*$$ -> Record + ^\s*(sFlow|SFlow)\s+AgentID:\s+${Agent_ID}\s*$$ -> Record + ^\s*$$ + ^\s*${Collectors_Cnt}\s+Collectors\s+configured:\s*$$ -> Record + ^\s*Name:\s*${Collector_Name}\s*IP\s*addr:\s*${Collector_IP}\s*UDP\s*port:\s*${Collector_Port}\s*$$ -> Record + +KLISH + ^\s*admin\s+state:\s+${State}\s*$$ -> Record + ^\s*polling-interval:\s+${Polling_Interval}\s*$$ -> Record + ^\s*agent-id:\s+${Agent_ID}\s*$$ -> Record + ^\s*configured\s+collectors:\s+${Collectors_Cnt}\s*$$ -> Record + ^\s*${Collector_Name}\s+${Collector_IP}\s+${Collector_Port}\s*$$ -> Record + diff --git a/spytest/templates/show_sflow_interface.tmpl b/spytest/templates/show_sflow_interface.tmpl new file mode 100644 index 00000000000..d1a88729544 --- /dev/null +++ b/spytest/templates/show_sflow_interface.tmpl @@ -0,0 +1,15 @@ +Value Interface (\S+) +Value Admin_Status (\S+) +Value Sampling_Rate (\d+) + +Start + ^\s*\|\s+Interface\s+\|\s+Admin\s+State\s+\|\s+Sampling\s+Rate\s+\| -> CLICK + ^\s*Interface\s+Admin\s+State\s+Sampling\s+Rate -> KLISH + ^\s*${Interface}\s*${Admin_Status}\s*${Sampling_Rate}\s*$$ -> Record + + +CLICK + ^\s*\|\s*${Interface}\s*\|\s*${Admin_Status}\s*\|\s*${Sampling_Rate}\s*\|\s*$$ -> Record + +KLISH + ^\s*${Interface}\s*${Admin_Status}\s*${Sampling_Rate}\s*$$ -> Record \ No newline at end of file diff --git a/spytest/templates/show_snmp_server_details.tmpl b/spytest/templates/show_snmp_server_details.tmpl new file mode 100644 index 00000000000..bdd22547af9 --- /dev/null +++ b/spytest/templates/show_snmp_server_details.tmpl @@ -0,0 +1,77 @@ +Value community (\w+) +Value group (\w+) +Value grp_name (\w+) +Value grp_model (\w+) +Value grp_security (\S+|\s*) +Value grp_read_view (\w+|\s*) +Value grp_write_view (\w+|\s*) +Value grp_notify_view (\w+|\s*) +Value view_name (\w+) +Value view_oid (\w+) +Value view_type (\w+) +Value user_name (\S+|\s*) +Value usr_grp_name (\w+) +Value usr_authentication (\w+) +Value usr_privacy (\S+) +Value target_address (\S+) +Value target_port (\d+) +Value target_type (\w+) +Value target_community_user (\S+) +Value target_version_security (\S+) +Value target_timeout (\d+) +Value target_retries (\d+) +Value system_name (\w+) +Value traps (\w+) +Value location (\w+) +Value engine_id (\w+) +Value contact (\w+) +Value agent_ip_address (\d+\.\d+\.\d+\.\d+) +Value agent_udp_port (\d+) +Value agent_interface (\w+) + + +Start + ^\s*Community\s+Name\s+Group\s+Name\s*$$ -> COMMUNITY + ^\s*Group\s+Name\s+Model:\s+Security\s+Read\s+View\s+Write\s+View\s+Notify\s+View\s*$$ -> GROUPS + ^\s*View\s+Name\s+OID\s+Tree\s+Type\s*$$ -> VIEWS + ^\s*User\s+Name\s+Group\s+Name\s+Auth\w*\s+Privacy\s*$$ -> USERS + ^\s*Target\s+Address\s+Port\s+Type\s+Community\s+Ver\w*\s+T\-Out\s+Retries\s*$$ -> HOST + ^\s*Target\s+Address\s+Type\s+Community\s+Ver\w*\s+T\-Out\s+Retries\s*$$ -> HOST + ^\s*Target\s+Address\s+Port\s+Type\s+User\s*Name\s+Security\s+T\-Out\s+Retries\s*$$ -> HOST + ^\s*Target\s+Address\s+Type\s+User\s*Name\s+Security\s+T\-Out\s+Retries\s*$$ -> HOST + ^\s*System\s+Name\s*:\s+${system_name}\s*$$ -> SERVER + ^\s*Location\s*:\s+${location}\s*$$ -> SERVER + ^\s*Contact\s*:\s+${contact}\s*$$ -> SERVER + ^\s*EngineID\s*:\s+${engine_id}\s*$$ -> SERVER + +COMMUNITY + ^\s*${community}\s+${group}\s*$$ -> Record + +GROUPS + ^\s*${grp_name}\s+${grp_model}\s*:\s+${grp_security}\s+${grp_read_view}\s+${grp_write_view}\s+${grp_notify_view}\s*$$ -> Record + +VIEWS + ^\s*${view_name}\s+${view_oid}\s+${view_type}\s*$$ -> Record + +USERS + ^\s*${user_name}\s+${usr_grp_name}\s+${usr_authentication}\s+${usr_privacy}\s*$$ -> Record + +HOST + ^\s*${target_address}\s+${target_port}\s+${target_type}\s+${target_community_user}\s+${target_version_security}\s+${target_timeout}\s+${target_retries}\s*$$ -> Record + ^\s*${target_address}\s+${target_type}\s+${target_community_user}\s+${target_version_security}\s+${target_timeout}\s+${target_retries}\s*$$ -> Record + ^\s*${target_address}\s+${target_port}\s+${target_type}\s+${target_timeout}\s+${target_retries}\s*$$ -> Record + ^\s*${target_address}\s+${target_type}\s+${target_timeout}\s+${target_retries}\s*$$ -> Record + +SERVER + ^\s*System\s+Name\s*:\s+${system_name}\s*$$ + ^\s*Traps\s*:\s+${traps}\s*$$ + ^\s*Location\s*:\s+${location}\s*$$ + ^\s*EngineID\s*:\s+${engine_id}\s*$$ + ^\s*Contact\s*:\s+${contact}\s*$$ + ^$$ -> Record + ^\s*Agent\s+Addresses: -> Continue + ^\s*IP\s*Address\s+UDP\s*Port\s+Interface\s* -> Continue + ^\s*(-*)\s*$$ -> Continue + ^\s*${agent_ip_address}\s+${agent_udp_port}\s+${agent_interface}\s*$$ -> Record + ^\s*${agent_ip_address}\s+${agent_udp_port}\s*$$ -> Record + ^\s*${agent_ip_address}\s*$$ -> Record \ No newline at end of file diff --git a/spytest/templates/show_spanning_tree.tmpl b/spytest/templates/show_spanning_tree.tmpl new file mode 100644 index 00000000000..13120cff8a2 --- /dev/null +++ b/spytest/templates/show_spanning_tree.tmpl @@ -0,0 +1,65 @@ +Value err ([\w\s:*]*) +Value Filldown mode (\w+) +#Per VLAN data +Value Filldown vid (\d+) +Value Filldown inst (\d+) +#Bridge Params +Value Filldown br_id (\w+) +Value Filldown br_maxage (\d+) +Value Filldown br_hello (\d+) +Value Filldown br_fwddly (\d+) +Value Filldown br_hold (\d+) +Value Filldown br_lasttopo (\d+) +Value Filldown br_topoch (\d+) +#Root Filldown Params +Value Filldown rt_id (\w+) +Value Filldown rt_pathcost (\d+) +Value Filldown rt_desigbridgeid (\w+) +Value Filldown rt_port (\S+) +Value Filldown rt_maxage (\d+) +Value Filldown rt_hello (\d+) +Value Filldown rt_fwddly (\d+) +#Port Params +Value port_name (\w+) +Value port_priority (\d+) +Value port_pathcost (\d+) +Value port_portfast (Y|N) +Value port_uplinkfast (Y|N) +Value port_state (\w+) +Value port_desigcost (\d+) +Value port_desigrootid (\w+) +Value port_desigbridgeid (\w+) +#Show spanning-tree root_guard +Value Filldown rg_timeout (\d+) +Value rg_ifname (\w+) +Value rg_vid (\d+) +Value rg_status (.*) +#Show spanning-tree bpdu_guard +Value bg_ifname (\w+) +Value bg_cfg_shut (Yes|No) +Value bg_oper_shut (Yes|No|NA) +#show spanning-tree statistics +Value st_vid (\d+) +Value st_inst (\w+) +Value st_portno (\d+) +Value st_bpdutx (\d+) +Value st_bpdurx (\d+) +Value st_tcntx (\d+) +Value st_tcnrx (\d+) + +Start + ^Spanning-tree is ${err}\s*$$ -> Record + ^Spanning-tree Mode: ${mode}\s*$$ + ^VLAN ${vid} - STP instance ${inst}\s*$$ + ^${br_id}\s+${br_maxage}\s+${br_hello}\s+${br_fwddly}\s+${br_hold}\s+${br_lasttopo}\s+${br_topoch}\s*$$ + ^${rt_id}\s+${rt_pathcost}\s+${rt_desigbridgeid}\s+${rt_port}\s+${rt_maxage}\s+${rt_hello}\s+${rt_fwddly}\s*$$ + ^${port_name}\s+${port_priority}\s+${port_pathcost}\s+${port_portfast}\s+${port_uplinkfast}\s+${port_state}\s+${port_desigcost}\s+${port_desigrootid}\s+${port_desigbridgeid}\s*$$ -> Record + ^Root guard timeout: ${rg_timeout} secs + ^${rg_ifname}\s+${rg_vid}\s+${rg_status}$$ -> Record + ^PortNum\s+Shutdown\s+Port Shut\s*$$ + ^${bg_ifname}\s+${bg_cfg_shut}\s+${bg_oper_shut}$$ -> Record + ^VLAN\s+${st_vid} - STP instance\s+${st_inst}$$ + ^PortNum\s+BPDU Tx\s+BPDU Rx\s+TCN Tx\s+TCN Rx$$ + ^${st_portno}\s+${st_bpdutx}\s+${st_bpdurx}\s+${st_tcntx}\s+${st_tcnrx}$$ -> Record + +EOF diff --git a/spytest/templates/show_spanning_tree_bpdu_guard.tmpl b/spytest/templates/show_spanning_tree_bpdu_guard.tmpl new file mode 100644 index 00000000000..b69b259221b --- /dev/null +++ b/spytest/templates/show_spanning_tree_bpdu_guard.tmpl @@ -0,0 +1,9 @@ +Value bg_ifname (\w+) +Value bg_cfg_shut (Yes|No) +Value bg_oper_shut (Yes|No|NA) + +Start + ^PortNum\s+Shutdown\s+Port Shut\s*$$ + ^${bg_ifname}\s+${bg_cfg_shut}\s+${bg_oper_shut}$$ -> Record + +EOF diff --git a/spytest/templates/show_spanning_tree_root_guard.tmpl b/spytest/templates/show_spanning_tree_root_guard.tmpl new file mode 100644 index 00000000000..f7bac767a9d --- /dev/null +++ b/spytest/templates/show_spanning_tree_root_guard.tmpl @@ -0,0 +1,10 @@ +Value Filldown rg_timeout (\d+) +Value rg_ifname (\w+) +Value rg_vid (\d+) +Value rg_status (.*) + +Start + ^Root guard timeout: ${rg_timeout} secs + ^${rg_ifname}\s+${rg_vid}\s+${rg_status}$$ -> Record + +EOF diff --git a/spytest/templates/show_spanning_tree_statistics.tmpl b/spytest/templates/show_spanning_tree_statistics.tmpl new file mode 100644 index 00000000000..4d7f589ec95 --- /dev/null +++ b/spytest/templates/show_spanning_tree_statistics.tmpl @@ -0,0 +1,15 @@ +Value Filldown st_vid (\d+) +Value Filldown st_inst (\d+) +Value st_portno (\S+) +Value st_bpdutx (\d+) +Value st_bpdurx (\d+) +Value st_tcntx (\d+) +Value st_tcnrx (\d+) + +Start + ^VLAN\s+${st_vid}\s+-\s+STP\s+instance\s+${st_inst}$$ + ^(-+\s*)*$$ + ^PortNum\s+BPDU\s+Tx\s+BPDU\s+Rx\s+TCN\s+Tx\s+TCN\s+Rx$$ + ^${st_portno}\s+${st_bpdutx}\s+${st_bpdurx}\s+${st_tcntx}\s+${st_tcnrx}\s*$$ -> Record + +EOF diff --git a/spytest/templates/show_spanning_tree_vlan.tmpl b/spytest/templates/show_spanning_tree_vlan.tmpl new file mode 100644 index 00000000000..d362e367765 --- /dev/null +++ b/spytest/templates/show_spanning_tree_vlan.tmpl @@ -0,0 +1,55 @@ +Value Filldown stp_mode (\w+) +Value Filldown vid (\d+) +Value Filldown inst (\d+) +#Bridge Params +Value Filldown br_id ([a-f0-9]{16}) +Value Filldown br_maxage (\d+) +Value Filldown br_hello (\d+) +Value Filldown br_fwddly (\d+) +Value Filldown br_hold (\d+) +Value Filldown br_lasttopo (\d+) +Value Filldown br_topoch (\d+) +#Root bridge Params +Value Filldown rt_id ([a-f0-9]{16}) +Value Filldown rt_pathcost (\d+) +Value Filldown rt_desigbridgeid ([a-f0-9]{16}) +Value Filldown rt_port (\S+) +Value Filldown rt_maxage (\d+) +Value Filldown rt_hello (\d+) +Value Filldown rt_fwddly (\d+) +#Port Params +Value Filldown port_name (\w+) +Value Filldown port_priority (\d+) +Value Filldown port_pathcost (\d+) +Value Filldown port_portfast (Y|N) +Value Filldown port_uplinkfast (Y|N) +Value Filldown port_bpdufilter (Y|N) +Value Filldown port_state (\w+(\-\w+)?) +Value Filldown port_desigcost (\d+) +Value Filldown port_desigrootid ([a-f0-9]{16}) +Value Filldown port_desigbridgeid ([a-f0-9]{16}) +Value Filldown p2pMac (Y|N) +Value Filldown Edgeport (Y|N) +Value Filldown Role (\S+|\s*) + + + +Start + ^\s*Spanning-tree\s+Mode:\s+${stp_mode}\s*$$ + ^\s*VLAN\s*${vid}\s*-\s*STP\s*instance\s*${inst}\s*$$ + ^\s*${br_id}\s+${br_maxage}\s+${br_hello}\s+${br_fwddly}\s+${br_hold}\s+${br_lasttopo}\s+${br_topoch}\s*$$ + ^\s*${rt_id}\s+${rt_pathcost}\s+${rt_desigbridgeid}\s+${rt_port}\s+${rt_maxage}\s+${rt_hello}\s+${rt_fwddly}\s*$$ + ^\s*(Port|Port Num)\s+(Prio|Priority)\s+(Path|Path Cost)\s+(Port|Port Fast)\s+(Uplink|Uplink Fast)\s+State\s+(Designated|Designated Cost)\s+(Designated|Designated Root)\s+(Designated|DesignatedBridge)\s*$$ -> NOBPDU + ^\s*(Port|Port Num)\s+(Prio|Priority)\s+(Path|Path Cost)\s+(Port|Port Fast)\s+(Uplink|Uplink Fast)\s+(BPDU|BPDU Filter)\s+State\s+(Designated|Designated Cost)\s+(Designated|Designated Root)\s+(Designated|DesignatedBridge)\s*$$ -> BPDU + ^\s*(Port|Port Num)\s+(Prio|Priority)\s+(Path|Path Cost)\s+(P2P|P2P Mac)\s+(Edge|Edge Port)\s+(BPDU|BPDU Filter)\s+Role\s+State\s+(Designa-|Designa-ted cost|Designated cost)\s+(Designated|DesignatedBridge)\s*$$ -> KLISH + +NOBPDU + ^\s*${port_name}\s+${port_priority}\s+${port_pathcost}\s+${port_portfast}\s+${port_uplinkfast}\s+${port_state}\s+${port_desigcost}\s+${port_desigrootid}\s+${port_desigbridgeid}\s*$$ -> Record + +BPDU + ^\s*${port_name}\s+${port_priority}\s+${port_pathcost}\s+${port_portfast}\s+${port_uplinkfast}\s+${port_bpdufilter}\s+${port_state}\s+${port_desigcost}\s+${port_desigrootid}\s+${port_desigbridgeid}\s*$$ -> Record + +KLISH + ^\s*${port_name}\s+${port_priority}\s+${port_pathcost}\s+${p2pMac}\s+${Edgeport}\s+${port_bpdufilter}\s+${Role}\s+${port_state}\s+${port_desigcost}\s+${port_desigbridgeid}\s*$$ -> Record + +EOF diff --git a/spytest/templates/show_ssh_server_vrfs.tmpl b/spytest/templates/show_ssh_server_vrfs.tmpl new file mode 100644 index 00000000000..4602d322a69 --- /dev/null +++ b/spytest/templates/show_ssh_server_vrfs.tmpl @@ -0,0 +1,7 @@ +Value vrf_name (\S+) +Value status (\S+) + +Start + ^\s*VRFS\s+Status\s*$$ + ^(-+\s*)*$$ + ^\s*${vrf_name}\s+${status} -> Record diff --git a/spytest/templates/show_ssh_vrf.tmpl b/spytest/templates/show_ssh_vrf.tmpl new file mode 100644 index 00000000000..f3564fafb06 --- /dev/null +++ b/spytest/templates/show_ssh_vrf.tmpl @@ -0,0 +1,10 @@ +Value vrfName (\S+) +Value status (\S+) + +Start + ^.*----- -> Routes + +Routes + ^\S+ -> Continue.Record + ^${vrfName}\s+${status} + diff --git a/spytest/templates/show_storm_control.tmpl b/spytest/templates/show_storm_control.tmpl new file mode 100644 index 00000000000..51c8fa02bea --- /dev/null +++ b/spytest/templates/show_storm_control.tmpl @@ -0,0 +1,11 @@ +Value Interface (\S+) +Value Type (\S+) +Value Rate (\d+) + +Start + ^\s*Interface\s+Type\s+Rate\s*$$ -> Continue + ^(-+\s*)*$$ -> Continue + ^\s*\|\s*${Interface}\s+\|\s+${Type}\s+\|\s+${Rate}\s+\|\s*$$ -> Record + ^$$ + +EOF \ No newline at end of file diff --git a/spytest/templates/show_stp_intf.tmpl b/spytest/templates/show_stp_intf.tmpl new file mode 100644 index 00000000000..e843dd7bebf --- /dev/null +++ b/spytest/templates/show_stp_intf.tmpl @@ -0,0 +1,9 @@ +Value link_type (point-to-point|auto|shared) +Value edge_port (yes|no) + +Start + ^\s*\d+\)\s*"link_type"\s*$$ + ^\s*\d+\)\s*"${link_type}"\s*$$ + ^\s*\d+\)\s*"edge_port"\s*$$ + ^\s*\d+\)\s*"${edge_port}"\s*$$ -> Record + diff --git a/spytest/templates/show_stp_port_state.tmpl b/spytest/templates/show_stp_port_state.tmpl new file mode 100644 index 00000000000..3c48e2eeffa --- /dev/null +++ b/spytest/templates/show_stp_port_state.tmpl @@ -0,0 +1,6 @@ +Value state (\d+) + +Start + ^\s*\d+\)\s*"state"\s*$$ + ^\s*\d+\)\s*"${state}"\s*$$ -> Record + diff --git a/spytest/templates/show_stp_port_states.tmpl b/spytest/templates/show_stp_port_states.tmpl new file mode 100644 index 00000000000..efe938d9587 --- /dev/null +++ b/spytest/templates/show_stp_port_states.tmpl @@ -0,0 +1,6 @@ +Value state (\d+) + +Start + ^\s*\d+\)\s*"state"\s*$$ + ^\s*\d+\)\s*"${state}"\s*$$-> Record + diff --git a/spytest/templates/show_stp_vlan.tmpl b/spytest/templates/show_stp_vlan.tmpl new file mode 100644 index 00000000000..423244ade9c --- /dev/null +++ b/spytest/templates/show_stp_vlan.tmpl @@ -0,0 +1,26 @@ +Value bridge_id (\S+) +Value max_age (\d+) +Value hello_time (\d+) +Value forward_delay (\d+) +Value root_bridge_id (\S+) +Value root_path_cost (\d+) +Value desig_bridge_id (\S+) +Value root_port (\S+) +Value root_max_age (\d+) +Value root_hello_time (\d+) +Value root_forward_delay (\d+) +Value stp_instance (\d+) + +Start + ^\s*2\)\s*"${bridge_id}"\s*$$ + ^\s*4\)\s*"${max_age}"\s*$$ + ^\s*6\)\s*"${hello_time}"\s*$$ + ^\s*8\)\s*"${forward_delay}"\s*$$ + ^\s*10\)\s*"${root_bridge_id}"\s*$$ + ^\s*12\)\s*"${root_path_cost}"\s*$$ + ^\s*14\)\s*"${desig_bridge_id}"\s*$$ + ^\s*16\)\s*"${root_port}"\s*$$ + ^\s*18\)\s*"${root_max_age}"\s*$$ + ^\s*20\)\s*"${root_hello_time}"\s*$$ + ^\s*22\)\s*"${root_forward_delay}"\s*$$ + ^\s*24\)\s*"${stp_instance}"\s*$$ -> Record diff --git a/spytest/templates/show_stp_vlan_instance.tmpl b/spytest/templates/show_stp_vlan_instance.tmpl new file mode 100644 index 00000000000..e2ccbca2dbd --- /dev/null +++ b/spytest/templates/show_stp_vlan_instance.tmpl @@ -0,0 +1,6 @@ +Value stp_instance (\d+) + +Start + ^\s*\d+\)\s*"stp_instance"\s*$$ + ^\s*\d+\)\s*"${stp_instance}"\s*$$ -> Record + diff --git a/spytest/templates/show_stp_vlan_intf.tmpl b/spytest/templates/show_stp_vlan_intf.tmpl new file mode 100644 index 00000000000..6e3723262b4 --- /dev/null +++ b/spytest/templates/show_stp_vlan_intf.tmpl @@ -0,0 +1,34 @@ +Value port_num (\d+) +Value priority (\d+) +Value path_cost (\d+) +Value port_state (\S+) +Value desig_cost (\d+) +Value desig_root (\S+) +Value desig_bridge (\S+) +Value desig_port (\d+) +Value bpdu_sent (\d+) +Value bpdu_received (\d+) +Value config_bpdu_sent (\d+) +Value config_bpdu_received (\d+) +Value tc_sent (\d+) +Value tc_received (\d+) +Value root_guard_timer (\d+) +Value role (\S+) + +Start + ^\s*2\)\s*"${port_num}"\s*$$ + ^\s*4\)\s*"${priority}"\s*$$ + ^\s*6\)\s*"${path_cost}"\s*$$ + ^\s*8\)\s*\"${port_state}\"\s*$$ + ^\s*10\)\s*\"${desig_cost}\"\s*$$ + ^\s*12\)\s*\"${desig_root}\"\s*$$ + ^\s*14\)\s*\"${desig_bridge}\"\s*$$ + ^\s*16\)\s*\"${desig_port}\"\s*$$ + ^\s*18\)\s*\"${bpdu_sent}\"\s*$$ + ^\s*20\)\s*\"${bpdu_received}\"\s*$$ + ^\s*22\)\s*\"${config_bpdu_sent}\"\s*$$ + ^\s*24\)\s*\"${config_bpdu_received}\"\s*$$ + ^\s*26\)\s*\"${tc_sent}\"\s*$$ + ^\s*28\)\s*\"${tc_received}\"\s*$$ + ^\s*30\)\s*\"${root_guard_timer}\"\s*$$ + ^\s*32\)\s*\"${role}\"\s*$$ -> Record diff --git a/spytest/templates/show_stp_vlan_intf_flush.tmpl b/spytest/templates/show_stp_vlan_intf_flush.tmpl new file mode 100644 index 00000000000..335563a53eb --- /dev/null +++ b/spytest/templates/show_stp_vlan_intf_flush.tmpl @@ -0,0 +1,7 @@ +Value state (\S+) + +Start + ^\s*\d+\)\s*"state"\s*$$ + ^\s*\d+\)\s*"${state}"\s*$$ + + diff --git a/spytest/templates/show_system_status.tmpl b/spytest/templates/show_system_status.tmpl new file mode 100644 index 00000000000..c83357f2fb0 --- /dev/null +++ b/spytest/templates/show_system_status.tmpl @@ -0,0 +1,18 @@ +Value status (ready|not ready) +Value swss (\S+) +Value bgp (\S+) +Value teamd (\S+) +Value pmon (\S+) +Value syncd (\S+) +Value database (\S+) + +Start + ^System is $status + ^System is $status - Core services are not up -> Record + ^\s+swss\s+: $swss + ^\s+bgp\s+: $bgp + ^\s+teamd\s+: $teamd + ^\s+pmon\s+: $pmon + ^\s+syncd\s+: $syncd + ^\s+database\s+: $database + diff --git a/spytest/templates/show_tacacs.tmpl b/spytest/templates/show_tacacs.tmpl new file mode 100644 index 00000000000..bf1c79d54d6 --- /dev/null +++ b/spytest/templates/show_tacacs.tmpl @@ -0,0 +1,21 @@ +Value Global_Auth_type (\S+\s*\(\S+\)|\S+) +Value Global_Timeout (\d+\s*\(\S+\)|\d+) +Value Global_Passkey (\S+\s*\(\S+\)|\S+) +Value Address (\S+) +Value Priority (\d+) +Value TCP_Port (\d+) +Value Auth_type (\S+) +Value Passkey (\S+) +Value Timeout (\d+) + +Start + ^TACPLUS\s+global\s+auth_type\s+${Global_Auth_type}\s*$$ + ^TACPLUS\s+global\s+timeout\s+${Global_Timeout}\s*$$ + ^TACPLUS\s+global\s+passkey\s+${Global_Passkey}\s*$$ + ^TACPLUS_SERVER\s+address\s+${Address}\s*$$ + ^\s*priority\s+${Priority}\s*$$ + ^\s*tcp_port\s+${TCP_Port}\s*$$ + ^\s*auth_type\s+${Auth_type}\s*$$ + ^\s*timeout\s+${Timeout}\s*$$ + ^\s*passkey\s+${Passkey}\s*$$ + ^$$ -> Record \ No newline at end of file diff --git a/spytest/templates/show_threshold_breaches.tmpl b/spytest/templates/show_threshold_breaches.tmpl new file mode 100644 index 00000000000..269090eb005 --- /dev/null +++ b/spytest/templates/show_threshold_breaches.tmpl @@ -0,0 +1,10 @@ +Value eventid (\d+) +Value buffer (\S+) +Value threshold_type (\S+) +Value port (\S+) +Value index (\d+) +Value value (\d+) +Value counter (\d+) + +Start + ^\s*${eventid}\s+${buffer}\s+${threshold_type}\s+${port}\s+${index}\s+${value}\s+${counter} -> Record diff --git a/spytest/templates/show_timedatectl_status.tmpl b/spytest/templates/show_timedatectl_status.tmpl new file mode 100644 index 00000000000..fbab4da069a --- /dev/null +++ b/spytest/templates/show_timedatectl_status.tmpl @@ -0,0 +1,17 @@ +Value Localtime (.*) +Value Universaltime (.*) +Value RTCtime (.*) +Value Timezone (.*) +Value Networktimeon (\S+) +Value NTPsynchronized (\S+) +Value RTCinlocalTZ (\S+) + +Start + ^\s+Local time:\s+${Localtime} + ^\s+Universal time:\s+${Universaltime} + ^\s+RTC time:\s+${RTCtime} + ^\s+Time zone:\s+${Timezone} + ^\s+Network time on:\s+${Networktimeon} + ^NTP synchronized:\s+${NTPsynchronized} + ^\s+RTC in local TZ:\s+${RTCinlocalTZ} + ^.*$$ -> Record \ No newline at end of file diff --git a/spytest/templates/show_udld_global.tmpl b/spytest/templates/show_udld_global.tmpl new file mode 100644 index 00000000000..881878a4873 --- /dev/null +++ b/spytest/templates/show_udld_global.tmpl @@ -0,0 +1,17 @@ +################################################ +#UDLD Global Information +# Admin State: UDLD enabled +# Mode: Aggressive +# UDLD message time: 1 secs +# UDLD multiplier: 3 +############################################### +Value udld_admin_state (\w+) +Value udld_mode (\w+) +Value udld_message_time (\d+) +Value udld_multiplier (\d+) + +Start + ^\s*Admin\s*State\s*:\s*UDLD\s*${udld_admin_state}$$ + ^\s*Mode\s*:\s*${udld_mode}$$ + ^\s*UDLD\s*Message\s*Time\s*:\s*${udld_message_time}\s+seconds$$ + ^\s*UDLD\s*Multiplier\s*:\s*${udld_multiplier}$$ -> Record diff --git a/spytest/templates/show_udld_interface.tmpl b/spytest/templates/show_udld_interface.tmpl new file mode 100644 index 00000000000..1cfe52cdc1d --- /dev/null +++ b/spytest/templates/show_udld_interface.tmpl @@ -0,0 +1,47 @@ +#show udld interface Ethernet1 +#UDLD information for Ethernet1 +# UDLD Admin State: Enabled +# Mode: Aggressive +# Status: Bidirectional +# Local device id: 3c2c.992d.8201 +# Local port id : Ethernet1 +# Local device name: Sonic +# Message time: 1 +# Timeout interval: 3 +# Neighbor Entry 1 +# ---------------------------------------------------------------------------------------- +# Neighbor device id: 3c2c.992d.8235 +# Neighbor port id: Ethernet0 +# Neighbor device name: Sonic +# Neighbor message time: 1 +# Neighbor timeout interval: 3 +################################################################################### +Value udld_admin_state (\w+) +Value udld_mode (\w+) +Value udld_status (\w+) +Value local_device_id (\w\w\w\w.\w\w\w\w.\w\w\w\w) +Value local_port (\w+) +Value local_device_name (\w+) +Value local_udld_message_time (\d+) +Value local_udld_multiplier (\d+) +Value neighbor_device_id (\w\w\w\w.\w\w\w\w.\w\w\w\w) +Value neighbor_port (\w+) +Value neighbor_device_name (\w+) +Value neighbor_udld_message_time (\d+) +Value neighbor_udld_multiplier (\d+) + +Start + ^\s*UDLD\s*Admin\s*State\s*:\s*${udld_admin_state}\s*$$ + ^\s*Mode\s*:\s*${udld_mode}\s*$$ + ^\s*Status\s*:\s*${udld_status}\s*$$ + ^\s*Local\s*Device\s*Id\s*:\s*${local_device_id}\s*$$ + ^\s*Local\s*Port\s*Id\s*:\s*${local_port}\s*$$ + ^\s*Local\s*Device\s*Name\s*:\s*${local_device_name}\s*$$ + ^\s*Message\s*Time\s*:\s*${local_udld_message_time}\s*seconds\s*$$ + ^\s*Timeout\s*Interval\s*:\s*${local_udld_multiplier}\s*seconds\s*$$ + ^\s*Neighbor\s*Device\s*Id\s*:\s*${neighbor_device_id}\s*$$ + ^\s*Neighbor\s*Port\s*Id\s*:\s*${neighbor_port}\s*$$ + ^\s*Neighbor\s*Device\s*Name\s*:\s*${neighbor_device_name}\s*$$ + ^\s*Neighbor\s*Message\s*Time\s*:\s*${neighbor_udld_message_time}\s*seconds\s*$$ + ^\s*Neighbor\s*Timeout\s*Interval\s*:\s*${neighbor_udld_multiplier}\s*seconds$$ -> Record + diff --git a/spytest/templates/show_udld_neighbors.tmpl b/spytest/templates/show_udld_neighbors.tmpl new file mode 100644 index 00000000000..2a2b28010da --- /dev/null +++ b/spytest/templates/show_udld_neighbors.tmpl @@ -0,0 +1,15 @@ +############################################################################### +#show udld neighbors +#Port Device Name Device ID Port ID Neighbor State +#--------------------------------------------------------------------------------- +#Ethernet1 sonic 3c2c.992d.8201 Ethernet0 Bidirectional +#Ethernet3 sonic 3c2c.992d.8201 Ethernet3 Bidirectional +############################################################################### +Value local_port (\w+) +Value device_name (\w+) +Value remote_device_id (\w\w\w\w.\w\w\w\w.\w\w\w\w) +Value remote_port (\w+) +Value neighbor_state (\w+) + +Start + ^\s*${local_port}\s+${device_name}\s+${remote_device_id}\s+${remote_port}\s+${neighbor_state}\s*$$ -> Record diff --git a/spytest/templates/show_udld_statistics.tmpl b/spytest/templates/show_udld_statistics.tmpl new file mode 100644 index 00000000000..875ea852c63 --- /dev/null +++ b/spytest/templates/show_udld_statistics.tmpl @@ -0,0 +1,28 @@ +################################################################## +#show udld statistics interface Ethernet0 +#UDLD Interface statistics for Ethernet0 +#Frames transmitted: 10 +#Frames received: 9 +#Frames with error: 0 +#############################OR################################### +#show udld statistics +#UDLD Interface statistics for Ethernet0 +#Frames transmitted: 10 +#Frames received: 9 +#Frames with error: 0 +# +#UDLD Interface statistics for Ethernet1 +#Frames transmitted: 11 +#Frames received: 12 +#Frames with error: 0 +################################################################## +Value udld_interface (\w+) +Value udld_tx (\d+) +Value udld_rx (\d+) +Value udld_errors (\d+) + +Start + ^\s*UDLD\s*Interface\s*statistics\s*for\s*${udld_interface}$$ + ^\s*Frames\s*transmitted:\s*${udld_tx}$$ + ^\s*Frames\s*received:\s*${udld_rx}$$ + ^\s*Frames\s*with\s*error:\s*${udld_errors}$$ -> Record Start diff --git a/spytest/templates/show_uptime.tmpl b/spytest/templates/show_uptime.tmpl new file mode 100644 index 00000000000..ea046451ace --- /dev/null +++ b/spytest/templates/show_uptime.tmpl @@ -0,0 +1,45 @@ +Value Filldown years (\d+) +Value Filldown months (\d+) +Value Filldown weeks (\d+) +Value Filldown days (\d+) +Value Filldown hours (\d+) +Value minutes (\d+) + +Start + ^up\s+${years}\s+(years|year),\s+${months}\s+(month|month),\s+${weeks}\s+(weeks|week),\s+${days}\s+(days|day),\s+${hours}\s+(hours|hour),\s+${minutes}\s+(minutes|minute)$$ -> Record + ^up\s+${years}\s+(years|year),\s+${minutes}\s+(minutes|minute)$$ -> Record + ^up\s+${years}\s+(years|year)\s*$$ -> Record + ^up\s+${months}\s+(months|month),\s+${weeks}\s+(weeks|week),\s+${hours}\s+(hours|hour),\s+${minutes}\s+(minutes|minute)$$ -> Record + ^up\s+${months}\s+(months|month),\s+${weeks}\s+(weeks|week),\s+${hours}\s+(hours|hour)$$ -> Record + ^up\s+${months}\s+(months|month),\s+${weeks}\s+(weeks|week),\s+${minutes}\s+(minutes|minute)$$ -> Record + ^up\s+${months}\s+(months|month),\s+${weeks}\s+(weeks|week),\s+${days}\s+(days|day),\s+${hours}\s+(hours|hour),\s+${minutes}\s+(minutes|minute)$$ -> Record + ^up\s+${months}\s+(months|month),\s+${weeks}\s+(weeks|week),\s+${days}\s+(days|day),\s+${hours}\s+(hours|hour)$$ -> Record + ^up\s+${months}\s+(months|month),\s+${weeks}\s+(weeks|week),\s+${days}\s+(days|day),\s+${minutes}\s+(minutes|minute)$$ -> Record + ^up\s+${months}\s+(months|month),\s+${weeks}\s+(weeks|week),\s+${days}\s+(days|day)$$ -> Record + ^up\s+${months}\s+(months|month),\s+${weeks}\s+(weeks|week)$$ -> Record + ^up\s+${months}\s+(months|month),\s+${days}\s+(days|day),\s+${hours}\s+(hours|hour),\s+${minutes}\s+(minutes|minute)$$ -> Record + ^up\s+${months}\s+(months|month),\s+${days}\s+(days|day),\s+${hours}\s+(hours|hour)$$ -> Record + ^up\s+${months}\s+(months|month),\s+${days}\s+(days|day),\s+${minutes}\s+(minutes|minute)$$ -> Record + ^up\s+${months}\s+(months|month),\s+${days}\s+(days|day)$$ -> Record + ^up\s+${months}\s+(months|month),\s+${hours}\s+(hours|hour),\s+${minutes}\s+(minutes|minute)$$ -> Record + ^up\s+${months}\s+(months|month),\s+${hours}\s+(hours|hour)$$ -> Record + ^up\s+${months}\s+(months|month),\s+${minutes}\s+(minutes|minute)$$ -> Record + ^up\s+${months}\s+(months|month)\s*$$ -> Record + ^up\s+${weeks}\s+(weeks|week),\s+${days}\s+(days|day),\s+${hours}\s+(hours|hour),\s+${minutes}\s+(minutes|minute)$$ -> Record + ^up\s+${weeks}\s+(weeks|week),\s+${days}\s+(days|day),\s+${hours}\s+(hours|hour)$$ -> Record + ^up\s+${weeks}\s+(weeks|week),\s+${days}\s+(days|day),\s+${minutes}\s+(minutes|minute)$$ -> Record + ^up\s+${weeks}\s+(weeks|week),\s+${days}\s+(days|day)$$ -> Record + ^up\s+${weeks}\s+(weeks|week),\s+${hours}\s+(hours|hour),\s+${minutes}\s+(minutes|minute)$$ -> Record + ^up\s+${weeks}\s+(weeks|week),\s+${hours}\s+(hours|hour)$$ -> Record + ^up\s+${weeks}\s+(weeks|week),\s+${minutes}\s+(minutes|minute)$$ -> Record + ^up\s+${weeks}\s+(weeks|week)\s*$$ -> Record + ^up\s+${days}\s+(days|day),\s+${hours}\s+(hours|hour),\s+${minutes}\s+(minutes|minute)$$ -> Record + ^up\s+${days}\s+(days|day),\s+${hours}\s+(hours|hour)$$ -> Record + ^up\s+${days}\s+(days|day),\s+${minutes}\s+(minutes|minute)$$ -> Record + ^up\s+${days}\s+(days|day)\s*$$ -> Record + ^up\s+${hours}\s+(hours|hour),\s+${minutes}\s+(minutes|minute)\s*$$ -> Record + ^up\s+${hours}\s+(hours|hour)\s*$$ -> Record + ^up\s+${minutes}\s+(minutes|minute)\s*$$ -> Record + ^$$ + +EOF diff --git a/spytest/templates/show_users.tmpl b/spytest/templates/show_users.tmpl new file mode 100644 index 00000000000..3e268b5f1cd --- /dev/null +++ b/spytest/templates/show_users.tmpl @@ -0,0 +1,16 @@ +#admin ttyS1 2019-03-25 20:31 +#admin ttyS1 2019-03-25 20:31 (100.127.20.23) +#admin pts/9 May 11 22:32 (100.127.20.23) +Value User (\w+) +Value SessionType (\S+) +Value Month (\S+) +Value MonthDay (\d+) +Value Hours (\d+) +Value Minutes (\d+) +Value Ip (\S+) +Value Year (\d+) + +Start + ^${User}\s+${SessionType}\s+${Year}\-${Month}\-${MonthDay}\s+${Hours}:${Minutes}$$ -> Record + ^${User}\s+${SessionType}\s+${Year}\-${Month}\-${MonthDay}\s+${Hours}:${Minutes}\s+${Ip}$$ -> Record + ^${User}\s+${SessionType}\s+${Month}\s+${MonthDay}\s+${Hours}:${Minutes}\s+${Ip}$$ -> Record \ No newline at end of file diff --git a/spytest/templates/show_version.tmpl b/spytest/templates/show_version.tmpl new file mode 100644 index 00000000000..dec446d2897 --- /dev/null +++ b/spytest/templates/show_version.tmpl @@ -0,0 +1,35 @@ +Value Version (\S+) +Value Product (.*) +Value Distribution (\S+\s+\d+.\d+) +Value Kernel (\S+) +Value Build_commit (\S+) +Value Build_date (.*) +Value Built_by (\S+) +Value Platform (\S+) +Value HwSKU (\S+) +Value ASIC (\S+) +Value Serial_Number (\S+) +Value Uptime (.*) +Value user (\d+) +Value load_average (.*) +Value REPOSITORY (\S+) +Value TAG (\S+) +Value IMAGE_ID (\S+) +Value SIZE (\S+) + +Start + ^SONiC Software Version:\s+${Version}\s*$$ + ^Product:\s+${Product}\s*$$ + ^Distribution:\s+${Distribution}\s*$$ + ^Kernel:\s+${Kernel}\s*$$ + ^Build commit:\s+${Build_commit}\s*$$ + ^Build date:\s+${Build_date}\s*$$ + ^Built by:\s+${Built_by}\s*$$ + ^Platform:\s+${Platform}\s*$$ + ^HwSKU:\s+${HwSKU}\s*$$ + ^ASIC:\s+${ASIC}\s*$$ + ^Serial Number:\s+${Serial_Number}\s*$$ + ^Uptime:\s+${Uptime},\s+${user}\s+user,\s+load average:\s+${load_average}\s*$$ -> Record + ^Docker images:\s*$$ + ^REPOSITORY\s+TAG\s+IMAGE ID\s+SIZE\s*$$ + ^${REPOSITORY}\s+${TAG}\s+${IMAGE_ID}\s+${SIZE}\s*$$ -> Record \ No newline at end of file diff --git a/spytest/templates/show_vlan_brief.tmpl b/spytest/templates/show_vlan_brief.tmpl new file mode 100644 index 00000000000..6edccbc49b6 --- /dev/null +++ b/spytest/templates/show_vlan_brief.tmpl @@ -0,0 +1,15 @@ +Value Filldown VID (\d+) +Value Filldown IpAdd (\S+) +Value Ports (\S+) +Value PortTagging (\S+) +Value DHCPHelperAdd (\S+) + +Start + ^\+\S+ + ^\|\s+VLAN ID\s+\|\s+IP Address\s+\|\s+Ports\s+\|\s+Port Tagging\s+\|\s+DHCP Helper Address\s+\| + ^\+\S+ + ^\|\s+${VID}\s+\|\s+(${IpAdd}|.)\s+\|\s+(${Ports}|.)\s+\|\s+(${PortTagging}|.)\s+\|\s+(${DHCPHelperAdd}|.)\s+\| -> Record + ^\|\s+\|\s+(${IpAdd}|.)\s+\|\s+(${Ports}|.)\s+\|\s+(${PortTagging}|.)\s+\|\s+(${DHCPHelperAdd}|.)\s+\| -> Record + ^\+\S+ + +EOF \ No newline at end of file diff --git a/spytest/templates/show_vlan_config.tmpl b/spytest/templates/show_vlan_config.tmpl new file mode 100644 index 00000000000..2967be5f3c0 --- /dev/null +++ b/spytest/templates/show_vlan_config.tmpl @@ -0,0 +1,15 @@ +Value Name (\S+) +Value VID (\d+) +Value Member (\S+) +Value Mode (\S+) + +Start + ^Name\s+VID\s+Member\s+Mode -> TYPE1 + ^Name\s+VID -> TYPE2 + +TYPE1 + ^${Name}\s+${VID}\s+${Member}\s+${Mode} -> Record + ^${Name}\s+${VID} -> Record + +TYPE2 + ^${Name}\s+${VID} -> Record diff --git a/spytest/templates/show_vlan_count.tmpl b/spytest/templates/show_vlan_count.tmpl new file mode 100644 index 00000000000..9dbd0f66a71 --- /dev/null +++ b/spytest/templates/show_vlan_count.tmpl @@ -0,0 +1,4 @@ +Value vlan_count (\d+) + +Start + ^\s*Total\s*Vlan\s*count:${vlan_count}\s*$$ -> Record diff --git a/spytest/templates/show_vrf.tmpl b/spytest/templates/show_vrf.tmpl new file mode 100644 index 00000000000..df15b9cded5 --- /dev/null +++ b/spytest/templates/show_vrf.tmpl @@ -0,0 +1,12 @@ +Value Required vrfName (Vrf\S+) +Value List interfaces (\S+) + +Start + ^.*----- -> Routes + +Routes + ^\S+ -> Continue.Record + ^${vrfName}\s+${interfaces} + ^${vrfName} + ^\s+${interfaces} + diff --git a/spytest/templates/show_vrf_management.tmpl b/spytest/templates/show_vrf_management.tmpl new file mode 100644 index 00000000000..383f1d21fb0 --- /dev/null +++ b/spytest/templates/show_vrf_management.tmpl @@ -0,0 +1,6 @@ +Value vrf_name (\S+) +Value interfaces (\S+) + +Start + ^VRF-Name\s+Interfaces + ^${vrf_name}\s+${interfaces}\s*$$ -> Record \ No newline at end of file diff --git a/spytest/templates/show_vrf_verbose.tmpl b/spytest/templates/show_vrf_verbose.tmpl new file mode 100644 index 00000000000..320bc5319da --- /dev/null +++ b/spytest/templates/show_vrf_verbose.tmpl @@ -0,0 +1,13 @@ +Value vrfName (\S+) +Value Required vrfTableID (\d+) +Value Required vrfID (\d+) +Value List interfaces (\S+) + +Start + ^.*----- -> Routes + +Routes + ^\S+\s+\d+ -> Continue.Record + ^${vrfName}\s+${vrfTableID}\s+${vrfID}\s+${interfaces} + ^\s+${interfaces} + diff --git a/spytest/templates/show_vrrp.tmpl b/spytest/templates/show_vrrp.tmpl new file mode 100644 index 00000000000..49d0cb57e1f --- /dev/null +++ b/spytest/templates/show_vrrp.tmpl @@ -0,0 +1,25 @@ +Value Required vrid (\d+) +Value interface ([\w\d]+) +Value version (\d+) +Value state (\w+) +Value vip ([\d\.]+) +Value vmac ([\w\d\.]+) +Value config_prio (\d+) +Value current_prio (\d+) +Value List track_interface_list (\w+) +Value List track_state_list (\w+) +Value List track_priority_list (\d+) +Value adv_interval (\d+) +Value preempt (\w+) + +Start + ^\s*${interface},\s*VRID\s*${vrid}$$ + ^\s*Version\s*is\s*${version}$$ + ^\s*State\s*is\s*${state}$$ + ^\s*${vip}$$ + ^\s*Virtual\s*MAC\s*address\s*is\s*${vmac}$$ + ^\s*Configured\s*Priority\s*is\s*${config_prio},\s*Current\s*Priority\s*is\s*${current_prio}$$ + ^\s*(.*)VRID(.*) -> Continue.Record + ^\s*${track_interface_list}\s*${track_state_list}\s*${track_priority_list} + ^\s*Advertisement\s*interval\s*is\s*${adv_interval}\s*sec$$ + ^\s*Preemption\s*is\s*${preempt}$$ -> Record diff --git a/spytest/templates/show_vrrp_summary.tmpl b/spytest/templates/show_vrrp_summary.tmpl new file mode 100644 index 00000000000..ae162489ce8 --- /dev/null +++ b/spytest/templates/show_vrrp_summary.tmpl @@ -0,0 +1,18 @@ +#################################################################### +#Interface_Name VRID State VIP Cfg_Prio Curr_Prio +# Vlan2901 50 Backup 88.88.88.59 195 196 +# Vlan2902 83 Master 67.67.67.6 197 197 +# Vlan2903 12 Backup 53.53.53.138 8 8 +# Vlan2904 86 Backup 85.85.85.145 19 19 +#################################################################### +Value interface (\w+) +Value vrid (\d+) +Value state (\w+) +Value vip ([\w\.]+) +Value config_prio (\d+) +Value current_prio (\d+) + +Start + ^\s*${interface}\s*${vrid}\s*${state}\s*${vip}\s*${config_prio}\s*${current_prio}$$ -> Record + + diff --git a/spytest/templates/show_vrrpv3.tmpl b/spytest/templates/show_vrrpv3.tmpl new file mode 100644 index 00000000000..c64b9a27d84 --- /dev/null +++ b/spytest/templates/show_vrrpv3.tmpl @@ -0,0 +1,25 @@ +Value Required vrid (\d+) +Value interface ([\w]+) +Value version (\d+) +Value state (\w+) +Value vip ([\d\:]+) +Value vmac ([\w\d\.]+) +Value config_prio (\d+) +Value current_prio (\d+) +Value List track_interface_list (\w+) +Value List track_state_list (\w+) +Value List track_priority_list (\d+) +Value adv_interval (\d+) +Value preempt (\w+) + +Start + ^\s*${interface},\s*VRID\s*${vrid}$$ + ^\s*Version\s*is\s*${version}$$ + ^\s*State\s*is\s*${state}$$ + ^\s*${vip}$$ + ^\s*Virtual\s*MAC\s*address\s*is\s*${vmac}$$ + ^\s*Configured\s*Priority\s*is\s*${config_prio},\s*Current\s*Priority\s*is\s*${current_prio}$$ + ^\s*(.*)VRID(.*) -> Continue.Record + ^\s*${track_interface_list}\s*${track_state_list}\s*${track_priority_list} + ^\s*Advertisement\s*interval\s*is\s*${adv_interval}\s*sec$$ + ^\s*Preemption\s*is\s*${preempt}$$ -> Record diff --git a/spytest/templates/show_vrrpv3_summary.tmpl b/spytest/templates/show_vrrpv3_summary.tmpl new file mode 100644 index 00000000000..b095338f2ed --- /dev/null +++ b/spytest/templates/show_vrrpv3_summary.tmpl @@ -0,0 +1,18 @@ +#################################################################### +#Interface_Name VRID State VIP Cfg_Prio Curr_Prio +# Vlan2901 50 Backup 2001::10 195 196 +# Vlan2902 83 Master Fe80::100 197 197 +# Vlan2903 12 Backup 3001::18 8 8 +# Vlan2904 86 Backup 4001::99 19 19 +#################################################################### +Value interface (\w+) +Value vrid (\d+) +Value state (\w+) +Value vip ([\w:]+) +Value config_prio (\d+) +Value current_prio (\d+) + +Start + ^\s*${interface}\s*${vrid}\s*${state}\s*${vip}\s*${config_prio}\s*${current_prio}$$ -> Record + + diff --git a/spytest/templates/show_vxlan_evpn_remote_mac_all.tmpl b/spytest/templates/show_vxlan_evpn_remote_mac_all.tmpl new file mode 100644 index 00000000000..32c49818ba2 --- /dev/null +++ b/spytest/templates/show_vxlan_evpn_remote_mac_all.tmpl @@ -0,0 +1,11 @@ +Value VLAN (\S+) +Value MAC (([0-9a-f]{2}[:]){5}[0-9a-f]{2}) +Value RVTEP (\d+\.\d+\.\d+\.\d) +Value VNI (\d+) +Value TYPE (\w+) +Value Fillup TOTAL_COUNT (\S+) + +Start + ^\|\s+${VLAN}\s+\|\s+${MAC}\s+\|\s+${RVTEP}\s+\|\s+${VNI}\s+\|\s+${TYPE}\s+ -> Record + ^\s*${VLAN}\s+${MAC}\s+${TYPE}\s+${RVTEP}\s+${VNI}\s* -> Record + ^\Total count :\s+${TOTAL_COUNT} diff --git a/spytest/templates/show_vxlan_evpn_remote_vni_all.tmpl b/spytest/templates/show_vxlan_evpn_remote_vni_all.tmpl new file mode 100644 index 00000000000..39dd6024f99 --- /dev/null +++ b/spytest/templates/show_vxlan_evpn_remote_vni_all.tmpl @@ -0,0 +1,10 @@ +Value VLAN (\S+) +Value RVTEP (\d+\.\d+\.\d+\.\d) +Value VNI (\d+) +Value Fillup TOTAL_COUNT (\S+) + +Start + ^\|\s+${VLAN}\s+\|\s+${RVTEP}\s+\|\s+${VNI}\s+ -> Record + ^\s*${VLAN}\s+${RVTEP}\s+${VNI}\s* -> Record + ^\Total count :\s+${TOTAL_COUNT} + diff --git a/spytest/templates/show_vxlan_interface.tmpl b/spytest/templates/show_vxlan_interface.tmpl new file mode 100644 index 00000000000..0d39814b285 --- /dev/null +++ b/spytest/templates/show_vxlan_interface.tmpl @@ -0,0 +1,10 @@ +Value VTEP_NAME (\S+) +Value SIP (\S+) +Value NVO_NAME (\S+) + +Start + ^\s*VTEP Name\s*:\s*${VTEP_NAME},\s*SIP\s*:\s*${SIP}\s* + ^\s*NVO Name\s*:\s*${NVO_NAME},.*$$ -> Record + ^\s*VTEP Name\s*:\s*${VTEP_NAME},\s*SIP\s*:\s*${SIP} -> Record + ^$$ + diff --git a/spytest/templates/show_vxlan_tunnel.tmpl b/spytest/templates/show_vxlan_tunnel.tmpl new file mode 100644 index 00000000000..2f515eef1f4 --- /dev/null +++ b/spytest/templates/show_vxlan_tunnel.tmpl @@ -0,0 +1,11 @@ +Value SRC_VTEP (\d+\.\d+\.\d+\.\d+) +Value REM_VTEP (\d+\.\d+\.\d+\.\d+) +Value TUN_TYPE (\S+) +Value TUN_STATUS (\S+) +Value NAME (\S+) +Value Fillup TOTAL_COUNT (\d+) + +Start + ^\|\s+${SRC_VTEP}\s+\|\s+${REM_VTEP}\s+\|\s+${TUN_TYPE}\s+\|\s+${TUN_STATUS}\s+\| -> Record + ^\s*${NAME}\s+${SRC_VTEP}\s+${REM_VTEP}\s+${TUN_TYPE}\s+${TUN_STATUS}\s* -> Record + ^\Total count :\s+${TOTAL_COUNT} diff --git a/spytest/templates/show_vxlan_vlanvnimap.tmpl b/spytest/templates/show_vxlan_vlanvnimap.tmpl new file mode 100644 index 00000000000..50c7bb69322 --- /dev/null +++ b/spytest/templates/show_vxlan_vlanvnimap.tmpl @@ -0,0 +1,8 @@ +Value VLAN (\S+) +Value VNI (\d+) +Value Fillup TOTAL_COUNT (\S+) + +Start + ^\|\s+${VLAN}\s+\|\s+${VNI}\s+\| -> Record + ^\s*${VLAN}\s+${VNI}\s* -> Record + ^\Total count :\s+${TOTAL_COUNT} diff --git a/spytest/templates/show_vxlan_vrfvnimap.tmpl b/spytest/templates/show_vxlan_vrfvnimap.tmpl new file mode 100644 index 00000000000..a6c03d450c4 --- /dev/null +++ b/spytest/templates/show_vxlan_vrfvnimap.tmpl @@ -0,0 +1,8 @@ +Value VRF (\S+) +Value VNI (\d+) +Value Fillup TOTAL_COUNT (\S+) + +Start + ^\|\s+${VRF}\s+\|\s+${VNI}\s+\| -> Record + ^\s*${VRF}\s+${VNI}\s* -> Record + ^\Total count :\s+${TOTAL_COUNT} diff --git a/spytest/templates/show_warm_restart_config.tmpl b/spytest/templates/show_warm_restart_config.tmpl new file mode 100644 index 00000000000..6aa36184cba --- /dev/null +++ b/spytest/templates/show_warm_restart_config.tmpl @@ -0,0 +1,8 @@ +Value name (\S+) +Value enable (\S+) +Value timer_name (\S+) +Value timer_duration (\d+|NULL) + + +Start + ^\s*${name}\s+${enable}\s+${timer_name}\s+${timer_duration}\s*$$ -> Record diff --git a/spytest/templates/show_warm_restart_state.tmpl b/spytest/templates/show_warm_restart_state.tmpl new file mode 100644 index 00000000000..bd2e8d8de22 --- /dev/null +++ b/spytest/templates/show_warm_restart_state.tmpl @@ -0,0 +1,7 @@ +Value name (\S+) +Value restore_count (\d+) +Value state (\S+) + +Start + ^\s*${name}\s+${restore_count}\s+${state}\s*$$ -> Record + ^\s*${name}\s+${restore_count}\s*$$ -> Record diff --git a/spytest/templates/show_watermark_interval.tmpl b/spytest/templates/show_watermark_interval.tmpl new file mode 100644 index 00000000000..8c540829bdd --- /dev/null +++ b/spytest/templates/show_watermark_interval.tmpl @@ -0,0 +1,4 @@ +Value SnapshotInterval (\d+) + +Start + ^\s*Snapshot\s*interval(\s+|:\s+)${SnapshotInterval}\s*second\(s\)\s*$$ -> Record \ No newline at end of file diff --git a/spytest/templates/show_watermark_telemetry_interval.tmpl b/spytest/templates/show_watermark_telemetry_interval.tmpl new file mode 100644 index 00000000000..f1493b93f42 --- /dev/null +++ b/spytest/templates/show_watermark_telemetry_interval.tmpl @@ -0,0 +1,4 @@ +Value TelemetryInterval (\d+) + +Start + ^\s*Telemetry\s*interval(\s+|:\s+)${TelemetryInterval}\s*second\(s\)\s*$$ -> Record \ No newline at end of file diff --git a/spytest/templates/sonic_acl_show.tmpl b/spytest/templates/sonic_acl_show.tmpl new file mode 100644 index 00000000000..c890c5e315b --- /dev/null +++ b/spytest/templates/sonic_acl_show.tmpl @@ -0,0 +1,14 @@ +Value RuleName (\S+) +Value TableName (\S+) +Value Type (\S+) +Value Priority (\d+) +Value Action (\S+) +Value PacketsCnt (\d+) +Value BytesCnt (\d+) +Value Bindpoint (\S+) + + +Start + ^\s*${RuleName}\s+${TableName}\s+${Type}\s+${Priority}\s+${Action}\s+${PacketsCnt}\s+${BytesCnt}\s*$$ -> Record + ^\s*${RuleName}\s+${TableName}\s+${Priority}\s+${Bindpoint}\s+${PacketsCnt}\s+${BytesCnt}\s*$$ -> Record + ^\s*${RuleName}\s+${TableName}\s+${Priority}\s+${PacketsCnt}\s+${BytesCnt}\s*$$ -> Record diff --git a/spytest/templates/sonic_snmp_yml.tmpl b/spytest/templates/sonic_snmp_yml.tmpl new file mode 100644 index 00000000000..5a01efb4d8f --- /dev/null +++ b/spytest/templates/sonic_snmp_yml.tmpl @@ -0,0 +1,15 @@ +Value snmp_rocommunity (\S+) +Value snmp_rocommunity6 (\S+) +Value snmp_location (\S+) +Value v1_trap_dest ([\S+ ]+) +Value v2_trap_dest ([\S+ ]+) +Value v3_trap_dest ([\S+ ]+) + + +Start + ^\s*snmp_rocommunity:\s+${snmp_rocommunity}\s*$$ + ^\s*snmp_rocommunity6:\s+${snmp_rocommunity6}\s*$$ + ^\s*snmp_location:\s+${snmp_location}\s*$$ + ^\s*v1_trap_dest:\s+${v1_trap_dest}\s*$$ + ^\s*v2_trap_dest:\s+${v2_trap_dest}\s*$$ + ^\s*v3_trap_dest:\s+${v3_trap_dest}\s*$$ diff --git a/spytest/templates/stpctl_port.tmpl b/spytest/templates/stpctl_port.tmpl new file mode 100644 index 00000000000..d1e7e5e671d --- /dev/null +++ b/spytest/templates/stpctl_port.tmpl @@ -0,0 +1,9 @@ +Value port_id (\d+) +Value stp_port_id (\d+) +Value mclag_sm_state (\S+) + +Start + ^\s*port_id\s+=\s+\d+\s+${port_id}\s*$$ + ^\s*stp_port_id\s+=\s+${stp_port_id}\s*$$ + ^\s*mclag_sm_state\s+=\s+${mclag_sm_state}\s*$$ + diff --git a/spytest/templates/sudo_sonic_installer_list.tmpl b/spytest/templates/sudo_sonic_installer_list.tmpl new file mode 100644 index 00000000000..6b8fce2ec6a --- /dev/null +++ b/spytest/templates/sudo_sonic_installer_list.tmpl @@ -0,0 +1,9 @@ +Value Current (\S+) +Value Next (\S+) +Value Available (\S+) + +Start + ^Current: ${Current} + ^Next: ${Next} + ^Available: + ^${Available}$$ -> Record \ No newline at end of file diff --git a/spytest/templates/tcpdump.tmpl b/spytest/templates/tcpdump.tmpl new file mode 100644 index 00000000000..a58aecfebe6 --- /dev/null +++ b/spytest/templates/tcpdump.tmpl @@ -0,0 +1,10 @@ +Value ARP_SRC (\S+) +Value ARP_DST (\S+) +Value NA_SRC (\S+) +Value NA_DST (\S+) + +Start + ^\s*ARP.+who-has\s${ARP_DST}\s.*tell\s${ARP_SRC},\s.+$$ -> Record + ^\s*IP6\s${NA_SRC}\s.+neighbor solicitation, who has\s${NA_DST},\s.+$$ -> Record + ^$$ + diff --git a/spytest/templates/teamdctl_portchannel_config.tmpl b/spytest/templates/teamdctl_portchannel_config.tmpl new file mode 100644 index 00000000000..7215f15e542 --- /dev/null +++ b/spytest/templates/teamdctl_portchannel_config.tmpl @@ -0,0 +1,4 @@ +Value device ([a-zA-Z0-9]+) + +Start + ^\s*\"device\":\s*\"${device}\",\s*$$ -> Record diff --git a/spytest/templates/teamdctl_portchannel_state.tmpl b/spytest/templates/teamdctl_portchannel_state.tmpl new file mode 100644 index 00000000000..e8cef3768a6 --- /dev/null +++ b/spytest/templates/teamdctl_portchannel_state.tmpl @@ -0,0 +1,16 @@ +Value runner (\S+) +Value member ([a-zA-Z0-9]+) +Value link_summary (\S+) +Value watch (\S+) +Value name (\S+) +Value link_state (\S+) +Value down_count (\d+) + +Start + ^\s*runner:\s*${runner}\s*$$ + ^\s*${member}\s*$$ + ^\s*link summary:\s*${link_summary}\s*$$ + ^\s*instance\[${watch}\]:\s*$$ + ^\s*name:\s*${name}\s*$$ + ^\s*link:\s*${link_state}\s*$$ + ^\s*down count:\s*${down_count}\s*$$ -> Record diff --git a/spytest/templates/telemetry_gnmi_get.tmpl b/spytest/templates/telemetry_gnmi_get.tmpl new file mode 100644 index 00000000000..117c0d42924 --- /dev/null +++ b/spytest/templates/telemetry_gnmi_get.tmpl @@ -0,0 +1,4 @@ +Value data (.*) + +Start + ^\s*json_ietf_val:\s*${data} -> Record diff --git a/spytest/templates/top.tmpl b/spytest/templates/top.tmpl new file mode 100644 index 00000000000..7940a799570 --- /dev/null +++ b/spytest/templates/top.tmpl @@ -0,0 +1,14 @@ +Value total (\d+) +Value free (\d+) +Value used (\d+) +Value buff_cache (\d+) +Value pid (\d+) +Value user (\S+) +Value cpu (\S+) +Value mem (\S+) +Value time (\S+) +Value command (\S+) + +Start + ^\s*KiB\s*Mem\s*:\s*${total}\s*total,\s*${free}\s*free,\s*${used}\s*used,\s*${buff_cache}\s*buff\/cache\s*$$ -> Record + ^\s*${pid}\s+${user}\s+\S+\s+\S+\s+\d+\s+\d+\s+\d+\s+\w+\s+${cpu}\s+${mem}\s+${time}\s+${command}\s*$$ -> Record diff --git a/spytest/templates/unix_ifcfg.tmpl b/spytest/templates/unix_ifcfg.tmpl new file mode 100644 index 00000000000..b6c301a70a3 --- /dev/null +++ b/spytest/templates/unix_ifcfg.tmpl @@ -0,0 +1,27 @@ +Value Required Interface ([^:]+) +Value MTU (\d+) +Value State ((in)?active) +Value MAC ([\d\w:]+) +Value List Inet ([\d\.]+) +Value List Netmask (\S+) +Value List Broadcast ([\d\.]+) +# Don't match interface local (fe80::/10) - achieved with excluding '%'. +Value List Inet6 ([^%]+) +Value List Prefix (\d+) +Value tx_packets (\d+) +Value tx_bytes (\d+) +Value rx_packets (\d+) +Value rx_bytes (\d+) + +Start + # Record interface record (if we have one). + ^\S+:.* -> Continue.Record + # Collect data for new interface. + ^${Interface}:.* mtu ${MTU} + ^\s+ether\s+${MAC} + ^\s+inet\s+${Inet}\s+netmask\s+${Netmask}\s+broadcast\s+${Broadcast} + ^\s+inet\s+${Inet}\s+netmask\s+${Netmask} + ^\s+inet6\s+${Inet6}\s+prefixlen\s+${Prefix} + ^\s+RX\s+packets\s+${rx_packets}\s+bytes\s+${rx_bytes}\s+\(.*\) + ^\s+TX\s+packets\s+${tx_packets}\s+bytes\s+${tx_bytes}\s+\(.*\) + ^$$ diff --git a/spytest/templates/ztp_status.tmpl b/spytest/templates/ztp_status.tmpl new file mode 100644 index 00000000000..cf0de48f180 --- /dev/null +++ b/spytest/templates/ztp_status.tmpl @@ -0,0 +1,16 @@ +Value Filldown AdminMode (\S+) +Value Filldown Service (\S+\s*\S+)|(\S+) +Value Filldown Status (\S+\s*\S+)|(\S+) +Value Filldown Source (\S+\s*\S+)|(\S+) +Value Filldown TimeStamp (\S+\s+\S+) +Value Filldown FileName (\S+\s*:\s+[\S+\s+]+) + +Start + ^\s*ZTP\s*Admin\s*Mode\s*:\s*${AdminMode}\s*$$ -> Record + ^\s*ZTP\s*Service\s*:\s*${Service}\s*$$ -> Record + ^\s*ZTP\s*Status\s*:\s*${Status}\s*$$ -> Record + ^\s*ZTP\s*Source\s*:\s*${Source}\s*$$ -> Record + ^\s*Timestamp\s*:\s*${TimeStamp}\s*$$ -> Record + ^\s*${FileName}\s*$$ -> Record + +EOF \ No newline at end of file diff --git a/spytest/templates/ztp_status_v.tmpl b/spytest/templates/ztp_status_v.tmpl new file mode 100644 index 00000000000..17224bf17ba --- /dev/null +++ b/spytest/templates/ztp_status_v.tmpl @@ -0,0 +1,20 @@ +Value FileName (\d+-\S+) +Value FileStatus (\S+) +Value FileTimeStamp (\S+\s+\S+\s*\S+) +Value FileExitCode (\d+) +Value FileError (\S+\s+\S+) +Value IgnoreResult (\S+) + + +Start + ^\s*-+\s* -> Plugin_Details + +Plugin_Details + ^\s*${FileName}\s* + ^\s*-+\s* + ^\s*Status\s*:\s*${FileStatus}\s*$$ + ^\s*Timestamp\s*:\s*${FileTimeStamp}\s*$$ + ^\s*Exit\s+Code\s*:\s*${FileExitCode}\s*$$ + ^\s*Error\s*:\s*${FileError}\s*$$ + ^\s*Ignore\s+Result\s*:\s*${IgnoreResult}\s*$$ -> Record + ^\s*$$ \ No newline at end of file diff --git a/spytest/testbeds/sample_sonic_2d_tgen.yaml b/spytest/testbeds/sample_sonic_2d_tgen.yaml new file mode 100644 index 00000000000..cc00123802a --- /dev/null +++ b/spytest/testbeds/sample_sonic_2d_tgen.yaml @@ -0,0 +1,47 @@ +version: 2.0 + +services: {default: !include sonic_services.yaml} + +params: !include sonic_params.yaml +builds: !include sonic_builds.yaml +speeds: !include sonic_speeds.yaml +errors: !include sonic_errors.yaml +instrument: !include sonic_instrument.yaml + +configs: + default: !include sonic_configs.yaml + empty: {current: [], restore: []} + +devices: + s6100-01: + device_type: DevSonic + access: {protocol: telnet, ip: 1.2.3.4, port: 2000} + rps: {model: Raritan, ip: 2.3.4.5, outlet: 10, username: admin, password: admin} + credentials: {username: admin, password: YourPaSsWoRd, altpassword: broadcom} + properties: {config: default, build: default, services: default, params: def_dut, speed: default} + s6100-02: + device_type: DevSonic + access: {protocol: telnet, ip: 1.2.3.4, port: 2001} + rps: {model: Raritan, ip: 2.3.4.5, outlet: 11, username: admin, password: admin} + credentials: {username: admin, password: YourPaSsWoRd, altpassword: broadcom} + properties: {config: default, build: default, services: default, params: def_dut, speed: default} + ixia-01: + device_type: TGEN + properties: {type: ixia, version: 8.42, ip: 10.59.130.4, ix_server: 10.75.203.138, params: def_tg} + stc-01: + device_type: TGEN + properties: {type: stc, version: 4.91, ip: 10.59.130.3, params: def_tg} + +topology: + s6100-01: + interfaces: + Ethernet11: {EndDevice: s6100-02, EndPort: Ethernet21, params: def_link} + Ethernet12: {EndDevice: s6100-02, EndPort: Ethernet22, params: def_link} + Ethernet15: {EndDevice: ixia-01, EndPort: 6/3, params: def_tg_link} + Ethernet16: {EndDevice: ixia-01, EndPort: 6/4, params: def_tg_link} + s6100-02: + interfaces: + Ethernet25: {EndDevice: stc-01, EndPort: 5/7, params: def_tg_link} + Ethernet26: {EndDevice: stc-01, EndPort: 5/8, params: def_tg_link} + + diff --git a/spytest/testbeds/sample_sonic_ssh_1d.yaml b/spytest/testbeds/sample_sonic_ssh_1d.yaml new file mode 100644 index 00000000000..52809295490 --- /dev/null +++ b/spytest/testbeds/sample_sonic_ssh_1d.yaml @@ -0,0 +1,43 @@ +version: 2.0 + +services: {default: !include sonic_services.yaml} + +params: !include sonic_params.yaml +builds: !include sonic_builds.yaml +speeds: !include sonic_speeds.yaml +errors: !include sonic_errors.yaml +instrument: !include sonic_instrument.yaml + +configs: + default: !include sonic_configs.yaml + empty: {current: [], restore: []} + +devices: + s6100-01: + device_type: DevSonic + access: {protocol: ssh, ip: 1.2.3.4, port: 22} + rps: {model: None, ip: 2.3.4.5, outlet: 10, username: admin, password: admin} + credentials: {username: admin, password: YourPaSsWoRd, altpassword: broadcom} + properties: {config: default, build: default, services: default, params: def_dut, speed: default} + s6100-02: + device_type: DevSonic + access: {protocol: telnet, ip: 1.2.3.4, port: 2001} + rps: {model: Raritan, ip: 2.3.4.5, outlet: 11, username: admin, password: admin} + credentials: {username: admin, password: YourPaSsWoRd, altpassword: broadcom} + properties: {config: default, build: default, services: default, params: def_dut, speed: default} + s6100-03: + device_type: DevSonic + access: {protocol: telnet, ip: 1.2.3.4, port: 2002} + rps: {model: Avocent, ip: 2.3.4.5, outlet: 12, username: admin, password: admin} + credentials: {username: admin, password: YourPaSsWoRd, altpassword: broadcom} + properties: {config: default, build: default, services: default, params: def_dut, speed: default} + ixia-01: + device_type: TGEN + properties: {type: ixia, version: 7.40, ip: 1.2.3.4, ix_server: 3.4.5.6, params: def_tg} + stc-01: + device_type: TGEN + properties: {type: stc, version: 4.95, ip: 1.2.3.4, params: def_tg} + +topology: + s6100-01: + diff --git a/spytest/testbeds/sample_sonic_terminal_3d.yaml b/spytest/testbeds/sample_sonic_terminal_3d.yaml new file mode 100644 index 00000000000..a615811aaba --- /dev/null +++ b/spytest/testbeds/sample_sonic_terminal_3d.yaml @@ -0,0 +1,56 @@ +version: 2.0 + +services: {default: !include sonic_services.yaml} + +params: !include sonic_params.yaml +builds: !include sonic_builds.yaml +speeds: !include sonic_speeds.yaml +errors: !include sonic_errors.yaml +instrument: !include sonic_instrument.yaml + +configs: + default: !include sonic_configs.yaml + empty: {current: [], restore: []} + +devices: + s6100-01: + device_type: DevSonic + access: {protocol: telnet, ip: 1.2.3.4, port: 2000} + rps: {model: Raritan, ip: 2.3.4.5, outlet: 10, username: admin, password: admin} + credentials: {username: admin, password: YourPaSsWoRd, altpassword: broadcom} + properties: {config: default, build: default, services: default, params: def_dut, speed: default} + s6100-02: + device_type: DevSonic + access: {protocol: telnet, ip: 1.2.3.4, port: 2001} + rps: {model: Raritan, ip: 2.3.4.5, outlet: 11, username: admin, password: admin} + credentials: {username: admin, password: YourPaSsWoRd, altpassword: broadcom} + properties: {config: default, build: default, services: default, params: def_dut, speed: default} + s6100-03: + device_type: DevSonic + access: {protocol: telnet, ip: 1.2.3.4, port: 2002} + rps: {model: Raritan, ip: 2.3.4.5, outlet: 12, username: admin, password: admin} + credentials: {username: admin, password: YourPaSsWoRd, altpassword: broadcom} + properties: {config: default, build: default, services: default, params: def_dut, speed: default} + ixia-01: + device_type: TGEN + properties: {type: ixia, version: 8.40, ip: 1.2.3.5, ix_server: 3.4.5.6, params: def_tg} + stc-01: + device_type: TGEN + properties: {type: stc, version: 4.95, ip: 1.2.3.6, params: def_tg} + +topology: + s6100-01: + interfaces: + Ethernet11: {EndDevice: s6100-02, EndPort: Ethernet21, params: def_link} + Ethernet12: {EndDevice: s6100-02, EndPort: Ethernet22, params: def_link} + Ethernet13: {EndDevice: s6100-02, EndPort: Ethernet23, params: def_link} + Ethernet15: {EndDevice: ixia-01, EndPort: 1/1, params: def_tg_link} + Ethernet16: {EndDevice: ixia-01, EndPort: 1/2, params: def_tg_link} + s6100-02: + interfaces: + Ethernet24: {EndDevice: s6100-03, EndPort: Ethernet34, params: def_link} + Ethernet25: {EndDevice: s6100-03, EndPort: Ethernet35, params: def_link} + Ethernet26: {EndDevice: s6100-03, EndPort: Ethernet36, params: def_link} + Ethernet27: {EndDevice: stc-01, EndPort: 2/1, params: def_tg_link} + Ethernet28: {EndDevice: stc-01, EndPort: 2/2, params: def_tg_link} + diff --git a/spytest/testbeds/sonic_builds.yaml b/spytest/testbeds/sonic_builds.yaml new file mode 100644 index 00000000000..4c4d1da8b20 --- /dev/null +++ b/spytest/testbeds/sonic_builds.yaml @@ -0,0 +1,33 @@ + +default : + current: #current.bin + restore: #golden.bin + +arlo : + current: http://10.59.132.240:9009/projects/csg_sonic/sonic_builds/daily/2.0/broadcom/LATEST_BUILD/sonic-broadcom.bin + #restore: http://10.59.132.240:9009/projects/csg_sonic/sonic_builds/daily/2.0/broadcom/GA_BUILD/sonic-broadcom.bin + +arloplus : + current: http://10.59.132.240:9009/projects/csg_sonic/sonic_builds/daily/2.1/broadcom/LATEST_BUILD/sonic-broadcom-cloud-advanced.bin + #restore: http://10.59.132.240:9009/projects/csg_sonic/sonic_builds/daily/2.1/broadcom/GA_BUILD/sonic-broadcom.bin + +arloplus_rc : + current: http://10.59.132.240:9009/projects/sonic_deliverables/sonic_2.1/release/broadcom/Broadcom_SONiC_2.1.0_RC11/sonic-broadcom-cloud-advanced.bin + #restore: http://10.59.132.240:9009/projects/csg_sonic/sonic_builds/daily/2.1/broadcom/GA_BUILD/sonic-broadcom.bin + +buzznik : + current: http://10.59.132.240:9009/projects/csg_sonic/sonic_builds/daily/3.0/broadcom/LATEST_BUILD_NGCOV/sonic-broadcom-cloud-advanced.bin + #restore: http://10.59.132.240:9009/projects/csg_sonic/sonic_builds/daily/3.0/broadcom/GA_BUILD/sonic-broadcom.bin + +daily : + current: http://10.59.132.240:9009/projects/csg_sonic/sonic_builds/daily/3.0/broadcom/LATEST_BUILD_NGCOV/sonic-broadcom-cloud-advanced.bin + #restore: http://10.59.132.240:9009/projects/csg_sonic/sonic_builds/daily/3.0/broadcom/GA_BUILD/sonic-broadcom.bin + +daily_gcov : + current: http://10.59.132.240:9009/projects/csg_sonic/sonic_builds/daily/3.0/broadcom/LATEST_BUILD_GCOV/sonic-broadcom-cloud-advanced.bin + #restore: http://10.59.132.240:9009/projects/csg_sonic/sonic_builds/daily/3.0/broadcom/GA_BUILD/sonic-broadcom.bin + +community : + current: http://10.59.132.240:9009/projects/csg_sonic/sonic_builds/community/LATEST_BUILD/sonic-broadcom.bin + #restore: http://10.59.132.240:9009/projects/csg_sonic/sonic_builds/community/GA_BUILD/sonic-broadcom.bin + diff --git a/spytest/testbeds/sonic_configs.yaml b/spytest/testbeds/sonic_configs.yaml new file mode 100644 index 00000000000..1dc688dabe6 --- /dev/null +++ b/spytest/testbeds/sonic_configs.yaml @@ -0,0 +1,15 @@ +############################################################### +# the file extension determines how to apply the file +# .bcm is applied as config.bcm +# .ini is applied as port_config.ini +# .json is applied as config_db.json +# .copp is applied as swss copp config +# .frr is applied as frr.conf +# .sh is applied as shell script +# .xml is applied as minigraph.xml +############################################################### + +#current: [ [current.bcm, current.ini, current.frr, current.json, current.copp], current.xml, current.cmds, current.sh] +#restore: [ [restore.bcm, restore.ini, restore.frr, restore.json, restore.copp], restore.xml, restore.cmds, restore.sh] +current: [ ] +restore: [ ] diff --git a/spytest/testbeds/sonic_errors.yaml b/spytest/testbeds/sonic_errors.yaml new file mode 100644 index 00000000000..c0eba958ebb --- /dev/null +++ b/spytest/testbeds/sonic_errors.yaml @@ -0,0 +1,139 @@ +################################################ +# error_identifier: +# command: +# search: +# action: +# actions supported +# raise: raise exception to the caller +# reboot: reboot the system +# ignore: ignore the match +# Note: +# The output is matched for error identifiers +# in the same order specified. The matching +# will stop once an error identifier matches +################################################ + +default: + containing_swss_service_failed: + command: ".*" + search: ".*Job for swss.service failed.*" + action: [raise, core-dump, tech-support] + result: [DUTFail, critical_swss_service_failed] + + containing_syncd_exited: + command: ".*" + search: ".*Process syncd exited unxepectedly.*" + action: [raise, core-dump, tech-support] + result: [DUTFail, critical_syncd_exited] + + containing_orchagent_exited: + command: ".*" + search: ".*Process orchagent exited unxepectedly.*" + action: [raise, core-dump, tech-support] + result: [DUTFail, critical_orchagent_exited] + + containing_vlanmgrd_exited: + command: ".*" + search: ".*Process vlanmgrd exited unxepectedly.*" + action: [raise, core-dump, tech-support] + result: [DUTFail, critical_vlanmgrd_exited] + + containing_abort: + command: ".*" + search: ".*terminated by SIGABRT.*" + action: [raise, core-dump, tech-support] + result: [DUTFail, critical_terminated_by_sigabrt] + + containing_err_syncd: + command: ".*" + search: ".*syncd_main: Runtime error.*" + action: [raise, core-dump, tech-support] + result: [DUTFail, critical_err_syncd] + + containing_core_dump: + command: ".*" + search: ".*core dumped.*" + action: [raise, core-dump, tech-support] + result: [DUTFail, critical_core_dumped] + + containing_kernel_panic: + command: ".*" + search: ".*Kernel panic - not syncing.*" + action: [raise, core-dump, tech-support] + result: [DUTFail, critical_core_dumped] + + containing_no_response_from_container: + command: ".*" + search: ".*Error response from daemon.*" + action: [raise] + result: [DUTFail, container_not_running] + + containing_failed_diag_cmd: + command: ".*" + search: ".*Failed to execute the diagnostic command.*" + action: [raise] + result: [DUTFail, config_cmd_error] + + containing_error_mgmt_rest_server: + command: ".*" + search: ".*Error: Could not connect to Management REST Server.*" + action: [raise] + result: [DUTFail, config_cmd_error] + + containing_python_traceback: + command: ".*" + search: ".*Traceback \\(most recent call last\\):.*" + action: [raise] + result: [DUTFail, config_cmd_error] + + containing_restartcheck_failed: + command: ".*reboot.*" + search: ".*RESTARTCHECK failed.*" + action: [raise] + + config_cmd_error: + command: ".*sudo config .*" + search: ".*Usage: config .*" + action: [raise] + result: config_cmd_error + + unrecognized_command: + command: ".*" + search: ".*Error: Unrecognized command.*" + action: [raise] + + containing_error: + command: ".*" + search: ".*Error:.*" + action: [raise] + + containing_bang: + command: ".*" + search: ".*\\^.*" + action: [raise] + + containing_failed: + command: ".*" + search: ".*Failed.*" + action: [raise] + + bash_command_not_found: + command: ".*" + search: ".*bash: .*: command not found.*" + action: [raise] + + syntax_error: + command: ".*" + search: ".*Syntax error:.*" + action: [raise] + + invalid_input: + command: ".*" + search: ".*Invalid input:.*" + action: [raise] + + unknown_command: + command: ".*" + search: ".*Unknown command:.*" + action: [raise] + diff --git a/spytest/testbeds/sonic_instrument.yaml b/spytest/testbeds/sonic_instrument.yaml new file mode 100644 index 00000000000..b57d9ce1079 --- /dev/null +++ b/spytest/testbeds/sonic_instrument.yaml @@ -0,0 +1,25 @@ +################################################ +# trigger: [action arguments] +# action: sh | py | spy | cmds | info | warn +################################################ + +default: + pre-infra-module: [] + pre-user-module: [] + pre-user-func: [] + pre-reboot: [] + post-infra-module: [] + post-user-module: [] + post-user-func: [] + post-reboot: [] + +example: + pre-infra-module: [sh, test-in-dut.sh] + pre-user-module: [cmds, test-cli.cmds] + pre-user-func: [py, test-in-dut.py] + pre-reboot: [warn, rebooting] + post-infra-module: [spy, test-out-dut.py] + post-user-module: [] + post-user-func: [] + post-reboot: [info, rebooted] + diff --git a/spytest/testbeds/sonic_params.yaml b/spytest/testbeds/sonic_params.yaml new file mode 100644 index 00000000000..2fb58d772fb --- /dev/null +++ b/spytest/testbeds/sonic_params.yaml @@ -0,0 +1,27 @@ + +def_dut: + show_delay : 0 + +def_tg: + foo: bar + +def_link: + type: ethernet + +def_tg_link: + type: ethernet + +dellemc_z9332f: + breakout: + native: 1 + name: "x86_64-dellemc_z9332f_d1508-r0" + ports: [0,8,16,24,32,40,48,56,64,72,80,88,96,104,112,120,128,136,144,152,160,168,176,184,192,200,208,216,224,232,240,248] + options: [ "1x400", "4x100", "4x25", "4x10", "4x50", "2x200", "2x100" ] + +dell_s6100: + breakout: + native: 1 + name: "x86_64-dell_s6100_c2538-r0" + ports: [0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126] + options: ["2x25", "2x10"] + diff --git a/spytest/testbeds/sonic_services.yaml b/spytest/testbeds/sonic_services.yaml new file mode 100644 index 00000000000..249a369913c --- /dev/null +++ b/spytest/testbeds/sonic_services.yaml @@ -0,0 +1,191 @@ + +tftp: + ip: 192.168.1.1 + path: /home/vmuser/tftp + +ftp: + ip: 192.168.1.1 + path: /home/vmuser/ftp + +scp: + ip: 192.168.1.1 + path: /home/vmuser/scp + username: vmuser + password: broadcom + +ssh: + ip: 192.168.1.1 + path: /tftpboot + username: admin + password: broadcom + +sftp: + ip: 192.168.1.1 + path: /tftpboot + username: admin + password: broadcom + +snmptrap: + ip: 10.59.143.179 + port: 22 + username: vagrant + password: vagrant + path: /var/log/snmptrap.log + +tacacs: + hosts: [{ip: 10.59.143.179, username: root, password: lvl7india, + timeout: 5, passkey: lvl7india, tcp_port: 49, auth_type: pap, priority: 1}, + {ip: 10.52.132.214, username: root, password: lvl7india, + timeout: 5, passkey: lvl7india, tcp_port: 49, auth_type: pap, priority: 2}, + {ip: 10.52.145.181, username: root, password: lvl7india, + timeout: 5, passkey: lvl7india, tcp_port: 49, auth_type: pap, priority: 3}, + {ip: 10.2.3.2, username: root, password: lvl7india, + timeout: 5, passkey: lvl7india, tcp_port: 49, auth_type: pap, priority: 4}, + {ip: 10.2.3.4, username: root, password: lvl7india, + timeout: 5, passkey: lvl7india, tcp_port: 49, auth_type: pap, priority: 5}, + {ip: 10.2.3.5, username: root, password: lvl7india, + timeout: 5, passkey: lvl7india, tcp_port: 49, auth_type: pap, priority: 6}, + {ip: 10.2.3.6, username: root, password: lvl7india, + timeout: 5, passkey: lvl7india, tcp_port: 49, auth_type: pap, priority: 7}, + {ip: 10.2.3.7, username: root, password: lvl7india, + timeout: 5, passkey: lvl7india, tcp_port: 49, auth_type: pap, priority: 8}] + +radius: + hosts: [{ip: 10.52.132.214, username: test, password: test, + timeout: 5, passkey: Lvl7india, udp_port: 1812, auth_type: pap, priority: 8}, + {ip: '10.52.1.1', username: root, password: lvl7india, + timeout: 5, passkey: lvl7india, udp_port: 1812, auth_type: pap, priority: 2}, + {ip: '10.52.145.181', username: root, password: lvl7india, + timeout: 5, passkey: lvl7india, udp_port: 1812, auth_type: pap, priority: 3}, + {ip: "4001::1", username: root, password: lvl7india, + timeout: 5, passkey: lvl7india, udp_port: 1812, auth_type: pap, priority: 4}, + {ip: "3001::1", username: root, password: lvl7india, + timeout: 5, passkey: lvl7india, udp_port: 1812, auth_type: pap, priority: 5}, + {ip: "2001::1", username: root, password: lvl7india, + timeout: 5, passkey: lvl7india, udp_port: 1812, auth_type: pap, priority: 6}, + {ip: "1001::1", username: root, password: lvl7india, + timeout: 5, passkey: lvl7india, udp_port: 1812, auth_type: pap, priority: 7}, + {ip: 10.2.3.7, username: root, password: lvl7india, + timeout: 5, passkey: lvl7india, udp_port: 1812, auth_type: pap, priority: 1}] + globals: [{source_ip: 10.52.1.1, passkey: Lvl7india, auth_type: pap, timeout: 7, retransmit: 2, auth_type_mschapv2: mschapv2}, + {passkey: lvl7india, auth_type_chap: chap}] + aaa: [{login_radius: radius, login_local: local,login_default: default, + failthrough_enable: enable, failthrough_disable: disable, failthrough_default: default, + fallback_enable: enable, fallback_disable: disable, fallback_default: default}, + {login_local_radius: 'local radius', login_radius_local: 'radius local'}] + local: {username: admin, password: YourPaSsWoRd} + invalid: {username: abcbd1234, password: abcd1234} + root_credentials: {username: root, password: broadcom} + invalid_passkey: {invalid_passkey: '@#_+%^'} + source_ip: {ipv6: '5001::2', ipv4: '10.5.2.3'} + logs_path: {path: '/var/log/auth.log'} + cisco_acs_server: {ip: 10.52.136.77, username: qauser, password: qauser123, passkey: lvl7india} + radius_conf_files: {path: '/usr/share/sonic/templates/radius_nss.conf.j2', backup_path: '/usr/share/sonic/templates/radius_nss_bkp.conf.j2'} + ro_user: {username: test1, password: password} + +syslog: + ip: 192.168.1.1 + port: 23 + username: root + password: lvl7india + path: /var/log/syslog + +ntp: + default: [216.239.35.4, 216.239.35.8, 216.239.35.12] + host: [162.246.94.71,192.111.144.114,47.190.36.230] + +smtp: + host: smtphost.broadcom.com + user: TAMAlert + pass: tamalert + domain: broadcom.com + sendor: SpyTest@broadcom.com + +ansible: + ip: 10.130.184.158 + username: qaadmin + password: broadcom + port: 22 + inventory: /etc/ansible/hosts + bkp_inventory: /etc/ansible/hosts.bkp + hosts: {"host1":{username: admin, password: broadcom}, + "host2":{username: admin, password: broadcom}} + playbook_dir: /etc/ansible + upgrade_path: http://10.130.64.137:4321/sonic-broadcom.bin + +ztp: + dhcp: {inband: {ip: 10.52.139.214, username: admin, password: broadcom, + config_file: /etc/dhcp/dhcpd.conf, static_ip: 192.168.2.1, interface: Ethernet46, + home_path: /home/admin/www, port: 0}, + outofband: {ip: 10.52.139.122, username: vmuser, password: broadcom, + config_file: /etc/dhcp/dhcpd.conf, static_ip: 192.168.1.1, interface: eth0, + home_path: /home/vmuser/www, port: 0}} + dhcpv6: {inband: {ip: 10.52.139.214, username: admin, password: broadcom, + config_file: /etc/dhcp/dhcpd6.conf, static_ip: "2001:db8:aad:aaa::99", interface: Ethernet46, + home_path: /home/admin/www, port: 0}, + outofband: {ip: 10.52.139.122, username: vmuser, password: broadcom, + config_file: /etc/dhcp/dhcpd6.conf, static_ip: "2002:db8:aad:aaa::99", interface: eth0, + home_path: /home/vmuser/www, port: 0}} + config_path: /sonic-ztp/json + firmware_path: /sonic-ztp/firmware + provision_script_path: /sonic-ztp/provisioning_scripts + xml_path: /sonic-ztp/xml + build_file_name: sonic-broadcom-cloud-base.bin.RC2 + uninstall_image: SONiC-OS-sonic_2.0_daily_190823_0000_262-dirty-20190823.000207 + dut_path: /host/ztp + ztp_cfg_file_name: ztp_cfg.json + docker_path: /sonic-ztp/firmware + docker_image: docker-teamd.gz + docker_component_name: teamd + minigraph_file: minigraph_as7726.xml + + +http: + ip: 1.1.1.1 + image1: sonic_broadcom.bin + path: /sonic_builds + version_upgrade_old_image_path: + version_upgrade_new_image_path: + +chef: + ip: 10.59.142.28 + username: vagrant + password: vagrant + user_home_folder: /home/vagrant + path: /home/vagrant/chef-repo + roles: /home/vagrant/chef-repo/roles + cookbook_path: /home/vagrant/chef-repo/cookbooks + client_path: /etc/chef + validation_file: validation.pem + client_rb: client.rb + client_log: /var/log/chef/client.log + default_rb: /cookbooks/sonic-cli/recipes/default.rb + mgmt_intf: eth0 + +vm_server: + ip: 10.0.0.10 + username: admin + password: broadcom + +dhcp_relay: + connected_port: Ethernet2 + server_ip: 1.1.1.1 + serverv6_ip: 1111::1 + ipv6_link_local_intf: Ethernet0 + ipv6_link_local_server_ip: 12.1.1.2 + ipv6_link_local_network: 12.1.1.0/24 + +calnex: + chassis_ip: 10.59.130.10 + controller_ip: 10.59.130.9 + +mod: + proto_file_name: r5_tapp_event.proto + protoc_path: /projects/scid/tools/protobuf/bin/protoc + +dhcp_server_vxlan: + server_mgmt_ip: 10.59.130.241 + dhcp_server_ip: 172.16.0.241 + dhcp_server_ipv6: 2072::241 + username: root + password: stratax120 diff --git a/spytest/testbeds/sonic_speeds.yaml b/spytest/testbeds/sonic_speeds.yaml new file mode 100644 index 00000000000..e6e2ce73547 --- /dev/null +++ b/spytest/testbeds/sonic_speeds.yaml @@ -0,0 +1,222 @@ +default: + +AS7326-GPOD: + Ethernet0: 10000 + Ethernet1: 10000 + Ethernet2: 10000 + Ethernet3: 10000 + +AS7326-25G: + Ethernet0: 25000 + Ethernet1: 25000 + Ethernet2: 25000 + Ethernet3: 25000 + Ethernet4: 25000 + Ethernet5: 25000 + Ethernet6: 25000 + Ethernet7: 25000 + Ethernet8: 25000 + Ethernet9: 25000 + Ethernet10: 25000 + Ethernet11: 25000 + Ethernet12: 25000 + Ethernet13: 25000 + Ethernet14: 25000 + Ethernet15: 25000 + Ethernet16: 25000 + Ethernet17: 25000 + Ethernet18: 25000 + Ethernet19: 25000 + Ethernet20: 25000 + Ethernet21: 25000 + Ethernet22: 25000 + Ethernet23: 25000 + Ethernet24: 25000 + Ethernet25: 25000 + Ethernet26: 25000 + Ethernet27: 25000 + Ethernet28: 25000 + Ethernet29: 25000 + Ethernet30: 25000 + Ethernet31: 25000 + Ethernet32: 25000 + Ethernet33: 25000 + Ethernet34: 25000 + Ethernet35: 25000 + Ethernet36: 25000 + Ethernet37: 25000 + Ethernet38: 25000 + Ethernet39: 25000 + Ethernet40: 25000 + Ethernet41: 25000 + Ethernet42: 25000 + Ethernet43: 25000 + Ethernet44: 25000 + Ethernet45: 25000 + Ethernet46: 25000 + Ethernet47: 25000 + +AS7326-10G: + Ethernet0: 10000 + Ethernet1: 10000 + Ethernet2: 10000 + Ethernet3: 10000 + Ethernet4: 10000 + Ethernet5: 10000 + Ethernet6: 10000 + Ethernet7: 10000 + Ethernet8: 10000 + Ethernet9: 10000 + Ethernet10: 10000 + Ethernet11: 10000 + Ethernet12: 10000 + Ethernet13: 10000 + Ethernet14: 10000 + Ethernet15: 10000 + Ethernet16: 10000 + Ethernet17: 10000 + Ethernet18: 10000 + Ethernet19: 10000 + Ethernet20: 10000 + Ethernet21: 10000 + Ethernet22: 10000 + Ethernet23: 10000 + Ethernet24: 10000 + Ethernet25: 10000 + Ethernet26: 10000 + Ethernet27: 10000 + Ethernet28: 10000 + Ethernet29: 10000 + Ethernet30: 10000 + Ethernet31: 10000 + Ethernet32: 10000 + Ethernet33: 10000 + Ethernet34: 10000 + Ethernet35: 10000 + Ethernet36: 10000 + Ethernet37: 10000 + Ethernet38: 10000 + Ethernet39: 10000 + Ethernet40: 10000 + Ethernet41: 10000 + Ethernet42: 10000 + Ethernet43: 10000 + Ethernet44: 10000 + Ethernet45: 10000 + Ethernet46: 10000 + Ethernet47: 10000 + +AS7326-10G_PG1: + Ethernet0: 10000 + Ethernet1: 10000 + Ethernet2: 10000 + Ethernet3: 10000 + Ethernet4: 10000 + Ethernet5: 10000 + Ethernet6: 10000 + Ethernet7: 10000 + Ethernet8: 10000 + Ethernet9: 10000 + Ethernet10: 10000 + Ethernet11: 10000 + +AS7326-10G_PG2: + Ethernet12: 10000 + Ethernet13: 10000 + Ethernet14: 10000 + Ethernet15: 10000 + Ethernet16: 10000 + Ethernet17: 10000 + Ethernet18: 10000 + Ethernet19: 10000 + Ethernet20: 10000 + Ethernet21: 10000 + Ethernet22: 10000 + Ethernet23: 10000 + +AS7326-10G_PG3: + Ethernet24: 10000 + Ethernet25: 10000 + Ethernet26: 10000 + Ethernet27: 10000 + Ethernet28: 10000 + Ethernet29: 10000 + Ethernet30: 10000 + Ethernet31: 10000 + Ethernet32: 10000 + Ethernet33: 10000 + Ethernet34: 10000 + Ethernet35: 10000 + +AS7326-10G_PG4: + Ethernet36: 10000 + Ethernet37: 10000 + Ethernet38: 10000 + Ethernet39: 10000 + Ethernet40: 10000 + Ethernet41: 10000 + Ethernet42: 10000 + Ethernet43: 10000 + Ethernet44: 10000 + Ethernet45: 10000 + Ethernet46: 10000 + Ethernet47: 10000 + +AS7326-SCH-10G-IXIA: + Ethernet44: 10000 + Ethernet45: 10000 + Ethernet46: 10000 + Ethernet47: 10000 + +IX8-IFA-10G: + Ethernet0: 10000 + Ethernet1: 10000 + Ethernet2: 10000 + Ethernet3: 10000 + Ethernet4: 10000 + Ethernet5: 10000 + Ethernet6: 10000 + Ethernet7: 10000 + Ethernet8: 10000 + Ethernet9: 10000 + Ethernet10: 10000 + Ethernet11: 10000 + Ethernet16: 10000 + Ethernet17: 10000 + Ethernet18: 10000 + Ethernet19: 10000 + Ethernet20: 10000 + Ethernet21: 10000 + Ethernet22: 10000 + Ethernet23: 10000 + Ethernet24: 10000 + Ethernet25: 10000 + Ethernet26: 10000 + Ethernet27: 10000 + Ethernet28: 10000 + Ethernet29: 10000 + Ethernet30: 10000 + Ethernet31: 10000 + Ethernet32: 10000 + Ethernet33: 10000 + Ethernet34: 10000 + Ethernet35: 10000 + Ethernet36: 10000 + Ethernet37: 10000 + Ethernet38: 10000 + Ethernet39: 10000 + Ethernet40: 10000 + Ethernet41: 10000 + Ethernet42: 10000 + Ethernet43: 10000 + Ethernet44: 10000 + Ethernet45: 10000 + Ethernet46: 10000 + Ethernet47: 10000 + +Force10-S6100-60-63-10G: + Ethernet60: 10000 + Ethernet61: 10000 + Ethernet62: 10000 + Ethernet63: 10000 + diff --git a/spytest/tests/__init__.py b/spytest/tests/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/spytest/tests/qos/__init__.py b/spytest/tests/qos/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/spytest/tests/qos/acl/__init__.py b/spytest/tests/qos/acl/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/spytest/tests/qos/acl/acl_json_config.py b/spytest/tests/qos/acl/acl_json_config.py new file mode 100644 index 00000000000..87e6ba37587 --- /dev/null +++ b/spytest/tests/qos/acl/acl_json_config.py @@ -0,0 +1,1280 @@ +acl_json_config_d1 = { + "ACL_TABLE": { + "L3_IPV4_INGRESS": { + "type": "L3", + "stage": "INGRESS", + "ports": [], + "policy_desc": "L3_IPV4_INGRESS" + }, + "L3_IPV4_EGRESS": { + "type": "L3", + "stage": "EGRESS", + "ports": [], + "policy_desc": "L3_IPV4_EGRESS" + }, + "L2_MAC_INGRESS": { + "type": "L2", + "stage": "INGRESS", + "ports": [], + "policy_desc": "L2_MAC_INGRESS" + }, + "L2_MAC_EGRESS": { + "type": "L2", + "stage": "EGRESS", + "ports": [], + "policy_desc": "L2_MAC_EGRESS" + } + }, + "ACL_RULE": { + "L3_IPV4_INGRESS|rule1": { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "1.1.1.1/32", + "DST_IP": "2.2.2.2/32", + "L4_SRC_PORT": 43, + "L4_DST_PORT_RANGE": "10-20", + "DSCP":62, + "IP_PROTOCOL": 6, + "PRIORITY": 1000 + }, + "L3_IPV4_INGRESS|rule2": { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "5.5.5.5/16", + "DST_IP": "9.9.9.9/16", + "L4_SRC_PORT_RANGE": "100-500", + "IP_PROTOCOL": 17, + "PRIORITY": 2000 + }, + "L3_IPV4_INGRESS|PermiAny": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "ipv4any", + "PRIORITY": 100 + }, + "L3_IPV4_INGRESS|rule4": { + "PACKET_ACTION": "DROP", + "SRC_IP": "9.9.9.9/32", + "DST_IP": "12.12.12.12/16", + "L4_DST_PORT_RANGE": "300-400", + "IP_PROTOCOL": 6, + "PRIORITY": 4000 + }, + "L3_IPV4_INGRESS|rule5": { + "PACKET_ACTION": "DROP", + "SRC_IP": "185.185.1.1/16", + "DST_IP": "18.18.1.1/16", + "TCP_FLAGS": "4/4", + "PRIORITY": 5000 + }, + "L3_IPV4_INGRESS|rule6": { + "PACKET_ACTION": "REDIRECT", + "SRC_IP": "176.185.1.1/16", + "DST_IP": "10.18.1.1/16", + "L4_SRC_PORT": 43, + "L4_DST_PORT": 567, + "IP_PROTOCOL": 6, + "PRIORITY": 5000 + }, + "L3_IPV4_INGRESS|rule7": { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "5.5.5.5/32", + "DST_IP": "9.9.9.9/32", + "IP_PROTOCOL": 17, + "PRIORITY": 2005 + }, + "L3_IPV4_INGRESS|DenyAny": { + "PACKET_ACTION": "DROP", + "ETHER_TYPE":'0x0800', + "PRIORITY": 50 + }, + "L3_IPV4_EGRESS|rule1": { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "192.138.10.1/32", + "DST_IP": "55.46.45.2/32", + "L4_SRC_PORT": 43, + "L4_DST_PORT": 567, + "DSCP":61, + "IP_PROTOCOL": 17, + "PRIORITY": 1000 + }, + "L3_IPV4_EGRESS|rule2": { + "PACKET_ACTION": "DROP", + "SRC_IP": "88.67.45.9/32", + "DST_IP": "12.12.12.12/16", + "IP_PROTOCOL": 17, + "PRIORITY": 4000 + }, + "L3_IPV4_EGRESS|rule3": { + "PACKET_ACTION": "DROP", + "SRC_IP": "185.185.1.1/16", + "DST_IP": "181.182.1.1/16", + "L4_DST_PORT": 567, + "IP_PROTOCOL": 6, + "TCP_FLAGS": "4/4", + "PRIORITY": 5000 + }, + "L3_IPV4_EGRESS|DenyAny": { + "PACKET_ACTION": "DROP", + "ETHER_TYPE":'0x0800', + "PRIORITY": 50 + }, + "L3_IPV4_EGRESS|PermiAny": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "ipv4any", + "PRIORITY": 100 + }, + "L2_MAC_INGRESS|macrule1": { + "PACKET_ACTION": "FORWARD", + "SRC_MAC": "00:0a:01:00:00:03/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:11:04/ff:ff:ff:ff:ff:ff", + "VLAN": "", + "PCP":4, + "DEI":1, + "PRIORITY": 1000 + }, + "L2_MAC_INGRESS|macrule2": { + "PACKET_ACTION": "DROP", + "SRC_MAC": "00:0a:01:00:00:05/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:11:06/ff:ff:ff:ff:ff:ff", + "VLAN": "", + "PRIORITY": 900 + }, + "L2_MAC_EGRESS|macrule3": { + "PACKET_ACTION": "FORWARD", + "SRC_MAC": "00:0a:01:00:11:04/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:00:03/ff:ff:ff:ff:ff:ff", + "VLAN": "", + "PRIORITY": 2000 + }, + "L2_MAC_EGRESS|macrule4": { + "PACKET_ACTION": "DROP", + "SRC_MAC": "00:0a:01:00:11:06/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:00:05/ff:ff:ff:ff:ff:ff", + "VLAN": "", + "PRIORITY": 9000 + }, + "L2_MAC_EGRESS|macrule5": { + "PACKET_ACTION": "DROP", + "VLAN": "", + "ETHER_TYPE":0x0810, + "PRIORITY": 90 + } + } +} + +acl_json_config_v4_switch = { + "ACL_TABLE": { + "L3_IPV4_INGRESS": { + "type": "L3", + "stage": "INGRESS", + "ports": [], + "policy_desc": "L3_IPV4_INGRESS" + }, + "L3_IPV4_EGRESS": { + "type": "L3", + "stage": "EGRESS", + "ports": [], + "policy_desc": "L3_IPV4_EGRESS" + } + }, + + "ACL_RULE": { + "L3_IPV4_INGRESS|rule1": { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "1.1.1.1/32", + "DST_IP": "2.2.2.2/32", + "L4_SRC_PORT": 43, + "L4_DST_PORT_RANGE": "10-20", + "DSCP":62, + "IP_PROTOCOL": 6, + "PRIORITY": 1000 + }, + "L3_IPV4_INGRESS|rule2": { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "5.5.5.5/16", + "DST_IP": "9.9.9.9/16", + "L4_SRC_PORT_RANGE": "100-500", + "IP_PROTOCOL": 17, + "PRIORITY": 2000 + }, + "L3_IPV4_INGRESS|PermiAny": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "ipv4any", + "PRIORITY": 100 + }, + "L3_IPV4_INGRESS|rule4": { + "PACKET_ACTION": "DROP", + "SRC_IP": "9.9.9.9/32", + "DST_IP": "12.12.12.12/16", + "L4_DST_PORT_RANGE": "300-400", + "IP_PROTOCOL": 6, + "PRIORITY": 4000 + }, + "L3_IPV4_INGRESS|rule5": { + "PACKET_ACTION": "DROP", + "SRC_IP": "185.185.1.1/16", + "DST_IP": "18.18.1.1/16", + "TCP_FLAGS": "4/4", + "PRIORITY": 5000 + }, + "L3_IPV4_INGRESS|rule6": { + "PACKET_ACTION": "DROP", + "SRC_IP": "176.185.1.1/16", + "DST_IP": "10.18.1.1/16", + "L4_SRC_PORT": 43, + "L4_DST_PORT": 567, + "IP_PROTOCOL": 6, + "PRIORITY": 5000 + }, + "L3_IPV4_INGRESS|DenyAny": { + "PACKET_ACTION": "DROP", + "ETHER_TYPE":'0x0800', + "PRIORITY": 50 + }, + "L3_IPV4_EGRESS|rule1": { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "192.138.10.1/32", + "DST_IP": "55.46.45.2/32", + "L4_SRC_PORT": 43, + "L4_DST_PORT": 567, + "DSCP":61, + "IP_PROTOCOL": 17, + "PRIORITY": 1000 + }, + "L3_IPV4_EGRESS|rule2": { + "PACKET_ACTION": "DROP", + "SRC_IP": "88.67.45.9/32", + "DST_IP": "12.12.12.12/16", + "IP_PROTOCOL": 17, + "PRIORITY": 4000 + }, + "L3_IPV4_EGRESS|rule3": { + "PACKET_ACTION": "DROP", + "SRC_IP": "185.185.1.1/16", + "DST_IP": "181.182.1.1/16", + "L4_DST_PORT": 567, + "IP_PROTOCOL": 6, + "TCP_FLAGS": "4/4", + "PRIORITY": 5000 + }, + "L3_IPV4_EGRESS|PermitAny": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "ipv4any", + "PRIORITY": 50 + } + } +} + +acl_json_config_switch_d3 = { + "ACL_TABLE": { + "L2_MAC_INGRESS": { + "type": "L2", + "stage": "INGRESS", + "ports": [], + "policy_desc": "L2_MAC_INGRESS" + }, + "L2_MAC_EGRESS": { + "type": "L2", + "stage": "EGRESS", + "ports": [], + "policy_desc": "L2_MAC_EGRESS" + } + }, + "ACL_RULE": { + "L2_MAC_INGRESS|macrule1": { + "PACKET_ACTION": "FORWARD", + "SRC_MAC": "00:0a:01:00:00:03/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:11:04/ff:ff:ff:ff:ff:ff", + "PCP":4, + "DEI":1, + "PRIORITY": 1000 + }, + "L2_MAC_INGRESS|macrule2": { + "PACKET_ACTION": "DROP", + "SRC_MAC": "00:0a:01:00:00:05/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:11:06/ff:ff:ff:ff:ff:ff", + "PRIORITY": 900 + }, + "L2_MAC_INGRESS|macrule3": { + "PACKET_ACTION": "FORWARD", + "SRC_MAC": "00:0a:01:00:11:04/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:00:03/ff:ff:ff:ff:ff:ff", + "PRIORITY": 2000 + }, + "L2_MAC_EGRESS|macrule1": { + "PACKET_ACTION": "FORWARD", + "SRC_MAC": "00:0a:01:00:00:03/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:11:04/ff:ff:ff:ff:ff:ff", + "PCP":4, + "DEI":1, + "PRIORITY": 1000 + }, + "L2_MAC_EGRESS|macrule3": { + "PACKET_ACTION": "FORWARD", + "SRC_MAC": "00:0a:01:00:11:04/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:00:03/ff:ff:ff:ff:ff:ff", + "PRIORITY": 2000 + }, + "L2_MAC_EGRESS|macrule4": { + "PACKET_ACTION": "DROP", + "SRC_MAC": "00:0a:01:00:11:06/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:00:05/ff:ff:ff:ff:ff:ff", + "PRIORITY": 90 + } + } +} +acl_json_config_switch_d3_egress = { + "ACL_TABLE": { + "L2_MAC_EGRESS": { + "type": "L2", + "stage": "EGRESS", + "ports": [], + "policy_desc": "L2_MAC_EGRESS" + } + }, + "ACL_RULE": { + "L2_MAC_EGRESS|macrule3": { + "PACKET_ACTION": "FORWARD", + "SRC_MAC": "00:0a:01:00:11:04/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:00:03/ff:ff:ff:ff:ff:ff", + "PRIORITY": 2000 + }, + "L2_MAC_EGRESS|macrule4": { + "PACKET_ACTION": "DROP", + "SRC_MAC": "00:0a:01:00:11:06/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:00:05/ff:ff:ff:ff:ff:ff", + "PRIORITY": 9000 + } + } +} +acl_json_config_port_d3 = { + "ACL_TABLE": { + "L2_MAC_INGRESS": { + "type": "L2", + "stage": "INGRESS", + "ports": [], + "policy_desc": "L2_MAC_INGRESS" + }, + "L2_MAC_EGRESS": { + "type": "L2", + "stage": "EGRESS", + "ports": [], + "policy_desc": "L2_MAC_EGRESS" + } + }, + "ACL_RULE": { + "L2_MAC_INGRESS|macrule1": { + "PACKET_ACTION": "FORWARD", + "SRC_MAC": "00:0a:01:00:00:03/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:11:04/ff:ff:ff:ff:ff:ff", + "PCP":4, + "DEI":1, + "PRIORITY": 1000 + }, + "L2_MAC_INGRESS|macrule2": { + "PACKET_ACTION": "DROP", + "SRC_MAC": "00:0a:01:00:00:05/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:11:06/ff:ff:ff:ff:ff:ff", + "ETHER_TYPE": 0x0800, + "PRIORITY": 900 + }, + "L2_MAC_INGRESS|macrule3": { + "PACKET_ACTION": "FORWARD", + "SRC_MAC": "00:0a:01:00:11:04/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:00:03/ff:ff:ff:ff:ff:ff", + "PRIORITY": 2000 + }, + "L2_MAC_EGRESS|macrule1": { + "PACKET_ACTION": "FORWARD", + "SRC_MAC": "00:0a:01:00:00:03/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:11:04/ff:ff:ff:ff:ff:ff", + "PCP":4, + "DEI":1, + "PRIORITY": 1000 + }, + "L2_MAC_EGRESS|macrule3": { + "PACKET_ACTION": "FORWARD", + "SRC_MAC": "00:0a:01:00:11:04/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:00:03/ff:ff:ff:ff:ff:ff", + "PRIORITY": 2000 + }, + "L2_MAC_EGRESS|macrule4": { + "PACKET_ACTION": "DROP", + "SRC_MAC": "00:0a:01:00:11:06/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:00:05/ff:ff:ff:ff:ff:ff", + "PRIORITY": 90 + } + } +} + +acl_json_config_vlan_d3 = { + "ACL_TABLE": { + "L2_MAC_INGRESS": { + "type": "L2", + "stage": "INGRESS", + "ports": [], + "policy_desc": "L2_MAC_INGRESS" + }, + "L2_MAC_EGRESS": { + "type": "L2", + "stage": "EGRESS", + "ports": [], + "policy_desc": "L2_MAC_EGRESS" + } + }, + "ACL_RULE": { + "L2_MAC_INGRESS|macrule1": { + "PACKET_ACTION": "FORWARD", + "SRC_MAC": "00:0a:01:00:00:03/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:11:04/ff:ff:ff:ff:ff:ff", + "PCP":4, + "DEI":1, + "PRIORITY": 1000 + }, + "L2_MAC_INGRESS|macrule2": { + "PACKET_ACTION": "DROP", + "SRC_MAC": "00:0a:01:00:00:05/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:11:06/ff:ff:ff:ff:ff:ff", + "PRIORITY": 900 + }, + "L2_MAC_INGRESS|macrule3": { + "PACKET_ACTION": "FORWARD", + "SRC_MAC": "00:0a:01:00:11:04/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:00:03/ff:ff:ff:ff:ff:ff", + "PRIORITY": 2000 + }, + "L2_MAC_EGRESS|macrule1": { + "PACKET_ACTION": "FORWARD", + "SRC_MAC": "00:0a:01:00:00:03/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:11:04/ff:ff:ff:ff:ff:ff", + "PCP":4, + "DEI":1, + "PRIORITY": 1000 + }, + "L2_MAC_EGRESS|macrule3": { + "PACKET_ACTION": "FORWARD", + "SRC_MAC": "00:0a:01:00:11:04/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:00:03/ff:ff:ff:ff:ff:ff", + "PRIORITY": 2000 + }, + "L2_MAC_EGRESS|macrule4": { + "PACKET_ACTION": "DROP", + "SRC_MAC": "00:0a:01:00:11:06/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:00:05/ff:ff:ff:ff:ff:ff", + "PRIORITY": 90 + } + } +} + + +acl_json_config_d2 = { + "ACL_TABLE": { + "L3_IPV6_INGRESS": { + "type": "L3V6", + "stage": "INGRESS", + "ports": [], + "policy_desc": "L3_IPV6_INGRESS" + }, + "L3_IPV6_EGRESS": { + "type": "L3V6", + "stage": "EGRESS", + "ports": [], + "policy_desc": "L3_IPV6_EGRESS" + } + }, + "ACL_RULE": { + "L3_IPV6_INGRESS|rule1": { + "PACKET_ACTION": "FORWARD", + "SRC_IPV6": "2001::10/128", + "DST_IPV6": "3001::10/128", + "L4_SRC_PORT_RANGE": "100-500", + "IP_PROTOCOL": 6, + "PRIORITY": 1000 + }, + "L3_IPV6_INGRESS|PermitAny": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "ipv6any", + "PRIORITY": 100 + }, + "L3_IPV6_INGRESS|rule3": { + "PACKET_ACTION": "DROP", + "SRC_IPV6": "6001::10/128", + "DST_IPV6": "7001::10/128", + "L4_DST_PORT_RANGE": "300-400", + "IP_PROTOCOL": 6, + "PRIORITY": 4000 + }, + "L3_IPV6_INGRESS|rule4": { + "PACKET_ACTION": "DROP", + "SRC_IPV6": "8001::10/32", + "DST_IPV6": "9001::10/32", + "L4_DST_PORT": 100, + "IP_PROTOCOL": 17, + "PRIORITY": 5000 + }, + "L3_IPV6_INGRESS|DenyAny": { + "PACKET_ACTION": "DROP", + "ETHER_TYPE":'0x086dd', + "PRIORITY": 50 + }, + "L3_IPV6_INGRESS|rule5": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "ipv6any", + "SRC_IPV6": "2001::2/96", + "PRIORITY": 1000 + }, + "L3_IPV6_EGRESS|rule1": { + "PACKET_ACTION": "FORWARD", + "SRC_IPV6": "2001::10/128", + "DST_IPV6": "3001::10/128", + "IP_PROTOCOL": 6, + "L4_DST_PORT": 560, + "PRIORITY": 1000 + }, + "L3_IPV6_EGRESS|rule4": { + "PACKET_ACTION": "DROP", + "SRC_IPV6": "8001::10/32", + "DST_IPV6": "9001::10/32", + "IP_PROTOCOL": 17, + "L4_SRC_PORT": 560, + "PRIORITY": 5000 + }, + "L3_IPV6_EGRESS|DenyAny": { + "PACKET_ACTION": "DROP", + "ETHER_TYPE":'0x086dd', + "PRIORITY": 50 + }, + "L3_IPV6_EGRESS|PermitAny": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "ipv6any", + "PRIORITY": 100 + }, + } +} + +acl_json_egress_configv4 = { + "ACL_TABLE": { + "L3_IPV4_EGRESS": { + "type": "L3", + "stage": "EGRESS", + "ports": [], + "policy_desc": "L3_IPV4_EGRESS" + } + }, + "ACL_RULE": { + "L3_IPV4_EGRESS|rule1": { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "192.138.10.1/32", + "DST_IP": "55.46.45.2/32", + "L4_SRC_PORT": 43, + "L4_DST_PORT": 567, + "IP_PROTOCOL": 17, + "PRIORITY": 1000 + }, + "L3_IPV4_EGRESS|rule2": { + "PACKET_ACTION": "DROP", + "SRC_IP": "88.67.45.9/32", + "DST_IP": "12.12.12.12/16", + "IP_PROTOCOL": 17, + "PRIORITY": 4000 + }, + "L3_IPV4_EGRESS|rule3": { + "PACKET_ACTION": "DROP", + "SRC_IP": "185.185.1.1/16", + "DST_IP": "181.182.1.1/16", + "L4_DST_PORT": 567, + "IP_PROTOCOL": 6, + "TCP_FLAGS": "4/4", + "PRIORITY": 5000 + }, + "L3_IPV4_EGRESS|DenyAny": { + "PACKET_ACTION": "DROP", + "IP_TYPE": "ipv4any", + "PRIORITY": 50 + } + } +} + +acl_json_ingress_configv4 = { + "ACL_TABLE": { + "L3_IPV4_INGRESS": { + "type": "L3", + "stage": "INGRESS", + "ports": [], + "policy_desc": "L3_IPV4_INGRESS" + } + }, + "ACL_RULE": { + "L3_IPV4_INGRESS|rule1": { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "1.1.1.1/32", + "DST_IP": "2.2.2.2/32", + "L4_SRC_PORT": 43, + "L4_DST_PORT_RANGE": "10-20", + "IP_PROTOCOL": 6, + "PRIORITY": 1000 + }, + "L3_IPV4_INGRESS|rule2": { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "5.5.5.5/16", + "DST_IP": "9.9.9.9/16", + "L4_SRC_PORT_RANGE": "100-500", + "IP_PROTOCOL": 17, + "PRIORITY": 2000 + }, + "L3_IPV4_INGRESS|rule4": { + "PACKET_ACTION": "DROP", + "SRC_IP": "9.9.9.9/32", + "DST_IP": "12.12.12.12/16", + "L4_DST_PORT_RANGE": "300-400", + "IP_PROTOCOL": 6, + "PRIORITY": 4000 + }, + "L3_IPV4_INGRESS|rule5": { + "PACKET_ACTION": "DROP", + "SRC_IP": "185.185.1.1/16", + "DST_IP": "18.18.1.1/16", + "TCP_FLAGS": "4/4", + "PRIORITY": 5000 + }, + "L3_IPV4_INGRESS|rule6": { + "PACKET_ACTION": "DROP", + "SRC_IP": "176.185.1.1/16", + "DST_IP": "10.18.1.1/16", + "L4_SRC_PORT": 43, + "L4_DST_PORT": 567, + "IP_PROTOCOL": 6, + "PRIORITY": 5000 + }, + "L3_IPV4_INGRESS|PermitAny": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "ipv4any", + "PRIORITY": 500 + } + } +} + +acl_json_config_v6_ingress_vlan = { + "ACL_TABLE": { + "L3_IPV6_INGRESS": { + "type": "L3V6", + "stage": "INGRESS", + "ports": [], + "policy_desc": "L3_IPV6_INGRESS" + } + }, + "ACL_RULE": { + "L3_IPV6_INGRESS|rule1": { + "PACKET_ACTION": "FORWARD", + "SRC_IPV6": "2001::10/128", + "DST_IPV6": "3001::10/128", + "L4_SRC_PORT_RANGE": "100-500", + "IP_PROTOCOL": 6, + "PRIORITY": 1000 + }, + "L3_IPV6_INGRESS|PermitAny": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "ipv6any", + "PRIORITY": 100 + }, + "L3_IPV6_INGRESS|rule3": { + "PACKET_ACTION": "DROP", + "SRC_IPV6": "6001::10/128", + "DST_IPV6": "7001::10/128", + "L4_DST_PORT_RANGE": "300-400", + "IP_PROTOCOL": 6, + "PRIORITY": 4000 + }, + "L3_IPV6_INGRESS|rule4": { + "PACKET_ACTION": "DROP", + "SRC_IPV6": "8001::10/32", + "DST_IPV6": "9001::10/32", + "L4_DST_PORT": 100, + "IP_PROTOCOL": 17, + "PRIORITY": 5000 + }, + "L3_IPV6_INGRESS|DenyAny": { + "PACKET_ACTION": "DROP", + "ETHER_TYPE":'0x086dd', + "PRIORITY": 50 + }, + "L3_IPV6_INGRESS|rule5": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "ipv6any", + "SRC_IPV6": "2001::2/96", + "PRIORITY": 1000 + } + } +} + +acl_json_config_v6_egress_vlan = { + "ACL_TABLE": { + "L3_IPV6_EGRESS": { + "type": "L3V6", + "stage": "EGRESS", + "ports": [], + "policy_desc": "L3_IPV6_EGRESS" + } + }, + "ACL_RULE": { + "L3_IPV6_EGRESS|rule1": { + "PACKET_ACTION": "FORWARD", + "SRC_IPV6": "2001::10/128", + "DST_IPV6": "3001::10/128", + "IP_PROTOCOL": 6, + "L4_DST_PORT": 560, + "PRIORITY": 1000 + }, + "L3_IPV6_EGRESS|rule4": { + "PACKET_ACTION": "DROP", + "SRC_IPV6": "8001::10/32", + "DST_IPV6": "9001::10/32", + "IP_PROTOCOL": 17, + "L4_SRC_PORT": 560, + "PRIORITY": 5000 + }, + "L3_IPV6_EGRESS|DenyAny": { + "PACKET_ACTION": "DROP", + "ETHER_TYPE": '0x086dd', + "PRIORITY": 50 + }, + "L3_IPV6_EGRESS|PermitAny": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "ipv6any", + "PRIORITY": 100 + } + } +} +acl_json_ingress_vlan_configv4 = { + "ACL_TABLE": { + "L3_IPV4_INGRESS": { + "type": "L3", + "stage": "INGRESS", + "ports": [], + "policy_desc": "L3_IPV4_INGRESS" + }, + "L3_IPV4_EGRESS": { + "type": "L3", + "stage": "EGRESS", + "ports": [], + "policy_desc": "L3_IPV4_EGRESS" + } + }, + "ACL_RULE": { + "L3_IPV4_INGRESS|rule1": { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "1.1.1.1/32", + "DST_IP": "2.2.2.2/32", + "L4_SRC_PORT": 43, + "L4_DST_PORT_RANGE": "10-20", + "IP_PROTOCOL": 6, + "PRIORITY": 1000 + }, + "L3_IPV4_INGRESS|rule2": { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "5.5.5.5/16", + "DST_IP": "9.9.9.9/16", + "L4_SRC_PORT_RANGE": "100-500", + "IP_PROTOCOL": 17, + "PRIORITY": 2000 + }, + "L3_IPV4_INGRESS|rule4": { + "PACKET_ACTION": "DROP", + "SRC_IP": "9.9.9.9/32", + "DST_IP": "12.12.12.12/16", + "L4_DST_PORT_RANGE": "300-400", + "IP_PROTOCOL": 6, + "PRIORITY": 4000 + }, + "L3_IPV4_INGRESS|rule5": { + "PACKET_ACTION": "DROP", + "SRC_IP": "185.185.1.1/16", + "DST_IP": "18.18.1.1/16", + "TCP_FLAGS": "4/4", + "PRIORITY": 5000 + }, + "L3_IPV4_INGRESS|PermiAny": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "ipv4any", + "PRIORITY": 100 + }, + + "L3_IPV4_EGRESS|rule1": { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "192.138.10.1/32", + "DST_IP": "55.46.45.2/32", + "L4_SRC_PORT": 43, + "L4_DST_PORT": 567, + "DSCP": 61, + "IP_PROTOCOL": 17, + "PRIORITY": 1000 + }, + "L3_IPV4_EGRESS|rule2": { + "PACKET_ACTION": "DROP", + "SRC_IP": "88.67.45.9/32", + "DST_IP": "12.12.12.12/16", + "IP_PROTOCOL": 17, + "PRIORITY": 4000 + }, + "L3_IPV4_EGRESS|rule3": { + "PACKET_ACTION": "DROP", + "SRC_IP": "185.185.1.1/16", + "DST_IP": "181.182.1.1/16", + "L4_DST_PORT": 567, + "IP_PROTOCOL": 6, + "TCP_FLAGS": "4/4", + "PRIORITY": 5000 + }, + "L3_IPV4_EGRESS|PermiAny": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "ipv4any", + "PRIORITY": 100 + } + } +} + +acl_json_config_portchannel_d3 = { + "ACL_TABLE": { + "L2_MAC_INGRESS": { + "type": "L2", + "stage": "INGRESS", + "ports": [], + "policy_desc": "L2_MAC_INGRESS" + } + }, + "ACL_RULE": { + "L2_MAC_INGRESS|macrule1": { + "PACKET_ACTION": "FORWARD", + "SRC_MAC": "00:0a:01:00:00:03/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:11:04/ff:ff:ff:ff:ff:ff", + "VLAN": [], + "PCP":4, + "DEI":1, + "PRIORITY": 1000 + }, + "L2_MAC_INGRESS|macrule2": { + "PACKET_ACTION": "DROP", + "SRC_MAC": "00:0a:01:00:00:05/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:11:06/ff:ff:ff:ff:ff:ff", + "VLAN": [], + "PRIORITY": 900 + } + + } +} + +acl_json_egress_configv4 = { + "ACL_TABLE": { + "L3_IPV4_EGRESS": { + "type": "L3", + "stage": "EGRESS", + "ports": [], + "policy_desc": "L3_IPV4_EGRESS" + } + }, + "ACL_RULE": { + "L3_IPV4_EGRESS|rule1": { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "192.138.10.1/32", + "DST_IP": "55.46.45.2/32", + "L4_SRC_PORT": 43, + "L4_DST_PORT": 567, + "IP_PROTOCOL": 17, + "PRIORITY": 1000 + }, + "L3_IPV4_EGRESS|rule2": { + "PACKET_ACTION": "DROP", + "SRC_IP": "88.67.45.9/32", + "DST_IP": "12.12.12.12/16", + "IP_PROTOCOL": 17, + "PRIORITY": 4000 + }, + "L3_IPV4_EGRESS|rule3": { + "PACKET_ACTION": "DROP", + "SRC_IP": "185.185.1.1/16", + "DST_IP": "181.182.1.1/16", + "L4_DST_PORT": 567, + "IP_PROTOCOL": 6, + "TCP_FLAGS": "4/4", + "PRIORITY": 5000 + }, + "L3_IPV4_EGRESS|DenyAny": { + "PACKET_ACTION": "DROP", + "IP_TYPE": "ipv4any", + "PRIORITY": 50 + } + } +} + +acl_json_ingress_configv6 = { + "ACL_TABLE": { + "L3_IPV6_INGRESS": { + "type": "L3V6", + "stage": "INGRESS", + "ports": [], + "policy_desc": "L3_IPV6_INGRESS" + } + }, + "ACL_RULE": { + "L3_IPV6_INGRESS|rule1": { + "PACKET_ACTION": "FORWARD", + "SRC_IPV6": "2001::10/128", + "DST_IPV6": "3001::10/128", + "IP_PROTOCOL": 6, + "PRIORITY": 1000 + }, + "L3_IPV6_INGRESS|PermitAny": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "ipv6any", + "PRIORITY": 600 + }, + "L3_IPV6_INGRESS|rule3": { + "PACKET_ACTION": "DROP", + "SRC_IPV6": "6001::10/128", + "DST_IPV6": "7001::10/128", + "IP_PROTOCOL": 6, + "PRIORITY": 4000 + }, + "L3_IPV6_INGRESS|rule4": { + "PACKET_ACTION": "DROP", + "SRC_IPV6": "8001::10/32", + "DST_IPV6": "9001::10/32", + "IP_PROTOCOL": 17, + "PRIORITY": 5000 + }, + "L3_IPV6_INGRESS|DenyAny": { + "PACKET_ACTION": "DROP", + "ETHER_TYPE":'0x086dd', + "PRIORITY": 50 + }, + "L3_IPV6_EGRESS|PermitAny": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "ipv6any", + "PRIORITY": 500 + } + } +} +acl_json_config_table = { + "ACL_TABLE": { + "L3_IPV4_INGRESS": { + "type": "L3", + "stage": "INGRESS", + "ports": [], + "policy_desc": "L3_IPV4_INGRESS" + }, + "L3_IPV4_EGRESS": { + "type": "L3", + "stage": "EGRESS", + "ports": [], + "policy_desc": "L3_IPV4_EGRESS" + }, + "L3_IPV6_INGRESS": { + "type": "L3V6", + "stage": "INGRESS", + "ports": [], + "policy_desc": "L3_IPV6_INGRESS" + }, + "L3_IPV6_EGRESS": { + "type": "L3V6", + "stage": "EGRESS", + "ports": [], + "policy_desc": "L3_IPV6_EGRESS" + } + } +} + +acl_json_config_priority = { + "ACL_TABLE": { + "L3_IPV4_INGRESS": { + "type": "L3", + "stage": "INGRESS", + "ports": [], + "policy_desc": "L3_IPV4_INGRESS" + }, + "L3_IPV4_EGRESS": { + "type": "L3", + "stage": "EGRESS", + "ports": [], + "policy_desc": "L3_IPV4_EGRESS" + }, + "L2_MAC_INGRESS": { + "type": "L2", + "stage": "INGRESS", + "ports": [], + "policy_desc": "L2_MAC_INGRESS" + }, + "L2_MAC_EGRESS": { + "type": "L2", + "stage": "EGRESS", + "ports": [], + "policy_desc": "L2_MAC_EGRESS" + } + }, + "ACL_RULE": { + "L3_IPV4_INGRESS|rule1": { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "1.1.1.1/32", + "DST_IP": "2.2.2.2/32", + "L4_SRC_PORT": 43, + "L4_DST_PORT_RANGE": "10-20", + "DSCP": 62, + "IP_PROTOCOL": 6, + "PRIORITY": 1000 + }, + "L3_IPV4_INGRESS|rule4": { + "PACKET_ACTION": "DROP", + "SRC_IP": "9.9.9.9/32", + "DST_IP": "12.12.12.12/16", + "L4_DST_PORT_RANGE": "300-400", + "IP_PROTOCOL": 6, + "PRIORITY": 4000 + }, + "L3_IPV4_EGRESS|rule1": { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "192.138.10.1/32", + "DST_IP": "55.46.45.2/32", + "L4_SRC_PORT": 43, + "L4_DST_PORT": 567, + "DSCP": 61, + "IP_PROTOCOL": 17, + "PRIORITY": 1000 + }, + "L3_IPV4_EGRESS|rule2": { + "PACKET_ACTION": "DROP", + "SRC_IP": "88.67.45.9/32", + "DST_IP": "12.12.12.12/16", + "IP_PROTOCOL": 17, + "PRIORITY": 4000 + }, + "L2_MAC_INGRESS|macrule1": { + "PACKET_ACTION": "FORWARD", + "SRC_MAC": "00:0a:01:00:00:01/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:11:02/ff:ff:ff:ff:ff:ff", + "VLAN": "", + "PRIORITY": 1000 + }, + "L2_MAC_EGRESS|macrule1": { + "PACKET_ACTION": "DROP", + "SRC_MAC": "00:0a:01:00:11:02/ff:ff:ff:ff:ff:ff", + "DST_MAC": "00:0a:01:00:00:01/ff:ff:ff:ff:ff:ff", + "VLAN": "", + "PRIORITY": 1000 + } + } +} + +acl_json_egress_configv6 = { + "ACL_TABLE": { + "L3_IPV6_EGRESS": { + "type": "L3V6", + "stage": "EGRESS", + "ports": [], + "policy_desc": "L3_IPV6_EGRESS" + } + }, + "ACL_RULE": { + "L3_IPV6_EGRESS|rule1": { + "PACKET_ACTION": "FORWARD", + "SRC_IPV6": "2001::10/128", + "DST_IPV6": "3001::10/128", + "IP_PROTOCOL": 6, + "L4_DST_PORT": 560, + "PRIORITY": 1000 + }, + "L3_IPV6_EGRESS|rule2": { + "PACKET_ACTION": "DROP", + "SRC_IPV6": "8001::10/32", + "DST_IPV6": "9001::10/32", + "IP_PROTOCOL": 17, + "L4_SRC_PORT": 560, + "PRIORITY": 5000 + }, + "L3_IPV6_EGRESS|DenyAny": { + "PACKET_ACTION": "DROP", + "ETHER_TYPE":'0x086dd', + "PRIORITY": 50 + } + } +} +acl_json_config_v4_l3_traffic = { + "ACL_TABLE": { + "L3_IPV4_INGRESS": { + "type": "L3", + "stage": "INGRESS", + "ports": [], + "policy_desc": "L3_IPV4_INGRESS" + }, + "L3_IPV4_EGRESS": { + "type": "L3", + "stage": "EGRESS", + "ports": [], + "policy_desc": "L3_IPV4_EGRESS" + } + }, + "ACL_RULE": { + "L3_IPV4_INGRESS|rule1": { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "1.1.1.2/24", + "DST_IP": "2.2.2.2/24", + "L4_SRC_PORT": 43, + "L4_DST_PORT_RANGE": "10-20", + "IP_PROTOCOL": 6, + "PRIORITY": 1000 + }, + "L3_IPV4_INGRESS|rule2": { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "1.1.1.4/24", + "DST_IP": "2.2.2.4/24", + "L4_SRC_PORT_RANGE": "100-500", + "IP_PROTOCOL": 17, + "PRIORITY": 2000 + }, + "L3_IPV4_INGRESS|PermiAny": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "ipv4any", + "PRIORITY": 100 + }, + "L3_IPV4_INGRESS|rule4": { + "PACKET_ACTION": "DROP", + "SRC_IP": "1.1.1.5/24", + "DST_IP": "2.2.2.5/24", + "L4_DST_PORT_RANGE": "300-400", + "IP_PROTOCOL": 6, + "PRIORITY": 4000 + }, + "L3_IPV4_INGRESS|rule5": { + "PACKET_ACTION": "DROP", + "SRC_IP": "1.1.1.6/24", + "DST_IP": "2.2.2.6/24", + "TCP_FLAGS": "4/4", + "PRIORITY": 5000 + }, + "L3_IPV4_INGRESS|DenyAny": { + "PACKET_ACTION": "DROP", + "ETHER_TYPE":'0x0800', + "PRIORITY": 50 + } + } +} +acl_json_config_v6_l3_traffic = { + "ACL_TABLE": { + "L3_IPV6_INGRESS": { + "type": "L3V6", + "stage": "INGRESS", + "ports": [], + "policy_desc": "L3_IPV6_INGRESS" + }, + "L3_IPV6_EGRESS": { + "type": "L3V6", + "stage": "EGRESS", + "ports": [], + "policy_desc": "L3_IPV6_EGRESS" + } + }, + "ACL_RULE": { + "L3_IPV6_INGRESS|rule1": { + "PACKET_ACTION": "FORWARD", + "SRC_IPV6": "2001::2/64", + "DST_IPV6": "1001::2/64", + "L4_SRC_PORT_RANGE": "100-500", + "IP_PROTOCOL": 6, + "PRIORITY": 1000 + }, + "L3_IPV6_INGRESS|PermitAny": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "ipv6any", + "PRIORITY": 100 + }, + "L3_IPV6_INGRESS|rule3": { + "PACKET_ACTION": "DROP", + "SRC_IPV6": "2001::2/64", + "DST_IPV6": "1001::2/64", + "L4_SRC_PORT": "100", + "L4_DST_PORT_RANGE": "300-400", + "IP_PROTOCOL": 6, + "PRIORITY": 4000 + }, + "L3_IPV6_INGRESS|rule4": { + "PACKET_ACTION": "DROP", + "SRC_IPV6": "2001::2/64", + "DST_IPV6": "1001::2/64", + "L4_DST_PORT": 100, + "IP_PROTOCOL": 17, + "PRIORITY": 5000 + }, + "L3_IPV6_INGRESS|DenyAny": { + "PACKET_ACTION": "DROP", + "ETHER_TYPE":'0x086dd', + "PRIORITY": 50 + } + } +} +acl_json_config_control_plane = { + "ACL_TABLE":{ + "SNMP_ACL":{ + "services":[ + "SNMP" + ], + "type":"CTRLPLANE", + "policy_desc":"SNMP_ACL" + }, + "SSH_ONLY":{ + "services":[ + "SSH" + ], + "type":"CTRLPLANE", + "policy_desc":"SSH_ONLY" + }, + "V6_SSH_ONLY":{ + "services":[ + "SSH" + ], + "type":"CTRLPLANE", + "policy_desc":"V6_SSH_ONLY" + } + }, + "ACL_RULE":{ + "SNMP_ACL|DEFAULT_RULE":{ + "PRIORITY":"1", + "PACKET_ACTION":"DROP", + "ETHER_TYPE":"0x0800" + }, + "SNMP_ACL|RULE_1":{ + "PRIORITY":"9999", + "PACKET_ACTION":"ACCEPT", + "SRC_IP":"", + "IP_PROTOCOL":"17" + }, + "SSH_ONLY|DEFAULT_RULE":{ + "PRIORITY":"1", + "PACKET_ACTION":"DROP", + "L4_DST_PORT":"22", + "ETHER_TYPE":"0x0800" + }, + "SSH_ONLY|RULE_1":{ + "PRIORITY":"9999", + "PACKET_ACTION":"ACCEPT", + "SRC_IP":"", + "IP_PROTOCOL":"6" + }, + "SSH_ONLY|RULE_2":{ + "PRIORITY":"9998", + "PACKET_ACTION":"ACCEPT", + "SRC_IP":"", + "L4_DST_PORT":"22", + "IP_PROTOCOL":"6" + }, + "V6_SSH_ONLY|DEFAULT_RULE":{ + "PRIORITY":"1", + "PACKET_ACTION":"DROP", + "L4_DST_PORT":"22", + "ETHER_TYPE":"0x86dd" + }, + "V6_SSH_ONLY|RULE_1":{ + "IP_PROTOCOL":"6", + "PACKET_ACTION":"ACCEPT", + "PRIORITY":"9999", + "L4_DST_PORT":"22", + "SRC_IPV6":"" + } + } +} + diff --git a/spytest/tests/qos/acl/acl_rules_data.py b/spytest/tests/qos/acl/acl_rules_data.py new file mode 100644 index 00000000000..4d82f8a66fc --- /dev/null +++ b/spytest/tests/qos/acl/acl_rules_data.py @@ -0,0 +1,229 @@ + +multiple_acl_rules = { + "acl": { + "acl-sets": { + "acl-set": { + "L3_IPV4_INGRESS": { + "acl-entries": { + "acl-entry": { + "1": { + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + }, + "config": { + "sequence-id": 1 + }, + "ip": { + "config": { + "source-ip-address": "192.138.10.1/32" + + } + } + }, + "2": { + "actions": { + "config": { + "forwarding-action": "DROP" + } + }, + "config": { + "sequence-id": 2 + }, + "ip": { + "config": { + "destination-ip-address": "12.12.12.12/16" + + } + } + } + } + } + }, + "L3_IPV4_EGRESS": { + "acl-entries": { + "acl-entry": { + "1": { + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + }, + "config": { + "sequence-id": 1 + }, + "ip": { + "config": { + "source-ip-address": "19.13.10.1/32" + + } + } + }, + "2": { + "actions": { + "config": { + "forwarding-action": "DROP" + } + }, + "config": { + "sequence-id": 2 + }, + "ip": { + "config": { + "destination-ip-address": "120.120.12.12/16" + + } + } + } + } + } + }, + "L3_IPV6_INGRESS": { + "acl-entries": { + "acl-entry": { + "1": { + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + }, + "config": { + "sequence-id": 1 + }, + "ip": { + "config": { + "source-ip-address": "2001::1/128" + + } + } + } + } + } + }, + "L3_IPV6_EGRESS": { + "acl-entries": { + "acl-entry": { + "1": { + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + }, + "config": { + "sequence-id": 1 + }, + "ip": { + "config": { + "source-ip-address": "6001::1/128" + + } + } + } + } + } + } + } + } + } +} +add_acl_rules = { + "acl": { + "acl-sets": { + "acl-set": { + "L3_IPV4_INGRESS": { + "acl-entries": { + "acl-entry": { + "3": { + "actions": { + "config": { + "forwarding-action": "DROP" + } + }, + "config": { + "sequence-id": 3 + }, + "ip": { + "config": { + "source-ip-address": "185.185.1.1/16", + "destination-ip-address": "181.182.1.1/16" + + } + } + } + + } + } + }, + "L3_IPV4_EGRESS": { + "acl-entries": { + "acl-entry": { + "3": { + "actions": { + "config": { + "forwarding-action": "DROP" + } + }, + "config": { + "sequence-id": 3 + }, + "ip": { + "config": { + "source-ip-address": "10.185.10.1/16", + "destination-ip-address": "11.12.10.1/16" + + } + } + } + + } + } + }, + "L3_IPV6_INGRESS": { + "acl-entries": { + "acl-entry": { + "2": { + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + }, + "config": { + "sequence-id": 2 + }, + "ip": { + "config": { + "destination-ip-address": "3001::1/128" + + } + } + } + } + } + }, + "L3_IPV6_EGRESS": { + "acl-entries": { + "acl-entry": { + "2": { + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + }, + "config": { + "sequence-id": 2 + }, + "ip": { + "config": { + "destination-ip-address": "4001::1/128" + + } + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/spytest/tests/qos/acl/acl_utils.py b/spytest/tests/qos/acl/acl_utils.py new file mode 100644 index 00000000000..feaa815b51b --- /dev/null +++ b/spytest/tests/qos/acl/acl_utils.py @@ -0,0 +1,167 @@ +from spytest import st +mask_tbd = 10 +def get_args(identifier, value, attribs, pps, tg_type): + tmp = {} + if identifier == "SRC_IP": + parts = value.split('/') + tmp['ip_src_addr'] = parts[0] + tmp['l3_protocol'] = "ipv4" + if len(parts) > 1 : + if parts[1] != "32": + tmp['ip_src_mode'] = "increment" + tmp['ip_src_step'] = "0.0.0.1" + tmp['ip_src_count'] = pps + if identifier == "DST_IP": + parts = value.split('/') + tmp['ip_dst_addr'] = parts[0] + tmp['l3_protocol'] = "ipv4" + if len(parts) > 1 : + if parts[1] != "32": + tmp['ip_dst_mode'] = "increment" + tmp['ip_dst_step'] = "0.0.0.1" + tmp['ip_dst_count'] = pps + if identifier == "SRC_IPV6": + parts = value.split('/') + tmp['ipv6_src_addr'] = parts[0] + tmp['l3_protocol'] = "ipv6" + if len(parts) > mask_tbd: + if parts[1] != "128": + tmp['ipv6_src_mode'] = "increment" + tmp['ipv6_src_step'] = "TBD" + tmp['ipv6_src_count'] = pps + if identifier == "DST_IPV6": + parts = value.split('/') + tmp['ipv6_dst_addr'] = parts[0] + tmp['l3_protocol'] = "ipv6" + if len(parts) > mask_tbd: + if parts[1] != "128": + tmp['ipv6_dst_mode'] = "increment" + tmp['ipv6_dst_step'] = "TBD" + tmp['ipv6_dst_count'] = pps + if identifier == "L4_SRC_PORT": + if attribs['IP_PROTOCOL'] == 6: + tmp['tcp_src_port'] = value + elif attribs['IP_PROTOCOL'] == 17: + tmp['udp_src_port'] = value + if identifier == "L4_DST_PORT": + if attribs['IP_PROTOCOL'] == 6: + tmp['tcp_dst_port'] = value + elif attribs['IP_PROTOCOL'] == 17: + tmp['udp_dst_port'] = value + if identifier == "L4_SRC_PORT_RANGE": + parts = value.split('-') + if attribs['IP_PROTOCOL'] == 6: + tmp['tcp_src_port'] = int(parts[0]) + tmp['tcp_src_port_mode'] = (tg_type == 'stc' and 'increment' or 'incr') #STC + tmp['tcp_src_port_count'] = 10 + tmp['tcp_src_port_step'] = 1 + elif attribs['IP_PROTOCOL'] == 17: + tmp['udp_src_port'] = int(parts[0]) + tmp['udp_src_port_mode'] = (tg_type == 'stc' and 'increment' or 'incr') #STC + tmp['udp_src_port_count'] = 10 + tmp['udp_src_port_step'] = 1 + if identifier == "L4_DST_PORT_RANGE": + parts = value.split('-') + if attribs['IP_PROTOCOL'] == 6: + tmp['tcp_dst_port'] = int(parts[0]) + tmp['tcp_dst_port_mode'] = (tg_type == 'stc' and 'increment' or 'incr') #STC + tmp['tcp_dst_port_count'] = 10 + tmp['tcp_dst_port_step'] = 1 + elif attribs['IP_PROTOCOL'] == 17: + tmp['udp_dst_port'] = int(parts[0]) + tmp['udp_dst_port_mode'] = (tg_type == 'stc' and 'increment' or 'incr') #STC + tmp['udp_dst_port_count'] = 10 + tmp['udp_dst_port_step'] = 1 + if identifier == "IP_PROTOCOL": + #tmp['ip_protocol'] = value + if attribs['IP_PROTOCOL'] == 6: + tmp['l4_protocol'] = "tcp" + elif attribs['IP_PROTOCOL'] == 17: + tmp['l4_protocol'] = "udp" + if identifier == "TCP_FLAGS": + if value == "4/4": + tmp['l4_protocol'] = "tcp" + tmp['tcp_rst_flag'] = 1 + return tmp + +def get_args_l3(identifier, value, attribs, pps, tg_type): + tmp = {} + if identifier == "SRC_IP": + parts = value.split('/') + tmp['ip_src_addr'] = parts[0] + tmp['l3_protocol'] = "ipv4" + if len(parts) > 1 : + if parts[1] != "32": + tmp['ip_src_mode'] = "fixed" + if identifier == "DST_IP": + parts = value.split('/') + tmp['ip_dst_addr'] = parts[0] + tmp['l3_protocol'] = "ipv4" + if len(parts) > 1 : + if parts[1] != "32": + tmp['ip_dst_mode'] = "fixed" + if identifier == "SRC_IPV6": + parts = value.split('/') + tmp['ipv6_src_addr'] = parts[0] + tmp['l3_protocol'] = "ipv6" + if len(parts) > mask_tbd: + if parts[1] != "128": + tmp['ipv6_src_mode'] = "fixed" + if identifier == "DST_IPV6": + parts = value.split('/') + tmp['ipv6_dst_addr'] = parts[0] + tmp['l3_protocol'] = "ipv6" + if len(parts) > mask_tbd: + if parts[1] != "128": + tmp['ipv6_dst_mode'] = "fixed" + if identifier == "L4_SRC_PORT": + if attribs['IP_PROTOCOL'] == 6: + tmp['tcp_src_port'] = value + elif attribs['IP_PROTOCOL'] == 17: + tmp['udp_src_port'] = value + if identifier == "L4_DST_PORT": + if attribs['IP_PROTOCOL'] == 6: + tmp['tcp_dst_port'] = value + elif attribs['IP_PROTOCOL'] == 17: + tmp['udp_dst_port'] = value + if identifier == "L4_SRC_PORT_RANGE": + parts = value.split('-') + if attribs['IP_PROTOCOL'] == 6: + tmp['tcp_src_port'] = int(parts[0]) + tmp['tcp_src_port_mode'] = (tg_type == 'stc' and 'increment' or 'incr') #STC + tmp['tcp_src_port_count'] = 10 + tmp['tcp_src_port_step'] = 1 + elif attribs['IP_PROTOCOL'] == 17: + tmp['udp_src_port'] = int(parts[0]) + tmp['udp_src_port_mode'] = (tg_type == 'stc' and 'increment' or 'incr') #STC + tmp['udp_src_port_count'] = 10 + tmp['udp_src_port_step'] = 1 + if identifier == "L4_DST_PORT_RANGE": + parts = value.split('-') + if attribs['IP_PROTOCOL'] == 6: + tmp['tcp_dst_port'] = int(parts[0]) + tmp['tcp_dst_port_mode'] = (tg_type == 'stc' and 'increment' or 'incr') #STC + tmp['tcp_dst_port_count'] = 10 + tmp['tcp_dst_port_step'] = 1 + elif attribs['IP_PROTOCOL'] == 17: + tmp['udp_dst_port'] = int(parts[0]) + tmp['udp_dst_port_mode'] = (tg_type == 'stc' and 'increment' or 'incr') #STC + tmp['udp_dst_port_count'] = 10 + tmp['udp_dst_port_step'] = 1 + if identifier == "IP_PROTOCOL": + #tmp['ip_protocol'] = value + if attribs['IP_PROTOCOL'] == 6: + tmp['l4_protocol'] = "tcp" + elif attribs['IP_PROTOCOL'] == 17: + tmp['l4_protocol'] = "udp" + if identifier == "TCP_FLAGS": + if value == "4/4": + tmp['l4_protocol'] = "tcp" + tmp['tcp_rst_flag'] = 1 + return tmp + +def report_result(status): + if status: + st.report_pass('test_case_passed') + else: + st.report_fail('test_case_failed') diff --git a/spytest/tests/qos/acl/test_acls.py b/spytest/tests/qos/acl/test_acls.py new file mode 100644 index 00000000000..4dcab60bc2e --- /dev/null +++ b/spytest/tests/qos/acl/test_acls.py @@ -0,0 +1,1159 @@ +import pprint +import pytest +import json + +from spytest import st, tgapi, SpyTestDict +from spytest.utils import random_vlan_list + +import apis.switching.vlan as vlan_obj +import apis.qos.acl as acl_obj +import tests.qos.acl.acl_json_config as acl_data +import tests.qos.acl.acl_rules_data as acl_rules_data +import tests.qos.acl.acl_utils as acl_utils +import apis.switching.portchannel as pc_obj +import apis.routing.ip as ipobj +import apis.system.gnmi as gnmiapi +from apis.system.interface import clear_interface_counters,get_interface_counters +from apis.system.rest import rest_status + +from utilities.parallel import ensure_no_exception +import utilities.common as utils + +YANG_MODEL = "sonic-acl:sonic-acl" +pp = pprint.PrettyPrinter(indent=4) + +vars = dict() +data = SpyTestDict() +data.rate_pps = 100 +data.pkts_per_burst = 10 +data.tx_timeout = 2 +data.TBD = 10 +data.portChannelName = "PortChannel001" +data.tg_type = 'ixia' +data.cli_type = "click" + + +def print_log(msg): + log_start = "\n================================================================================\n" + log_end = "\n================================================================================" + st.log("{} {} {}".format(log_start, msg, log_end)) + + +def get_handles(): + ''' + ######################## Topology ############################ + + +---------+ +-------+ + | +------------------+ | + TG1 -----| DUT1 | portchannel | DUT2 +----- TG2 + | +------------------+ | + +---------+ +-------+ + + ############################################################## + ''' + global vars, tg_port_list + vars = st.ensure_min_topology("D1D2:2", "D1T1:2", "D2T1:1") + tg1, tg_ph_1 = tgapi.get_handle_byname("T1D1P1") + tg2, tg_ph_2 = tgapi.get_handle_byname("T1D2P1") + tg3, tg_ph_3 = tgapi.get_handle_byname("T1D1P2") + if tg1.tg_type == 'stc': data.tg_type = 'stc' + tg_port_list = [tg_ph_1, tg_ph_2, tg_ph_3] + tg1.tg_traffic_control(action="reset", port_handle=tg_ph_1) + tg2.tg_traffic_control(action="reset", port_handle=tg_ph_2) + tg3.tg_traffic_control(action="reset", port_handle=tg_ph_3) + return (tg1, tg2, tg3, tg_ph_1, tg_ph_2, tg_ph_3) + + +def acl_delete(dut): + if st.is_community_build(dut): + + names = acl_obj.show_acl_table(dut) + acl_name = list() + for name in names: + acl_name.append(name.keys()) + print("acl name", str(acl_name)) + for name in acl_name: + for i in name: + if "Name" in i: + pass + else: + acl_obj.delete_acl_table(dut, acl_table_name=i) + + else: + acl_obj.delete_acl_table(dut) + + +def apply_module_configuration(): + print_log("Applying module configuration") + + data.vlan = str(random_vlan_list()[0]) + data.dut1_lag_members = [vars.D1D2P1, vars.D1D2P2] + data.dut2_lag_members = [vars.D2D1P1, vars.D2D1P2] + + # create portchannel + utils.exec_all(True, [ + utils.ExecAllFunc(pc_obj.create_portchannel, vars.D1, data.portChannelName, cli_type=data.cli_type), + utils.ExecAllFunc(pc_obj.create_portchannel, vars.D2, data.portChannelName, cli_type=data.cli_type), + ]) + + # add portchannel members + utils.exec_all(True, [ + utils.ExecAllFunc(pc_obj.add_portchannel_member, vars.D1, data.portChannelName, data.dut1_lag_members, data.cli_type), + utils.ExecAllFunc(pc_obj.add_portchannel_member, vars.D2, data.portChannelName, data.dut2_lag_members, data.cli_type), + ]) + + # create vlan + utils.exec_all(True, [ + utils.ExecAllFunc(vlan_obj.create_vlan, vars.D1, data.vlan, data.cli_type), + utils.ExecAllFunc(vlan_obj.create_vlan, vars.D2, data.vlan, data.cli_type), + ]) + + # add vlan members + utils.exec_all(True, [ + utils.ExecAllFunc(vlan_obj.add_vlan_member, vars.D1, data.vlan, [vars.D1T1P1, vars.D1T1P2, + data.portChannelName], True, cli_type=data.cli_type), + utils.ExecAllFunc(vlan_obj.add_vlan_member, vars.D2, data.vlan, [vars.D2T1P1, data.portChannelName], True, + cli_type=data.cli_type), + ]) + + + + +def clear_module_configuration(): + print_log("Clearing module configuration") + + # delete vlan members + utils.exec_all(True, [ + utils.ExecAllFunc(vlan_obj.delete_vlan_member, vars.D1, data.vlan, [vars.D1T1P1, vars.D1T1P2, + data.portChannelName], cli_type=data.cli_type), + utils.ExecAllFunc(vlan_obj.delete_vlan_member, vars.D2, data.vlan, [vars.D2T1P1, data.portChannelName], + cli_type=data.cli_type), + ]) + + # delete portchannel members + utils.exec_all(True, [ + utils.ExecAllFunc(pc_obj.delete_portchannel_member, vars.D1, data.portChannelName, data.dut1_lag_members, + data.cli_type), + utils.ExecAllFunc(pc_obj.delete_portchannel_member, vars.D2, data.portChannelName, data.dut2_lag_members, + data.cli_type), + ]) + # delete portchannel + utils.exec_all(True, [ + utils.ExecAllFunc(pc_obj.delete_portchannel, vars.D1, data.portChannelName, data.cli_type), + utils.ExecAllFunc(pc_obj.delete_portchannel, vars.D2, data.portChannelName, data.cli_type), + ]) + # delete vlan + utils.exec_all(True, [ + utils.ExecAllFunc(vlan_obj.delete_vlan, vars.D1, data.vlan, data.cli_type), + utils.ExecAllFunc(vlan_obj.delete_vlan, vars.D2, data.vlan, data.cli_type), + ]) + # delete acl tables and rules + [output, exceptions] = utils.exec_all(True, + [[acl_delete, vars.D1], [acl_delete, vars.D2]]) + ensure_no_exception(exceptions) + + +def add_port_to_acl_table(config, table_name, port): + config['ACL_TABLE'][table_name]['ports'] = [] + config['ACL_TABLE'][table_name]['ports'].append(port) + + +def change_acl_rules(config, rule_name, attribute, value): + config["ACL_RULE"][rule_name][attribute] = value + + +def apply_acl_config(dut, config): + json_config = json.dumps(config) + json.loads(json_config) + st.apply_json2(dut, json_config) + + +def create_streams(tx_tg, rx_tg, rules, match, mac_src, mac_dst,dscp=None,pcp=None, dei=None,ether_type_val=None): + # use the ACL rule definitions to create match/non-match traffic streams + # instead of hardcoding the traffic streams + my_args = { + 'port_handle': data.tgmap[tx_tg]['handle'], 'mode': 'create', 'frame_size': '128', + 'transmit_mode': 'continuous', 'length_mode': 'fixed', + 'l2_encap': 'ethernet_ii_vlan', + 'vlan_id': data.vlan, 'vlan': 'enable', 'rate_pps': data.rate_pps, + 'high_speed_result_analysis': 0, 'mac_src': mac_src, 'mac_dst': mac_dst, + 'port_handle2': data.tgmap[rx_tg]['handle'] + } + if dscp: + my_args.update({"ip_dscp": dscp}) + if pcp: + my_args.update({"vlan_user_priority": pcp}) + if dei: + my_args.update({"vlan_cfi": dei}) + if ether_type_val: + my_args.update({"l2_encap": 'ethernet_ii'}) + my_args.update({"ethernet_value": ether_type_val}) + + for rule, attributes in rules.items(): + if ("IP_TYPE" in attributes) or ("ETHER_TYPE" in attributes): + continue + if match in rule: + params = {} + tmp = dict(my_args) + for key, value in attributes.items(): + params.update(acl_utils.get_args(key, value, attributes, data.rate_pps, data.tg_type)) + tmp.update(params) + stream = data.tgmap[tx_tg]['tg'].tg_traffic_config(**tmp) + stream_id = stream['stream_id'] + s = {} + s[stream_id] = attributes + s[stream_id]['TABLE'] = rule + data.tgmap[tx_tg]['streams'].update(s) + + +def transmit(tg): + print_log("Transmitting streams") + data.tgmap[tg]['tg'].tg_traffic_control(action='clear_stats', port_handle=tg_port_list) + data.tgmap[tg]['tg'].tg_traffic_control(action='run', stream_handle = list(data.tgmap[tg]['streams'].keys()), + duration=1) + + +def verify_acl_hit_counters(dut, table_name): + result = True + acl_rule_counters = acl_obj.show_acl_counters(dut, acl_table=table_name) + for rule in acl_rule_counters: + if rule['packetscnt'] == 0: + return False + return result + + +def verify_packet_count(tx, tx_port, rx, rx_port, table): + st.log("#######################################################################") + st.log("# Validating stream statistics, by invoking 'validate_tgen_traffic' #") + st.log("# API, this API presently returns boolean status checking each stream #") + st.log("# statistics. Jira(SONIC-6791) is raised to enhance the API to report #") + st.log("# status for individual stream statistic. Revisit this module after #") + st.log("# the fix to improve further execution time. #") + st.log("#######################################################################") + result = True + tg_tx = data.tgmap[tx] + tg_rx = data.tgmap[rx] + exp_ratio = 0 + action = "DROP" + for s_id, attr in tg_tx['streams'].iteritems(): + if table in attr['TABLE']: + if attr["PACKET_ACTION"] == "FORWARD": + exp_ratio = 1 + action = "FORWARD" + else: + exp_ratio = 0 + action = "DROP" + traffic_details = { + '1': { + 'tx_ports': [tx_port], + 'tx_obj': [tg_tx["tg"]], + 'exp_ratio': [exp_ratio], + 'rx_ports': [rx_port], + 'rx_obj': [tg_rx["tg"]], + 'stream_list': [[s_id]] + }, + } + result1 = tgapi.validate_tgen_traffic(traffic_details=traffic_details, mode='streamblock', + comp_type='packet_count') + result = result and result1 + if result1: + if action == "FORWARD": + msg = "Traffic successfully forwarded for the rule: {}".format(json.dumps(attr)) + print_log(msg) + else: + msg = "Traffic successfully dropped for the rule: {}".format(json.dumps(attr)) + print_log(msg) + else: + if action == "FORWARD": + msg = "Traffic failed to forward for the rule: {}".format(json.dumps(attr)) + print_log(msg) + else: + msg = "Traffic failed to drop for the rule: {}".format(json.dumps(attr)) + print_log(msg) + return result + + +def initialize_topology(): + print_log("Initializing Topology") + (tg1, tg2, tg3, tg_ph_1, tg_ph_2, tg_ph_3) = get_handles() + data.tgmap = { + "tg1": { + "tg": tg1, + "handle": tg_ph_1, + "streams": {} + }, + "tg2": { + "tg": tg2, + "handle": tg_ph_2, + "streams": {} + }, + "tg3": { + "tg": tg3, + "handle": tg_ph_3, + "streams": {} + } + } + data.vars = vars + +@pytest.fixture(scope="module", autouse=True) +def acl_v4_module_hooks(request): + # initialize topology + initialize_topology() + + # apply module configuration + apply_module_configuration() + change_acl_rules(acl_data.acl_json_config_d1, "L3_IPV4_INGRESS|rule6", "PACKET_ACTION", "REDIRECT:" + vars.D1T1P2) + change_acl_rules(acl_data.acl_json_config_d1, "L2_MAC_INGRESS|macrule1", "VLAN", data.vlan) + change_acl_rules(acl_data.acl_json_config_d1, "L2_MAC_INGRESS|macrule2", "VLAN", data.vlan) + change_acl_rules(acl_data.acl_json_config_d1, "L2_MAC_EGRESS|macrule3", "VLAN", data.vlan) + change_acl_rules(acl_data.acl_json_config_d1, "L2_MAC_EGRESS|macrule4", "VLAN", data.vlan) + acl_config1 = acl_data.acl_json_config_d1 + add_port_to_acl_table(acl_config1, 'L3_IPV4_INGRESS', vars.D1T1P1) + add_port_to_acl_table(acl_config1, 'L3_IPV4_EGRESS', vars.D1T1P1) + add_port_to_acl_table(acl_config1, 'L2_MAC_INGRESS', vars.D1T1P1) + add_port_to_acl_table(acl_config1, 'L2_MAC_EGRESS', vars.D1T1P1) + acl_config2 = acl_data.acl_json_config_d2 + add_port_to_acl_table(acl_config2, 'L3_IPV6_INGRESS', vars.D2T1P1) + add_port_to_acl_table(acl_config2, 'L3_IPV6_EGRESS', vars.D2T1P1) + + def config_dut1(): + apply_acl_config(vars.D1, acl_config1) + + def config_dut2(): + apply_acl_config(vars.D2, acl_config2) + + def tg_config(): + # create streams + print_log('Creating streams') + create_streams("tg1", "tg2", acl_config1['ACL_RULE'], "L3_IPV4_INGRESS", \ + mac_src="00:0a:01:00:00:01", mac_dst="00:0a:01:00:11:02", dscp=62) + create_streams("tg1", "tg2", acl_config2['ACL_RULE'], "L3_IPV6_EGRESS", \ + mac_src="00:0a:01:00:00:01", mac_dst="00:0a:01:00:11:02") + create_streams("tg2", "tg1", acl_config2['ACL_RULE'], "L3_IPV6_INGRESS", \ + mac_src="00:0a:01:00:11:02", mac_dst="00:0a:01:00:00:01") + create_streams("tg2", "tg1", acl_config1['ACL_RULE'], "L3_IPV4_EGRESS", \ + mac_src="00:0a:01:00:11:02", mac_dst="00:0a:01:00:00:01",dscp=61) + create_streams("tg1", "tg2", acl_config1['ACL_RULE'], "L2_MAC_INGRESS|macrule1", \ + mac_src="00:0a:01:00:00:03", mac_dst="00:0a:01:00:11:04", pcp=4, dei=1) + create_streams("tg2", "tg1", acl_config1['ACL_RULE'], "L2_MAC_EGRESS|macrule3", \ + mac_src="00:0a:01:00:11:04", mac_dst="00:0a:01:00:00:03", pcp=4, dei=1) + create_streams("tg1", "tg2", acl_config1['ACL_RULE'], "L2_MAC_INGRESS|macrule2", \ + mac_src="00:0a:01:00:00:05", mac_dst="00:0a:01:00:11:06", pcp=4, dei=1,ether_type_val=0x0800) + create_streams("tg2", "tg1", acl_config1['ACL_RULE'], "L2_MAC_EGRESS|macrule4", \ + mac_src="00:0a:01:00:11:06", mac_dst="00:0a:01:00:00:05", pcp=4, dei=1) + print_log('Completed module configuration') + + utils.exec_all(True, [utils.ExecAllFunc(tg_config), utils.ExecAllFunc(config_dut1), utils.ExecAllFunc(config_dut2)], + first_on_main=True) + + yield + clear_module_configuration() + + +def verify_rule_priority(dut, table_name): + acl_rule_counters = acl_obj.show_acl_counters(dut, acl_table=table_name, acl_rule='PermitAny') + if len(acl_rule_counters) == 1: + if (int(acl_rule_counters[0]['packetscnt']) != 0): + print_log("ACL Rule priority test failed") + return False + else: + return True + else: + return True + + +@pytest.mark.acl_test +def test_ft_acl_ingress_ipv4(): + ''' + IPv4 Ingress ACL is applied on DUT1 port connected to TG Port#1 + Traffic is sent on TG Port #1 + Traffic is recieved at TG Port #2 + ''' + acl_obj.delete_acl_table(vars.D1, acl_table_name='L2_MAC_INGRESS') + acl_obj.delete_acl_table(vars.D1, acl_table_name='L2_MAC_EGRESS') + transmit('tg1') + result1 = verify_packet_count('tg1', vars.T1D1P1, 'tg2', vars.T1D2P1, "L3_IPV4_INGRESS") + print_log('Verifing IPv4 Ingress ACL hit counters') + result2 = verify_acl_hit_counters(vars.D1, "L3_IPV4_INGRESS") + result3 = verify_rule_priority(vars.D1, "L3_IPV4_INGRESS") + stats1 = data.tgmap['tg3']['tg'].tg_traffic_stats(port_handle=data.tgmap['tg3']['handle'], mode='aggregate') + total_rx1 = int(stats1[data.tgmap['tg3']['handle']]['aggregate']['rx']['total_pkts']) + st.log("total_rx1={}".format(total_rx1)) + if total_rx1 > 100: + print_log("Traffic successfully redirected") + else: + st.report_fail("test_case_failed") + acl_utils.report_result(result1 and result2 and result3) + + +@pytest.mark.acl_test6789 +@pytest.mark.community +@pytest.mark.community_fail +def test_ft_acl_ingress_ipv6(): + ''' + IPv6 Ingress ACL is applied on DUT2 port connected to TG Port #2 + Traffic is sent on TG Port #2 + Traffic is recieved at TG Port #1 + ''' + [output, exceptions] = utils.exec_all(True, + [[clear_interface_counters, vars.D1], [clear_interface_counters, vars.D2]]) + ensure_no_exception(exceptions) + [output, exceptions] = utils.exec_all(True, + [[get_interface_counters, vars.D1, vars.D1T1P1], + [get_interface_counters, vars.D2, vars.D2T1P1]]) + ensure_no_exception(exceptions) + transmit('tg2') + [output, exceptions] = utils.exec_all(True, + [[get_interface_counters, vars.D1, vars.D1T1P1], + [get_interface_counters, vars.D2, vars.D2T1P1]]) + ensure_no_exception(exceptions) + result1 = verify_packet_count('tg2', vars.T1D2P1, 'tg1', vars.T1D1P1, "L3_IPV6_INGRESS") + print_log('Verifing IPv6 Ingress ACL hit counters') + + result2 = verify_acl_hit_counters(vars.D2, "L3_IPV6_INGRESS") + result3 = verify_rule_priority(vars.D2, "L3_IPV6_INGRESS") + acl_utils.report_result(result1 and result2 and result3) + +@pytest.mark.acl_test +def test_ft_acl_egress_ipv4(): + ''' + IPv4 Egress ACL is applied on DUT1 port connected to TG Port#1 + Traffic is sent on TG Port #2 + Traffic is recieved at TG Port #1 + ''' + transmit('tg2') + result1 = verify_packet_count('tg2', vars.T1D2P1, 'tg1', vars.T1D1P1, "L3_IPV4_EGRESS") + print_log('Verifing IPv4 Egress ACL hit counters') + result2 = verify_acl_hit_counters(vars.D1, "L3_IPV4_EGRESS") + acl_utils.report_result(result1 and result2) + +@pytest.mark.acl_test678 +def test_ft_acl_egress_ipv6(): + ''' + IPv6 Egress ACL is applied on DUT2 port connected to TG Port #2 + Traffic is sent on TG Port #1 + Traffic is recieved at TG Port #2 + ''' + [output, exceptions] = utils.exec_all(True, + [[clear_interface_counters, vars.D1], [clear_interface_counters, vars.D2]]) + ensure_no_exception(exceptions) + [output, exceptions] = utils.exec_all(True, + [[get_interface_counters, vars.D1, vars.D1T1P1], + [get_interface_counters, vars.D2, vars.D2T1P1]]) + ensure_no_exception(exceptions) + transmit('tg1') + [output, exceptions] = utils.exec_all(True, + [[get_interface_counters, vars.D1, vars.D1T1P1], + [get_interface_counters, vars.D2, vars.D2T1P1]]) + ensure_no_exception(exceptions) + result1 = verify_packet_count('tg1', vars.T1D1P1, 'tg2', vars.T1D2P1, "L3_IPV6_EGRESS") + print_log('Verifing IPv6 Egress ACL hit counters') + result2 = verify_acl_hit_counters(vars.D2, "L3_IPV6_EGRESS") + acl_utils.report_result(result1 and result2) + + +@pytest.mark.acl_test +def test_ft_mac_acl_port(): + ''' + MAC Ingress ACL is applied on DUT1 port connected to TG Port#1 + Traffic is sent on TG Port #1 + Traffic is recieved at TG Port #2 + ''' + print_log('Creating MAC ACL table and apply on Port ') + acl_obj.delete_acl_table(vars.D1, acl_table_name='L3_IPV4_INGRESS') + acl_obj.delete_acl_table(vars.D1, acl_table_name='L3_IPV4_EGRESS') + acl_config = acl_data.acl_json_config_port_d3 + add_port_to_acl_table(acl_config, 'L2_MAC_INGRESS', vars.D1T1P1) + add_port_to_acl_table(acl_config, 'L2_MAC_EGRESS', vars.D1T1P1) + change_acl_rules(acl_data.acl_json_config_port_d3, "L2_MAC_INGRESS|macrule1", "VLAN", data.vlan) + change_acl_rules(acl_data.acl_json_config_port_d3, "L2_MAC_INGRESS|macrule2", "VLAN", data.vlan) + change_acl_rules(acl_data.acl_json_config_port_d3, "L2_MAC_EGRESS|macrule3", "VLAN", data.vlan) + change_acl_rules(acl_data.acl_json_config_port_d3, "L2_MAC_EGRESS|macrule4", "VLAN", data.vlan) + apply_acl_config(vars.D1, acl_config) + st.wait(2) + transmit('tg1') + print_log('Verifying MAC Ingress packet count') + result1 = verify_packet_count('tg1', vars.T1D1P1, 'tg2', vars.T1D2P1, "L2_MAC_INGRESS") + print_log('Verifying MAC Ingress ACL hit counters') + result2 = verify_acl_hit_counters(vars.D1, "L2_MAC_INGRESS") + transmit('tg2') + print_log('Verifying MAC Ingress packet count') + result3 = verify_packet_count('tg2', vars.T1D2P1, 'tg1', vars.T1D1P1, "L2_MAC_EGRESS") + print_log('Verifing MAC Egress ACL hit counters') + result4 = verify_acl_hit_counters(vars.D1, "L2_MAC_EGRESS") + acl_utils.report_result(result1 and result2 and result3 and result4) + + +@pytest.mark.acl_testacl2 +def test_ft_acl_port_channel_ingress(): + ''' + IPv6 Ingress ACL is applied on DUT1 port channel + Traffic is sent on TG Port #2 + Traffic is recieved at TG Port #1 + ''' + # deleting same streams are used for both IPv6 and PortChannel test + # to avoid conflicts, delete IPv6 rules + [output, exceptions] = utils.exec_all(True, + [[acl_delete, vars.D1], [acl_delete, vars.D2]]) + ensure_no_exception(exceptions) + # Creating Ingress ACL table and rules + print_log('Creating Ingress ACL table and apply on Port channel') + acl_config = acl_data.acl_json_ingress_configv6 + add_port_to_acl_table(acl_config, 'L3_IPV6_INGRESS', data.portChannelName) + apply_acl_config(vars.D1, acl_config) + st.wait(2) + + transmit('tg2') + result1 = verify_packet_count('tg2', vars.T1D2P1, 'tg1', vars.T1D1P1, "L3_IPV6_INGRESS") + acl_utils.report_result(result1) + +@pytest.mark.acl_test6 +def test_ft_acl_port_channel_egress(): + ''' + IPv6 Egress ACL is applied on DUT1 port channel + Traffic is sent on TG Port #1 + Traffic is recieved at TG Port #2 + ''' + print_log('Creating Egress ACL table and apply on Port channel') + # SONiC supports only one egress table for Switch + # so deleting already created Egress rule. Revisit this test case, + # when the support is added + acl_obj.delete_acl_table(vars.D1, acl_table_name='L3_IPV4_EGRESS') + acl_config = acl_data.acl_json_egress_configv6 + add_port_to_acl_table(acl_config, 'L3_IPV6_EGRESS', data.portChannelName) + apply_acl_config(vars.D1, acl_config) + st.wait(2) + transmit('tg1') + result1 = verify_packet_count('tg1', vars.T1D1P1, 'tg2', vars.T1D2P1, "L3_IPV6_EGRESS") + acl_utils.report_result(result1) + +@pytest.mark.acl_test8 +def test_ft_acl_port_channel_V4_egress(): + ''' + IPv6 Ingress ACL is applied on DUT1 port channel + Traffic is sent on TG Port #2 + Traffic is recived at TG Port #1 + ''' + # deleting same streams are used for both IPv6 and PortChannel test + # to avoid conflicts, delete IPv6 rules + acl_obj.delete_acl_table(vars.D2, acl_table_name='L3_IPV4_EGRESS') + acl_obj.delete_acl_table(vars.D2, acl_table_name='L3_IPV4_INGRESS') + # Creating Ingress ACL table and rules + print_log('Creating Ingress ACL table and apply on Port channel') + acl_config = acl_data.acl_json_egress_configv4 + add_port_to_acl_table(acl_config, 'L3_IPV4_EGRESS', data.portChannelName) + apply_acl_config(vars.D2, acl_config) + st.wait(2) + + transmit('tg2') + result1 = verify_packet_count('tg2', vars.T1D2P1, 'tg1', vars.T1D1P1, "L3_IPV4_EGRESS") + acl_utils.report_result(result1) + + + +@pytest.mark.acl_test678 +def test_ft_acl_vlan_v6_egress(): + ''' + IPv6 Egress ACL is applied on DUT2 vlan + Traffic is sent on TG Port #1 + Traffic is recieved at TG Port #2 + ''' + + # Creating Ingress ACL table and rules + [output, exceptions] = utils.exec_all(True, + [[acl_delete, vars.D1], [acl_delete, vars.D2]]) + ensure_no_exception(exceptions) + print_log('Creating Egress ACL table and apply on VLAN') + acl_config = acl_data.acl_json_config_v6_egress_vlan + add_port_to_acl_table(acl_config, 'L3_IPV6_EGRESS', "Vlan{}".format(data.vlan)) + apply_acl_config(vars.D2, acl_config) + [output, exceptions] = utils.exec_all(True, + [[clear_interface_counters, vars.D1], [clear_interface_counters, vars.D2]]) + ensure_no_exception(exceptions) + [output, exceptions] = utils.exec_all(True, + [[get_interface_counters, vars.D1, vars.D1T1P1], + [get_interface_counters, vars.D2, vars.D2T1P1]]) + ensure_no_exception(exceptions) + st.wait(2) + transmit('tg1') + [output, exceptions] = utils.exec_all(True, + [[get_interface_counters, vars.D1, vars.D1T1P1], + [get_interface_counters, vars.D2, vars.D2T1P1]]) + ensure_no_exception(exceptions) + result1 = verify_packet_count('tg1', vars.T1D1P1, 'tg2', vars.T1D2P1, "L3_IPV6_EGRESS") + acl_utils.report_result(result1) + +@pytest.mark.acl_test +def test_ft_acl_vlan_v6_ingress(): + ''' + IPv6 Egress ACL is applied on DUT2 vlan + Traffic is sent on TG Port #1 + Traffic is recieved at TG Port #2 + ''' + + [output, exceptions] = utils.exec_all(True, + [[acl_delete, vars.D1], [acl_delete, vars.D2]]) + ensure_no_exception(exceptions) + # Creating Ingress ACL table and rules + print_log('Creating ACL table and apply on VLAN') + acl_config = acl_data.acl_json_config_v6_ingress_vlan + add_port_to_acl_table(acl_config, 'L3_IPV6_INGRESS', "Vlan{}".format(data.vlan)) + apply_acl_config(vars.D2, acl_config) + st.wait(2) + + transmit('tg2') + result1 = verify_packet_count('tg2', vars.T1D2P1,'tg1', vars.T1D1P1, "L3_IPV6_INGRESS") + acl_utils.report_result(result1) + +@pytest.mark.acl_test +def test_ft_acl_vlan_V4_ingress(): + ''' + IPv4 Ingress ACL is applied on DUT1 vlan + Traffic is sent on TG Port #1 + Traffic is recieved at TG Port #2 + ''' + [output, exceptions] = utils.exec_all(True, + [[acl_delete, vars.D1], [acl_delete, vars.D2]]) + ensure_no_exception(exceptions) + # Creating Ingress ACL table and rules + print_log('Creating ACL table and apply on VLAN') + acl_config = acl_data.acl_json_ingress_vlan_configv4 + add_port_to_acl_table(acl_config, 'L3_IPV4_INGRESS', "Vlan{}".format(data.vlan)) + add_port_to_acl_table(acl_config, 'L3_IPV4_EGRESS', "Vlan{}".format(data.vlan)) + change_acl_rules(acl_data.acl_json_config_d1, "L3_IPV4_INGRESS|rule6", "PACKET_ACTION", "FORWARD") + apply_acl_config(vars.D1, acl_config) + st.wait(2) + + transmit('tg1') + result1 = verify_packet_count('tg1', vars.T1D1P1, 'tg2', vars.T1D2P1, "L3_IPV4_INGRESS") + transmit('tg2') + result2 = verify_packet_count('tg2', vars.T1D2P1,'tg1', vars.T1D1P1, "L3_IPV4_EGRESS") + acl_utils.report_result(result1 and result2) + +@pytest.mark.acl_test +def test_ft_acl_port_channel_V4_ingress(): + ''' + IPv6 Ingress ACL is applied on DUT1 port channel + Traffic is sent on TG Port #2 + Traffic is recieved at TG Port #1 + ''' + # deleting same streams are used for both IPv6 and PortChannel test + # to avoid conflicts, delete IPv6 rules + [output, exceptions] = utils.exec_all(True, + [[acl_delete, vars.D1], [acl_delete, vars.D2]]) + ensure_no_exception(exceptions) + # Creating Ingress ACL table and rules + print_log('Creating Ingress ACL table and apply on Port channel') + acl_config = acl_data.acl_json_ingress_configv4 + add_port_to_acl_table(acl_config, 'L3_IPV4_INGRESS', data.portChannelName) + change_acl_rules(acl_data.acl_json_config_d1, "L3_IPV4_INGRESS|rule6", "PACKET_ACTION", "DROP") + apply_acl_config(vars.D2, acl_config) + st.wait(2) + + transmit('tg1') + result1 = verify_packet_count('tg1', vars.T1D1P1, 'tg2', vars.T1D2P1, "L3_IPV4_INGRESS") + acl_utils.report_result(result1) + +@pytest.mark.acl_test678 +def test_ft_v4_acl_switch(): + ''' + IPv4 Ingress ACL is applied on DUT1 Switch + Traffic is sent on TG Port #1 and received at TG Port #2 for ingress + Traffic is sent on TG Port #2 and received at TG Port #1 for egress + ''' + [output, exceptions] = utils.exec_all(True, + [[acl_delete, vars.D1], [acl_delete, vars.D2]]) + ensure_no_exception(exceptions) + print_log('Creating ACL table and apply on switch') + acl_config = acl_data.acl_json_config_v4_switch + add_port_to_acl_table(acl_config, 'L3_IPV4_INGRESS', "Switch") + add_port_to_acl_table(acl_config, 'L3_IPV4_EGRESS', "Switch") + change_acl_rules(acl_data.acl_json_config_d1, "L3_IPV4_INGRESS|rule6", "PACKET_ACTION", "DROP") + apply_acl_config(vars.D1, acl_config) + st.wait(2) + [output, exceptions] = utils.exec_all(True, + [[clear_interface_counters, vars.D1], [clear_interface_counters, vars.D2]]) + ensure_no_exception(exceptions) + [output, exceptions] = utils.exec_all(True, + [[get_interface_counters, vars.D1, vars.D1T1P1], + [get_interface_counters, vars.D2, vars.D2T1P1]]) + ensure_no_exception(exceptions) + transmit('tg1') + [output, exceptions] = utils.exec_all(True, + [[get_interface_counters, vars.D1, vars.D1T1P1], + [get_interface_counters, vars.D2, vars.D2T1P1]]) + ensure_no_exception(exceptions) + result1 = verify_packet_count('tg1', vars.T1D1P1, 'tg2', vars.T1D2P1, "L3_IPV4_INGRESS") + result2 = verify_acl_hit_counters(vars.D1, "L3_IPV4_INGRESS") + [output, exceptions] = utils.exec_all(True, + [[clear_interface_counters, vars.D1], [clear_interface_counters, vars.D2]]) + ensure_no_exception(exceptions) + [output, exceptions] = utils.exec_all(True, + [[get_interface_counters, vars.D1, vars.D1T1P1], + [get_interface_counters, vars.D2, vars.D2T1P1]]) + ensure_no_exception(exceptions) + transmit('tg2') + [output, exceptions] = utils.exec_all(True, + [[get_interface_counters, vars.D1, vars.D1T1P1], + [get_interface_counters, vars.D2, vars.D2T1P1]]) + ensure_no_exception(exceptions) + result3 = verify_packet_count('tg2', vars.T1D2P1, 'tg1', vars.T1D1P1, "L3_IPV4_EGRESS") + result4 = verify_acl_hit_counters(vars.D1, "L3_IPV4_EGRESS") + + acl_utils.report_result(result1 and result2 and result3 and result4) + + +@pytest.mark.acl_test +def test_ft_mac_acl_switch(): + ''' + IPv4 Ingress ACL is applied on switch + Traffic is sent on TG Port #1 + Traffic is recieved at TG Port #2 + ''' + [output, exceptions] = utils.exec_all(True, + [[acl_delete, vars.D1], [acl_delete, vars.D2]]) + ensure_no_exception(exceptions) + # Creating Ingress ACL table and rules + + print_log('Creating ACL table and apply on switch') + acl_config = acl_data.acl_json_config_switch_d3 + add_port_to_acl_table(acl_config, 'L2_MAC_INGRESS', "Switch") + apply_acl_config(vars.D1, acl_config) + acl_obj.show_acl_rule(vars.D1) + st.wait(2) + transmit('tg1') + result1 = verify_packet_count('tg1', vars.T1D1P1, 'tg2', vars.T1D2P1, "L2_MAC_INGRESS") + result2 = verify_acl_hit_counters(vars.D1, "L2_MAC_INGRESS") + acl_utils.report_result(result1 and result2) + + +@pytest.mark.acl_test +def test_ft_mac_acl_switch_egress(): + ''' + IPv4 Egress ACL is applied on switch + Traffic is sent on TG Port #1 + Traffic is recieved at TG Port #2 + ''' + + [output, exceptions] = utils.exec_all(True, + [[acl_delete, vars.D1], [acl_delete, vars.D2]]) + ensure_no_exception(exceptions) + # Creating Ingress ACL table and rules + + print_log('Creating ACL table and apply on switch') + acl_config1 = acl_data.acl_json_config_switch_d3_egress + add_port_to_acl_table(acl_config1, 'L2_MAC_EGRESS', "Switch") + apply_acl_config(vars.D1, acl_config1) + acl_obj.show_acl_rule(vars.D1) + st.wait(2) + transmit('tg2') + result1 = verify_packet_count('tg2', vars.T1D2P1, 'tg1', vars.T1D1P1, "L2_MAC_EGRESS") + result2 = verify_acl_hit_counters(vars.D1, "L2_MAC_EGRESS") + + acl_utils.report_result(result1 and result2) + + +@pytest.mark.acl_test +def test_ft_mac_acl_vlan(): + ''' + IPv4 Ingress ACL is applied on DUT1 vlan + Traffic is sent on TG Port #1 + Traffic is recieved at TG Port #2 + ''' + [output, exceptions] = utils.exec_all(True, + [[acl_delete, vars.D1], [acl_delete, vars.D2]]) + ensure_no_exception(exceptions) + # Creating Ingress ACL table and rules + print_log('Creating ACL table and apply on VLAN') + acl_config = acl_data.acl_json_config_vlan_d3 + add_port_to_acl_table(acl_config, 'L2_MAC_INGRESS', "Vlan{}".format(data.vlan)) + add_port_to_acl_table(acl_config, 'L2_MAC_EGRESS', "Vlan{}".format(data.vlan)) + apply_acl_config(vars.D1, acl_config) + st.wait(2) + transmit('tg1') + result1 = verify_packet_count('tg1', vars.T1D1P1, 'tg2', vars.T1D2P1, "L2_MAC_INGRESS") + result2 = verify_acl_hit_counters(vars.D1, "L2_MAC_INGRESS") + transmit('tg2') + result3 = verify_packet_count('tg2', vars.T1D2P1, 'tg1', vars.T1D1P1, "L2_MAC_EGRESS") + result4 = verify_acl_hit_counters(vars.D1, "L2_MAC_EGRESS") + acl_utils.report_result(result1 and result2 and result3 and result4) + + +@pytest.mark.acl_test +def test_ft_mac_acl_portchannel(): + ''' + IPv4 Ingress ACL is applied on DUT1 vlan + Traffic is sent on TG Port #1 + Traffic is received at TG Port #2 + ''' + [output, exceptions] = utils.exec_all(True, + [[acl_delete, vars.D1], [acl_delete, vars.D2]]) + ensure_no_exception(exceptions) + # Creating Ingress ACL table and rules + print_log('Creating Ingress ACL table and apply on Port channel') + acl_config = acl_data.acl_json_config_portchannel_d3 + add_port_to_acl_table(acl_config, 'L2_MAC_INGRESS', data.portChannelName) + change_acl_rules(acl_data.acl_json_config_portchannel_d3, "L2_MAC_INGRESS|macrule1", "VLAN", data.vlan) + change_acl_rules(acl_data.acl_json_config_portchannel_d3, "L2_MAC_INGRESS|macrule2", "VLAN", data.vlan) + apply_acl_config(vars.D2, acl_config) + st.wait(2) + + transmit('tg1') + result1 = verify_packet_count('tg1', vars.T1D1P1, 'tg2', vars.T1D2P1, "L2_MAC_INGRESS") + result2 = verify_acl_hit_counters(vars.D2, "L2_MAC_INGRESS") + acl_utils.report_result(result1 and result2) + + +@pytest.mark.acl_test +def test_ft_acl_loader(): + ''' + ACL rule update using config-loader + ACL rule add + check for rule upgrade + ''' + data.v4_in_tab = 'L3_IPV4_INGRESS' + data.v4_eg_tab = 'L3_IPV4_EGRESS' + data.v6_in_tab = 'L3_IPV6_INGRESS' + data.v6_eg_tab = 'L3_IPV6_EGRESS' + [output, exceptions] = utils.exec_all(True, + [[acl_delete, vars.D1], [acl_delete, vars.D2]]) + ensure_no_exception(exceptions) + acl_config = acl_data.acl_json_config_table + add_port_to_acl_table(acl_config, data.v4_in_tab, vars.D1T1P1) + add_port_to_acl_table(acl_config, data.v4_eg_tab, vars.D1T1P1) + add_port_to_acl_table(acl_config, data.v6_in_tab, vars.D1T1P1) + add_port_to_acl_table(acl_config, data.v6_eg_tab, vars.D1T1P2) + apply_acl_config(vars.D1, acl_config) + data.json_data = acl_rules_data.multiple_acl_rules + data.json_data1 = acl_rules_data.add_acl_rules + acl_obj.show_acl_table(vars.D1) + st.log('Configure acl rules using "acl-loader update"') + acl_obj.config_acl_loader_update(vars.D1, 'full', data.json_data, config_type="acl_update") + rule_update = acl_obj.get_acl_rule_count(vars.D1) + st.log('Add acl rules using "acl-loader add" to existing rules') + acl_obj.config_acl_loader_update(vars.D1, 'add', data.json_data1, config_type="acl_add") + rule_add = acl_obj.get_acl_rule_count(vars.D1) + if (rule_add[data.v4_in_tab] > rule_update[data.v4_in_tab] + and rule_add[data.v6_in_tab] > rule_update[data.v6_in_tab] + and rule_add[data.v4_eg_tab] > rule_update[data.v4_eg_tab] + and rule_add[data.v6_eg_tab] > rule_update[data.v6_eg_tab]): + print_log("New rules successfully added using acl-loader") + else: + st.report_fail('test_case_failed') + print_log('Configure acl rules using "config acl update"') + acl_obj.config_acl_loader_update(vars.D1, 'full', data.json_data) + config_acl_full = acl_obj.get_acl_rule_count(vars.D1) + if (config_acl_full[data.v4_in_tab] < rule_add[data.v4_in_tab] + and config_acl_full[data.v6_in_tab] < rule_add[data.v6_in_tab] + and config_acl_full[data.v4_eg_tab] < rule_add[data.v4_eg_tab] + and config_acl_full[data.v6_eg_tab] < rule_add[data.v6_eg_tab]): + print_log("Successfully added rules using 'config acl update'") + else: + st.report_fail('test_case_failed') + print_log('Add acl rules using "config acl add" to existing rules') + acl_obj.config_acl_loader_update(vars.D1, 'add', data.json_data1) + config_acl_add = acl_obj.get_acl_rule_count(vars.D1) + if not(config_acl_add[data.v4_in_tab] > config_acl_full[data.v4_in_tab] + and config_acl_add[data.v6_in_tab] > config_acl_full[data.v6_in_tab] + and config_acl_add[data.v4_eg_tab] > config_acl_full[data.v4_eg_tab] + and config_acl_add[data.v6_eg_tab] > config_acl_full[data.v6_eg_tab]): + print_log("Failed to add new rules using config acl") + st.report_fail('test_case_failed') + else: + print_log("New rules successfully added using config acl") + st.report_pass("test_case_passed") + + +@pytest.mark.acl_test +def test_ft_acl_icmpv6(): + ''' + TC_id: ft_acl_v6_in_intf + Description: Verify that ipv6 ingress acl works fine when bound to interface + ''' + ipv6_src_address = "2001::2" + ipv6_src_address1 = "2001::3" + data.af_ipv6 = "ipv6" + utils.exec_all(True, [ + utils.ExecAllFunc(ipobj.config_ip_addr_interface, vars.D1, "Vlan" + str(data.vlan), ipv6_src_address, 96, + family=data.af_ipv6), + utils.ExecAllFunc(ipobj.config_ip_addr_interface, vars.D2, "Vlan" + str(data.vlan), ipv6_src_address1, 96, + family=data.af_ipv6), + ]) + if not ipobj.ping(vars.D1, ipv6_src_address ,family='ipv6', count=3): + st.report_fail("ping_fail",ipv6_src_address ) + else: + st.log("Successfully forwarded icmp packet") + utils.exec_all(True, [ + utils.ExecAllFunc(ipobj.delete_ip_interface, vars.D1, "Vlan" + str(data.vlan), ipv6_src_address, 96, + family=data.af_ipv6), + utils.ExecAllFunc(ipobj.delete_ip_interface, vars.D2, "Vlan" + str(data.vlan), ipv6_src_address1, 96, + family=data.af_ipv6), + ]) + st.report_pass("ping_success") + + +@pytest.mark.acl_testacl1 +def test_ft_mac_acl_port_adv(): + [output, exceptions] = utils.exec_all(True, + [[acl_delete, vars.D1], [acl_delete, vars.D2]]) + ensure_no_exception(exceptions) + acl_obj.config_hw_acl_mode(vars.D1, counter='per-interface-rule') + acl_config = acl_data.acl_json_config_d1 + add_port_to_acl_table(acl_config, 'L2_MAC_INGRESS', vars.D1T1P1) + apply_acl_config(vars.D1, acl_config) + acl_obj.show_acl_rule(vars.D1) + transmit('tg1') + st.wait(5) + if not acl_obj.verify_acl_stats(vars.D1, 'L2_MAC_INGRESS', 'macrule1'): + st.report_fail("test_case_failed") + [output, exceptions] = utils.exec_all(True, + [[acl_delete, vars.D1], [acl_delete, vars.D2]]) + ensure_no_exception(exceptions) + acl_obj.config_hw_acl_mode(vars.D1, counter='per-rule') + st.report_pass("test_case_passed") + + +@pytest.mark.acl_testacl1 +def test_ft_acl_ingress_ipv4_adv(): + [output, exceptions] = utils.exec_all(True, + [[acl_delete, vars.D1], [acl_delete, vars.D2]]) + ensure_no_exception(exceptions) + acl_obj.config_hw_acl_mode(vars.D1, counter='per-interface-rule') + acl_config = acl_data.acl_json_config_d1 + add_port_to_acl_table(acl_config, 'L3_IPV4_INGRESS', vars.D1T1P1) + apply_acl_config(vars.D1, acl_config) + acl_obj.show_acl_table(vars.D1) + transmit('tg1') + st.wait(5) + if not acl_obj.verify_acl_stats(vars.D1, 'L3_IPV4_INGRESS',"rule5"): + st.report_fail("test_case_failed") + [output, exceptions] = utils.exec_all(True, + [[acl_delete, vars.D1], [acl_delete, vars.D2]]) + ensure_no_exception(exceptions) + acl_obj.config_hw_acl_mode(vars.D1, counter='per-rule') + st.report_pass("test_case_passed") + + +@pytest.mark.acl_testacl222 +def test_ft_mac_acl_prioirty_ingress(): + ''' + MAC and IPv4 Ingress ACL is applied on DUT1 + Traffic is sent on TG Port #1 + Traffic is recieved at TG Port #2 + ''' + + [output, exceptions] = utils.exec_all(True, + [[acl_delete, vars.D1], [acl_delete, vars.D2]]) + ensure_no_exception(exceptions) + # Creating Ingress ACL table and rules + print_log('Creating ACL table and apply on port') + acl_config = acl_data.acl_json_config_priority + add_port_to_acl_table(acl_config, 'L3_IPV4_INGRESS', vars.D1T1P1) + add_port_to_acl_table(acl_config, 'L3_IPV4_EGRESS', vars.D1T1P1) + add_port_to_acl_table(acl_config, 'L2_MAC_INGRESS', vars.D1T1P1) + add_port_to_acl_table(acl_config, 'L2_MAC_EGRESS', vars.D1T1P1) + apply_acl_config(vars.D1, acl_config) + st.wait(2) + transmit('tg1') + #transmit('tg2') + print_log('Check acl priority to verify packets are forwarded when MAC and IPv4 ACLs rules are in "forward" ') + result1 = verify_packet_count('tg1', vars.T1D1P1, 'tg2', vars.T1D2P1, "L3_IPV4_INGRESS|rule1") + print_log('Check acl priority to verify packets are dropped when MAC acl rule is forward and IPv4 ACL \ + rule is in "drop" ') + result2 = verify_packet_count('tg1', vars.T1D1P1, 'tg2', vars.T1D2P1, "L3_IPV4_INGRESS|rule4") + verify_acl_hit_counters(vars.D1, "L2_MAC_INGRESS") + print_log('Verify ACL hit counters on IPv4)" ') + result3 = verify_acl_hit_counters(vars.D1, "L3_IPV4_INGRESS") + acl_utils.report_result(result1 and result2 and result3) + +@pytest.mark.acl_test +def test_ft_mac_acl_prioirty_egress(): + ''' + MAC and IPv4 Ingress ACL is applied on DUT1 + Traffic is sent on TG Port #1 + Traffic is recieved at TG Port #2 + ''' + [output, exceptions] = utils.exec_all(True, + [[acl_delete, vars.D1], [acl_delete, vars.D2]]) + ensure_no_exception(exceptions) + # Creating Ingress ACL table and rules + print_log('Creating ACL table and apply on port') + acl_config = acl_data.acl_json_config_priority + add_port_to_acl_table(acl_config, 'L3_IPV4_INGRESS', vars.D1T1P1) + add_port_to_acl_table(acl_config, 'L3_IPV4_EGRESS', vars.D1T1P1) + add_port_to_acl_table(acl_config, 'L2_MAC_INGRESS', vars.D1T1P1) + add_port_to_acl_table(acl_config, 'L2_MAC_EGRESS', vars.D1T1P1) + apply_acl_config(vars.D1, acl_config) + st.wait(2) + transmit('tg2') + print_log('Check acl priority to verify packets are dropped when MAC rule is drop and \ + IPv4 ACLs rules are in "forward" ') + result1 = verify_packet_count('tg2', vars.T1D2P1,'tg1', vars.T1D1P1, "L3_IPV4_EGRESS|rule1") + result2 = verify_packet_count('tg2', vars.T1D2P1, 'tg1', vars.T1D1P1, "L2_MAC_EGRESS|rule1") + print_log('Check acl priority to verify packets are dropped when MAC rule is drop and \ + IPv4 ACLs rules are in "drop" ') + result3 = verify_packet_count('tg2', vars.T1D2P1,'tg1', vars.T1D1P1, "L3_IPV4_EGRESS|rule2") + print_log('Verify ACL hit counters on IPv4)" ') + result4 = verify_acl_hit_counters(vars.D1,"L2_MAC_EGRESS") + verify_acl_hit_counters(vars.D1, "L3_IPV4_EGRESS") + acl_utils.report_result(result2 and result3 and result4) + + +@pytest.mark.acl_test678 +def test_ft_acl_mac(): + print_log('Creating MAC ACL table and apply on Port ') + acl_obj.delete_acl_table(vars.D1, acl_table_name='L3_IPV4_INGRESS') + acl_obj.delete_acl_table(vars.D1, acl_table_name='L3_IPV4_EGRESS') + acl_config = acl_data.acl_json_config_port_d3 + add_port_to_acl_table(acl_config, 'L2_MAC_INGRESS', vars.D1T1P1) + add_port_to_acl_table(acl_config, 'L2_MAC_EGRESS', vars.D1T1P1) + change_acl_rules(acl_data.acl_json_config_port_d3, "L2_MAC_INGRESS|macrule1", "VLAN", data.vlan) + change_acl_rules(acl_data.acl_json_config_port_d3, "L2_MAC_INGRESS|macrule2", "VLAN", data.vlan) + apply_acl_config(vars.D1, acl_config) + [output, exceptions] = utils.exec_all(True, + [[clear_interface_counters, vars.D1], [clear_interface_counters, vars.D2]]) + ensure_no_exception(exceptions) + [output, exceptions] = utils.exec_all(True, + [[get_interface_counters, vars.D1, vars.D1T1P1], + [get_interface_counters, vars.D2, vars.D2T1P1]]) + ensure_no_exception(exceptions) + st.wait(2) + transmit('tg1') + [output, exceptions] = utils.exec_all(True, + [[get_interface_counters, vars.D1, vars.D1T1P1], + [get_interface_counters, vars.D2, vars.D2T1P1]]) + ensure_no_exception(exceptions) + print_log('Verifying MAC Ingress packet count') + result1 = verify_packet_count('tg1', vars.T1D1P1, 'tg2', vars.T1D2P1, "L2_MAC_INGRESS") + [output, exceptions] = utils.exec_all(True, + [[clear_interface_counters, vars.D1], [clear_interface_counters, vars.D2]]) + ensure_no_exception(exceptions) + [output, exceptions] = utils.exec_all(True, + [[get_interface_counters, vars.D1, vars.D1T1P1], + [get_interface_counters, vars.D2, vars.D2T1P1]]) + ensure_no_exception(exceptions) + print_log('Verifying MAC Ingress ACL hit counters') + transmit('tg2') + result2 = verify_acl_hit_counters(vars.D1, "L2_MAC_INGRESS") + [output, exceptions] = utils.exec_all(True, + [[get_interface_counters, vars.D1, vars.D1T1P1], + [get_interface_counters, vars.D2, vars.D2T1P1]]) + ensure_no_exception(exceptions) + print_log('Verifying MAC Ingress packet count') + + acl_utils.report_result(result1 and result2) + + +@pytest.mark.acl_test +def test_acl_rest(): + acl_aclname = "L3_IPV4_INGRESS" + acl_aclname1 = "L3_IPV4_EGRESS" + acl_rulename = "rule2" + acl_rulename1 = "rule2" + acl_in_stage = 'INGRESS' + acl_eg_stage = 'EGRESS' + acl_src_interface = vars.D1T1P1 + acl_priority = 2000 + acl_priority1 = 4000 + acl_ip_protocol = 17 + acl_src_ip = "5.5.5.5/16" + acl_dst_ip = "9.9.9.9/16" + acl_src_ip1 = "88.67.45.9/32" + acl_dst_ip1 = "12.12.12.12/16" + acl_l4_src_port_range = "100-500" + acl_pkt_action = "FORWARD" + acl_pkt_action_drop = "DROP" + [output, exceptions] = utils.exec_all(True, + [[acl_delete, vars.D1], [acl_delete, vars.D2]]) + ensure_no_exception(exceptions) + rest_url = "/restconf/data/{}".format(YANG_MODEL) + ACL_TABLE = {"ACL_TABLE_LIST": [ + {"aclname": acl_aclname, "stage": acl_in_stage, "type": "L3", "ports": [acl_src_interface]}, + {"aclname": acl_aclname1, "stage": acl_eg_stage, "type": "L3", "ports": [acl_src_interface]}] + } + ACL_RULE = {"ACL_RULE_LIST": [{"aclname": acl_aclname, "rulename": acl_rulename, "PRIORITY": acl_priority, + "PACKET_ACTION": acl_pkt_action, + "IP_PROTOCOL": acl_ip_protocol, "L4_SRC_PORT_RANGE": acl_l4_src_port_range, + "SRC_IP": acl_src_ip, "DST_IP": acl_dst_ip}, + {"aclname": acl_aclname1, "rulename": acl_rulename1, "PRIORITY": acl_priority1, + "PACKET_ACTION": acl_pkt_action_drop, "IP_PROTOCOL": acl_ip_protocol, + "SRC_IP": acl_src_ip1, "DST_IP": acl_dst_ip1} + ]} + Final_dict = {'sonic-acl:sonic-acl': {'ACL_TABLE': ACL_TABLE, 'ACL_RULE': ACL_RULE}} + st.log("#################") + st.log(Final_dict) + if not Final_dict: + st.report_fail("operation_failed_msg", 'to form acl data') + op = st.rest_modify(vars.D1, rest_url, Final_dict) + if not rest_status(op['status']): + st.report_fail("operation_failed") + response = st.rest_read(vars.D1, rest_url) + if response and response["status"] == 200: + data1 = response["output"][YANG_MODEL]["ACL_TABLE"]["ACL_TABLE_LIST"] + if not data1: + st.log("DATA IN RESPONSE IS EMPTY -- {}".format(data1)) + else: + data2 = response["output"][YANG_MODEL]["ACL_RULE"]["ACL_RULE_LIST"] + if not data2: + st.log("DATA IN RESPONSE IS EMPTY -- {}".format(data2)) + else: + st.log("RESPONSE -- {}".format(response)) + transmit('tg1') + result1 = verify_packet_count('tg1', vars.T1D1P1, 'tg2', vars.T1D2P1, "L3_IPV4_INGRESS|rule2") + transmit('tg2') + result2 = verify_packet_count('tg2', vars.T1D2P1, 'tg1', vars.T1D1P1, "L3_IPV4_EGRESS|rule2") + acl_utils.report_result(result1 and result2) + + +@pytest.mark.acl_test +def test_ft_acl_gnmi(): + """Verify that ipv4 acls working fine on gNMI""" + acl_aclname = "L3_IPV4_INGRESS" + acl_rulename = "rule2" + acl_in_stage = 'INGRESS' + acl_priority = 2000 + acl_ip_protocol = 17 + acl_pkt_action = "FORWARD" + acl_l4_src_port_range = "100-500" + acl_src_ip = "5.5.5.5/16" + acl_dst_ip = "9.9.9.9/16" + acl_src_interface = vars.D1T1P1 + [output, exceptions] = utils.exec_all(True, + [[acl_delete, vars.D1], [acl_delete, vars.D2]]) + ensure_no_exception(exceptions) + xpath = "/sonic-acl:sonic-acl/" + ACL_TABLE = {"ACL_TABLE_LIST": [ + {"aclname": acl_aclname, "stage": acl_in_stage, "type": "L3", "ports": [acl_src_interface]}]} + ACL_RULE = {"ACL_RULE_LIST": [{"aclname": acl_aclname, "rulename": acl_rulename, "PRIORITY": acl_priority, + "PACKET_ACTION": acl_pkt_action, + "IP_PROTOCOL": acl_ip_protocol, "L4_SRC_PORT_RANGE": acl_l4_src_port_range, + "SRC_IP": acl_src_ip, "DST_IP": acl_dst_ip}, + ]} + json_content = {'sonic-acl:sonic-acl': {'ACL_TABLE': ACL_TABLE, 'ACL_RULE': ACL_RULE}} + gnmi_set_out = gnmiapi.gnmi_set(vars.D1, xpath, json_content) + if not gnmi_set_out: + st.report_fail("error_string_found", ' ', ' ') + gnmi_get_out = gnmiapi.gnmi_get(vars.D1, xpath) + if "rpc error:" in gnmi_get_out: + st.report_fail("error_string_found", 'rpc error:', ' ') + transmit('tg1') + result1 = verify_packet_count('tg1', vars.T1D1P1, 'tg2', vars.T1D2P1, "L3_IPV4_INGRESS|rule2") + acl_utils.report_result(result1) + + +@pytest.mark.acl_testklish +def test_ft_acl_klish(): + """Verify that ipv4 acls working fine on klish_cli""" + acl_aclname = "L3_IPV4_INGRESS" + acl_rulename = "rule2" + acl_obj.delete_acl_table(vars.D1) + acl_obj.create_acl_rule(vars.D1,table_name=acl_aclname ,rule_name=acl_rulename,l4_protocol= 17, SRC_IP="5.5.5.5/16", + DST_IP = "9.9.9.9/16", packet_action = "permit", cli_type="klish") + acl_obj.config_access_group(vars.D1,table_name=acl_aclname, port = vars.D1T1P1, + access_group_action= "in", config = "yes") + transmit('tg1') + result1 = verify_packet_count('tg1', vars.T1D1P1, 'tg2', vars.T1D2P1, "L3_IPV4_INGRESS|rule2") + acl_obj.config_access_group(vars.D1, table_name=acl_aclname, port=vars.D1T1P1, + access_group_action="in", config="no") + acl_obj.delete_acl_table(vars.D1,acl_aclname,cli_type="klish") + acl_utils.report_result(result1) \ No newline at end of file diff --git a/spytest/tests/qos/test_qos_save_reboot.py b/spytest/tests/qos/test_qos_save_reboot.py new file mode 100644 index 00000000000..9726152f058 --- /dev/null +++ b/spytest/tests/qos/test_qos_save_reboot.py @@ -0,0 +1,195 @@ +import pytest +import json +import apis.system.reboot as rb_obj +import apis.qos.cos as cos_obj +import tests.qos.wred_ecn_config_json as wred_config +import apis.qos.qos as qos_obj +import apis.qos.acl as acl_obj +import apis.system.switch_configuration as sconf_obj +from spytest import st +from spytest.dicts import SpyTestDict +import utilities.common as utils + +data=SpyTestDict() +data.cos_name = "COS" +data.acl_ipv4_table_name = 'acl_table_v4' +data.acl_ipv6_table_name = 'acl_table_v6' +data.src_ip = '192.168.11.1' +data.src_ipv6 = '2001::10' +data.mask = '24' +data.mask_ipv6 = '128' +data.description = 'INGRESS_drop' +data.acl_rule = 'ipv4_acl_rule' +data.acl_rule_v6 = 'ipv6_acl_rule' +data.priority = '55' +data.type = 'L3' +data.type_ipv6 = 'L3V6' +data.packet_action = 'drop' +data.stage = 'INGRESS' + +@pytest.fixture(scope="module", autouse=True) +def qos_save_reboot_module_hooks(request): + # add things at the start of this module + global vars + vars = dict() + st.log("Ensuring minimum topology") + vars = st.ensure_min_topology("D1T1:1") + + st.log("Configuring supported QoS features") + wred_ecn_json_config = wred_config.wred_ecn_json_config + st.log('Creating WRED and ECN table') + utils.exec_all(True, [utils.ExecAllFunc(apply_wred_ecn_config, vars.D1, wred_ecn_json_config)]) + st.log("Checking for wred config before save and reboot") + wred_verify() + st.log("checking for ecn config before save and reboot") + ecn_verify() + st.log("Configuring IPV4 ACL with rule") + ipv4_acl_config() + st.log("Configuring IPV6 ACL with rule") + ipv6_acl_config() + st.log("Checking for IPV4 ACL config before save and reboot") + ipv4_acl_verify() + st.log("Checking for IPV6 ACL config before save and reboot") + ipv6_acl_verify() + st.log("Configuring COS") + cos_config() + st.log("Checking for COS config before save and reboot") + cos_config_verify() + + yield + # add things at the end of this module" + #Below step will clear COS, WRED and ECN config from the device. + qos_obj.clear_qos_config(vars.D1) + #Below step will clear all ACL config from the device. + acl_obj.clear_acl_config(vars.D1) + +@pytest.fixture(scope="function", autouse=True) +def qos_save_reboot_func_hooks(request): + # add things at the start every test case + # use 'request.function.func_name' to compare + # if any thing specific a particular test case + + yield + # add things at the end every test case + # use 'request.function.func_name' to compare + # if any thing specific a particular test case + +def cos_config(): + st.log("configuring cos config") + cos_obj.config_tc_to_queue_map(vars.D1, data.cos_name,{"0": "0", "1": "1", "2": "2", "3": "3", "4": "4", "5": "5", "6": "6", "7": "7"}) + +def cos_config_verify(): + st.log("verifying cos config is present in running-config - FtFpSoQoSCoSCfg001") + if not sconf_obj.verify_running_config(vars.D1, "TC_TO_QUEUE_MAP", data.cos_name, "0", "0"): + st.report_fail("content_not_found") + else: + st.log("configuring cos queue mapping '0' is successful") + if not sconf_obj.verify_running_config(vars.D1, "TC_TO_QUEUE_MAP", data.cos_name, "1", "1"): + st.report_fail("content_not_found") + else: + st.log("configuring cos queue mapping '1' is successful") + if not sconf_obj.verify_running_config(vars.D1, "TC_TO_QUEUE_MAP", data.cos_name, "2", "2"): + st.report_fail("content_not_found") + else: + st.log("configuring cos queue mapping '2' is successful") + if not sconf_obj.verify_running_config(vars.D1, "TC_TO_QUEUE_MAP", data.cos_name, "3", "3"): + st.report_fail("content_not_found") + else: + st.log("configuring cos queue mapping '3' is successful") + if not sconf_obj.verify_running_config(vars.D1, "TC_TO_QUEUE_MAP", data.cos_name, "4", "4"): + st.report_fail("content_not_found") + else: + st.log("configuring cos queue mapping '4' is successful") + if not sconf_obj.verify_running_config(vars.D1, "TC_TO_QUEUE_MAP", data.cos_name, "5", "5"): + st.report_fail("content_not_found") + else: + st.log("configuring cos queue mapping '5' is successful") + if not sconf_obj.verify_running_config(vars.D1, "TC_TO_QUEUE_MAP", data.cos_name, "6", "6"): + st.report_fail("content_not_found") + else: + st.log("configuring cos queue mapping '6' is successful") + if not sconf_obj.verify_running_config(vars.D1, "TC_TO_QUEUE_MAP", data.cos_name, "7", "7"): + st.report_fail("content_not_found") + else: + st.log("configuring cos queue mapping '7' is successful") + +def ipv4_acl_config(): + st.log('Creating IPv4 ACL in ACL table:') + acl_obj.create_acl_table(vars.D1, name=data.acl_ipv4_table_name, type=data.type, description=data.description, stage=data.stage, ports= [vars.D1T1P1]) + st.log('Adding IPv4 ACL source_ip drop rule in ACL rule table:') + acl_obj.create_acl_rule(vars.D1, table_name=data.acl_ipv4_table_name, rule_name=data.acl_rule, + packet_action=data.packet_action, priority=data.priority, + SRC_IP="{}/{}".format(data.src_ip, data.mask)) + +def ipv6_acl_config(): + st.log('Creating IPv6 ACL in ACL table:') + acl_obj.create_acl_table(vars.D1, name=data.acl_ipv6_table_name, type=data.type_ipv6, description=data.description, stage=data.stage, ports= [vars.D1T1P1]) + st.log('Adding IPv4 ACL source_ip drop rule in ACL rule table:') + acl_obj.create_acl_rule(vars.D1, table_name=data.acl_ipv6_table_name, rule_name=data.acl_rule_v6, + packet_action=data.packet_action, priority=data.priority, + SRC_IPV6="{}/{}".format(data.src_ipv6, data.mask_ipv6)) + +def ipv4_acl_verify(): + st.log("Verfiying IPV4 ACL and rule present in running config - FtOpSoQosAclCmFn001, ft_acl_configvalues_save_reload") + if not acl_obj.verify_acl_table(vars.D1, acl_table=data.acl_ipv4_table_name): + st.report_fail("Failed to create ACL") + else: + st.log("IPv4 ACL table {} creation successful".format(data.acl_ipv4_table_name)) + if not acl_obj.verify_acl_table_rule(vars.D1, acl_table=data.acl_ipv4_table_name, acl_rule=data.acl_rule): + st.report_fail("Failed to create ACL Rule") + else: + st.log("IPv4 ACL table rule {} configuration successful".format(data.acl_rule)) + +def ipv6_acl_verify(): + st.log('Verifying IPv6 ACL and rule present in running config - FtOpSoQosAclCmFn002') + if not acl_obj.verify_acl_table(vars.D1, acl_table=data.acl_ipv6_table_name): + st.report_fail('failed_to_create_acl', data.acl_ipv6_table_name) + else: + st.log("Ip ACl table {} creation successful".format(data.acl_ipv6_table_name)) + if not acl_obj.verify_acl_table_rule(vars.D1, acl_table=data.acl_ipv6_table_name, acl_rule=data.acl_rule_v6): + st.report_fail("failed_to_create_acl_rule") + else: + st.log("IPv6 ACL table rule {} configuration successful".format(data.acl_rule_v6)) + +def apply_wred_ecn_config(dut, config): + st.log("loading wred and ecn config from wred_ecn_config_json.py") + json_config = json.dumps(config) + json.loads(json_config) + st.apply_json2(dut, json_config) + +def wred_verify(): + st.log("verifying wred config in running config - FtOpSoQosWredCfg001") + if not sconf_obj.verify_running_config(vars.D1, "WRED_PROFILE", "WRED", "green_max_threshold","900000",): + st.report_fail("wred_config_not_updated_in_config_db") + else: + st.log("wred configuration successful") + +def ecn_verify(): + st.log("verifying ecn config in running config - ft_ecn_config_db_to_running_config_after_save_and_reload, ft_ecn_config_to_config_db_json_after_save_and_reboot, FtOpSoQosEcnCfg001") + if not sconf_obj.verify_running_config(vars.D1, "WRED_PROFILE", "WRED", "ecn", "ecn_all"): + st.report_fail("ecn_config_not_updated_in_config_db") + else: + st.log("ecn configuration successful") + +@pytest.mark.savereboot +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_qos_config_mgmt_verifying_config_with_save_reboot(): + st.log("performing Config save") + rb_obj.config_save(vars.D1) + st.log("performing reboot") + st.reboot(vars.D1) + st.log("Checking whether config is loaded to running config from config_db after save and reboot") + st.log("Checking for IPV4 ACL config after save and reboot") + ipv4_acl_verify() + st.log("Checking for IPV6 ACL config after save and reboot") + ipv6_acl_verify() + st.log("Checking for COS config after save and reboot") + cos_config_verify() + st.log("Checking for wred config after save and reboot") + wred_verify() + st.log("checking for ecn config after save and reboot") + ecn_verify() + st.log("configuration is successfully stored to config_db file after save and reboot") + st.report_pass("test_case_passed") + diff --git a/spytest/tests/qos/wred_ecn_config_json.py b/spytest/tests/qos/wred_ecn_config_json.py new file mode 100644 index 00000000000..e728292d0e7 --- /dev/null +++ b/spytest/tests/qos/wred_ecn_config_json.py @@ -0,0 +1,43 @@ +wred_ecn_json_config = { + "WRED_PROFILE": { + "WRED": { + "ecn": "ecn_all", + "red_max_threshold": "100000", + "wred_green_enable": "true", + "green_min_threshold": "100000", + "red_min_threshold": "10000", + "wred_yellow_enable": "true", + "yellow_min_threshold": "30000", + "wred_red_enable": "true", + "yellow_max_threshold": "300000", + "green_max_threshold": "900000", + "green_drop_probability": "10", + "yellow_drop_probability": "40", + "red_drop_probability": "50" + } + } +} +wred_config_json = { + "WRED_PROFILE": { + "WRED": { + "ecn": "ecn_none", + "red_max_threshold": "100000", + "wred_green_enable": "true", + "green_min_threshold": "100000", + "red_min_threshold": "10000", + "wred_yellow_enable": "true", + "yellow_min_threshold": "30000", + "wred_red_enable": "true", + "yellow_max_threshold": "300000", + "green_max_threshold": "900000", + "green_drop_probability": "10", + "yellow_drop_probability": "40", + "red_drop_probability": "50" + } + }, + "QUEUE": { + "Ethernet57,Ethernet58,Ethernet59|3-4": { + "wred_profile": "[WRED_PROFILE|WRED]" + } + } +} diff --git a/spytest/tests/routing/BGP/__init__.py b/spytest/tests/routing/BGP/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/spytest/tests/routing/BGP/bgp4nodelib.py b/spytest/tests/routing/BGP/bgp4nodelib.py new file mode 100644 index 00000000000..2903d5d69b9 --- /dev/null +++ b/spytest/tests/routing/BGP/bgp4nodelib.py @@ -0,0 +1,222 @@ +#BGP 4 node linear topology +from spytest import st +from spytest.dicts import SpyTestDict +import apis.routing.ip as ipapi +import apis.routing.bgp as bgpapi +import utilities.common as utils +import BGP.bgplib as bgplib + +global topo +topo = SpyTestDict() + +def l3_ipv4v6_address_config_unconfig(config='yes', vrf_type='all', config_type='all'): + """ + + :param config: + :param vrf_type: + :param config_type: + :return: + """ + st.banner("{}Configuring IP Addresses between linear topology nodes.".format('Un' if config != 'yes' else '')) + tb_vars = st.get_testbed_vars() + st.log("TestBed Vars => {}\n".format(tb_vars)) + + topo['dut_list'] = tb_vars.dut_list + st.log("topo dut_list {}".format(topo['dut_list'])) + + config = 'add' if config == 'yes' else 'remove' + ipv4_adr = '11' + ipv6_adr = '67fe' + result = True + k = 1 + + i=0 + while i < (len(topo['dut_list']) - 1): + dut = topo['dut_list'][i] + peer_dut = topo['dut_list'][i+1] + link = 1 + for local, partner, remote in st.get_dut_links(dut, peer_dut): + if config_type == 'ipv4' or config_type == 'all': + ipaddr1 = "{}.{}.0.1".format(ipv4_adr, k) + ipaddr2 = "{}.{}.0.2".format(ipv4_adr, k) + topo['D{}D{}P{}'.format(i+1,i+2,link)] = local + topo['D{}D{}P{}_ipv4'.format(i+1,i+2,link)] = ipaddr1 + topo['D{}D{}P{}_neigh_ipv4'.format(i+1,i+2,link)] = ipaddr2 + topo['D{}D{}P{}'.format(i+2,i+1,link)] = remote + topo['D{}D{}P{}_ipv4'.format(i+2,i+1,link)] = ipaddr2 + topo['D{}D{}P{}_neigh_ipv4'.format(i+2,i+1,link)] = ipaddr1 + [out, exceptions] = utils.exec_all(bgplib.fast_start,[[ipapi.config_ip_addr_interface, dut, local, ipaddr1, '24', "ipv4", config],[ipapi.config_ip_addr_interface, peer_dut, remote, ipaddr2,'24', "ipv4", config]]) + st.log([out, exceptions]) + + if config_type == 'ipv6' or config_type == 'all': + ip6addr1 = "{}:{}::1".format(ipv6_adr, k) + ip6addr2 = "{}:{}::2".format(ipv6_adr, k) + topo['D{}D{}P{}'.format(i+1,i+2,link)] = local + topo['D{}D{}P{}_ipv6'.format(i+1,i+2,link)] = ip6addr1 + topo['D{}D{}P{}_neigh_ipv6'.format(i+1,i+2,link)] = ip6addr2 + topo['D{}D{}P{}'.format(i+2,i+1,link)] = remote + topo['D{}D{}P{}_ipv6'.format(i+2,i+1,link)] = ip6addr2 + topo['D{}D{}P{}_neigh_ipv6'.format(i+2,i+1,link)] = ip6addr1 + [out, exceptions] = utils.exec_all(bgplib.fast_start,[[ipapi.config_ip_addr_interface, dut, local, ip6addr1, '64', "ipv6", config],[ipapi.config_ip_addr_interface, peer_dut, remote, ip6addr2,'64', "ipv6", config]]) + st.log([out, exceptions]) + link += 1 + break + k += 1 + i += 1 + return result + + +def l3tc_vrfipv4v6_address_ping_test(vrf_type='all', config_type='all', ping_count=3): + """ + + :param vrf_type: + :param config_type: + :param ping_count: + :return: + """ + st.banner("Ping Checking between Spine and Leaf nodes.") + ipv4_adr = '11' + ipv6_adr = '67fe' + result = True + k = 1 + + i=0 + while i < (len(topo['dut_list']) - 1): + dut = topo['dut_list'][i] + peer_dut = topo['dut_list'][i+1] + link = 1 + for local, partner, remote in st.get_dut_links(dut, peer_dut): + if config_type == 'ipv4' or config_type == 'all': + ipaddr1 = "{}.{}.0.1".format(ipv4_adr, k) + ipaddr2 = "{}.{}.0.2".format(ipv4_adr, k) + if not ipapi.ping(dut, ipaddr2, family='ipv4', count=ping_count): + st.log("{}- {} configured on {} - ping failed".format(dut, local, ipaddr2)) + result = False + + if config_type == 'ipv6' or config_type == 'all': + ip6addr1 = "{}:{}::1".format(ipv6_adr, k) + ip6addr2 = "{}:{}::2".format(ipv6_adr, k) + if not ipapi.ping(dut, ip6addr2, family='ipv6', count=ping_count): + st.log("{}- {} configured on {} - ping v6 failed".format(dut, local, ip6addr2)) + result = False + link += 1 + break + k += 1 + i += 1 + return result + +def l3tc_vrfipv4v6_confed_bgp_config(config='yes', vrf_type='all', config_type='all'): + """ + + :param config: + :param vrf_type: + :param config_type: + :return: + """ + st.banner("{}Configuring BGP with 4-node confederation topology.".format('Un' if config != 'yes' else '')) + #Confedration topo: + #DUT1 in sub-AS1 (AS1 = 24) + #DUT2, DUT3, DUT 4 in sub-AS2 (AS2 = 35) + #IBGP AS = 100 + config = 'add' if config == 'yes' else 'remove' + + leftconfed_as = 24 + rightconfed_as = 35 + iBGP_as = 100 + + topo['D1_as'] = 24 + topo['D2_as'] = 35 + topo['D3_as'] = 35 + topo['D4_as'] = 35 + + result = True + + if config == 'add': + if config_type == 'ipv4' or config_type == 'all': + #Confederation config for DUT1 + dut = topo['dut_list'][0] + neighbor = topo['D1D2P1_neigh_ipv4'] + bgpapi.config_bgp(dut, local_as = leftconfed_as, config = 'yes', conf_peers = rightconfed_as, conf_identf = iBGP_as, remote_as = rightconfed_as, config_type_list = ["neighbor"], neighbor = neighbor) + + #Confederation config for DUT2 + dut = topo['dut_list'][1] + neighbor = topo['D2D3P1_neigh_ipv4'] + bgpapi.config_bgp(dut, local_as = rightconfed_as, config = 'yes', conf_peers = leftconfed_as, conf_identf = iBGP_as, remote_as = rightconfed_as, config_type_list = ["neighbor"], neighbor = neighbor) + bgpapi.create_bgp_neighbor(dut, rightconfed_as, topo['D2D1P1_neigh_ipv4'], leftconfed_as) + + #Confederation config for DUT3 + dut = topo['dut_list'][2] + neighbor = topo['D3D4P1_neigh_ipv4'] + bgpapi.config_bgp(dut, local_as = rightconfed_as, config = 'yes', conf_peers = leftconfed_as, conf_identf = iBGP_as, remote_as = rightconfed_as, config_type_list = ["neighbor"], neighbor = neighbor) + bgpapi.create_bgp_neighbor(dut, rightconfed_as, topo['D3D2P1_neigh_ipv4'], rightconfed_as) + + #Confederation config for DUT4 + dut = topo['dut_list'][3] + neighbor = topo['D4D3P1_neigh_ipv4'] + bgpapi.config_bgp(dut, local_as = rightconfed_as, config = 'yes', conf_peers = leftconfed_as, conf_identf = iBGP_as, remote_as = rightconfed_as, config_type_list = ["neighbor"], neighbor = neighbor) + + if config_type == 'ipv6' or config_type == 'all': + #Confederation config for DUT1 + dut = topo['dut_list'][0] + neighbor = topo['D1D2P1_neigh_ipv6'] + bgpapi.config_bgp(dut, local_as = leftconfed_as, config = 'yes', addr_family ='ipv6', conf_peers = rightconfed_as, conf_identf = iBGP_as, remote_as = rightconfed_as, config_type_list = ["neighbor", "activate"], neighbor = neighbor) + + #Confederation config for DUT2 + dut = topo['dut_list'][1] + neighbor = topo['D2D3P1_neigh_ipv6'] + bgpapi.config_bgp(dut, local_as = rightconfed_as, config = 'yes', addr_family ='ipv6', conf_peers = leftconfed_as, conf_identf = iBGP_as, remote_as = rightconfed_as, config_type_list = ["neighbor", "activate"], neighbor = neighbor) + bgpapi.create_bgp_neighbor(dut, rightconfed_as, topo['D2D1P1_neigh_ipv6'], leftconfed_as, family="ipv6") + + #Confederation config for DUT3 + dut = topo['dut_list'][2] + neighbor = topo['D3D4P1_neigh_ipv6'] + bgpapi.config_bgp(dut, local_as = rightconfed_as, config = 'yes', addr_family ='ipv6', conf_peers = leftconfed_as, conf_identf = iBGP_as, remote_as = rightconfed_as, config_type_list = ["neighbor", "activate"], neighbor = neighbor) + bgpapi.create_bgp_neighbor(dut, rightconfed_as, topo['D3D2P1_neigh_ipv6'], rightconfed_as, family="ipv6") + + #Confederation config for DUT4 + dut = topo['dut_list'][3] + neighbor = topo['D4D3P1_neigh_ipv6'] + bgpapi.config_bgp(dut, local_as = rightconfed_as, config = 'yes', addr_family ='ipv6', conf_peers = leftconfed_as, conf_identf = iBGP_as, remote_as = rightconfed_as, config_type_list = ["neighbor", "activate"], neighbor = neighbor) + + else: + bgpapi.cleanup_router_bgp(topo['dut_list']) + + return result + +def l3tc_vrfipv4v6_address_confed_bgp_check(config_type='all'): + st.banner("BGP Neighbor Checking in confederation topology") + + result = True + if config_type == 'ipv4' or config_type == 'all': + #Check link between DUT 1----DUT2 and DUT2----DUT3 + neigh_list = [] + neigh_list.append(topo['D2D3P1_neigh_ipv4']) + neigh_list.append(topo['D2D1P1_neigh_ipv4']) + neigh_list = list(set(neigh_list)) + if not bgpapi.verify_bgp_summary(topo['dut_list'][1], family='ipv4', neighbor=neigh_list, state='Established'): + st.log("{} - Neighbor {} is failed to Establish".format(topo['dut_list'][1], neigh_list)) + result = False + + #Check link between DUT3----DUT4 + if not bgpapi.verify_bgp_summary(topo['dut_list'][2], family='ipv4', neighbor=topo['D3D4P1_neigh_ipv4'], state='Established'): + st.log("{} - Neighbor {} is failed to Establish".format(topo['dut_list'][2], topo['D3D4P1_neigh_ipv4'])) + result = False + + if config_type == 'ipv6' or config_type == 'all': + #Check link between DUT 1----DUT2 and DUT2----DUT3 + neigh_list = [] + neigh_list.append(topo['D2D3P1_neigh_ipv6']) + neigh_list.append(topo['D2D1P1_neigh_ipv6']) + neigh_list = list(set(neigh_list)) + if not bgpapi.verify_bgp_summary(topo['dut_list'][1], family='ipv6', neighbor=neigh_list, state='Established'): + st.log("{} - Neighbor {} is failed to Establish".format(topo['dut_list'][1], neigh_list)) + result = False + + #Check link between DUT3----DUT4 + if not bgpapi.verify_bgp_summary(topo['dut_list'][2], family='ipv6', neighbor=topo['D3D4P1_neigh_ipv6'], state='Established'): + st.log("{} - Neighbor {} is failed to Establish".format(topo['dut_list'][2], topo['D3D4P1_neigh_ipv6'])) + result = False + return result + +def get_confed_topology_info(): + return topo diff --git a/spytest/tests/routing/BGP/bgplib.py b/spytest/tests/routing/BGP/bgplib.py new file mode 100644 index 00000000000..241a97da98c --- /dev/null +++ b/spytest/tests/routing/BGP/bgplib.py @@ -0,0 +1,1442 @@ +############################################################################################## +# This file contains the library function to perform BPG elastic operations. +# Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) +############################################################################################## + +from spytest.tgen.tg import * +from spytest.tgen.tgen_utils import * +import random +import pprint +from spytest import st +from spytest.dicts import SpyTestDict +import apis.routing.ip as ipapi +import apis.routing.bgp as bgpapi +import apis.routing.route_map as rmapapi +import apis.switching.portchannel as poapi +import apis.switching.vlan as vlanapi +import utilities.utils as utils_obj +import BGP.resource as res_data_obj +import utilities.common as utils +from spytest.utils import filter_and_select + + +# Global vars +global fast_start, tg_info, topo_info, loopback_info, static_rt_info, fixed_nw_info, tg_connected_routers +global as_info, bgp_password, underlay_info +fast_start = True # To Enable parallel config +tg_info = SpyTestDict() +topo_info = SpyTestDict() +loopback_info = SpyTestDict() +static_rt_info = SpyTestDict() +fixed_nw_info = SpyTestDict() +tg_connected_routers = [] +as_info = SpyTestDict() +bgp_password = 'Test_127' +underlay_info = SpyTestDict() + + +def init_resource_data(vars): + global data + data = res_data_obj.resource_data(vars) + + +""" +Configure underlay interface and form underlay list +for physical interfaces - it is just a list of dut_links +for ves -it is vlan interface names +for veLag - this api configures lag, adds to vlan, and forms ve underlay list +currently it supports following underlays - physical interface, ve over lag +future this function can be expanded to support different other variations +""" + + +def l3tc_underlay_config_unconfig(config='yes', config_type='phy'): + """ + + :param config: + :config_type:'phy' - physical interface ; 'veLag' - ve over lag ; 'l3Lag' - L3 over LAG + :return: + """ + st.banner("{}Configuring Underlay between Spine and Leaf nodes.".format('Un' if config != 'yes' else '')) + max_mem = int(data['l3_max_links_leaf_spine']) + po_id = int(data['start_lag_id']) + vlan_id = int(data['start_vlan_id']) + result = True + leaf_index = 1 + underlay_info['links'] = SpyTestDict() + for leaf in data['leaf_routers']: + underlay_info['links'][leaf] = SpyTestDict() + ipapi.get_interface_ip_address(data[leaf], family="ipv4") + ipapi.get_interface_ip_address(data[leaf], family="ipv6") + for spine in data['spine_routers']: + ipapi.get_interface_ip_address(data[spine], family="ipv4") + ipapi.get_interface_ip_address(data[spine], family="ipv6") + underlay_info['links'][leaf][spine] = [] + po_name = 'PortChannel'+str(po_id) + # for physical interface underlay just store the underlay link info + if config_type == 'phy': + link_index = 1 + for local, partner, remote in st.get_dut_links(data[leaf], data[spine]): + if link_index <= max_mem: + underlay_info['links'][leaf][spine].append([local, remote]) + link_index += 1 + # for ve over lag + elif config_type == 'veLag': + local_lag_mbrs = [] + remote_lag_mbrs = [] + ve_int_name = 'Vlan'+str(vlan_id) + underlay_info['links'][leaf][spine].append([ve_int_name, ve_int_name]) + link_index = 1 + for local, partner, remote in st.get_dut_links(data[leaf], data[spine]): + if link_index <= max_mem: + local_lag_mbrs.append(local) + remote_lag_mbrs.append(remote) + link_index += 1 + l3tc_underlay_ve_lag_config_unconfig(config, data[leaf], + data[spine], vlan_id, po_name, local_lag_mbrs, remote_lag_mbrs) + # for ve over lag + elif config_type == 'l3Lag': + local_lag_mbrs = [] + remote_lag_mbrs = [] + underlay_info['links'][leaf][spine].append([po_name, po_name]) + link_index = 1 + for local, partner, remote in st.get_dut_links(data[leaf], data[spine]): + if link_index <= max_mem: + local_lag_mbrs.append(local) + remote_lag_mbrs.append(remote) + link_index += 1 + l3tc_underlay_lag_config_unconfig(config, data[leaf], + data[spine], po_name, local_lag_mbrs, remote_lag_mbrs) + po_id += 1 + vlan_id += 1 + leaf_index += 1 + return result + + +def l3tc_underlay_ve_lag_config_unconfig(config, dut1, dut2, vlan_id, po_name, members_dut1, members_dut2): + """ + + :param config: + :param dut1: + :param dut2: + :param vlan_id: + :param po_name: + :param members_dut1: + :param members_dut2: + :return: + """ + st.banner("{}Configuring VE over LAG between Spine and Leaf node.".format('Un' if config != 'yes' else '')) + result = True + + if config == 'yes': + # configure vlan + [out, exceptions] = \ + utils.exec_all(fast_start, + [[vlanapi.create_vlan, dut1, vlan_id], + [vlanapi.create_vlan, dut2, vlan_id]]) + st.log([out, exceptions]) + + # configure po and add members + [out, exceptions] = \ + utils.exec_all(fast_start, [[poapi.config_portchannel, dut1, dut2, po_name, + members_dut1, members_dut2, "add"]]) + st.log([out, exceptions]) + + # add po to vlan + [out, exceptions] = \ + utils.exec_all(fast_start, + [[vlanapi.add_vlan_member, dut1, vlan_id, po_name, True], + [vlanapi.add_vlan_member, dut2, vlan_id, po_name, True]]) + st.log([out, exceptions]) + else: + # del po from vlan + [out, exceptions] = \ + utils.exec_all(fast_start, + [[vlanapi.delete_vlan_member, dut1, vlan_id, po_name], + [vlanapi.delete_vlan_member, dut2, vlan_id, po_name]]) + st.log([out, exceptions]) + + # del po and delete members + [out, exceptions] = \ + utils.exec_all(fast_start, [[poapi.config_portchannel, dut1, dut2, po_name, members_dut1, + members_dut2, "del"]]) + st.log([out, exceptions]) + + # del vlan + [out, exceptions] = \ + utils.exec_all(fast_start, + [[vlanapi.delete_vlan, dut1, vlan_id], + [vlanapi.delete_vlan, dut2, vlan_id]]) + st.log([out, exceptions]) + return result + + +def l3tc_underlay_lag_config_unconfig(config, dut1, dut2, po_name, members_dut1, members_dut2): + """ + + :param config: + :param dut1: + :param dut2: + :param po_name: + :param members_dut1: + :param members_dut2: + :return: + """ + st.banner("{}Configuring LAG between Spine and Leaf node.".format('Un' if config != 'yes' else '')) + result = True + + if config == 'yes': + # configure po and add members + [out, exceptions] = \ + utils.exec_all(fast_start, [[poapi.config_portchannel, dut1, dut2, po_name, + members_dut1, members_dut2, "add"]]) + st.log([out, exceptions]) + else: + # del po and delete members + [out, exceptions] = \ + utils.exec_all(fast_start, [[poapi.config_portchannel, dut1, dut2, po_name, + members_dut1, members_dut2, "del"]]) + st.log([out, exceptions]) + return result + + +def l3tc_vrfipv4v6_address_leafspine_config_unconfig(config='yes', vrf_type='all', config_type='all'): + """ + + :param config: + :param vrf_type: + :param config_type: + :return: + """ + st.banner("{}Configuring IP Addresses between Spine and Leaf nodes.".format('Un' if config != 'yes' else '')) + + topo_info['ipv4'] = SpyTestDict() + topo_info['ipv6'] = SpyTestDict() + + config = 'add' if config == 'yes' else 'remove' + ipv4_adr = data['ipv4_addres_first_octet'] + ipv6_adr = data['ipv6_addres_first_octet'] + result = True + k = 1 + for leaf in data['leaf_routers']: + topo_info['ipv4'][leaf] = [] + topo_info['ipv6'][leaf] = [] + + for spine in data['spine_routers']: + topo_info['ipv4'][spine] = [] + topo_info['ipv6'][spine] = [] + + link = 1 + for local, remote in underlay_info['links'][leaf][spine]: + if config_type == 'ipv4' or config_type == 'all': + ipaddr1 = "{}.{}.{}.1".format(ipv4_adr, k, link) + ipaddr2 = "{}.{}.{}.2".format(ipv4_adr, k, link) + topo_info['ipv4'][leaf].append([local, ipaddr1, spine, remote, ipaddr2, link]) + topo_info['ipv4'][spine].append([remote, ipaddr2, leaf, local, ipaddr1, link]) + + [out, exceptions] = \ + utils.exec_all(fast_start, + [[ipapi.config_ip_addr_interface, data[leaf], local, ipaddr1, '24', + "ipv4", config], + [ipapi.config_ip_addr_interface, data[spine], remote, ipaddr2, '24', + "ipv4", config]]) + st.log([out, exceptions]) + + if config_type == 'ipv6' or config_type == 'all': + ip6addr_1 = "{}:{}:{}::1".format(ipv6_adr, k, link) + ip6addr_2 = "{}:{}:{}::2".format(ipv6_adr, k, link) + topo_info['ipv6'][leaf].append([local, ip6addr_1, spine, remote, ip6addr_2, link]) + topo_info['ipv6'][spine].append([remote, ip6addr_2, leaf, local, ip6addr_1, link]) + + [out, exceptions] = \ + utils.exec_all(fast_start, + [[ipapi.config_ip_addr_interface, data[leaf], local, ip6addr_1, '64', + "ipv6", config], + [ipapi.config_ip_addr_interface, data[spine], remote, ip6addr_2, '64', + "ipv6", config]]) + st.log([out, exceptions]) + link += 1 + k += 1 + return result + + +def l3tc_vrfipv4v6_address_leafspine_loopback_config_unconfig(config='yes', config_type='all'): + """ + + :param config: + :param config_type: + :return: + """ + st.banner("{}Configuring Loopback Addresses on both Spine and Leaf nodes.".format('Un' if config != 'yes' else '')) + lo_config = config + config = 'add' if config == 'yes' else 'remove' + result = True + i = 1 + loopback_info['ipv4'] = SpyTestDict() + loopback_info['ipv6'] = SpyTestDict() + thread4_info = [] + thread6_info = [] + loop_back_int_create = [] + for j, leaf in enumerate(data['leaf_routers'], start=0): + loopback1 = "{}.{}.{}.{}".format(i, i+1, i+2, i+3) + loopback6addr_1 = "{}::{}".format(6000+i, i) + loopback_info['ipv4'][leaf] = loopback1 + loopback_info['ipv6'][leaf] = loopback6addr_1 + + loop_back_int_create.append(utils.ExecAllFunc(ipapi.configure_loopback, data[leaf], loopback_name="Loopback0", + config=lo_config)) + thread4_info.append([ipapi.config_ip_addr_interface, data[leaf], "Loopback0", loopback1, '32', "ipv4", config]) + thread6_info.append([ipapi.config_ip_addr_interface, data[leaf], "Loopback0", loopback6addr_1, '128', "ipv6", + config]) + i += 1 + + for j, spine in enumerate(data['spine_routers'], start=0): + loopback1 = "{}.{}.{}.{}".format(i, i+1, i+2, i+3) + loopback6addr_1 = "{}::{}".format(6000+i, i) + loopback_info['ipv4'][spine] = loopback1 + loopback_info['ipv6'][spine] = loopback6addr_1 + + loop_back_int_create.append(utils.ExecAllFunc(ipapi.configure_loopback, data[spine], loopback_name="Loopback0", + config=lo_config)) + thread4_info.append([ipapi.config_ip_addr_interface, data[spine], "Loopback0", loopback1, '32', "ipv4", config]) + thread6_info.append([ipapi.config_ip_addr_interface, data[spine], "Loopback0", loopback6addr_1, '128', "ipv6", + config]) + i += 1 + + if config == 'add': + [out, exceptions] = utils.exec_all(fast_start, loop_back_int_create) + st.log([out, exceptions]) + if config_type == 'ipv4' or config_type == 'all': + [out, exceptions] = utils.exec_all(fast_start, thread4_info) + st.log([out, exceptions]) + + if config_type == 'ipv6' or config_type == 'all': + [out, exceptions] = utils.exec_all(fast_start, thread6_info) + st.log([out, exceptions]) + if config == 'remove': + [out, exceptions] = utils.exec_all(fast_start, loop_back_int_create) + st.log([out, exceptions]) + + return result + + +def l3tc_vrfipv4v6_static_route_leafspine_config_unconfig(config='yes', vrf_type='all', config_type='all'): + """ + + :param config: + :param vrf_type: + :param config_type: + :return: + """ + st.banner("{}Configuring Static route between Spine and Leaf nodes.".format('Un' if config != 'yes' else '')) + + static_rt_info['ipv4'] = SpyTestDict() + static_rt_info['ipv6'] = SpyTestDict() + + result = True + config = 'add' if config == 'yes' else 'remove' + + dut_name_map = SpyTestDict() + dut_name_list = {each:[] for each in data['spine_routers'] + data['leaf_routers']} + for dut_idx, dut_name in enumerate(dut_name_list, start=1): + dut_name_map[dut_name] = "D{}".format(dut_idx) + + if topo_info: + for dut_idx, dut_name in enumerate(dut_name_list, start=1): + static_rt_info['ipv4'][dut_name] = [] + static_rt_info['ipv6'][dut_name] = [] + + actual_dut = data[dut_name] + + if config_type == 'ipv4' or config_type == 'all': + afamily = 'ipv4' + dut_port_list = [each for each in topo_info[afamily][dut_name] if each[2] in dut_name_map] + for port_idx, each_port in enumerate(dut_port_list, start=1): + port_rmt_ip4 = each_port[4] + port_lnk_num = each_port[5] + + staticip = "{}.{}.{}.0/24".format(data['ipv4_static_addres_first_octet'], dut_idx, port_idx) + nexthop = port_rmt_ip4 + + if config == 'yes': + static_rt_info[afamily][dut_name].append([staticip, nexthop]) + static_rt_info[afamily][dut_name].append([staticip, nexthop]) + + if config == 'add': + [out, exceptions] = \ + utils.exec_all(fast_start, [[ipapi.create_static_route, actual_dut, nexthop, staticip, + "vtysh", 'ipv4']]) + elif config == 'remove': + [out, exceptions] = \ + utils.exec_all(fast_start, [[ipapi.delete_static_route, actual_dut, nexthop, staticip, + 'ipv4', "vtysh"]]) + st.log([out, exceptions]) + + if config_type == 'ipv6' or config_type == 'all': + afamily = 'ipv6' + dut_port_list = [each for each in topo_info[afamily][dut_name] if each[2] in dut_name_map] + for port_idx, each_port in enumerate(dut_port_list, start=1): + port_rmt_ip6 = each_port[4] + port_lnk_num = each_port[5] + + staticip = "{}:{}:{}::2/64".format(data['ipv6_static_addres_first_octet'], dut_idx, port_idx) + nexthop = port_rmt_ip6 + + static_rt_info[afamily][dut_name].append([staticip, nexthop]) + static_rt_info[afamily][dut_name].append([staticip, nexthop]) + + if config == 'add': + [out, exceptions] = \ + utils.exec_all(fast_start, [[ipapi.create_static_route, actual_dut, nexthop, staticip, + "vtysh", 'ipv6']]) + elif config == 'remove': + [out, exceptions] = \ + utils.exec_all(fast_start, [[ipapi.delete_static_route, actual_dut, nexthop, staticip, + 'ipv6', "vtysh"]]) + st.log([out, exceptions]) + else: + st.log("Topology information not available") + return False + return result + + +def l3tc_vrfipv4v6_address_leafspine_ping_test(vrf_type='all', config_type='all', ping_count=3): + """ + + :param vrf_type: + :param config_type: + :param ping_count: + :return: + """ + st.banner("Ping Checking between Spine and Leaf nodes.") + ipv4_adr = data['ipv4_addres_first_octet'] + ipv6_adr = data['ipv6_addres_first_octet'] + result = True + k = 1 + for leaf in data['leaf_routers']: + for spine in data['spine_routers']: + link = 1 + for local, remote in underlay_info['links'][leaf][spine]: + if config_type == 'ipv4' or config_type == 'all': + ipaddr1 = "{}.{}.{}.1".format(ipv4_adr, k, link) + ipaddr2 = "{}.{}.{}.2".format(ipv4_adr, k, link) + if not ipapi.ping(data[leaf], ipaddr2, family='ipv4', count=ping_count): + st.log("{}- {} configured on {} - ping failed".format(data[leaf], local, ipaddr2)) + result = False + if config_type == 'ipv6' or config_type == 'all': + ip6addr_1 = "{}:{}:{}::1".format(ipv6_adr, k, link) + ip6addr_2 = "{}:{}:{}::2".format(ipv6_adr, k, link) + if not ipapi.ping(data[leaf], ip6addr_2, family='ipv6', count=ping_count): + st.log("{}- {} configured on {} - ping v6 failed".format(data[leaf], local, ip6addr_2)) + result = False + link += 1 + k += 1 + return result + + +def l3tc_vrfipv4v6_address_leafspine_bgp_config(config='yes', vrf_type='all', config_type='all', **kwargs): + """ + + :param config: + :param vrf_type: + :param config_type: + :return: + """ + st.banner("{}Configuring BGP between Spine and Leaf nodes.".format('Un' if config != 'yes' else '')) + + config = 'add' if config == 'yes' else 'remove' + ipv4_adr = data['ipv4_addres_first_octet'] + ipv6_adr = data['ipv6_addres_first_octet'] + spine_as = int(data['spine_as']) + leaf_as = int(data['leaf_as']) + if kwargs.has_key('rr_enable'): + leaf_as = spine_as + + result = True + thread4_info = [] + thread6_info = [] + if config == 'add': + k = 1 + for leaf in data['leaf_routers']: + as_info[leaf] = leaf_as + for spine in data['spine_routers']: + as_info[spine] = spine_as + link = 1 + if config == 'add': + spine_neigh_list, leaf_neigh_list, spine_neigh6_list, leaf_neigh6_list = [], [], [], [] + for local, remote in underlay_info['links'][leaf][spine]: + if config_type == 'ipv4' or config_type == 'all': + ipaddr1 = "{}.{}.{}.1".format(ipv4_adr, k, link) + ipaddr2 = "{}.{}.{}.2".format(ipv4_adr, k, link) + spine_neigh_list.append(ipaddr2) + leaf_neigh_list.append(ipaddr1) + if config_type == 'ipv6' or config_type == 'all': + ip6addr_1 = "{}:{}:{}::1".format(ipv6_adr, k, link) + ip6addr_2 = "{}:{}:{}::2".format(ipv6_adr, k, link) + spine_neigh6_list.append(ip6addr_2) + leaf_neigh6_list.append(ip6addr_1) + link += 1 + k += 1 + if config_type == 'ipv4' or config_type == 'all': + thread4_info.append(utils.ExecAllFunc(bgpapi.config_bgp_multi_neigh_use_peergroup, data[leaf], + local_asn=leaf_as, peer_grp_name='leaf_spine', + remote_asn=spine_as, neigh_ip_list=spine_neigh_list, + family='ipv4', activate=1, password=bgp_password)) + thread4_info.append(utils.ExecAllFunc(bgpapi.config_bgp_multi_neigh_use_peergroup, data[spine], + local_asn=spine_as, peer_grp_name='spine_leaf', + remote_asn=leaf_as, neigh_ip_list=leaf_neigh_list, + family='ipv4', activate=1, password=bgp_password)) + + if config_type == 'ipv6' or config_type == 'all': + thread6_info.append(utils.ExecAllFunc(bgpapi.config_bgp_multi_neigh_use_peergroup, data[leaf], + local_asn=leaf_as, peer_grp_name='leaf_spine6', + remote_asn=spine_as, neigh_ip_list=spine_neigh6_list, + family='ipv6', activate=1, password=bgp_password)) + thread6_info.append(utils.ExecAllFunc(bgpapi.config_bgp_multi_neigh_use_peergroup, data[spine], + local_asn=spine_as, peer_grp_name='spine_leaf6', + remote_asn=leaf_as, neigh_ip_list=leaf_neigh6_list, + family='ipv6', activate=1, password=bgp_password)) + + if config_type == 'ipv4' or config_type == 'all': + [out, exceptions] = utils.exec_all(False, thread4_info) + st.log([out, exceptions]) + + if config_type == 'ipv6' or config_type == 'all': + [out, exceptions] = utils.exec_all(False, thread6_info) + st.log([out, exceptions]) + else: + bgpapi.cleanup_bgp_config([data[dut] for dut in data['leaf_routers']+data['spine_routers']]) + + return result + + +def l3tc_vrfipv4v6_bgp_network_leafspine_config_unconfig(config='yes', vrf_type='all', config_type='all'): + """ + + :param config: + :param vrf_type: + :param config_type: + :return: + """ + st.banner("{}Configuring network route between Spine and Leaf nodes.".format('Un' if config != 'yes' else '')) + + fixed_nw_info['ipv4'] = SpyTestDict() + fixed_nw_info['ipv6'] = SpyTestDict() + + result = True + thread4_info = [] + thread6_info = [] + + dut_name_map = SpyTestDict() + dut_name_list = {each:[] for each in data['spine_routers'] + data['leaf_routers']} + for dut_idx, dut_name in enumerate(dut_name_list, start=1): + dut_name_map[dut_name] = "D{}".format(dut_idx) + + if topo_info: + for dut_idx, dut_name in enumerate(dut_name_list, start=1): + fixed_nw_info['ipv4'][dut_name] = [] + fixed_nw_info['ipv6'][dut_name] = [] + + actual_dut = data[dut_name] + + if config_type == 'ipv4' or config_type == 'all': + afamily = 'ipv4' + dut_port_list = [each for each in topo_info[afamily][dut_name] if each[2] in dut_name_map] + for port_idx, each_port in enumerate(dut_port_list, start=1): + port_lnk_num = each_port[5] + networkip = "{}.{}.{}.0/24".format(data['ipv4_nw_addres_first_octet'], dut_idx, port_idx) + + if config == 'yes': + fixed_nw_info[afamily][dut_name].append(networkip) + fixed_nw_info[afamily][dut_name].append(networkip) + + if as_info: + bgpapi.config_bgp(actual_dut, config=config, + addr_family=afamily, local_as=as_info[dut_name], + config_type_list=["network"], network=networkip) + ''' + [out, exceptions] = \ + utils.exec_all(fast_start, [[bgpapi.config_bgp, actual_dut, config=config, + addr_family=afamily, local_as=as_info[dut_name], + config_type_list=["network"], network=networkip]]) + thread4_info.append(utils.ExecAllFunc(bgpapi.config_bgp, actual_dut, config=config, + addr_family=afamily, local_as=as_info[dut_name], + config_type_list=["network"], network=networkip)) + ''' + + if config_type == 'ipv6' or config_type == 'all': + afamily = 'ipv6' + dut_port_list = [each for each in topo_info[afamily][dut_name] if each[2] in dut_name_map] + for port_idx, each_port in enumerate(dut_port_list, start=1): + port_lnk_num = each_port[5] + networkip = "{}:{}:{}::2/64".format(data['ipv6_nw_addres_first_octet'], dut_idx, port_idx) + + fixed_nw_info[afamily][dut_name].append(networkip) + fixed_nw_info[afamily][dut_name].append(networkip) + + if as_info: + bgpapi.config_bgp(actual_dut, config=config, + addr_family=afamily, local_as=as_info[dut_name], + config_type_list=["network"], network=networkip) + ''' + [out, exceptions] = \ + utils.exec_all(fast_start, [[bgpapi.config_bgp, actual_dut, config=config, + addr_family=afamily, local_as=as_info[dut_name], + config_type_list=["network"], network= networkip]]) + thread6_info.append(utils.ExecAllFunc(bgpapi.config_bgp, actual_dut, config=config, + addr_family=afamily, local_as=as_info[dut_name], + config_type_list=["network"], network= networkip)) + ''' + else: + st.log("Topology information not available") + return False + + ''' + if config_type == 'ipv4' or config_type == 'all': + [out, exceptions] = utils.exec_all(fast_start, thread4_info) + st.log([out, exceptions]) + + if config_type == 'ipv6' or config_type == 'all': + [out, exceptions] = utils.exec_all(fast_start, thread6_info) + st.log([out, exceptions]) + ''' + return result + + +def l3tc_vrfipv4v6_address_leafspine_bgp_check(config_type='all'): + """ + + :param config_type: + :return: + """ + st.banner("BGP Neighbor Checking between Spine and Leaf nodes.") + ipv4_adr = data['ipv4_addres_first_octet'] + ipv6_adr = data['ipv6_addres_first_octet'] + result = True + k = 1 + for leaf in data['leaf_routers']: + spine_neigh_list, leaf_neigh_list, spine_neigh6_list, leaf_neigh6_list = [], [], [], [] + for spine in data['spine_routers']: + link = 1 + for local, remote in underlay_info['links'][leaf][spine]: + if config_type == 'ipv4' or config_type == 'all': + ipaddr1 = "{}.{}.{}.1".format(ipv4_adr, k, link) + ipaddr2 = "{}.{}.{}.2".format(ipv4_adr, k, link) + spine_neigh_list.append(ipaddr2) + leaf_neigh_list.append(ipaddr1) + + if config_type == 'ipv6' or config_type == 'all': + ip6addr_1 = "{}:{}:{}::1".format(ipv6_adr, k, link) + ip6addr_2 = "{}:{}:{}::2".format(ipv6_adr, k, link) + spine_neigh6_list.append(ip6addr_2) + leaf_neigh6_list.append(ip6addr_1) + link += 1 + k += 1 + + if config_type == 'ipv4' or config_type == 'all': + neigh_list = list(set(spine_neigh_list)) + if not bgpapi.verify_bgp_summary(data[leaf], family='ipv4', neighbor=neigh_list, state='Established'): + st.log("{} - Neighbor {} is failed to Establish".format(data[leaf], neigh_list)) + result = False + + if config_type == 'ipv6' or config_type == 'all': + neigh_list = list(set(spine_neigh6_list)) + if not bgpapi.verify_bgp_summary(data[leaf], family='ipv6', neighbor=neigh_list, state='Established'): + st.log("{} - Neighbor {} is failed to Establish".format(data[leaf], neigh_list)) + result = False + + return result + + +def l3tc_vrfipv4v6_address_leafspine_tg_config_unconfig(config='yes', vrf_type='all', config_type='all'): + """ + + :param config: + :param vrf_type: + :param config_type: + :return: + """ + st.banner("{}Configuring IP Addresses to TG connected interfaces and on TG.". + format('Un' if config != 'yes' else '')) + + global tg_connected_routers + config = 'add' if config == 'yes' else 'remove' + max_mem = int(data['l3_max_tg_links_each_leaf_spine']) + mas_addr_spine = data['mac_addres_tg_src_spine'] + mas_addr_leaf = data['mac_addres_tg_src_leaf'] + ipv4_adr_spine = data['ipv4_addres_first_octet_tg_spine'] + ipv6_adr_spine = data['ipv6_addres_first_octet_tg_spine'] + ipv4_adr_leaf = data['ipv4_addres_first_octet_tg_leaf'] + ipv6_adr_leaf = data['ipv6_addres_first_octet_tg_leaf'] + tg_in_spine = sum([len(st.get_tg_links(data[spine])) for spine in data['spine_routers']]) + tg_in_leaf = sum([len(st.get_tg_links(data[leaf])) for leaf in data['leaf_routers']]) + mac_list_spine = utils_obj.get_mac_address(mas_addr_spine, start=1, end=tg_in_spine+1, step=1) + mac_list_leaf = utils_obj.get_mac_address(mas_addr_leaf, start=1, end=tg_in_leaf+1, step=1) + + result = True + + # Logic to scan and pick TG ports in 1:1 or N:N topology. + tg_connected_routers = [] + step = 0 + if len(data['leaf_routers']) == 1 and (len(data['spine_routers']) == 1 or len(data['spine_routers']) >= 2): + if not len(st.get_tg_links(data[data['leaf_routers'][0]])) >= 1: + step += 1 + if not len(st.get_tg_links(data[data['spine_routers'][0]])) >= 1: + step += 1 + if step: + st.error("Expecting altest one TG port connected to each of Leaf1 and Spine1 in 1:1 topology case.") + st.error("Failed due to TG ports shortage") + st.report_env_fail('test_case_not_executeds') + else: + st.log("TG Mode 1:1 mode") + tg_connected_routers.append(data['leaf_routers'][0]) + tg_connected_routers.append(data['spine_routers'][0]) + st.log("tg_connected_routers {}".format(tg_connected_routers)) + else: + st.log("TG Mode N:N mode") + start, stop = 0, 0 + for leaf in data['leaf_routers']: + if len(st.get_tg_links(data[leaf])) >= 1: + if not start: + start = leaf + tg_connected_routers.append(leaf) + for leaf in data['leaf_routers'][::-1]: + if len(st.get_tg_links(data[leaf])) >= 1: + if not stop: + stop = leaf + tg_connected_routers.append(leaf) + st.log("start {}".format(start)) + st.log("stop {}".format(stop)) + st.log("tg_connected_routers {}".format(tg_connected_routers)) + if start == stop: + st.error("Expecting altest one TG port connected to atlest 2 of Leafs in N:N topology case.") + st.error("Failed due to TG ports shortage") + st.report_env_fail('test_case_not_executeds') + + i = 0 + for j, device_type in enumerate(tg_connected_routers, start=1): + if config == 'add': + tg_info[device_type] = SpyTestDict() + tg_info[device_type]['ipv4'] = SpyTestDict() + tg_info[device_type]['ipv6'] = SpyTestDict() + + link = 1 + for local, partner, remote in st.get_tg_links(data[device_type]): + if link <= max_mem: + # TG reset. + tg = tgen_obj_dict[partner] + tg.tg_traffic_control(action="reset", port_handle=tg.get_port_handle(remote)) + + if config_type == 'ipv4' or config_type == 'all': + ipaddr1 = "{}.{}.{}.1".format(ipv4_adr_leaf, j, link) + ipaddr2 = "{}.{}.{}.2".format(ipv4_adr_leaf, j, link) + + ipapi.config_ip_addr_interface(dut=data[device_type], interface_name=local, + ip_address=ipaddr1, subnet='24', family="ipv4", config=config) + if config == 'add': + tg_info[device_type]['ipv4'][local] = create_routing_interface_on_tg(partner, remote, ipaddr2, + '255.255.255.0', + ipaddr1, config=config, + handle='', af='ipv4') + else: + tg_info[device_type]['ipv4'][local] = create_routing_interface_on_tg(partner, remote, ipaddr2, + '255.255.255.0', ipaddr1, config=config, + handle=tg_info[device_type]['ipv4'][local][2], af='ipv4') + tg_info[device_type]['ipv4'][local].append([ipaddr1, ipaddr2]) + + if config_type == 'ipv6' or config_type == 'all': + ip6addr_1 = "{}:{}:{}::1".format(ipv6_adr_leaf, j, link) + ip6addr_2 = "{}:{}:{}::2".format(ipv6_adr_leaf, j, link) + + ipapi.config_ip_addr_interface(dut=data[device_type], interface_name=local, + ip_address=ip6addr_1, subnet='64', family="ipv6", config=config) + if config == 'add': + tg_info[device_type]['ipv6'][local] = create_routing_interface_on_tg(partner, remote, + ip6addr_2, '64', + ip6addr_1, config=config, + handle='', af='ipv6') + else: + tg_info[device_type]['ipv6'][local] = create_routing_interface_on_tg(partner, remote, + ip6addr_2, '64', + ip6addr_1, config=config, + handle=tg_info[device_type] + ['ipv6'][local][2], + af='ipv6') + tg_info[device_type]['ipv6'][local].append([ip6addr_1, ip6addr_2]) + + i += 1 + link += 1 + + return result, tg_info + + +def l3tc_vrfipv4v6_address_leafspine_tg_bgp_config(config='yes', vrf_type='all', config_type='all', + class_reconfig='No'): + """ + + :param config: + :param vrf_type: + :param config_type: + :param class_reconfig: + :return: + """ + st.banner("{}Configuring BGP on TG connected interafces and on TG.".format('Un' if config != 'yes' else '')) + st.banner("OPTION class_reconfig is provided : {}".format(class_reconfig)) + + config = 'add' if config == 'yes' else 'remove' + max_mem = int(data['l3_max_tg_links_each_leaf_spine']) + ipv4_adr_leaf = data['ipv4_addres_first_octet_tg_leaf'] + ipv6_adr_leaf = data['ipv6_addres_first_octet_tg_leaf'] + + spine_as = int(data['spine_as']) + leaf_as = int(data['leaf_as']) + spine_tg_as = int(data['spine_tg_as']) + leaf_tg_as = int(data['leaf_tg_as']) + + result = True + if config == "add": + i = 0 + for j, device_type in enumerate(tg_connected_routers, start=1): + tg_neigh_list, leaf_neigh_list, tg_neigh6_list, leaf_neigh6_list = [], [], [], [] + link = 1 + tg_as = leaf_tg_as + dut_as = leaf_as + if 'spine' in device_type: + tg_as = spine_tg_as + dut_as = spine_as + + for local, partner, remote in st.get_tg_links(data[device_type]): + if link <= max_mem: + if config_type == 'ipv4' or config_type == 'all': + ipaddr1 = "{}.{}.{}.1".format(ipv4_adr_leaf, j, link) + ipaddr2 = "{}.{}.{}.2".format(ipv4_adr_leaf, j, link) + tg_neigh_list.append(ipaddr2) + leaf_neigh_list.append(ipaddr1) + if class_reconfig == "No": + [tg, tg_ph_x, h1, ip_info] = tg_info[device_type]['ipv4'][local] + rv = config_bgp_on_tg(tg, h1, dut_as, tg_as+j-1, ipaddr1, action='start', af='ipv4') + tg_info[device_type]['ipv4'][local].append(rv) + + if config_type == 'ipv6' or config_type == 'all': + ip6addr_1 = "{}:{}:{}::1".format(ipv6_adr_leaf, j, link) + ip6addr_2 = "{}:{}:{}::2".format(ipv6_adr_leaf, j, link) + tg_neigh6_list.append(ip6addr_2) + leaf_neigh6_list.append(ip6addr_1) + if class_reconfig == "No": + [tg, tg_ph_x, h1, ip_info] = tg_info[device_type]['ipv6'][local] + rv = config_bgp_on_tg(tg, h1, dut_as, tg_as+j-1, ip6addr_1, action='start', af='ipv6') + tg_info[device_type]['ipv6'][local].append(rv) + + i += 1 + link += 1 + + if config_type == 'ipv4' or config_type == 'all': + bgpapi.config_bgp_multi_neigh_use_peergroup(data[device_type], local_asn=dut_as, + peer_grp_name='leaf_tg', remote_asn=tg_as+j-1, + neigh_ip_list=tg_neigh_list, family='ipv4', activate=1) + + if config_type == 'ipv6' or config_type == 'all': + bgpapi.config_bgp_multi_neigh_use_peergroup(data[device_type], local_asn=dut_as, + peer_grp_name='leaf_tg6', remote_asn=tg_as+j-1, + neigh_ip_list=tg_neigh6_list, family='ipv6', activate=1) + + else: + bgpapi.cleanup_bgp_config([data[dut] for dut in data['leaf_routers']+data['spine_routers']]) + + return result, tg_info + + +def l3tc_vrfipv4v6_address_leafspine_rr_tg_bgp_config(config='yes', vrf_type='all', config_type='all', **kwargs): + """ + + :param config: + :param vrf_type: + :param config_type: + :param kwargs: + :return: + """ + st.banner("{}Configuring BGP on TG connected interafces and on TG.".format('Un' if config != 'yes' else '')) + + config = 'add' if config == 'yes' else 'remove' + max_mem = int(data['l3_max_tg_links_each_leaf_spine']) + mas_addr_spine = data['mac_addres_tg_src_spine'] + mas_addr_leaf = data['mac_addres_tg_src_leaf'] + ipv4_adr_spine = data['ipv4_addres_first_octet_tg_spine'] + ipv6_adr_spine = data['ipv6_addres_first_octet_tg_spine'] + ipv4_adr_leaf = data['ipv4_addres_first_octet_tg_leaf'] + ipv6_adr_leaf = data['ipv6_addres_first_octet_tg_leaf'] + + spine_as = int(data['spine_as']) + leaf_as = int(data['leaf_as']) + leaf_tg_as = int(data['leaf_tg_as']) + spine_tg_as = int(data['spine_tg_as']) + if kwargs.has_key('rr_enable'): + spine_tg_as = spine_as + leaf_as = spine_as + + result = True + if config == "add": + i = 0 + for j, device_type in enumerate(tg_connected_routers, start=1): + tg_neigh_list, leaf_neigh_list, tg_neigh6_list, leaf_neigh6_list = [], [], [], [] + link = 1 + tg_as = leaf_tg_as + dut_as = leaf_as + if 'spine' in device_type: + tg_as = spine_tg_as + dut_as = spine_as + + for local, partner, remote in st.get_tg_links(data[device_type]): + if link <= max_mem: + if config_type == 'ipv4' or config_type == 'all': + ipaddr1 = "{}.{}.{}.1".format(ipv4_adr_leaf, j, link) + ipaddr2 = "{}.{}.{}.2".format(ipv4_adr_leaf, j, link) + tg_neigh_list.append(ipaddr2) + leaf_neigh_list.append(ipaddr1) + [tg, tg_ph_x, h1, ip_info] = tg_info[device_type]['ipv4'][local] + rv = config_bgp_on_tg(tg, h1, dut_as, tg_as, ipaddr1, action='start', af='ipv4') + tg_info[device_type]['ipv4'][local].append(rv) + + if config_type == 'ipv6' or config_type == 'all': + ip6addr_1 = "{}:{}:{}::1".format(ipv6_adr_leaf, j, link) + ip6addr_2 = "{}:{}:{}::2".format(ipv6_adr_leaf, j, link) + tg_neigh6_list.append(ip6addr_2) + leaf_neigh6_list.append(ip6addr_1) + + [tg, tg_ph_x, h1, ip_info] = tg_info[device_type]['ipv6'][local] + rv = config_bgp_on_tg(tg, h1, dut_as, tg_as, ip6addr_1, action='start', af='ipv6') + tg_info[device_type]['ipv6'][local].append(rv) + + i += 1 + link += 1 + + if config_type == 'ipv4' or config_type == 'all': + bgpapi.config_bgp_multi_neigh_use_peergroup(data[device_type], local_asn=dut_as, + peer_grp_name='leaf_tg', remote_asn=tg_as, + neigh_ip_list=tg_neigh_list, family='ipv4', activate=1) + + if config_type == 'ipv6' or config_type == 'all': + bgpapi.config_bgp_multi_neigh_use_peergroup(data[device_type], local_asn=dut_as, + peer_grp_name='leaf_tg6', remote_asn=tg_as, + neigh_ip_list=tg_neigh6_list, family='ipv6', activate=1) + + else: + bgpapi.cleanup_bgp_config([data[dut] for dut in data['leaf_routers']+data['spine_routers']]) + + return result, tg_info + + +def create_routing_interface_on_tg(tg, tg_port, intf_ip_addr, netmask, gateway, config, handle='none', af='ipv4'): + """ + + :param tg: + :param tg_port: + :param intf_ip_addr: + :param netmask: + :param gateway: + :param af: + :return: + """ + tg = tgen_obj_dict[tg] + tg_ph_x = tg.get_port_handle(tg_port) + config = 'config' if config == 'add' else 'destroy' + if af == 'ipv4': + if config == 'config': + h1 = tg.tg_interface_config(port_handle=tg_ph_x, mode=config, intf_ip_addr=intf_ip_addr, gateway=gateway, + netmask=netmask, arp_send_req='1') + else: + tg.tg_interface_config(port_handle=tg_ph_x, handle=handle['handle'], mode=config) + else: + if config == 'config': + h1 = tg.tg_interface_config(port_handle=tg_ph_x, mode=config, ipv6_intf_addr=intf_ip_addr, + ipv6_prefix_length=netmask, ipv6_gateway=gateway, arp_send_req='1') + else: + tg.tg_interface_config(port_handle=tg_ph_x, handle=handle['handle'], mode=config) + + if config == 'config': + st.log("#"*30) + st.log("h1 = {}".format(h1)) + st.log("#"*30) + return [tg, tg_ph_x, h1] + else: + return [tg, tg_ph_x] + + +def config_bgp_on_tg(tg, handle, local_asn, tg_asn, local_ipaddr, action='start', af='ipv4'): + """ + + :param tg: + :param handle: + :param local_asn: + :param tg_asn: + :param local_ipaddr: + :param action: + :param af: + :return: + """ + + # STC / IXIA + handle_key_v4 = 'handle' + handle_key_v6 = 'handle' + if af == 'ipv4': + bgp_rtr1 = tg.tg_emulation_bgp_config(handle=handle[handle_key_v4], mode='enable', active_connect_enable='1', + local_as=tg_asn, remote_as=local_asn, remote_ip_addr=local_ipaddr, + enable_4_byte_as='1') + st.wait(5) + tg.tg_emulation_bgp_control(handle=bgp_rtr1['handle'], mode='start') + + else: + + bgp_rtr1 = tg.tg_emulation_bgp_config(handle=handle[handle_key_v6], mode='enable', ip_version='6', + active_connect_enable='1', local_as=tg_asn, remote_as=local_asn, + remote_ipv6_addr=local_ipaddr, enable_4_byte_as='1') + st.wait(5) + tg.tg_emulation_bgp_control(handle=bgp_rtr1['handle'], mode='start') + + st.log("#"*30) + st.log("bgp_rtr1 = {}".format(bgp_rtr1)) + st.log("#"*30) + return bgp_rtr1 + + +def get_tg_topology_leafspine_bgp(dut_type, max_tg_links, nodes, af='all'): + """ + + :param dut_type: + :param max_tg_links: + :param nodes: + :param af: + :return: + """ + st.banner("Getting TG topology info") + st.log("dut_type {}, max_tg_links {}, nodes {}".format(dut_type, max_tg_links, nodes)) + + rv = [] + if dut_type == '': + return rv + # For 1:1 mode TG handled here, dut_type over written + if 'spine' in ','.join(tg_connected_routers) and nodes >= 2: + dut_type = "leaf-spine" + + final_rv = SpyTestDict() + if dut_type in ['leaf-spine', 'spine-leaf']: + temp = {} + for i, spine in enumerate(data['spine_routers'], start=1): + tg_list = st.get_tg_links(data[spine]) + if int(max_tg_links) <= len(tg_list): + temp[spine] = tg_list + for i, leaf in enumerate(data['leaf_routers'], start=1): + tg_list = st.get_tg_links(data[leaf]) + if int(max_tg_links) <= len(tg_list): + temp[leaf] = tg_list + + if int(nodes) <= len(temp): + for each in temp: + final_rv[each] = random.sample(temp[each][:int(max_tg_links)], k=int(max_tg_links)) + else: + st.log("Requested topology not found.") + return False + while True: + # TODO : Need to check this - 'leaf-spine', 'spine-leaf' + rv = {each: final_rv[each] for each in random.sample(final_rv.keys(), k=int(nodes))} + if 'spine' in str(rv.keys()) and 'leaf' in str(rv.keys()): + break + + elif dut_type == 'spine': + temp = {} + for i, spine in enumerate(data['spine_routers'], start=1): + tg_list = st.get_tg_links(data[spine]) + if int(max_tg_links) <= len(tg_list): + temp[spine] = tg_list + + if int(nodes) <= len(temp): + for each in temp: + final_rv[each] = random.sample(temp[each][:int(max_tg_links)], k=int(max_tg_links)) + else: + st.log("Requested topology not found.") + return False + rv = {each: final_rv[each] for each in random.sample(final_rv.keys(), k=int(nodes))} + + elif dut_type == 'leaf': + temp = {} + for i, leaf in enumerate(data['leaf_routers'], start=1): + tg_list = st.get_tg_links(data[leaf]) + if int(max_tg_links) <= len(tg_list): + temp[leaf] = tg_list + + if int(nodes) <= len(temp): + for each in temp: + final_rv[each] = random.sample(temp[each][:int(max_tg_links)], k=int(max_tg_links)) + else: + st.log("Requested topology not found.") + return False + rv = {each: final_rv[each] for each in random.sample(final_rv.keys(), k=int(nodes))} + + rv = get_leaf_spine_topology_info(rv, af) + return rv + + +def get_leaf_spine_topology_info(input=[], af='all'): + """ + + :param input: + :param af: + :return: + """ + max_mem = int(data['l3_max_tg_links_each_leaf_spine']) + st.banner("Getting topology info") + if not input: + input = {each:[] for each in data['spine_routers']+data['leaf_routers']} + temp = {} + for i, dut_type in enumerate(input, start=1): + if dut_type in tg_connected_routers: + tg_list = st.get_tg_links(data[dut_type]) + if int(max_mem) <= len(tg_list): + temp[dut_type] = tg_list + for each in temp: + input[each] = random.sample(temp[each][:int(max_mem)], k=int(max_mem)) + + st.log("input = {}".format(input)) + debug_print() + + if af == "ipv4": + afmly = ['ipv4'] + elif af == "ipv6": + afmly = ['ipv6'] + else: + afmly = ['ipv4', 'ipv6'] + st.log("afmly = {}".format(afmly)) + + rv = SpyTestDict() + temp = SpyTestDict() + rv['dut_list'] = [] + rv['tg_dut_list'] = [] + rv['tg_dut_list_name'] = [] + rv['leaf_list'] = [] + rv['spine_list'] = [] + for i, dut_type in enumerate(input, start=1): + temp[dut_type] = "D{}".format(i) + rv["D{}_name".format(i)] = dut_type + rv["D{}".format(i)] = data[dut_type] + + # List items + rv['dut_list'].append(data[dut_type]) + if 'leaf' in dut_type: + rv['leaf_list'].append(data[dut_type]) + if 'spine' in dut_type: + rv['spine_list'].append(data[dut_type]) + if dut_type in tg_connected_routers: + rv['tg_dut_list'].append(data[dut_type]) + rv['tg_dut_list_name'].append("D{}".format(i)) + + if tg_info: + for i, dut_type in enumerate(input, start=1): + if dut_type in tg_connected_routers: + for j, each_tgport in enumerate(input[dut_type], start=1): + rv["{}T1P{}".format(temp[dut_type], j)] = each_tgport[0] + rv["T1{}P{}".format(temp[dut_type], j)] = each_tgport[2] + for each_af in afmly: + rv["T1{}P{}_tg_obj".format(temp[dut_type], j)] = tg_info[dut_type][each_af][each_tgport[0]][0] + rv["T1{}P{}_{}_tg_ph".format(temp[dut_type], j, each_af)] = \ + tg_info[dut_type][each_af][each_tgport[0]][1] + rv["T1{}P{}_{}_tg_ih".format(temp[dut_type], j, each_af)] = \ + tg_info[dut_type][each_af][each_tgport[0]][2] + rv["T1{}P{}_{}_neigh".format(temp[dut_type], j, each_af)] = \ + tg_info[dut_type][each_af][each_tgport[0]][3][0] + rv["T1{}P{}_{}".format(temp[dut_type], j, each_af)] = \ + tg_info[dut_type][each_af][each_tgport[0]][3][1] + rv["T1{}P{}_{}_tg_bh".format(temp[dut_type], j, each_af)] = \ + tg_info[dut_type][each_af][each_tgport[0]][4] + + if topo_info: + for i, dut_type in enumerate(input, start=1): + for each_af in afmly: + if each_af == "ipv4": + list_of_port = [each for each in topo_info[each_af][dut_type] if each[2] in temp] + for each_port in list_of_port: + k = each_port[5] + rv["{}{}P{}".format(temp[dut_type], temp[each_port[2]], k)] = each_port[0] + rv["{}{}P{}_ipv4".format(temp[dut_type], temp[each_port[2]], k)] = each_port[1] + rv["{}{}P{}_neigh".format(temp[dut_type], temp[each_port[2]], k)] = data[each_port[2]] + rv["{}{}P{}_neigh_ipv4".format(temp[dut_type], temp[each_port[2]], k)] = each_port[4] + rv["{}{}P{}_neigh_port".format(temp[dut_type], temp[each_port[2]], k)] = each_port[3] + + rv["{}{}P{}".format(temp[each_port[2]], temp[dut_type], k)] = each_port[3] + rv["{}{}P{}_ipv4".format(temp[each_port[2]], temp[dut_type], k)] = each_port[4] + rv["{}{}P{}_neigh".format(temp[each_port[2]], temp[dut_type], k)] = data[dut_type] + rv["{}{}P{}_neigh_ipv4".format(temp[each_port[2]], temp[dut_type], k)] = each_port[1] + rv["{}{}P{}_neigh_port".format(temp[each_port[2]], temp[dut_type], k)] = each_port[0] + + if each_af == "ipv6": + list_of_port = [each for each in topo_info[each_af][dut_type] if each[2] in temp] + for each_port in list_of_port: + k = each_port[5] + rv["{}{}P{}".format(temp[dut_type], temp[each_port[2]], k)] = each_port[0] + rv["{}{}P{}_ipv6".format(temp[dut_type], temp[each_port[2]], k)] = each_port[1] + rv["{}{}P{}_neigh".format(temp[dut_type], temp[each_port[2]], k)] = data[each_port[2]] + rv["{}{}P{}_neigh_ipv6".format(temp[dut_type], temp[each_port[2]], k)] = each_port[4] + rv["{}{}P{}_neigh_port".format(temp[dut_type], temp[each_port[2]], k)] = each_port[3] + + rv["{}{}P{}".format(temp[each_port[2]], temp[dut_type], k)] = each_port[3] + rv["{}{}P{}_ipv6".format(temp[each_port[2]], temp[dut_type], k)] = each_port[4] + rv["{}{}P{}_neigh".format(temp[each_port[2]], temp[dut_type], k)] = data[dut_type] + rv["{}{}P{}_neigh_ipv6".format(temp[each_port[2]], temp[dut_type], k)] = each_port[1] + rv["{}{}P{}_neigh_port".format(temp[each_port[2]], temp[dut_type], k)] = each_port[0] + + for i, dut_type in enumerate(input, start=1): + if as_info: + rv["{}_as".format(temp[dut_type])] = as_info[dut_type] + for each_af in afmly: + if loopback_info: + if each_af == "ipv4": + rv["{}_loopback_ipv4".format(temp[dut_type])] = loopback_info[each_af][dut_type] + if each_af == "ipv6": + rv["{}_loopback_ipv6".format(temp[dut_type])] = loopback_info[each_af][dut_type] + + st.log(pprint.pformat(rv, width=2)) + return rv + + +def get_topo_info(): + """ + + :return: + """ + return topo_info + + +def get_tg_info(): + """ + + :return: + """ + return tg_info + + +def get_loopback_info(): + """ + + :return: + """ + return loopback_info + + +def get_as_info(): + """ + + :return: + """ + return as_info + + +def get_static_rt_info(): + """ + + :return: + """ + return static_rt_info + + +def get_fixed_nw_info(): + """ + + :return: + """ + return fixed_nw_info + + +def get_underlay_info(): + """ + + :return: + """ + return underlay_info + + +def get_route_attribute(output, parameter, **kwargs): + st.log("GET ROUTE ATTR -- {}".format(output)) + st.log("PARAMS -- {}".format(parameter)) + st.log("KWARGS -- {}".format(kwargs)) + nw_route = filter_and_select(output, [parameter], kwargs) + st.log("NW_ROUTE -- {}".format(nw_route)) + if not nw_route: + st.report_fail("entry_not_found") + return nw_route[0][parameter] + + +def debug_print(): + """ + + :return: + """ + st.log("get_tg_info(): \n{} ".format(get_tg_info())) + st.log("get_topo_info(): \n{} ".format(get_topo_info())) + st.log("get_as_info(): \n{} ".format(get_as_info())) + st.log("get_loopback_info(): \n{} ".format(get_loopback_info())) + st.log("get_static_rt_info(): \n{} ".format(get_static_rt_info())) + st.log("get_fixed_nw_info(): \n{} ".format(get_fixed_nw_info())) + st.log("get_underlay_info(): \n{} ".format(get_underlay_info())) + + +def configure_base_for_route_adv_and_filter(dut1, dut2, topo, config_items): + """ + + :param dut1: + :param dut2: + :param topo: + :param config_items: + :return: + """ + + use_global_rmap = rmapapi.RouteMap("UseGlobal") + use_global_rmap.add_permit_sequence('10') + use_global_rmap.add_sequence_set_ipv6_next_hop_prefer_global('10') + + config_items['dut1'] = [] + config_items['dut2'] = [] + + bgpapi.config_bgp_network_advertise(dut1, topo['dut1_as'], '101.1.1.0/24') + bgpapi.config_bgp_network_advertise(dut1, topo['dut1_as'], '102.1.1.0/24') + bgpapi.config_bgp_network_advertise(dut1, topo['dut1_as'], '101:1::/64', addr_family='ipv6') + bgpapi.config_bgp_network_advertise(dut1, topo['dut1_as'], '102:1::/64', addr_family='ipv6') + + bgpapi.config_bgp_network_advertise(dut2, topo['dut2_as'], '201.1.1.0/24') + bgpapi.config_bgp_network_advertise(dut2, topo['dut2_as'], '202.1.1.0/24') + bgpapi.config_bgp_network_advertise(dut2, topo['dut2_as'], '201:1::/64', addr_family='ipv6') + bgpapi.config_bgp_network_advertise(dut2, topo['dut2_as'], '202:1::/64', addr_family='ipv6') + + use_global_rmap.execute_command(dut1) + config_items['dut1'].append(use_global_rmap) + + bgpapi.config_bgp(dut=dut1, local_as=topo['dut1_as'], addr_family='ipv6', + config='yes', + neighbor=topo['dut2_addr_ipv6'], + config_type_list=["routeMap"], routeMap='UseGlobal', diRection='in') + + use_global_rmap.execute_command(dut2) + config_items['dut2'].append(use_global_rmap) + + bgpapi.config_bgp(dut=dut2, local_as=topo['dut2_as'], addr_family='ipv6', + config='yes', + neighbor=topo['dut1_addr_ipv6'], + config_type_list=["routeMap"], routeMap='UseGlobal', diRection='in') + + leaf_ip_acl_11 = ipapi.AccessList("11") + leaf_ip_acl_11.add_match_deny_sequence('102.1.1.0/24') + leaf_ip_acl_11.add_match_permit_sequence('any') + + leaf_ip_acl_12 = ipapi.AccessList("12", family='ipv6') + leaf_ip_acl_12.add_match_deny_sequence('102:1::/64') + leaf_ip_acl_12.add_match_permit_sequence('any') + + leaf_prefix_list_202 = ipapi.PrefixList("PREFIXOUT") + leaf_prefix_list_202.add_match_deny_sequence('202.1.1.0/24') + leaf_prefix_list_202.add_match_permit_sequence('any') + + leaf_prefix_list_202_v6 = ipapi.PrefixList("PREFIXOUT6", family='ipv6') + leaf_prefix_list_202_v6.add_match_deny_sequence('202:1::/64') + leaf_prefix_list_202_v6.add_match_permit_sequence('any') + + aspath_acl = bgpapi.ASPathAccessList("FILTER") + aspath_acl.add_match_deny_sequence(['{}'.format(topo['dut1_as'])]) + + leaf1_cmd = leaf_ip_acl_11.config_command_string() + leaf_ip_acl_12.config_command_string() \ + + leaf_prefix_list_202.config_command_string() + leaf_prefix_list_202_v6.config_command_string() \ + + aspath_acl.config_command_string() + config_items['dut2'].append(leaf_ip_acl_11) + config_items['dut2'].append(leaf_ip_acl_12) + config_items['dut2'].append(leaf_prefix_list_202) + config_items['dut2'].append(leaf_prefix_list_202_v6) + config_items['dut2'].append(aspath_acl) + + leaf_prefix_list_101 = ipapi.PrefixList("MATCHPREFIX1") + leaf_prefix_list_101.add_match_permit_sequence('101.1.1.0/24') + leaf_prefix_list_101.add_match_deny_sequence('any') + leaf_prefix_list_102 = ipapi.PrefixList("MATCHPREFIX2") + leaf_prefix_list_102.add_match_permit_sequence('102.1.1.0/24') + leaf_prefix_list_102.add_match_deny_sequence('any') + leaf_prefix_list_101_v6 = ipapi.PrefixList("MATCHPREFIX61", family='ipv6') + leaf_prefix_list_101_v6.add_match_permit_sequence('101:1::/64') + leaf_prefix_list_101_v6.add_match_deny_sequence('any') + leaf_prefix_list_102_v6 = ipapi.PrefixList("MATCHPREFIX62", family='ipv6') + leaf_prefix_list_102_v6.add_match_permit_sequence('102:1::/64') + leaf_prefix_list_102_v6.add_match_deny_sequence('any') + + leaf1_cmd += leaf_prefix_list_101.config_command_string() + leaf_prefix_list_102.config_command_string() \ + + leaf_prefix_list_101_v6.config_command_string() + leaf_prefix_list_102_v6.config_command_string() + config_items['dut2'].append(leaf_prefix_list_101) + config_items['dut2'].append(leaf_prefix_list_102) + config_items['dut2'].append(leaf_prefix_list_101_v6) + config_items['dut2'].append(leaf_prefix_list_102_v6) + + leaf_rmap_setprops = rmapapi.RouteMap('SETPROPS') + leaf_rmap_setprops.add_permit_sequence('10') + leaf_rmap_setprops.add_sequence_match_prefix_list('10', 'MATCHPREFIX1') + leaf_rmap_setprops.add_sequence_set_local_preference('10', '200') + leaf_rmap_setprops.add_permit_sequence('20') + leaf_rmap_setprops.add_sequence_match_prefix_list('20', 'MATCHPREFIX2') + leaf_rmap_setprops.add_sequence_set_metric('20', '400') + + leaf_rmap_setprops_v6 = rmapapi.RouteMap('SETPROPS6') + leaf_rmap_setprops_v6.add_permit_sequence('10') + leaf_rmap_setprops_v6.add_sequence_set_ipv6_next_hop_prefer_global('10') + leaf_rmap_setprops_v6.add_sequence_match_prefix_list('10', 'MATCHPREFIX61', family='ipv6') + leaf_rmap_setprops_v6.add_sequence_set_local_preference('10', '6200') + leaf_rmap_setprops_v6.add_permit_sequence('20') + leaf_rmap_setprops_v6.add_sequence_set_ipv6_next_hop_prefer_global('20') + leaf_rmap_setprops_v6.add_sequence_match_prefix_list('20', 'MATCHPREFIX62', family='ipv6') + leaf_rmap_setprops_v6.add_sequence_set_metric('20', '6400') + + leaf1_cmd += leaf_rmap_setprops.config_command_string() + leaf_rmap_setprops_v6.config_command_string() + config_items['dut2'].append(leaf_rmap_setprops) + config_items['dut2'].append(leaf_rmap_setprops_v6) + + st.vtysh_config(dut2, leaf1_cmd) + + +def unconfigure_base_for_route_adv_and_filter(dut1, dut2, topo, config_items): + """ + + :param dut1: + :param dut2: + :param topo: + :param config_items: + :return: + """ + bgpapi.config_bgp_network_advertise(dut1, topo['dut1_as'], '101.1.1.0/24', config='no') + bgpapi.config_bgp_network_advertise(dut1, topo['dut1_as'], '102.1.1.0/24', config='no') + bgpapi.config_bgp_network_advertise(dut1, topo['dut1_as'], '101:1::/64', addr_family='ipv6', config='no') + bgpapi.config_bgp_network_advertise(dut1, topo['dut1_as'], '102:1::/64', addr_family='ipv6', config='no') + + bgpapi.config_bgp_network_advertise(dut2, topo['dut2_as'], '201.1.1.0/24', config='no') + bgpapi.config_bgp_network_advertise(dut2, topo['dut2_as'], '202.1.1.0/24', config='no') + bgpapi.config_bgp_network_advertise(dut2, topo['dut2_as'], '201:1::/64', addr_family='ipv6', config='no') + bgpapi.config_bgp_network_advertise(dut2, topo['dut2_as'], '202:1::/64', addr_family='ipv6', config='no') + + bgpapi.config_bgp(dut=dut1, local_as=topo['dut1_as'], addr_family='ipv6', + config='no', + neighbor=topo['dut2_addr_ipv6'], + config_type_list=["routeMap"], routeMap='UseGlobal', diRection='in') + + bgpapi.config_bgp(dut=dut2, local_as=topo['dut2_as'], addr_family='ipv6', + config='no', + neighbor=topo['dut1_addr_ipv6'], + config_type_list=["routeMap"], routeMap='UseGlobal', diRection='in') + + leaf1_cmd = '' + for item in reversed(config_items['dut2']): + leaf1_cmd += item.unconfig_command_string() + + spine1_cmd = '' + for item in reversed(config_items['dut1']): + spine1_cmd += item.unconfig_command_string() + + st.vtysh_config(dut2, leaf1_cmd) + st.vtysh_config(dut1, spine1_cmd) + + +def show_bgp_neighbors(dut, af='ipv4'): + if af in ['ipv4', 'both']: + utils.exec_foreach(True, utils.make_list(dut), bgpapi.show_bgp_ipv4_neighbor_vtysh) + if af in ['ipv6', 'both']: + utils.exec_foreach(True, utils.make_list(dut), bgpapi.show_bgp_ipv6_neighbor_vtysh) diff --git a/spytest/tests/routing/BGP/bgpsplib.py b/spytest/tests/routing/BGP/bgpsplib.py new file mode 100644 index 00000000000..266bfcdc524 --- /dev/null +++ b/spytest/tests/routing/BGP/bgpsplib.py @@ -0,0 +1,4344 @@ +# BGP SP Topology APIs +# Author: Naveena Suvarna (naveen.suvarna@broadcom.com) + +import copy + +from spytest import st, utils +from spytest.dicts import SpyTestDict +import apis.routing.ip as ipapi +import apis.routing.bgp as bgpapi +import apis.system.interface as ifapi +from spytest.tgen.tg import tgen_obj_dict +import BGP.bgplib as bgplib + +global sp_topo, bgp_topo +sp_topo = SpyTestDict() +bgp_topo = SpyTestDict() + + +class BGPSP: + + + @staticmethod + def bgp_sp_topology_data_present(): + if not sp_topo['dut_list'] or len(sp_topo['dut_list']) == 0 : + return False + return True + + + @staticmethod + def bgp_sp_dut_present(dut): + if dut in sp_topo['dut_list'] : + return True + if dut in sp_topo['tg_list'] : + return True + return False + + + @staticmethod + def bgp_sp_dut_list_present(dut_name_list = []): + + if not dut_name_list or len(dut_name_list) == 0 : + return False + for dut_name in dut_name_list: + if dut_name not in sp_topo['dut_list'] : + return False + return True + + + @staticmethod + def bgp_sp_get_dut_count(): + return len (sp_topo['dut_list']) + + + @staticmethod + def bgp_sp_get_dut_list(): + return copy.deepcopy(sp_topo['dut_list']) + + + @staticmethod + def bgp_sp_get_dut_from_device(device_name): + + for dut in sp_topo['dut_list'] : + if device_name == sp_topo[dut]['device'] : + st.log("BGP SP - DUT device {} is dut {}".format(device_name, dut)) + return dut + for dut in sp_topo['tg_list'] : + if device_name == sp_topo[dut]['device'] : + st.log("BGP SP - TG device {} is dut {}".format(device_name, dut)) + return dut + st.log("BGP SP - device {} not in dut list".format(device_name)) + return "" + + + @staticmethod + def bgp_sp_get_dut_device(dut): + + if dut in sp_topo['dut_list'] : + return sp_topo[dut]['device'] + return '' + + + @staticmethod + def bgp_sp_get_tg_list(): + return copy.deepcopy(sp_topo['tg_list']) + + + @staticmethod + def bgp_sp_dut_is_tg(dut): + + if dut in sp_topo['tg_list'] : + if dut in sp_topo.keys() : + if sp_topo[dut]['type'] == 'TG' : + return True + return False + + + @staticmethod + def bgp_sp_valid_link_type(link_type): + + if link_type == "ETH" : + return True + if link_type == "LBK" : + return True + return False + + + @staticmethod + def bgp_sp_dut_link_present(dut, link_name): + + if not BGPSP.bgp_sp_dut_present(dut) : + st.log("BGP SP - Link dut {} not in dut list".format(dut)) + return False + + if link_name not in sp_topo[dut]['intf'].keys(): + return False + + return True + + + @staticmethod + def bgp_sp_link_present(link_name): + + if not link_name or link_name == '' : + return False + for dut in BGPSP.bgp_sp_get_dut_list(): + if link_name in sp_topo[dut]['intf'].keys(): + return True + for dut in BGPSP.bgp_sp_get_tg_list(): + if link_name in sp_topo[dut]['intf'].keys(): + return True + return False + + + @staticmethod + def bgp_sp_link_list_present(link_name_list = []): + + if not link_name_list or len(link_name_list) == 0 : + return False + + topo_links = sp_topo[dut]['intf'].keys() + + for link_name in link_name_list: + if link_name not in topo_links : + return False + return True + + + @staticmethod + def bgp_sp_dut_get_all_links(dut): + + if not BGPSP.bgp_sp_dut_present(dut): + return [] + + link_name_list = [] + for link_name, link_data in sp_topo[dut]['intf'].items(): + if link_data['type'] == 'LBK' : + continue + link_name_list.append(link_name) + + return copy.deepcopy(link_name_list) + + + @staticmethod + def bgp_sp_get_link_dut_interface(dut, link_name): + + if BGPSP.bgp_sp_dut_link_present(dut, link_name): + if_data = sp_topo[dut]['intf'][link_name] + if 'if' in if_data.keys(): + return if_data['if'] + + return '' + + + @staticmethod + def bgp_sp_dut_link_connected(dut, link_name): + + if BGPSP.bgp_sp_dut_link_present(dut, link_name): + if_data = sp_topo[dut]['intf'][link_name] + if 'rmt_dut' in if_data.keys(): + if 'rmt_link' in if_data.keys(): + return True + + return False + + + @staticmethod + def bgp_sp_dut_get_remote_dut(dut, link_name): + + if BGPSP.bgp_sp_dut_link_present(dut, link_name): + if_data = sp_topo[dut]['intf'][link_name] + if 'rmt_dut' in if_data.keys(): + return sp_topo[dut]['intf'][link_name]['rmt_dut'] + + return '' + + + @staticmethod + def bgp_sp_dut_get_remote_link(dut, link_name): + + if BGPSP.bgp_sp_dut_link_present(dut, link_name): + if_data = sp_topo[dut]['intf'][link_name] + if 'rmt_dut' in if_data.keys(): + if 'rmt_link' in if_data.keys(): + return sp_topo[dut]['intf'][link_name]['rmt_link'] + + return '' + + @staticmethod + def bgp_sp_is_tg_connected_link(dut, link_name): + + rmt_dut = BGPSP.bgp_sp_dut_get_remote_dut(dut, link_name) + if rmt_dut == '' : + return False + + if BGPSP.bgp_sp_dut_is_tg(rmt_dut): + return True + + return False + + + @staticmethod + def bgp_sp_dut_get_tg_connected_links(dut): + + if not BGPSP.bgp_sp_dut_present(dut): + return [] + + link_name_list = [] + for link_name, link_data in sp_topo[dut]['intf'].items(): + if 'rmt_dut' in link_data.keys(): + rmt_dut = link_data['rmt_dut'] + if BGPSP.bgp_sp_dut_is_tg(rmt_dut): + link_name_list.append(link_name) + + return link_name_list + + + @staticmethod + def bgp_sp_dut_get_tg_connected_link_data(dut, link_name): + + if not BGPSP.bgp_sp_is_tg_connected_link(dut, link_name): + return {} + + link_data = sp_topo[dut]['intf'][link_name] + rmt_dut = link_data['rmt_dut'] + if BGPSP.bgp_sp_dut_is_tg(rmt_dut): + return copy.deepcopy(link_data) + + return {} + + + @staticmethod + def bgp_sp_dut_get_connected_first_link(from_dut, to_dut): + + if not BGPSP.bgp_sp_dut_present(from_dut): + return '' + + if not BGPSP.bgp_sp_dut_present(to_dut): + return '' + + for link_name, link_data in sp_topo[from_dut]['intf'].items(): + if 'rmt_dut' in link_data.keys(): + if link_data['rmt_dut'] == to_dut : + if 'rmt_link' in link_data.keys(): + return link_name + + return '' + + + @staticmethod + def bgp_sp_dut_get_connected_links(from_dut, to_dut): + + if not BGPSP.bgp_sp_dut_present(from_dut): + return [] + + if not BGPSP.bgp_sp_dut_present(to_dut): + return [] + + link_name_list = [] + for link_name, link_data in sp_topo[from_dut]['intf'].items(): + if 'rmt_dut' in link_data.keys(): + if link_data['rmt_dut'] == to_dut : + if 'rmt_link' in link_data.keys(): + link_name_list.append(link_name) + + return link_name_list + + + @staticmethod + def bgp_sp_dut_link_connected_to_each_other(from_dut, from_link, to_dut, to_link): + + if not BGPSP.bgp_sp_dut_link_connected(from_dut, from_link): + return False + + if not BGPSP.bgp_sp_dut_link_connected(to_dut, to_link): + return False + + from_if_info = sp_topo[from_dut]['intf'][from_link] + to_if_info = sp_topo[to_dut]['intf'][to_link] + + if from_if_info['rmt_dut'] != to_dut: + return False + if to_if_info['rmt_dut'] != from_dut: + return False + if from_if_info['rmt_link'] != to_link: + return False + if to_if_info['rmt_link'] == from_link : + return False + + return True + + + @staticmethod + def bgp_sp_get_unused_dut_interface(dut): + + if not BGPSP.bgp_sp_dut_present(dut): + st.log("BGP SP - not present in {}".format(dut)) + return '' + + dut_if_list = [] + for link_name, link_data in sp_topo[dut]['intf'].items(): + if 'if' in link_data.keys(): + dut_if_list.append(link_data['if']) + + if_idx = 80 + while if_idx < 100: + if_name = "Ethernet{}".format(if_idx) + if if_name not in dut_if_list : + st.log("BGP SP - Found unused interface {} in dut {}".format(if_name, dut)) + return copy.deepcopy(if_name) + if_idx += 4 + + st.log("BGP SP - No unused interfaces in {}".format(dut)) + return '' + + + @staticmethod + def bgp_sp_addr_family_valid(addr_family): + + if addr_family != 'ipv4' and addr_family != 'ipv6' : + st.log("BGP SP - Invalid address family {}".format(addr_family)) + return False + return True + + + @staticmethod + def bgp_sp_get_address_family_list(addr_family): + + addr_family_list = [] + if addr_family == 'ipv4' or addr_family == 'all': + addr_family_list.append('ipv4') + if addr_family == 'ipv6' or addr_family == 'all': + addr_family_list.append('ipv6') + return addr_family_list + + + @staticmethod + def bgp_sp_ip_prefix_to_route_prefix(prefix, addr_family): + + route_prefix = prefix + + if not BGPSP.bgp_sp_addr_family_valid(addr_family) : + st.log("BGP SP - Invalid address family {}".format(addr_family)) + return route_prefix + + if addr_family == 'ipv6' : + temp_prefix = prefix.partition(":0/") + if temp_prefix and len(temp_prefix) == 3 and temp_prefix[1] == ":0/" : + route_prefix = "{}:/{}".format(temp_prefix[0], temp_prefix[2]) + + return route_prefix + + + @staticmethod + def bgp_sp_ip_prefix_list_to_route_prefix_list(prefix_list, addr_family): + + route_prefix_list = [] + + if not BGPSP.bgp_sp_addr_family_valid(addr_family) : + st.log("BGP SP - Invalid address family {}".format(addr_family)) + return route_prefix_list + + for prefix in prefix_list : + route_prefix = BGPSP.bgp_sp_ip_prefix_to_route_prefix(prefix, addr_family) + if route_prefix != '': + route_prefix_list.append(route_prefix) + + #st.log("BGP SP - route_prefix list {}".format(route_prefix_list)) + return copy.deepcopy(route_prefix_list) + + + @staticmethod + def bgp_sp_dut_ip_link_present(dut, link_name, addr_family): + + if not BGPSP.bgp_sp_addr_family_valid(addr_family) : + st.log("BGP SP - Invalid address family {}".format(addr_family)) + return False + + if BGPSP.bgp_sp_dut_link_present(dut, link_name): + if link_name in sp_topo[dut][addr_family]['link'].keys(): + return True + + return False + + + @staticmethod + def bgp_sp_dut_get_ip_link(dut, link_name, addr_family): + + if not BGPSP.bgp_sp_addr_family_valid(addr_family) : + st.log("BGP SP - Invalid address family {}".format(addr_family)) + return {} + + if BGPSP.bgp_sp_dut_link_present(dut, link_name): + if link_name in sp_topo[dut][addr_family]['link'].keys(): + ip_data = sp_topo[dut][addr_family]['link'][link_name] + return copy.deepcopy(ip_data) + + return {} + + + @staticmethod + def bgp_sp_dut_link_has_ip(dut, link_name, addr_family): + + if not BGPSP.bgp_sp_addr_family_valid(addr_family) : + st.log("BGP SP - Invalid address family {}".format(addr_family)) + return False + + if BGPSP.bgp_sp_dut_link_present(dut, link_name): + if link_name in sp_topo[dut][addr_family]['link'].keys(): + ip_data = sp_topo[dut][addr_family]['link'][link_name] + if 'ip' in ip_data.keys(): + return True + + st.log("BGP SP - {} {} doesnot have ip address".format(dut, link_name)) + return False + + + @staticmethod + def bgp_sp_dut_get_link_local_ip(dut, link_name, addr_family): + + if not BGPSP.bgp_sp_addr_family_valid(addr_family) : + st.log("BGP SP - Invalid address family {}".format(addr_family)) + return False + + st.log("BGP SP - Find local ip {} {} {}".format(dut, link_name, addr_family)) + if BGPSP.bgp_sp_dut_link_present(dut, link_name): + if link_name in sp_topo[dut][addr_family]['link'].keys(): + ip_data = sp_topo[dut][addr_family]['link'][link_name] + if 'ip' in ip_data.keys(): + return ip_data['ip'] + + st.log("BGP SP - {} {} doesnot have local ip address".format(dut, link_name)) + return "" + + + @staticmethod + def bgp_sp_dut_get_link_remote_ip(dut, link_name, addr_family): + + if not BGPSP.bgp_sp_addr_family_valid(addr_family) : + st.log("BGP SP - Invalid address family {}".format(addr_family)) + return False + + if BGPSP.bgp_sp_dut_link_present(dut, link_name): + if link_name in sp_topo[dut][addr_family]['link'].keys(): + ip_data = sp_topo[dut][addr_family]['link'][link_name] + if 'rmt_ip' in ip_data.keys(): + return ip_data['rmt_ip'] + + st.log("BGP SP - {} {} doesnot have local remote address".format(dut, link_name)) + return "" + + + @staticmethod + def bgp_sp_get_dut_loopback_ip(dut, lpbk_num, addr_family): + + if not BGPSP.bgp_sp_addr_family_valid(addr_family) : + st.log("BGP SP - Invalid address family {}".format(addr_family)) + return "" + + link_name = "{}L{}".format(dut, lpbk_num) + + if not BGPSP.bgp_sp_dut_link_present(dut, link_name): + st.log("BGP SP - Link {} not in intf list".format(link_name)) + return '' + + if link_name in sp_topo[dut][addr_family]['link'].keys(): + ip_data = sp_topo[dut][addr_family]['link'][link_name] + if 'ip' not in ip_data.keys(): + st.log("BGP SP - {} doesnt have ip address".format(link_name)) + return '' + + return ip_data['ip'] + + return '' + + + @staticmethod + def bgp_sp_get_dut_loopback_ip_list(dut, addr_family): + + if not BGPSP.bgp_sp_addr_family_valid(addr_family) : + return [] + + if not BGPSP.bgp_sp_dut_present(dut): + st.log("BGP SP - Dut {} not present".format(dut)) + return [] + + lpbk_ip_list = [] + for link_name, ip_data in sp_topo[dut][addr_family]['link'].items(): + if ip_data['type'] == 'LBK' : + if 'ip' in ip_data.keys(): + lpbk_ip_list.append(ip_data['ip']) + + return copy.deepcopy(lpbk_ip_list) + + + @staticmethod + def bgp_sp_get_loopback_ip_in_dut_list(dut_list=[], addr_family='ipv4'): + + if not BGPSP.bgp_sp_addr_family_valid(addr_family) : + return [] + + lpbk_ip_list = [] + + for dut in dut_list: + if not BGPSP.bgp_sp_dut_present(dut): + continue + + for link_name, ip_data in sp_topo[dut][addr_family]['link'].items(): + if ip_data['type'] == 'LBK' : + if 'ip' in ip_data.keys(): + if ip_data['ip'] not in lpbk_ip_list: + lpbk_ip_list.append(ip_data['ip']) + + return copy.deepcopy(lpbk_ip_list) + + + @staticmethod + def bgp_sp_get_dut_ip_address_list(dut, addr_family, vrf='default'): + + if not BGPSP.bgp_sp_addr_family_valid(addr_family) : + return [] + + if not BGPSP.bgp_sp_dut_present(dut): + st.log("BGP SP - Dut {} not present".format(dut)) + return [] + + ip_addr_list = [] + for link_name, ip_data in sp_topo[dut][addr_family]['link'].items(): + if 'ip' in ip_data.keys(): + ip_addr_list.append(ip_data['ip']) + + st.log("BGP SP - Dut {} has host ip {}".format(dut, ip_addr_list)) + return copy.deepcopy(ip_addr_list) + + + @staticmethod + def bgp_sp_get_dut_static_network_prefixes(dut, addr_family): + + if not BGPSP.bgp_sp_dut_present(dut): + st.log("BGP SP - Dut {} not present".format(dut)) + return [] + + if not BGPSP.bgp_sp_addr_family_valid(addr_family): + return [] + + snw_list = [] + for prefix, snw_data in sp_topo[dut][addr_family]['static_nw'].items() : + prefix_subnet = "{}/{}".format(prefix, snw_data['subnet']) + snw_list.append(prefix_subnet) + + return copy.deepcopy(snw_list) + + + @staticmethod + def bgp_sp_get_dut_static_route_prefixes(dut, addr_family): + + if not BGPSP.bgp_sp_dut_present(dut): + st.log("BGP SP - Dut {} not present".format(dut)) + return [] + + if not BGPSP.bgp_sp_addr_family_valid(addr_family): + return [] + + srtp_list = [] + for prefix, rt_data in sp_topo[dut][addr_family]['static_rt'].items() : + prefix_subnet = "{}/{}".format(prefix, rt_data['subnet']) + srtp_list.append(prefix_subnet) + + return copy.deepcopy(srtp_list) + + + @staticmethod + def bgp_sp_get_dut_null_nhop_static_route_prefixes(dut, addr_family): + + if not BGPSP.bgp_sp_dut_present(dut): + st.log("BGP SP - Dut {} not present".format(dut)) + return [] + + if not BGPSP.bgp_sp_addr_family_valid(addr_family): + return [] + + srtp_list = [] + for prefix, rt_data in sp_topo[dut][addr_family]['static_rt'].items() : + if rt_data['nexthop'] == 'Null0' : + prefix_subnet = "{}/{}".format(prefix, rt_data['subnet']) + srtp_list.append(prefix_subnet) + + return copy.deepcopy(srtp_list) + + + @staticmethod + def bgp_sp_get_dut_static_route_prefix_data_list(dut, addr_family): + + if not BGPSP.bgp_sp_dut_present(dut): + st.log("BGP SP - Dut {} not present".format(dut)) + return {} + + if not BGPSP.bgp_sp_addr_family_valid(addr_family): + return {} + + srtp_data_list = {} + for prefix, rt_data in sp_topo[dut][addr_family]['static_rt'].items() : + srtp_data_list.update({prefix: rt_data}) + + return copy.deepcopy(srtp_data_list) + + + @staticmethod + def bgp_sp_find_linear_topo_in_dut_list(dut_list=[], start_dut='', node_limit=0, save_path='yes'): + + st.log("BGP SP - Find Linear Topo in Dut list {} length {}".format(dut_list, node_limit)) + sp_topo_dut_list = BGPSP.bgp_sp_get_dut_list() + + found_path = {} + found_path['found'] = False + + if not dut_list or len(dut_list) == 0 : + dut_list = sp_topo_dut_list + else : + for dut in dut_list : + if dut not in sp_topo_dut_list : + st.log("Dut {} not in Topo dut lidt {}".format(dut, sp_topo_dut_list)) + return found_path + + if start_dut and start_dut != '' : + if start_dut not in dut_list : + st.log("Start dut {} not in dut list {}".format(start_dut, dut_list)) + return found_path + + if node_limit <= 0 : + length_limit = len (dut_list) + else : + length_limit = node_limit + + st.log("Modified Dut list {} length_limit {}".format(dut_list, length_limit)) + + longest_path = [] + + for dut in dut_list : + + if start_dut and start_dut != '' and start_dut != dut : + continue + + if BGPSP.bgp_sp_dut_is_tg(dut) : + continue + + st.log(" Starting dut {} ".format(dut)) + + sp_topo_stack = [] + sp_topo_path = [] + sp_topo_stack.append(dut) + + while sp_topo_stack and len(sp_topo_stack) : + + st.log(" sp stack {}".format(sp_topo_stack)) + st.log(" sp path {}".format(sp_topo_path)) + + curr_dut = sp_topo_stack.pop() + sp_topo_path.append(curr_dut) + + leaf_dut = True + for link_name, link_data in sp_topo[curr_dut]['intf'].items(): + if 'rmt_dut' in link_data.keys(): + next_dut = link_data['rmt_dut'] + + if BGPSP.bgp_sp_dut_is_tg(next_dut): + continue + + if next_dut in sp_topo_path : + continue + + if next_dut not in dut_list : + continue + + if next_dut not in sp_topo_stack : + sp_topo_stack.append(next_dut) + + leaf_dut = False + + if len(sp_topo_path) == length_limit : + leaf_dut = True + + if leaf_dut == True : + st.log(" Linear found Dut {} ".format(curr_dut)) + st.log(" Linear found sp path {} ".format(sp_topo_path)) + st.log(" Linear found longest path {} ".format(longest_path)) + + if len(longest_path) < len(sp_topo_path) : + if node_limit > 0 : + if len(sp_topo_path) <= length_limit : + longest_path = copy.deepcopy(sp_topo_path) + st.log(" New longest path set as curr new linear path") + else : + longest_path = copy.deepcopy(sp_topo_path) + st.log(" New longest path set as curr new linear path") + + if len(longest_path) >= length_limit : + st.log(" Path length limit provided {} and reached".format(length_limit)) + break + + sp_topo_path.pop() + + + if len(longest_path) == length_limit : + break + + st.log("BGP SP - Longest path len {} with path {}".format(len(longest_path), longest_path)) + + path_length = len(longest_path) + found_path['found'] = True if path_length else False + + path_length = len(longest_path) + found_path['found'] = True if path_length else False + found_path['dut_list'] = [] + found_path['segment'] = {} + found_path['segment_count'] = 0 + found_path['type'] = 'Linear' + + if found_path['found'] : + for dut in longest_path : + found_path['dut_list'].append(dut) + + from_dut = longest_path[0] + found_path['start_dut'] = from_dut + dut_idx = 1 + while dut_idx < path_length : + to_dut = longest_path[dut_idx] + segt_link_idx = 0 + for link_name, link_data in sp_topo[from_dut]['intf'].items(): + if 'rmt_dut' in link_data.keys(): + if link_data['rmt_dut'] == to_dut : + + rmt_link = link_data['rmt_link'] + segt_link = { 'lcl_dut' : from_dut, 'lcl_link': link_name, + 'rmt_dut' : to_dut, 'rmt_link' : rmt_link } + + if segt_link_idx == 0 : found_path['segment'][dut_idx - 1] = {} + found_path['segment'][dut_idx - 1].update({ segt_link_idx: segt_link}) + + if segt_link_idx == 0: + found_path['segment_count'] += 1 + segt_link_idx += 1 + #st.log(" Path node {} is {}".format(dut_idx - 1, segt_link)) + from_dut = to_dut + dut_idx += 1 + + if save_path == 'yes' : + sp_topo['subtopo']['linear'] = copy.deepcopy(found_path) + + BGPSP.bgp_sp_show_topo_path(found_path) + return found_path + + + @staticmethod + def bgp_sp_dut_get_saved_linear_topo(): + return copy.deepcopy(sp_topo['subtopo']['linear']) + + + @staticmethod + def bgp_sp_find_ring_topo_in_dut_list(dut_list=[], start_dut='', node_limit=0, save_path='yes'): + + st.log("BGP SP - Find Linear Topo in Dut list {} length {}".format(dut_list, node_limit)) + sp_topo_dut_list = BGPSP.bgp_sp_get_dut_list() + + found_path = {} + found_path['found'] = False + + if not dut_list or len(dut_list) == 0 : + dut_list = sp_topo_dut_list + else : + for dut in dut_list : + if dut not in sp_topo_dut_list : + st.log("Dut {} not in Topo dut lidt {}".format(dut, sp_topo_dut_list)) + return found_path + + if start_dut and start_dut != '' : + if start_dut not in dut_list : + st.log("Start dut {} not in dut list {}".format(start_dut, dut_list)) + return found_path + + if node_limit <= 0 : + length_limit = len(dut_list) + 1 + else : + length_limit = node_limit + 1 + + st.log("Modified Dut list {} length_limit {}".format(dut_list, length_limit)) + + longest_path = [] + loop_count = 0 + + for dut in dut_list : + + if length_limit <= 3 : + break + + if start_dut and start_dut != '' and start_dut != dut : + continue + + if BGPSP.bgp_sp_dut_is_tg(dut) : + continue + + st.log(" Starting at dut {} with longest path {}.".format(dut, longest_path)) + + sp_topo_stack = [] + sp_topo_path = [] + sp_topo_stack.append(dut) + + while sp_topo_stack and len(sp_topo_stack) : + + loop_count += 1 + if loop_count > 100 : + break + + st.log(" sp stack {}".format(sp_topo_stack)) + st.log(" sp path {}".format(sp_topo_path)) + + curr_dut = sp_topo_stack.pop() + sp_topo_path.append(curr_dut) + + st.log(" modified sp path {}".format(sp_topo_path)) + + leaf_dut = True + ring_found = False + + for link_name, link_data in sp_topo[curr_dut]['intf'].items(): + if 'rmt_dut' in link_data.keys(): + next_dut = link_data['rmt_dut'] + + if next_dut == dut : + ring_found = True + + if BGPSP.bgp_sp_dut_is_tg(next_dut): + continue + + if next_dut not in dut_list : + continue + + if next_dut in sp_topo_path : + continue + + if next_dut not in sp_topo_stack : + sp_topo_stack.append(next_dut) + + leaf_dut = False + + if ring_found : + st.log(" Ring found Dut {} ".format(curr_dut)) + st.log(" Ring found sp path {} ".format(sp_topo_path)) + st.log(" Ring found longest path {} ".format(longest_path)) + + if len(sp_topo_path) > 2 : + + sp_topo_path.append(dut) + + st.log(" new ring sp path {} ".format(sp_topo_path)) + st.log(" ring longest path {} ".format(longest_path)) + + if len(longest_path) < len(sp_topo_path) : + if node_limit > 0 : + if len(sp_topo_path) <= length_limit : + longest_path = copy.deepcopy(sp_topo_path) + st.log(" New longest path set as curr new ring sp path") + else : + longest_path = copy.deepcopy(sp_topo_path) + st.log(" New longest path set as curr new ring sp path") + + if len(longest_path) >= length_limit : + st.log(" Path length limit provided {} and reached".format(length_limit)) + break + + sp_topo_path.pop() + + if leaf_dut == True : + sp_topo_path.pop() + + if len(longest_path) == length_limit : + break + + st.log("BGP SP - Longest path len {} with path {}".format(len(longest_path), longest_path)) + + path_length = len(longest_path) + found_path['found'] = True if path_length else False + found_path['dut_list'] = [] + found_path['segment'] = {} + found_path['segment_count'] = 0 + found_path['type'] = 'Ring' + + if found_path['found'] : + for dut in longest_path : + found_path['dut_list'].append(dut) + + from_dut = longest_path[0] + found_path['start_dut'] = from_dut + dut_idx = 1 + while dut_idx < path_length : + to_dut = longest_path[dut_idx] + segt_link_idx = 0 + for link_name, link_data in sp_topo[from_dut]['intf'].items(): + if 'rmt_dut' in link_data.keys(): + if link_data['rmt_dut'] == to_dut : + + rmt_link = link_data['rmt_link'] + segt_link = { 'lcl_dut' : from_dut, 'lcl_link': link_name, + 'rmt_dut' : to_dut, 'rmt_link' : rmt_link } + + if segt_link_idx == 0 : found_path['segment'][dut_idx - 1] = {} + found_path['segment'][dut_idx - 1].update({ segt_link_idx: segt_link}) + + if segt_link_idx == 0: + found_path['segment_count'] += 1 + segt_link_idx += 1 + #st.log(" Path node {} is {}".format(dut_idx - 1, segt_link)) + + from_dut = to_dut + dut_idx += 1 + found_path['dut_list'].pop() + + if save_path == 'yes' : + sp_topo['subtopo']['ring'] = copy.deepcopy(found_path) + + BGPSP.bgp_sp_show_topo_path(found_path) + return found_path + + + @staticmethod + def bgp_sp_dut_get_saved_ring_topo(): + return copy.deepcopy(sp_topo['subtopo']['ring']) + + + @staticmethod + def bgp_sp_find_star_topo_in_dut_list(dut_list=[], core_dut = "", path_spoke_limit=0, save_path='yes'): + + st.log("BGP SP - Find Star Topo in Dut list {} length {}".format(dut_list, path_spoke_limit)) + sp_topo_dut_list = BGPSP.bgp_sp_get_dut_list() + + found_path = {} + found_path['found'] = False + + if not dut_list or len(dut_list) == 0 : + dut_list = sp_topo_dut_list + else : + for dut in dut_list : + if dut not in sp_topo_dut_list : + st.log("Dut {} not in Topo dut list {}".format(dut, sp_topo_dut_list)) + return found_path + + if core_dut and core_dut != '' : + if core_dut not in dut_list : + st.log("Core dute {} not in dut list {}".format(core_dut, dut_list)) + return found_path + + if path_spoke_limit <= 0 : + spoke_limit = len (dut_list) + else : + spoke_limit = path_spoke_limit + + st.log("Modified Dut list {} length_limit {}".format(dut_list, spoke_limit)) + + largest_star = [] + + for dut in dut_list : + + if core_dut and core_dut != '' and core_dut != dut : + continue + + if BGPSP.bgp_sp_dut_is_tg(dut) : + continue + + st.log(" Starting dut {} ".format(dut)) + + sp_topo_path = [] + sp_topo_path.append(dut) + + excl_list = list(dut_list) + excl_list.remove(dut) + + for next_dut in excl_list : + + st.log(" sp path {}".format(sp_topo_path)) + + leaf_dut = True + for link_name, link_data in sp_topo[dut]['intf'].items(): + if 'rmt_dut' in link_data.keys(): + rmt_dut = link_data['rmt_dut'] + + if rmt_dut != next_dut : + continue + + sp_topo_path.append(next_dut) + break + + if len(largest_star) < len(sp_topo_path) : + largest_star = sp_topo_path + + path_spoke_count = len(largest_star) - 1 + if path_spoke_limit > 0 : + if path_spoke_count == path_spoke_limit : + st.log(" Path spoke limit provided {} and reached".format(path_spoke_limit)) + break + else : + if path_spoke_count == spoke_limit : + st.log(" Path max possible spoke {} reached".format(length_limit)) + break + + st.log("BGP SP - {} Star with nodes {}".format(len(largest_star), largest_star)) + + path_length = len(largest_star) + + found_path['found'] = True if path_length else False + found_path['dut_list'] = [] + found_path['segment'] = {} + found_path['segment_count'] = 0 + found_path['type'] = 'Star' + + if found_path['found'] : + + for dut in largest_star : + found_path['dut_list'].append(dut) + + from_dut = largest_star[0] + found_path['start_dut'] = from_dut + + dut_idx = 1 + while dut_idx < path_length : + to_dut = largest_star[dut_idx] + segt_link_idx = 0 + for link_name, link_data in sp_topo[from_dut]['intf'].items(): + if 'rmt_dut' in link_data.keys(): + if link_data['rmt_dut'] == to_dut : + rmt_link = link_data['rmt_link'] + segt_link = { 'lcl_dut' : from_dut, 'lcl_link': link_name, + 'rmt_dut' : to_dut, 'rmt_link' : rmt_link } + + if segt_link_idx == 0 : found_path['segment'][dut_idx - 1] = {} + found_path['segment'][dut_idx - 1].update({ segt_link_idx: segt_link}) + + if segt_link_idx == 0: + found_path['segment_count'] += 1 + segt_link_idx += 1 + #st.log(" Path node {} is {}".format(dut_idx - 1, segt_link)) + + dut_idx += 1 + + if save_path == 'yes' : + sp_topo['subtopo']['star'] = copy.deepcopy(found_path) + + BGPSP.bgp_sp_show_topo_path(found_path) + return found_path + + + @staticmethod + def bgp_sp_dut_get_saved_star_topo(): + return copy.deepcopy(sp_topo['subtopo']['star']) + + + @staticmethod + def bgp_sp_find_spine_leaf_topo_in_dut_list(spine_list=[], leaf_list=[], save_path='yes'): + + st.log("BGP SP - Find Spine Leaf paths in {} and {}.".format(spine_list, leaf_list)) + sp_topo_dut_list = BGPSP.bgp_sp_get_dut_list() + + found_path = {} + found_path['found'] = False + + for dut in spine_list: + if dut not in sp_topo_dut_list: + st.log("Spine dut {} not in topo dut list {}".format(dut, sp_topo_dut_list)) + return found_path + + for dut in leaf_list: + if dut not in sp_topo_dut_list: + st.log("Leaf dut {} not in topo dut list {}".format(dut, sp_topo_dut_list)) + return found_path + + for dut in spine_list: + if dut in leaf_list: + st.log("Dut {} in both spine and leaf list {}".format(dut, spine_list)) + return found_path + + found_path['spine_list'] = spine_list + found_path['leaf_list'] = leaf_list + found_path['dut_list'] = [] + found_path['spine_path'] = {} + found_path['type'] = 'SpineLeaf' + + for spine_dut in spine_list : + + dut_list = copy.deepcopy(leaf_list) + dut_list.append(spine_dut) + + spine_path = BGPSP.bgp_sp_find_star_topo_in_dut_list(dut_list, spine_dut, save_path='no') + + st.log("Spine Leaf paths from {} is {}.\n".format(spine_dut, spine_path)) + + if spine_path['found'] : + found_path['found'] = True + + if spine_dut not in found_path['dut_list']: + found_path['dut_list'].append(spine_dut) + + for leaf_dut in spine_path['dut_list']: + if leaf_dut not in found_path['dut_list']: + found_path['dut_list'].append(leaf_dut) + + spine_path = copy.deepcopy(spine_path) + found_path['spine_path'].update({ spine_dut : spine_path }) + + if save_path == 'yes' : + sp_topo['subtopo']['spine_leaf'] = copy.deepcopy(found_path) + + st.log("BGP SP - Spine Leaf paths {}\n".format(found_path)) + return found_path + + + @staticmethod + def bgp_sp_dut_get_saved_spine_leaf_topo(): + return copy.deepcopy(sp_topo['subtopo']['spine_leaf']) + + + @staticmethod + def bgp_sp_dut_get_connected_ip_links(from_dut, to_dut, addr_family): + + ip_link_list = [] + if not BGPSP.bgp_sp_addr_family_valid(addr_family): + return ip_link_list + + link_name_list = bgp_sp_dut_get_connected_link_names(from_dut, to_dut) + if not link_name_list or len(link_name_list) == 0 : + return ip_link_list + + ip_link_list = [] + for link_name in link_name_list: + if link_name in sp_topo[dut][addr_family]['link'].keys(): + ip_data = sp_topo[dut][addr_family]['link'][link_name] + if 'rmt_dut' in ip_data.keys(): + if 'rmt_link' in ip_data.keys(): + if ip_data['rmt_dut'] == to_dut : + ip_link_list.append(link_name) + + return ip_link_list + + + @staticmethod + def bgp_sp_add_del_dut(dut, device_name, device_type='DUT', add='yes'): + + action_str = "Add" if add == 'yes' else 'Delete' + + if add == 'yes' : + + if BGPSP.bgp_sp_dut_present(dut): + st.log("BGP SP - device {} exists as dut {}".format(device_name, dut)) + return False + + dut2 = BGPSP.bgp_sp_get_dut_from_device(device_name) + if dut2 != "" and dut != dut2 : + st.log("BGP SP - device {} exists as dut {}".format(device_name, dut2)) + return False + + st.log("BGP SP - {} {} {} {}".format(action_str, device_type, dut, device_name)) + if device_type == 'DUT' : + sp_topo['dut_list'].append(dut) + sp_topo['dut_list'].sort() + else : + sp_topo['tg_list'].append(dut) + sp_topo['tg_list'].sort() + + sp_topo[dut] = {} + sp_topo[dut]['type'] = device_type + sp_topo[dut]['device'] = device_name + sp_topo[dut]['intf'] = {} + sp_topo[dut]['nwoctet'] = 0 + sp_topo[dut]['vrf'] = {} + + sp_topo[dut]['ipv4'] = {} + sp_topo[dut]['ipv4']['static_nw'] = {} + sp_topo[dut]['ipv4']['static_rt'] = {} + sp_topo[dut]['ipv4']['link'] = {} + sp_topo[dut]['ipv4']['nwoctet'] = 0 + + sp_topo[dut]['ipv6'] = {} + sp_topo[dut]['ipv6']['static_nw'] = {} + sp_topo[dut]['ipv6']['static_rt'] = {} + sp_topo[dut]['ipv6']['link'] = {} + sp_topo[dut]['ipv6']['nwoctet'] = 0 + + return True + + else : + + if not BGPSP.bgp_sp_dut_present(dut): + st.log("BGP SP - dut doesnt exists {}".format(dut)) + return False + + if device_name != '' and device_name != sp_topo[dut]['device']: + st.log("BGP SP - device {} isnot dut {}".format(device_name, dut)) + return False + + device_name = sp_topo[dut]['device'] + + if len(sp_topo[dut]['intf']) != 0 : + st.log("BGP SP - device {} {} interface exists".format(device_name, dut)) + return False + + st.log("BGP SP - Deleting device {} {} ".format(device_name, dut)) + del sp_topo[dut] + if device_type == 'DUT' : + del sp_topo['dut_list'][dut] + sp_topo['dut_list'].sort() + else : + del sp_topo['tg_list'][dut] + sp_topo['tg_list'].sort() + + return True + + st.log("BGP SP - Dut {} FAILED".format(action_str)) + return False + + + @staticmethod + def bgp_sp_add_del_link(dut, link_type, link_name, intf_name, add='yes'): + + action_str = "Add" if add == 'yes' else 'Delete' + st.log("BGP SP - Link {} for {} {}".format(action_str, dut, link_name)) + + if not BGPSP.bgp_sp_dut_present(dut): + st.log("BGP SP - Dut {} doesnt exist".format(dut)) + return False + + if not BGPSP.bgp_sp_valid_link_type(link_type): + st.log("BGP SP - Invalid intface type {}".format(link_type)) + return False + + if dut == "" or link_name=="" or intf_name == "" : + st.log("BGP SP - Invalid dut {} or intf {}".format(dut, link_name, intf_name)) + return False + + if add == 'yes' : + if BGPSP.bgp_sp_dut_link_present(dut, link_name): + st.log("BGP SP - dut {} link {} already present".format(dut, link_name)) + return False + + if_data = { 'if': intf_name, 'type': link_type } + sp_topo[dut]['intf'].update({link_name : if_data }) + + return True + + else: + if not BGPSP.bgp_sp_dut_link_present(dut, link_name): + st.log("BGP SP - dut {} doesnt have intf {}".format(dut, link_name)) + return False + + if BGPSP.bgp_sp_dut_link_connected(dut, link_name): + st.log("BGP SP - dut {} link {} connected".format(dut, link_name)) + return False + + if BGPSP.bgp_sp_dut_link_has_ip(dut, link_name, 'ipv4'): + st.log("BGP SP - dut {} link {} has ipv4 addr".format(dut, link_name)) + return False + + if BGPSP.bgp_sp_dut_link_has_ip(dut, link_name, 'ipv6'): + st.log("BGP SP - dut {} link {} has ipv6 addr".format(dut, link_name)) + return False + + st.log("BGP SP - Deleting link {} in dut ".format(dut, link_name)) + del sp_topo[dut]['intf'][link_name] + return True + + st.log("BGP SP - Link {} FAILED".format(action_str)) + return False + + + @staticmethod + def bgp_sp_connect_links(from_dut, from_link, to_dut, to_link, add='yes'): + + action_str = "Add" if add == 'yes' else 'Delete' + st.log("BGP SP - Link connect {} for {} {}".format(action_str, from_link, to_link)) + + if not BGPSP.bgp_sp_dut_link_present(from_dut, from_link): + st.log("BGP SP - dut {} link {} not present".format(from_dut, from_link)) + return False + + if not BGPSP.bgp_sp_dut_link_present(to_dut, to_link): + st.log("BGP SP - dut {} link {} not present".format(to_dut, to_link)) + return False + + if add == 'yes' : + + if BGPSP.bgp_sp_dut_link_connected(from_dut, from_link): + st.log("BGP SP - dut {} link {} already connected".format(from_dut, from_link)) + return False + + if BGPSP.bgp_sp_dut_link_connected(to_dut, to_link): + st.log("BGP SP - dut {} link {} already connected".format(to_dut, to_link)) + return False + + sp_topo[from_dut]['intf'][from_link].update({'rmt_dut': to_dut}) + sp_topo[from_dut]['intf'][from_link].update({'rmt_link': to_link}) + + sp_topo[to_dut]['intf'][to_link].update({'rmt_dut': from_dut}) + sp_topo[to_dut]['intf'][to_link].update({'rmt_link': from_link}) + + if BGPSP.bgp_sp_dut_link_connected(from_dut, from_link): + st.log("BGP SP - {} {} {} {} connected".format(from_dut, from_link, to_dut, to_link)) + return True + + else: + + if not BGPSP.bgp_sp_dut_link_connected_to_each_other(from_dut, from_link, to_dut, to_link): + st.log("BGP SP - {} {} {} {} not connected".format(from_dut, from_link, to_dut, to_link)) + return False + + del sp_topo[from_dut]['intf'][from_link]['rmt_dut'] + del sp_topo[from_dut]['intf'][from_link]['rmt_link'] + del sp_topo[to_dut]['intf'][to_link]['rmt_dut'] + del sp_topo[to_dut]['intf'][to_link]['rmt_link'] + + st.log("BGP SP - {} {} {} {} disconnected".format(from_dut, from_link, to_dut, to_link)) + return True + + return False + + + @staticmethod + def bgp_sp_add_del_link_ip(dut, link_name, ip_addr, subnet, rmt_ip, addr_family, add='yes'): + + action_str = "Add" if add == 'yes' else 'Delete' + st.log("BGP SP - Link ip {} for {} {} {}".format(action_str, dut, link_name, ip_addr)) + + if not BGPSP.bgp_sp_dut_link_connected(dut, link_name): + st.log("BGP SP - {} link not in connected state".format(link_name)) + + if add == 'yes' : + + if BGPSP.bgp_sp_dut_ip_link_present(dut, link_name, addr_family) : + st.log("BGP SP - {} {} already has {} address".format(dut, link_name, addr_family)) + return False + + if_data = sp_topo[dut]['intf'][link_name] + ip_data = { "ip": ip_addr, "subnet": subnet, "if": if_data['if'], 'type': if_data['type']} + + if 'rmt_dut' in if_data.keys(): + ip_data.update({'rmt_dut': if_data['rmt_dut']}) + ip_data.update({'rmt_link': if_data['rmt_link']}) + + if rmt_ip and rmt_ip != "": + ip_data.update({'rmt_ip': rmt_ip}) + + sp_topo[dut][addr_family]['link'].update({link_name: ip_data}) + + #st.log("BGP SP - Added IP link {} {}".format(link_name, ip_data)) + return True + + else: + + if not BGPSP.bgp_sp_dut_ip_link_present(dut, link_name, addr_family) : + st.log("BGP SP - {} {} doesnot exist".format(dut, link_name, addr_family)) + return True + + if_data = sp_topo[dut]['intf'][link_name] + ip_data = sp_topo[dut][addr_family]['link'][link_name] + + del sp_topo[dut][addr_family]['link'][link_name] + + #st.log("BGP SP - Deleted IP link {} {}".format(link_name, ip_data)) + return True + + st.log("BGP SP - Link ip {} FAILED".format(action_str)) + return False + + + @staticmethod + def bgp_sp_connect_all_ip_links(): + + st.log("BGP SP - IP link connect all") + + nbr_visited = {} + for dut in sp_topo['dut_list']: + nbr_visited[dut] = False + + addr_family_list = BGPSP.bgp_sp_get_address_family_list("all") + + dut_list = BGPSP.bgp_sp_get_dut_list() + dut_list += BGPSP.bgp_sp_get_tg_list() + + for lcl_dut in dut_list: + for lcl_link, link_data in sp_topo[lcl_dut]['intf'].items(): + if 'rmt_dut' in link_data.keys(): + rmt_dut = link_data['rmt_dut'] + rmt_link = link_data['rmt_link'] + + for afmly in addr_family_list: + if lcl_link in sp_topo[lcl_dut][afmly]['link'].keys(): + if rmt_link in sp_topo[rmt_dut][afmly]['link'].keys(): + + lcl_ip = sp_topo[lcl_dut][afmly]['link'][lcl_link]['ip'] + rmt_ip = sp_topo[rmt_dut][afmly]['link'][rmt_link]['ip'] + + sp_topo[lcl_dut][afmly]['link'][lcl_link].update({'rmt_link': rmt_link}) + sp_topo[lcl_dut][afmly]['link'][lcl_link].update({'rmt_dut': rmt_dut}) + sp_topo[lcl_dut][afmly]['link'][lcl_link].update({'rmt_ip': rmt_ip}) + + sp_topo[rmt_dut][afmly]['link'][rmt_link].update({'rmt_link': lcl_link}) + sp_topo[rmt_dut][afmly]['link'][rmt_link].update({'rmt_dut': lcl_dut}) + sp_topo[rmt_dut][afmly]['link'][rmt_link].update({'rmt_ip': lcl_ip}) + + nbr_visited[lcl_dut] = True + + return True + + + @staticmethod + def bgp_sp_add_del_dut_static_network_prefix(dut, prefix, subnet, addr_family, add='yes'): + + action_str = "Add" if add == 'yes' else 'Delete' + st.log("BGP SP - Static nw {} for {} {}".format(action_str, dut, prefix)) + + if not BGPSP.bgp_sp_dut_present(dut): + st.log("BGP SP - Dut {} not present".format(dut)) + return False + + if not BGPSP.bgp_sp_addr_family_valid(addr_family): + return False + + if add == 'yes' : + snw_data = {'subnet': subnet} + sp_topo[dut][addr_family]['static_nw'].update({prefix: snw_data}) + else : + if prefix in sp_topo[dut][addr_family]['static_nw']: + del sp_topo[dut][addr_family]['static_nw'][prefix] + + return True + + + @staticmethod + def bgp_sp_add_del_dut_static_route_prefix(dut, prefix, subnet, next_hop, addr_family, add='yes'): + + action_str = "Add" if add == 'yes' else 'Delete' + st.log("BGP SP - {} Static route {} pfx {} nhop {}.".format(action_str, dut, prefix, next_hop)) + + if not BGPSP.bgp_sp_dut_present(dut): + st.log("BGP SP - Dut {} not present".format(dut)) + return False + + if not BGPSP.bgp_sp_addr_family_valid(addr_family): + return False + + if add == 'yes' : + strt_data = {'nexthop' : next_hop , 'subnet': subnet} + sp_topo[dut][addr_family]['static_rt'].update({prefix: strt_data}) + else : + if prefix in sp_topo[dut][addr_family]['static_rt'].keys(): + del sp_topo[dut][addr_family]['static_rt'][prefix] + + return True + + + @staticmethod + def bgp_sp_add_del_dut_network_num(dut, nw_num, addr_family, add='yes'): + + action_str = "Add" if add == 'yes' else 'Delete' + st.log("BGP SP - Nw num {} for {} {}".format(action_str, dut, nw_num)) + + if not BGPSP.bgp_sp_dut_present(dut): + st.log("BGP SP - Dut {} not present".format(dut)) + return False + + if not BGPSP.bgp_sp_addr_family_valid(addr_family): + return False + + if add == 'yes' : + sp_topo[dut][addr_family]['nwoctet'] = nw_num + else : + sp_topo[dut][addr_family]['nwoctet'] = 0 + + return True + + + @staticmethod + def bgp_sp_add_del_link_address_octate(link_name, addr_oct_list=[], add='yes'): + + action_str = "Add" if add == 'yes' else 'Delete' + st.log("BGP SP - Addr octate {} for {} {}".format(action_str, link_name, addr_oct_list)) + + if add == 'yes' : + sp_topo['network'].update({link_name: addr_oct_list}) + else : + if link_name in sp_topo['network'].keys(): + del sp_topo['network'][link_name] + + return True + + + @staticmethod + def bgp_sp_bgp_verify_routes_in_dut_list(dut_list=[], route_list=[], addr_family='ipv4', present='yes'): + + st.log("BGP SP - verify route list routes in list of duts") + + if not BGPSP.bgp_sp_addr_family_valid(addr_family): + st.log("BGP SP - Invalid address family {}".format(addr_family)) + return False + + if len(dut_list) == 0 : + st.log("BGP SP - Dut list List empty") + return False + + if len(route_list) == 0 : + st.log("BGP SP - Route List empty") + if present == 'yes' : + return True + else : + return False + + for dut in dut_list: + tb_dut = BGPSP.bgp_sp_get_dut_device(dut) + + result = bgpapi.verify_ip_bgp_route_network_list(tb_dut, addr_family, route_list) + if present == 'yes' : + if not result : + st.log("BGP SP - {} doesnot have routes {} - failed result".format(dut, route_list)) + return False + else : + st.log("BGP SP - {} has routes {}".format(dut, route_list)) + else : + if result : + st.log("BGP SP - {} has routes {} - failed result".format(dut, route_list)) + return False + else : + st.log("BGP SP - {} doesnot have routes {}".format(dut, route_list)) + + if present == 'yes' : + st.log("BGP SP - {} has routes {} - Success".format(dut_list, route_list)) + else: + st.log("BGP SP - {} doesnot have routes {} - Success".format(dut_list, route_list)) + + return True + + + @staticmethod + def bgp_sp_bgp_verify_static_route(dut_list=[], afmly_list=[], present='yes'): + + st.log("BGP SP - verify every has other network due to root reflection") + for dut in dut_list: + other_dut_list = copy.deepcopy(dut_list) + other_dut_list.remove(dut) + + for afmly in afmly_list: + #strt_prefix_list = BGPSP.bgp_sp_get_dut_static_route_prefixes(dut, afmly) + strt_prefix_list = BGPSP.bgp_sp_get_dut_null_nhop_static_route_prefixes(dut, afmly) + strt_prefix_list = BGPSP.bgp_sp_ip_prefix_list_to_route_prefix_list(strt_prefix_list, afmly) + + st.log("BGP SP - {} static route prefixes {}".format(dut, strt_prefix_list)) + + result = BGPSP.bgp_sp_bgp_verify_routes_in_dut_list(other_dut_list, strt_prefix_list, afmly, present=present) + if not result : + st.log("BGP SP - Static route check FAILED") + return False + + st.log("BGP SP - Static route check Passed") + return True + + + @staticmethod + def bgp_sp_get_matching_entries(entries=[], match=None): + matched_entries = utils.filter_and_select(entries, None, match) + if not matched_entries: + st.log("\nBGP SP no match {} in\n {}\n".format(match, entries)) + else : + st.log("\nBGP SP Matched {} entries\n {}\n".format(match, matched_entries)) + return matched_entries + + + @staticmethod + def bgp_sp_entries_are_matching(entries=[], match=None): + matched_entries = BGPSP.bgp_sp_get_matching_entries(entries, match) + if not matched_entries: + return False + return True + + + @staticmethod + def bgp_sp_get_matching_bgp_ip_routes(dut, route_prefix_list=[], addr_family='ipv4'): + + matched_entries = [] + tb_dut = BGPSP.bgp_sp_get_dut_device(dut) + show_output = bgpapi.show_ip_bgp_route(tb_dut, family=addr_family) + #st.log("\nBGP SP ip bgp route \n {}\n".format(show_output)) + + if not route_prefix_list : + return show_output + + for route_prefix in route_prefix_list: + match = {'network': route_prefix} + entries = utils.filter_and_select(show_output, None, match) + #st.log("\nBGP SP filtered entries \n {}\n".format(entries)) + if entries: + matched_entries += entries + else : + if len(matched_entries) : + st.log("BGP SP - Few entries dont match") + return [] + + #st.log("\nBGP SP route_prefixes Matched entries {}\n".format(matched_entries)) + return matched_entries + + + @staticmethod + def bgp_sp_bgp_ip_route_is_matching(dut, route_prefix_list=[], addr_family='ipv4', match=None): + + matched_entries = BGPSP.bgp_sp_get_matching_bgp_ip_routes(dut, route_prefix_list, addr_family) + if not matched_entries : + return False + + if not match: + return True + + result = BGPSP.bgp_sp_entries_are_matching(matched_entries, match) + return result + + + @staticmethod + def bgp_sp_bgp_ip_route_is_selected(dut, route_prefix_list=[], addr_family='ipv4', match=None): + + matched_entries = BGPSP.bgp_sp_get_matching_bgp_ip_routes(dut, route_prefix_list, addr_family) + if not matched_entries : + return False + + match_selected ={'status_code': '*>'} + selected_entries = BGPSP.bgp_sp_get_matching_entries(matched_entries, match_selected) + if not matched_entries: + return False + + if not match: + return True + + result = BGPSP.bgp_sp_entries_are_matching(selected_entries, match) + return result + + + @staticmethod + def bgp_sp_bgp_ip_routes_matching(dut_list=[], route_prefix_list=[], addr_family='ipv4', match=None): + + fail_result_list = [] + for dut in dut_list : + matched_entries = BGPSP.bgp_sp_get_matching_bgp_ip_routes(dut, route_prefix_list, addr_family) + if not matched_entries : + st.log("BGP SP - {} doesnt have all routes to {}".format(dut, route_prefix_list)) + fail_result = "BGP SP - {} doesnt have all matching routes ".format(dut) + fail_result_list.append(fail_result) + continue + + if not match: + continue + + result = BGPSP.bgp_sp_entries_are_matching(matched_entries, match) + if not result : + st.log("BGP SP - {} routes do not match condition {}".format(dut, match)) + fail_result = "BGP SP - {} routes dont match route condition".format(dut) + fail_result_list.append(fail_result) + continue + + if len(fail_result_list): + st.log("BGP SP - Dut List {}".format(dut_list)) + st.log("BGP SP - Route Prefix {}".format(route_prefix_list)) + st.log("BGP SP - Match condition {}".format(match)) + for fail_result in fail_result_list: + st.log("{}".format(fail_result)) + st.log("BGP SP - IP routes not matching") + return False + + st.log("BGP SP - IP routes matching") + return True + + + @staticmethod + def bgp_sp_bgp_ip_routes_not_matching(dut_list=[], route_prefix_list=[], addr_family='ipv4', match=None): + + result = BGPSP.bgp_sp_bgp_ip_routes_matching(dut_list, route_prefix_list, addr_family, match) + if result : + return False + else : + return True + + + @staticmethod + def bgp_sp_dut_verify_bgp_ip_routes(dut, route_prefix_list=[], addr_family='ipv4', match=None): + + if not BGPSP.bgp_sp_dut_present(dut): + st.log("Dut {} not present".format(dut)) + return False + + matched_entries = BGPSP.bgp_sp_get_matching_bgp_ip_routes(dut, route_prefix_list, addr_family) + if not matched_entries : + st.log("BGP SP - {} doesnt have all routes {}".format(dut, route_prefix_list)) + return False + + if match: + result = BGPSP.bgp_sp_entries_are_matching(matched_entries, match) + if not result : + st.log("BGP SP - {} routes do not match condition {}".format(dut, match)) + return False + + st.log("BGP SP - {} IP routes matching".format(dut)) + return True + + + @staticmethod + def bgp_sp_verify_bgp_ip_routes(dut_list, route_prefix_list=[], addr_family='ipv4', match=None, threaded_run=True): + + st.log("BGP SP - Verify that {} has BGP routes {}".format(dut_list,route_prefix_list)) + + result = True + + dut_list = list(dut_list) if isinstance(dut_list, list) else [dut_list] + if not dut_list or len(dut_list) < 2: threaded_run = False + + dut_thread = [] + fail_result_list = [] + + for dut in dut_list : + dut_result = True + if threaded_run: + dut_thread.append([BGPSP.bgp_sp_dut_verify_bgp_ip_routes, dut, route_prefix_list, addr_family, match]) + else : + dut_result = BGPSP.bgp_sp_dut_verify_bgp_ip_routes(dut, route_prefix_list, addr_family, match) + + if not dut_result: + result = False + st.log("BGP SP - {} routes do not match condition {}".format(dut, match)) + fail_result = "BGP SP - {} routes dont match route condition".format(dut) + fail_result_list.append(fail_result) + break + + if threaded_run: + [out, exceptions] = utils.exec_all(bgplib.fast_start, dut_thread) + st.log("BGP SP - BGP Route match Threaded Run result {}".format([out, exceptions])) + if False in out : result = False + + if not result or len(fail_result_list): + st.log("BGP SP - Dut List {}".format(dut_list)) + st.log("BGP SP - Route Prefix {}".format(route_prefix_list)) + st.log("BGP SP - Match condition {}".format(match)) + for fail_result in fail_result_list: + st.log("{}".format(fail_result)) + st.log("BGP SP - IP routes not matching") + return False + + st.log("BGP SP - IP routes matching") + return True + + + @staticmethod + def bgp_sp_verify_no_bgp_ip_routes(dut_list, route_prefix_list=[], addr_family='ipv4', match=None, threaded_run=True): + + result = BGPSP.bgp_sp_verify_bgp_ip_routes(dut_list, route_prefix_list, addr_family, match, threaded_run) + if not result : + result = True + else : + result = False + return result + + + @staticmethod + def bgp_sp_find_tb_connected_link(lcl_dut, lcl_if, rmt_tb, rmt_if): + + connected_link = { 'connected': False, + 'lcl_dut' : lcl_dut, + 'lcl_tb' : '', + 'lcl_link': '', + 'lcl_if' : lcl_if, + 'rmt_dut' : '', + 'rmt_tb' : rmt_tb, + 'rmt_link': '', + 'rmt_if' : rmt_if } + + connected_link['lcl_tb'] = BGPSP.bgp_sp_get_dut_device(lcl_dut) + if connected_link['lcl_tb'] == '' : + st.log("BGP SP - No lcl_tb, Link NOT connected {}".format(connected_link)) + return connected_link + + connected_link['rmt_dut'] = BGPSP.bgp_sp_get_dut_from_device(rmt_tb) + if connected_link['rmt_dut'] == '' : + st.log("BGP SP - No rmt dut, Link NOT connected {}".format(connected_link)) + return connected_link + + tb_vars = st.get_testbed_vars() + tb_vars_keys = tb_vars.keys() + + for port_idx in range(1,20) : + link_name = "{}{}P{}".format(connected_link['lcl_dut'], + connected_link['rmt_dut'], port_idx) + if link_name in tb_vars_keys : + temp_lcl_if = tb_vars[link_name] + if temp_lcl_if == lcl_if : + connected_link['lcl_link'] = link_name + break + + for port_idx in range(1,20) : + link_name = "{}{}P{}".format(connected_link['rmt_dut'], + connected_link['lcl_dut'], port_idx) + if link_name in tb_vars_keys : + temp_rmt_if = tb_vars[link_name] + if temp_rmt_if == rmt_if : + connected_link['rmt_link'] = link_name + break + + if connected_link['lcl_link'] != '' and connected_link['rmt_link'] != '' : + connected_link['connected'] = True + st.log("BGP SP - Link connected {}".format(connected_link)) + return copy.deepcopy(connected_link) + + st.log("BGP SP - Link NOT connected {}".format(connected_link)) + return {'connected': False } + + + @staticmethod + def bgp_sp_setup_testbed_topology(per_node_nw='no', nw_ip_octet='10'): + st.banner("BGP SP - BUILD TOPOLOGY - START") + tb_vars = st.get_testbed_vars() + tb_var_keys = tb_vars.keys() + st.log("TestBed Vars => {}\n".format(tb_vars)) + + sub_nw_idx = 32 + sp_topo['dut_list'] = [] + sp_topo['tg_list'] = [] + sp_topo['dut_map'] = {} + sp_topo['tg_map'] = {} + sp_topo['network'] = {} + sp_topo['subtopo'] = {} + sp_topo['subtopo']['linear'] = { 'found': False } + sp_topo['subtopo']['ring'] = { 'found': False } + sp_topo['subtopo']['star'] = {'found': False} + sp_topo['subtopo']['spine_leaf'] = {'found': False} + + + tb_dut_count = len(tb_vars.dut_list) + for dut_idx in range(1, tb_dut_count+1) : + dut = "D{}".format(dut_idx) + if dut in tb_var_keys : + sp_topo['dut_map'][dut] = tb_vars[dut] + + tb_tg_count = len(tb_vars.tgen_list) + for tg_idx in range(1, tb_tg_count+1) : + tgen = "T{}".format(tg_idx) + if tgen in tb_var_keys : + sp_topo['tg_map'][tgen] = tb_vars[tgen] + + st.log("BGP SP - Testbed Dut List {}".format(sp_topo['dut_map'])) + st.log("BGP SP - Testbed Tgen List {}".format(sp_topo['tg_map'])) + + dut_idx = 0 + for dut, tb_dut_name in sp_topo['dut_map'].items(): + + dut_idx += 1 + + result = BGPSP.bgp_sp_add_del_dut(dut, tb_dut_name, add='yes') + if not result: + st.log("BGP SP - Dut {} add {} FAILED".format(dut, tb_dut_name)) + + if per_node_nw == 'no' : + nw_ipv4_octet = nw_ip_octet + else : + nw_ipv4_octet = int(nw_ip_octet) + dut_idx + + BGPSP.bgp_sp_add_del_dut_network_num(dut, nw_ipv4_octet, 'ipv4', 'yes') + nw_ipv6_octet = "97{}".format(nw_ipv4_octet) + BGPSP.bgp_sp_add_del_dut_network_num(dut, nw_ipv6_octet, 'ipv6', 'yes') + + + for dut, tb_dut_name in sp_topo['tg_map'].items(): + + dut_idx += 1 + + result = BGPSP.bgp_sp_add_del_dut(dut, tb_dut_name, device_type='TG', add='yes') + if not result: + st.log("BGP SP - TG Dut {} add {} FAILED".format(dut, tb_dut_name)) + + if per_node_nw == 'no' : + nw_ipv4_octet = nw_ip_octet + else : + nw_ipv4_octet = int(nw_ip_octet) + dut_idx + + BGPSP.bgp_sp_add_del_dut_network_num(dut, nw_ipv4_octet, 'ipv4', 'yes') + nw_ipv6_octet = "97{}".format(nw_ipv4_octet) + BGPSP.bgp_sp_add_del_dut_network_num(dut, nw_ipv6_octet, 'ipv6', 'yes') + + + sp_topo['dut_list'].sort() + sp_topo['tg_list'].sort() + #st.log("SP topo after dut add:\n{}\n".format(sp_topo)) + + for from_dut_idx, from_dut in enumerate(sp_topo['dut_list'], start = 1): + + for count in range(0,2): + intf_name = "Loopback{}".format(count) + link_name = "{}L{}".format(from_dut, count) + + result = BGPSP.bgp_sp_add_del_link(from_dut, 'LBK', link_name, intf_name, add='yes') + if not result: + st.log("Loopback interface {} add FAILED".format(link_name)) + + nwoct4 = "{}".format(sp_topo[from_dut]['ipv4']['nwoctet']) + nwoct3 = 8 + nwoct2 = count + 1 + + lo_ip = "{}.{}.{}.{}".format(nwoct4, nwoct3, nwoct2, from_dut_idx) + result = BGPSP.bgp_sp_add_del_link_ip(from_dut, link_name, lo_ip, 32, "", 'ipv4', add='yes') + if not result: + st.log("Loopback interface IPv4 {} add FAILED".format(link_name)) + + lo_ip = "{}:{}{}:{}{}::{}".format(nwoct4, from_dut_idx, nwoct3, nwoct2, count+1, from_dut_idx) + result = BGPSP.bgp_sp_add_del_link_ip(from_dut, link_name, lo_ip, 128, "", 'ipv6', add='yes') + if not result: + st.log("Loopback interface IPv6 {} add FAILED".format(link_name)) + + addr_oct_list = [nwoct4, nwoct3, nwoct2, from_dut_idx] + BGPSP.bgp_sp_add_del_link_address_octate(link_name, addr_oct_list, add='yes') + + #st.log("SP topo after dut loopback add :\n{}\n".format(sp_topo)) + + lcl_dut = from_dut + lcl_tb = BGPSP.bgp_sp_get_dut_device(lcl_dut) + + dut_links = st.get_dut_links(lcl_tb) + tg_links = st.get_tg_links(lcl_tb) + + dut_all_links = dut_links + tg_links + st.log("BGP SP - Dut {} links {}".format(lcl_dut, dut_all_links)) + + for link_idx, link in enumerate(dut_all_links , start = 1): + + link_data = BGPSP.bgp_sp_find_tb_connected_link(lcl_dut, link[0], link[1], link[2]) + if not link_data['connected'] : + continue + + rmt_dut = link_data['rmt_dut'] + rmt_tb = link_data['rmt_tb'] + + lcl_if = link_data['lcl_if'] + rmt_if = link_data['rmt_if'] + + lcl_link = link_data['lcl_link'] + rmt_link = link_data['rmt_link'] + + BGPSP.bgp_sp_add_del_link(lcl_dut, 'ETH', lcl_link, lcl_if, add='yes') + + if BGPSP.bgp_sp_dut_is_tg(rmt_dut) : + BGPSP.bgp_sp_add_del_link(rmt_dut, 'ETH', rmt_link, rmt_if, add='yes') + + if BGPSP.bgp_sp_dut_link_present(rmt_dut, rmt_link): + BGPSP.bgp_sp_connect_links(lcl_dut, lcl_link, rmt_dut, rmt_link) + + if lcl_link in sp_topo['network'].keys() : + nwoct4 = sp_topo['network'][lcl_link][0] + nwoct3 = sp_topo['network'][lcl_link][1] + nwoct2 = sp_topo['network'][lcl_link][2] + elif rmt_link in sp_topo['network'].keys(): + nwoct4 = sp_topo['network'][rmt_link][0] + nwoct3 = sp_topo['network'][rmt_link][1] + nwoct2 = sp_topo['network'][rmt_link][2] + else : + nwoct4 = "{}".format(sp_topo[lcl_dut]['ipv4']['nwoctet']) + nwoct3 = sub_nw_idx + sub_nw_idx += 2 + nwoct2 = link_idx #from_dut_idx + + if link_data['lcl_dut'] < link_data['rmt_dut'] : + lcl_host_num = 1 + rmt_host_num = 2 + else: + lcl_host_num = 2 + rmt_host_num = 1 + + lcl_ip = "{}.{}.{}.{}".format(nwoct4, nwoct3, nwoct2, lcl_host_num) + rmt_ip = "{}.{}.{}.{}".format(nwoct4, nwoct3, nwoct2, rmt_host_num) + + BGPSP.bgp_sp_add_del_link_ip(lcl_dut, lcl_link, lcl_ip, 24, rmt_ip, 'ipv4', add='yes') + + if BGPSP.bgp_sp_dut_is_tg(rmt_dut) : + BGPSP.bgp_sp_add_del_link_ip(rmt_dut, rmt_link, rmt_ip, 24, lcl_ip, 'ipv4', add='yes') + + lcl_ip = "{}:{}:{}::{}".format(nwoct4, nwoct3, nwoct2, lcl_host_num) + rmt_ip = "{}:{}:{}::{}".format(nwoct4, nwoct3, nwoct2, rmt_host_num) + + BGPSP.bgp_sp_add_del_link_ip(lcl_dut, lcl_link, lcl_ip, 64, rmt_ip, 'ipv6', add='yes') + + if BGPSP.bgp_sp_dut_is_tg(rmt_dut) : + BGPSP.bgp_sp_add_del_link_ip(rmt_dut, rmt_link, rmt_ip, 64, lcl_ip, 'ipv6', add='yes') + + addr_oct_list = [nwoct4, nwoct3, nwoct2, lcl_host_num] + BGPSP.bgp_sp_add_del_link_address_octate(lcl_link, addr_oct_list, add='yes') + + if BGPSP.bgp_sp_dut_is_tg(rmt_dut) : + BGPSP.bgp_sp_add_del_link_address_octate(rmt_link, addr_oct_list, add='yes') + + + #st.log("SP topo after {} interface add :\n{}\n".format(from_dut, sp_topo)) + + for count in range(1,3): + link_name = "{}N{}".format(from_dut, count) + nwoct4 = 216 + nwoct3 = 50 + count + nwoct2 = from_dut_idx + + st_nw = "{}.{}.{}.{}".format(nwoct4, nwoct3, nwoct2, 0) + BGPSP.bgp_sp_add_del_dut_static_network_prefix(from_dut, st_nw, 24, 'ipv4', add='yes') + + st_nw = "{}:{}:{}::{}".format(nwoct4, nwoct3, nwoct2, 0) + BGPSP.bgp_sp_add_del_dut_static_network_prefix(from_dut, st_nw, 86, 'ipv6', add='yes') + + addr_oct_list = [nwoct4, nwoct3, nwoct2, 0] + BGPSP.bgp_sp_add_del_link_address_octate(link_name, addr_oct_list, add='yes') + + for count in range(1,2): + link_name = "{}RN{}".format(from_dut, count) + nwoct4 = 209 + nwoct3 = 90 + count + nwoct2 = from_dut_idx + next_hop = "Null0" + + st_rt = "{}.{}.{}.{}".format(nwoct4, nwoct3, nwoct2, 0) + BGPSP.bgp_sp_add_del_dut_static_route_prefix(from_dut, st_rt, 24, next_hop, 'ipv4', add='yes') + + st_rt = "{}:{}:{}::{}".format(nwoct4, nwoct3, nwoct2, 0) + BGPSP.bgp_sp_add_del_dut_static_route_prefix(from_dut, st_rt, 64, next_hop, 'ipv6', add='yes') + + addr_oct_list = [nwoct4, nwoct3, nwoct2, 0] + BGPSP.bgp_sp_add_del_link_address_octate(link_name, addr_oct_list, add='yes') + + for count in range(1,2): + link_name = "{}RS{}".format(from_dut, count) + nwoct4 = 208 + nwoct3 = 80 + count + nwoct2 = from_dut_idx + + st_rt = "{}.{}.{}.{}".format(nwoct4, nwoct3, nwoct2, 0) + next_hop = BGPSP.bgp_sp_get_dut_loopback_ip(from_dut, 0, 'ipv4') + next_hop = BGPSP.bgp_sp_get_unused_dut_interface(from_dut) + BGPSP.bgp_sp_add_del_dut_static_route_prefix(from_dut, st_rt, 24, next_hop, 'ipv4', add='yes') + + st_rt = "{}:{}:{}::{}".format(nwoct4, nwoct3, nwoct2, 0) + next_hop = BGPSP.bgp_sp_get_dut_loopback_ip(from_dut, 0, 'ipv6') + next_hop = BGPSP.bgp_sp_get_unused_dut_interface(from_dut) + BGPSP.bgp_sp_add_del_dut_static_route_prefix(from_dut, st_rt, 64, next_hop, 'ipv6', add='yes') + + addr_oct_list = [nwoct4, nwoct3, nwoct2, 0] + BGPSP.bgp_sp_add_del_link_address_octate(link_name, addr_oct_list, add='yes') + + #st.log("SP topo for {} :\n{}\n".format(from_dut, sp_topo)) + + #st.log("SP topo at testbed topobuild complete:\n{}\n".format(sp_topo)) + + BGPSP.bgp_sp_connect_all_ip_links() + + BGPSP.bgp_sp_show_dut_topo_data() + + st.banner("BGP SP - BUILD TOPOLOGY - END") + + return True + + + @staticmethod + def bgp_sp_clear_testbed_topology(per_node_nw='no', nw_ip_octet='10'): + sp_topo = {} + bgp_topo = {} + + + @staticmethod + def bgp_sp_test_topo_present(topo_path=None, dut_count=None, segment_count=None): + + if dut_count : + if BGPSP.bgp_sp_get_dut_count() < dut_count : + st.log("BGP SP - Test case needs minimum {} duts in testbed".format(dut_count)) + return False + + if not topo_path : + st.log("BGP SP - Testbed Topology path is Null") + return False + + if 'found' not in topo_path.keys() : + st.log("BGP SP - Invalid Path") + return False + + if not topo_path['found'] : + st.log("BGP SP - Required Topology path not found") + return False + + if segment_count : + if topo_path['segment_count'] < segment_count : + st.log("BGP SP - Test case needs minimum {} segments in Topology path".format(segment_count)) + return False + + return True + + + @staticmethod + def bgp_sp_show_dut_topo_data(dut_list = []): + + if not dut_list : + dut_list = BGPSP.bgp_sp_get_dut_list() + dut_list += BGPSP.bgp_sp_get_tg_list() + + st.log("\n") + st.log("BGP SP - Dut List: {}".format(sp_topo['dut_list'])) + st.log("BGP SP - Dut Dev Map: {}".format(sp_topo['dut_map'])) + st.log("BGP SP - TG List: {}".format(sp_topo['tg_list'])) + st.log("BGP SP - TG Dev Map: {}".format(sp_topo['tg_map'])) + + for dut in dut_list: + if not BGPSP.bgp_sp_dut_present(dut) : + continue + + st.log("\n") + st.log("BGP SP - Dut {} {} {}".format(dut, sp_topo[dut]['type'], sp_topo[dut]['device'])) + + for intf, intf_data in sp_topo[dut]['intf'].items(): + st.log(" Intf {} {}".format(intf, intf_data)) + + for link, link_data in sp_topo[dut]['ipv4']['link'].items(): + st.log(" Ipv4 Link {} {}".format(link, link_data)) + for link, link_data in sp_topo[dut]['ipv6']['link'].items(): + st.log(" Ipv6 Link {} {}".format(link, link_data)) + + for stnw, stnw_data in sp_topo[dut]['ipv4']['static_nw'].items(): + st.log(" Static Ipv4 Nw {} {}".format(stnw, stnw_data)) + for stnw, stnw_data in sp_topo[dut]['ipv6']['static_nw'].items(): + st.log(" Static IPv6 Nw {} {}".format(stnw, stnw_data)) + + for strt, strt_data in sp_topo[dut]['ipv4']['static_rt'].items(): + st.log(" Static Ipv4 Route {} {}".format(strt, strt_data)) + for strt, strt_data in sp_topo[dut]['ipv6']['static_rt'].items(): + st.log(" Static IPv6 Route {} {}".format(strt, strt_data)) + + st.log(" Ipv4 Network Octates {}".format(sp_topo[dut]['ipv4']['nwoctet'])) + st.log(" IPv6 Network Octates {}".format(sp_topo[dut]['ipv6']['nwoctet'])) + + st.log("\n") + + + @staticmethod + def bgp_sp_show_topo_path(path): + + if not path : + st.log("BGP SP - Path Null") + return + + if 'type' not in path.keys(): + st.log("BGP SP - Path Type Not found") + return + + if 'found' not in path.keys(): + st.log("BGP SP - Path Invalid") + return + + path_found = "Found" if path['found'] else "Not Found" + + st.log("BGP SP - {} Topo Path {}".format(path['type'], path_found)) + if not path['found'] : return + + st.log(" Dut List: {}".format(path['dut_list'])) + st.log(" Segt Count: {}".format(path['segment_count'])) + for segt_idx, segt_data in path['segment'].items(): + st.log(" Segment-{}: ".format(segt_idx)) + for link_idx, link_data in segt_data.items(): + st.log(" Link-{}: {}".format(link_idx, link_data)) + st.log("\n") + + + @staticmethod + def bgp_sp_show_dut_if_cmd_logs(dut): + tb_dut = BGPSP.bgp_sp_get_dut_device(dut) + st.show(tb_dut, "show ip interface") + st.show(tb_dut, "show ipv6 interface") + + + @staticmethod + def bgp_sp_show_dut_route_cmd_logs(dut): + tb_dut = BGPSP.bgp_sp_get_dut_device(dut) + st.vtysh_show(tb_dut, "show ip route") + st.vtysh_show(tb_dut, "show ipv6 route") + + + @staticmethod + def bgp_sp_show_dut_bgp_cmd_logs(dut): + tb_dut = BGPSP.bgp_sp_get_dut_device(dut) + st.vtysh_config(tb_dut, "do show running-config bgp") + st.vtysh_show(tb_dut, "show ip bgp summary") + st.vtysh_show(tb_dut, "show bgp ipv4") + st.vtysh_show(tb_dut, "show bgp ipv6") + + + @staticmethod + def bgp_sp_show_dut_cmd_logs(dut): + BGPSP.bgp_sp_show_dut_if_cmd_logs(dut) + BGPSP.bgp_sp_show_dut_route_cmd_logs(dut) + + @staticmethod + def bgp_sp_show_dut_bgp_running_config(dut_list=[]): + for dut in dut_list : + tb_dut = BGPSP.bgp_sp_get_dut_device(dut) + st.vtysh_config(tb_dut, "do show running-config bgp") + + @staticmethod + def bgp_sp_loopback_interface_config_unconfig(config='yes', vrf='default'): + """ + + :param config: + :param vrf: + :return: + """ + action_str = 'Config' if config == 'yes' else 'Unconfig' + st.banner("{}uring LOOPBACK Interface on all nodes.".format(action_str)) + + result = True + threaded_run = True + + dut_list = BGPSP.bgp_sp_get_dut_list() #+ BGPSP.bgp_sp_get_tg_list() + dut_thread = [] + + for dut in dut_list : + tb_dut = sp_topo[dut]['device'] + lpbk_if_data = {} + + if BGPSP.bgp_sp_dut_is_tg(dut) : + st.log("BGP SP - TG {} Loopback config not done for now".format(dut)) + continue + + for link_name, link_data in sp_topo[dut]['intf'].items(): + if link_data['type'] != 'LBK': + continue + + if_name = link_data['if'] + lpbk_if_data[if_name] = 'default' + + + if threaded_run : + dut_thread.append([ifapi.config_loopback_interfaces, tb_dut, lpbk_if_data, config]) + else : + result = ifapi.config_loopback_interfaces(tb_dut, lpbk_if_data, config) + + if not result : + st.log("{}uring {} loopback interfaces FAILED".format(action_str, dut)) + return False + + if threaded_run: + [out, exceptions] = utils.exec_all(bgplib.fast_start, dut_thread) + st.log("BGP SP - Threaded Run result {}".format([out, exceptions])) + if False in out : result = False + + return result + + + @staticmethod + def bgp_sp_loopback_address_config_unconfig(config='yes', vrf='default', addr_family='all'): + """ + + :param config: + :param vrf: + :param addr_family: + :return: + """ + action_str = 'Config' if config == 'yes' else 'Unconfig' + st.banner("{}uring LOOPBACK Addresses on all nodes.".format(action_str)) + + if not BGPSP.bgp_sp_topology_data_present() : + st.log("BGP SP Topology data not available") + st.log("SP topo:\n{}\n".format(sp_topo)) + return False + + threaded_run = True + debug_run = False + result = True + config = 'add' if config == 'yes' else 'remove' + + addr_family_list = BGPSP.bgp_sp_get_address_family_list(addr_family) + dut_thread = [] + + dut_list = BGPSP.bgp_sp_get_dut_list() #+ BGPSP.bgp_sp_get_tg_list() + + for dut in dut_list : + tb_dut = sp_topo[dut]['device'] + if_data_list = [] + + if BGPSP.bgp_sp_dut_is_tg(dut) : + st.log("BGP SP - TG {} Loopback IP config not done for now".format(dut)) + continue + + for afmly in addr_family_list: + for link_name, link_data in sp_topo[dut][afmly]['link'].items(): + if link_data['type'] != 'LBK': + continue + + lpbk_if = link_data['if'] + lpbk_ip = link_data['ip'] + subnet = link_data['subnet'] + + if_data_list.append({'name': lpbk_if, 'ip': lpbk_ip, 'subnet': subnet, 'family': afmly }) + st.log("{}uring {} Loopback {}:{} {} {} ".format(action_str, afmly, dut, tb_dut, lpbk_if, lpbk_ip)) + + if threaded_run: + dut_thread.append([ipapi.config_unconfig_interface_ip_addresses, tb_dut, if_data_list, config]) + else : + result = ipapi.config_unconfig_interface_ip_addresses(tb_dut, if_data_list, config=config) + + if not result: + BGPSP.bgp_sp_show_dut_cmd_logs(dut) + st.log("{}uring {} loopback address FAILED".format(action_str, dut)) + return False + + if debug_run: + BGPSP.bgp_sp_show_dut_if_cmd_logs(dut) + + if threaded_run: + [out, exceptions] = utils.exec_all(bgplib.fast_start, dut_thread) + st.log("BGP SP - Threaded Run result {}".format([out, exceptions])) + if False in out : result = False + + return result + + + @staticmethod + def bgp_sp_interface_address_all_config_unconfig(config='yes', vrf='default', addr_family='all'): + """ + + :param config: + :param vrf: + :param addr_family: + :return: + """ + + action_str = 'Config' if config == 'yes' else 'Unconfig' + st.banner("{}uring Interface Addresses on all nodes.".format(action_str)) + + if not BGPSP.bgp_sp_topology_data_present() : + st.log("BGP SP Topology data not available") + st.log("SP topo:\n{}\n".format(sp_topo)) + return False + + threaded_run = True + debug_run = False + result = True + + config = 'add' if config == 'yes' else 'remove' + + addr_family_list = BGPSP.bgp_sp_get_address_family_list(addr_family) + dut_thread = [] + + dut_list = BGPSP.bgp_sp_get_dut_list() + + for dut in dut_list : + tb_dut = sp_topo[dut]['device'] + + if_data_list = [] + + for afmly in addr_family_list: + for link_name, link_data in sp_topo[dut][afmly]['link'].items(): + if link_data['type'] == 'LBK': + continue + + link_ip = link_data['ip'] + link_if = link_data['if'] + subnet = link_data['subnet'] + + if_data_list.append({'name': link_if, 'ip': link_ip, 'subnet': subnet, 'family':afmly }) + + st.log("{}uring {} Interface {}:{} {}:{} {} ".format(action_str, afmly, dut, + tb_dut, link_name, link_if, link_ip)) + + if threaded_run: + dut_thread.append([ipapi.config_unconfig_interface_ip_addresses, tb_dut, if_data_list, config]) + else : + result = ipapi.config_unconfig_interface_ip_addresses(tb_dut, if_data_list, config=config) + + if not result: + BGPSP.bgp_sp_show_dut_cmd_logs(dut) + st.log("{}uring {} Interface address FAILED".format(action_str, dut)) + return False + + if debug_run: + BGPSP.bgp_sp_show_dut_if_cmd_logs(dut) + + if threaded_run: + [out, exceptions] = utils.exec_all(bgplib.fast_start, dut_thread) + st.log("BGP SP - Threaded Run result {}".format([out, exceptions])) + if False in out : result = False + + return result + + + @staticmethod + def bgp_sp_tg_interface_ip_all_config_unconfig(config='yes', vrf='default', addr_family='all'): + """ + + :param config: + :param vrf: + :param addr_family: + :return: + """ + + action_str = 'Config' if config == 'yes' else 'Unconfig' + st.banner("{}uring Interface Addresses on all TGENs.".format(action_str)) + + if not BGPSP.bgp_sp_topology_data_present() : + st.log("BGP SP Topology data not available") + st.log("SP topo:\n{}\n".format(sp_topo)) + return False + + result = True + threaded_run = True + dut_thread = [] + + dut_list = BGPSP.bgp_sp_get_tg_list() + + for dut in dut_list : + tb_dut = sp_topo[dut]['device'] + tg = tgen_obj_dict[tb_dut] + + for link_name, link_data in sp_topo[dut]['intf'].items(): + if link_data['type'] == 'LBK': + continue + + tb_if = link_data['if'] + tg_port_handle = tg.get_port_handle(tb_if) + + if config == 'yes' : + st.log("\n") + st.log("BGP SP - Resetting TG port {} {}".format(tb_dut, tb_if)) + tg.tg_traffic_control(action="reset", port_handle=tg_port_handle) + st.log("\n") + + if threaded_run: + dut_thread.append([BGPSP.bgp_sp_tg_link_ip_config_unconfig, dut, link_name, addr_family, vrf, config]) + else : + result = BGPSP.bgp_sp_tg_link_ip_config_unconfig(dut, link_name, addr_family, vrf, config=config) + + if not result: + BGPSP.bgp_sp_show_dut_cmd_logs(dut) + st.log("{}uring TG {} Interface address FAILED".format(action_str, dut)) + + if threaded_run: + [out, exceptions] = utils.exec_all(bgplib.fast_start, dut_thread) + st.log("BGP SP - Threaded Run result {}".format([out, exceptions])) + if False in out : result = False + + return result + + + @staticmethod + def bgp_sp_dut_link_ip_config_unconfig(dut, link_name, addr_family='all', vrf='default', config='yes'): + + action_str = 'Config' if config == 'yes' else 'Unconfig' + st.log("{}uring Interface Addresses on TG link.".format(action_str)) + + result = True + addr_family_list = BGPSP.bgp_sp_get_address_family_list(addr_family) + + if not BGPSP.bgp_sp_dut_link_present(dut, link_name) : + st.log("BGP SP - Dut {} link {} not present".format(dut, link_name)) + return False + + tb_dut = sp_topo[dut]['device'] + link_data = sp_topo[dut]['intf'][link_name] + tb_if = link_data['if'] + + for afmly in addr_family_list: + + if link_name not in sp_topo[dut][afmly]['link'].keys(): + st.log("BGP SP - {} {} {} address not assigned".format(dut, link_name, afmly)) + continue + + ip_data = sp_topo[dut][afmly]['link'][link_name] + + link_ip = ip_data['ip'] + link_if = ip_data['if'] + rmt_ip = ip_data['rmt_ip'] + subnet = ip_data['subnet'] + + st.log("{}uring {} Interface {} {}:{} {} ".format(action_str, afmly, + tb_dut, link_name, link_if, link_ip)) + + result = ipapi.config_ip_addr_interface(tb_dut, link_if, link_ip, subnet, afmly, config) + + if not result: + BGPSP.bgp_sp_show_dut_cmd_logs(dut) + st.log("{}uring {} Interface address FAILED".format(action_str, dut)) + break + + return result + + + @staticmethod + def bgp_sp_tg_link_ip_config_unconfig(dut, link_name, addr_family='all', vrf='default', config='yes'): + + action_str = 'Config' if config == 'yes' else 'Unconfig' + st.log("{}uring Interface Addresses on TG link.".format(action_str)) + + result = True + + addr_family_list = BGPSP.bgp_sp_get_address_family_list(addr_family) + + if not BGPSP.bgp_sp_dut_link_present(dut, link_name) : + st.log("BGP SP - Dut {} link {} not present".format(dut, link_name)) + return False + + tb_dut = sp_topo[dut]['device'] + link_data = sp_topo[dut]['intf'][link_name] + tb_if = link_data['if'] + + tg = tgen_obj_dict[tb_dut] + tg_port_handle = tg.get_port_handle(tb_if) + + for afmly in addr_family_list: + + if link_name not in sp_topo[dut][afmly]['link'].keys(): + st.log("BGP SP - {} {} {} address not assigned".format(dut, link_name, afmly)) + continue + + ip_data = sp_topo[dut][afmly]['link'][link_name] + + link_ip = ip_data['ip'] + link_if = ip_data['if'] + rmt_ip = ip_data['rmt_ip'] + subnet = ip_data['subnet'] + + st.log("{}uring {} Interface {} {}:{} {} ".format(action_str, afmly, + tb_dut, link_name, link_if, link_ip)) + + if config =='yes' : + if afmly == 'ipv4': + tg_result = tg.tg_interface_config(port_handle=tg_port_handle, mode='config', + intf_ip_addr=link_ip, + gateway=rmt_ip, arp_send_req='1') + else: + tg_result = tg.tg_interface_config(port_handle=tg_port_handle, mode='config', + ipv6_intf_addr=link_ip, + ipv6_prefix_length=subnet, + ipv6_gateway=rmt_ip, arp_send_req='1') + + st.log("BGP SP - Port ip config tg api result = {}".format(tg_result)) + + if 'handle' in tg_result.keys(): + sp_topo[dut][afmly]['link'][link_name]['tg_handle'] = tg_result['handle'] + else : + result = False + break + + else : + handle = '' + if 'tg_handle' in ip_data.keys(): + handle = ip_data['tg_handle'] + + if handle == '' : + st.log("BGP SP - {} {} {} tg handle invalid".format(dut, link_name, afmly)) + continue + + if afmly == 'ipv4': + tg_result = tg.tg_interface_config(port_handle=tg_port_handle, handle=handle, mode='destroy') + else: + tg_result = tg.tg_interface_config(port_handle=tg_port_handle, handle=handle, mode='destroy') + + st.log("BGP SP - Port ip Unconfig tg api result = {}".format(tg_result)) + + sp_topo[dut][afmly]['link'][link_name]['tg_handle'] = '' + + if not result: + BGPSP.bgp_sp_show_dut_cmd_logs(dut) + st.log("{}uring TG {} Interface address FAILED".format(action_str, dut)) + + return result + + + @staticmethod + def bgp_sp_static_route_config_unconfig(config='yes', vrf='default', addr_family='all'): + """ + + :param config: + :param vrf: + :param addr_family: + :return: + """ + action_str = 'Config' if config == 'yes' else 'Unconfig' + st.banner("{}uring Static Route on all nodes.".format(action_str)) + + if not BGPSP.bgp_sp_topology_data_present() : + st.log("BGP SP Topology data not available") + st.log("SP topo:\n{}\n".format(sp_topo)) + return False + + threaded_run = True + debug_run = False + result = True + config = 'add' if config == 'yes' else 'remove' + + addr_family_list = BGPSP.bgp_sp_get_address_family_list(addr_family) + thread_info = {'ipv4': [], 'ipv6': []} + dut_thread = [] + + for dut in sp_topo['dut_list'] : + tb_dut = sp_topo[dut]['device'] + rt_data_list = [] + + for afmly in addr_family_list: + + for prefix, strt_data in sp_topo[dut][afmly]['static_rt'].items(): + + nexthop = strt_data['nexthop'] + subnet = strt_data['subnet'] + rt_data_list.append({ 'ip': prefix, 'subnet': subnet, 'nexthop': nexthop, 'family': afmly }) + + st.log("{}uring {} Static route {}:{} pfx {} nh {} .".format(action_str, afmly, dut, tb_dut, prefix, nexthop)) + + ''' + prefix_sn = "{}/{}".format(prefix, subnet) + if config == 'add': + if threaded_run: + thread_info[afmly].append([ipapi.create_static_route, tb_dut, nexthop, prefix_sn, 'vtysh', afmly]) + else: + result = ipapi.create_static_route(tb_dut, nexthop, prefix_sn, 'vtysh', afmly) + else: + if threaded_run: + thread_info[afmly].append([ipapi.delete_static_route, tb_dut, nexthop, prefix_sn, afmly, 'vtysh']) + else: + result = ipapi.delete_static_route(tb_dut, nexthop, prefix_sn, afmly, 'vtysh') + result = True + ''' + + if threaded_run: + dut_thread.append([ipapi.config_unconfig_static_routes, tb_dut, rt_data_list, "vtysh", config]) + else : + result = ipapi.config_unconfig_static_routes(tb_dut, rt_data_list, shell="vtysh", config=config) + + if not result: + BGPSP.bgp_sp_show_dut_cmd_logs(dut) + st.log("{}uring {} Static route FAILED".format(action_str, dut)) + return False + + if debug_run: + BGPSP.bgp_sp_show_dut_route_cmd_logs(dut) + + if threaded_run: + [out, exceptions] = utils.exec_all(bgplib.fast_start, dut_thread) + st.log("BGP SP - Threaded Run result {}".format([out, exceptions])) + if False in out : result = False + + return result + + + @staticmethod + def bgp_sp_dut_interface_address_ping_test(dut, vrf='default', addr_family='all', ping_count=3): + + st.log("BGP SP - {} interface IP address Ping test".format(dut)) + + debug_run = False + result = True + + if not BGPSP.bgp_sp_dut_present(dut): + st.log("BGP SP - Dut {} not present".format(dut)) + return False + + tb_dut = BGPSP.bgp_sp_get_dut_device(dut) + BGPSP.bgp_sp_show_dut_route_cmd_logs(dut) + + addr_family_list = BGPSP.bgp_sp_get_address_family_list(addr_family) + + for afmly in addr_family_list: + for link_name, link_data in sp_topo[dut][afmly]['link'].items(): + if link_data['type'] == 'LBK' : + continue + if 'rmt_ip' not in link_data.keys(): + continue + + if BGPSP.bgp_sp_is_tg_connected_link(dut, link_name): + st.log("Not Trying Pinf test for TG connected link {}".format(link_name)) + continue #only for now + + lcl_ip = link_data['ip'] + rmt_ip = link_data['rmt_ip'] + st.log("Pingtest for {} {} {} --{}-> {} ".format(afmly, tb_dut, lcl_ip, link_name, rmt_ip)) + + if not ipapi.ping(tb_dut, rmt_ip, family=afmly, count=ping_count): + st.log("Ping FAILED for {} {} {} --{}-> {} ".format(afmly, tb_dut, lcl_ip, link_name, rmt_ip)) + st.log("ERROR Dut {} Ping to {} FAILED ".format(tb_dut, rmt_ip)) + result = False + break + + if not result: + st.log("{} Ping Test FAILED".format(dut)) + BGPSP.bgp_sp_show_dut_cmd_logs(dut) + return False + + return result + + + @staticmethod + def bgp_sp_interface_address_ping_test(vrf='default', addr_family='all', ping_count=3): + """ + + :param config: + :param vrf: + :param addr_family: + :return: + """ + + st.log("BGP SP network Ping test for interface IP addressess") + + if not BGPSP.bgp_sp_topology_data_present() : + st.log("BGP SP Topology data not available") + st.log("SP topo:\n{}\n".format(sp_topo)) + return False + + threaded_run = True + result = True + dut_thread = [] + + dut_list = BGPSP.bgp_sp_get_dut_list() + + if not dut_list or len(dut_list) < 2: threaded_run = False + + for dut in dut_list : + if threaded_run: + dut_thread.append([BGPSP.bgp_sp_dut_interface_address_ping_test, dut, vrf, addr_family, ping_count]) + else : + result = BGPSP.bgp_sp_dut_interface_address_ping_test(dut, vrf, addr_family, ping_count) + + if not result: + BGPSP.bgp_sp_show_dut_if_cmd_logs(dut) + st.log("BGP SP - Ping Test Failed for {}".format(dut)) + break + + if threaded_run: + [out, exceptions] = utils.exec_all(bgplib.fast_start, dut_thread) + st.log("BGP SP - Ping Test Threaded Run result {}".format([out, exceptions])) + if False in out : result = False + + if not result: + st.log("BGP SP - Interface Ping Test FAILED") + + return result + + + @staticmethod + def bgp_sp_dut_interface_shut_noshut(dut, link_name, shut='yes'): + + action_str = "Shut down" if shut == 'yes' else 'Startup' + + tb_dut = BGPSP.bgp_sp_get_dut_device(dut) + tb_intf = BGPSP.bgp_sp_get_link_dut_interface(dut, link_name) + + if tb_dut == '' or tb_intf == '' : + st.log("BGP SP - tb dut {} or if {} empty".format(tb_dut, tb_intf)) + return False + + st.log("BGP SP - {} {} {}".format(action_str, dut, link_name)) + + if shut == 'yes': + result = ifapi.interface_shutdown(tb_dut, tb_intf) + else : + result = ifapi.interface_noshutdown(tb_dut, tb_intf) + + if not result : + st.log("BGP SP - {} {} {} Failed".format(action_str, dut, link_name)) + + return result + + + @staticmethod + def bgp_sp_config_ip_topology_on_testbed(): + st.banner("BGP SP Base Class Pre CONFIG - START") + result = BGPSP.bgp_sp_loopback_interface_config_unconfig(config='yes', vrf='default') + result = BGPSP.bgp_sp_loopback_address_config_unconfig(config='yes', addr_family='all') + result = BGPSP.bgp_sp_interface_address_all_config_unconfig(config='yes', addr_family='all') + result = BGPSP.bgp_sp_static_route_config_unconfig(config='yes', addr_family='all') + st.banner("BGP SP Base Class Pre CONFIG - END") + + + @staticmethod + def bgp_sp_unconfig_ip_topology_on_testbed(): + st.banner("BGP SP Base Class Pre CONFIG CLEANUP - START") + result = BGPSP.bgp_sp_static_route_config_unconfig('no') + result = BGPSP.bgp_sp_interface_address_all_config_unconfig(config='no') + result = BGPSP.bgp_sp_loopback_address_config_unconfig(config='no') + result = BGPSP.bgp_sp_loopback_interface_config_unconfig(config='no') + st.banner("BGP SP Base Class Pre CONFIG CLEANUP - END") + + + @staticmethod + def bgp_sp_bgp_configured(dut, vrf='default'): + + if dut not in bgp_topo.keys(): + return False + + if vrf not in bgp_topo[dut].keys(): + return False + + if bgp_topo[dut][vrf]['asn'] == 0 : + return False + + if bgp_topo[dut][vrf]['asn'] == '0' : + return False + + if bgp_topo[dut][vrf]['asn'] == '' : + return False + + return True + + + @staticmethod + def bgp_sp_get_bgp_asn(dut, vrf='default'): + + if not BGPSP.bgp_sp_bgp_configured(dut, vrf): + return 0 + + return int(bgp_topo[dut][vrf]['asn']) + + + @staticmethod + def bgp_sp_bgp_asn_match(dut, asn = 0, vrf='default'): + + if not BGPSP.bgp_sp_bgp_configured(dut, vrf): + return 0 + + bgp_asn = BGPSP.bgp_sp_get_bgp_asn(dut, vrf) + + if bgp_asn == asn : + return True + + return False + + + @staticmethod + def bgp_sp_is_ip_bgp_neigbour(dut, nbr_ip, addr_family, vrf='default'): + + if not BGPSP.bgp_sp_addr_family_valid(addr_family): + return False + + if not BGPSP.bgp_sp_bgp_configured(dut, vrf): + return False + + if nbr_ip in bgp_topo[dut][vrf][addr_family]['nbr'].keys(): + return True + + return False + + + @staticmethod + def bgp_sp_get_bgp_neigbour_ip_list(dut, addr_family, vrf='default'): + + if not BGPSP.bgp_sp_addr_family_valid(addr_family): + return [] + + if not BGPSP.bgp_sp_bgp_configured(dut, vrf): + return [] + + nbr_ip_list = [] + for nbr_ip in bgp_topo[dut][vrf][addr_family]['nbr'].keys(): + nbr_ip_list.append(nbr_ip) + + return copy.deepcopy(nbr_ip_list) + + + @staticmethod + def bgp_sp_get_bgp_neigbour_list(dut, addr_family, vrf='default'): + + if not BGPSP.bgp_sp_addr_family_valid(addr_family): + return {} + + if not BGPSP.bgp_sp_bgp_configured(dut, vrf): + return {} + + nbr_ip_data_list = {} + for nbr_ip, nbr_data in bgp_topo[dut][vrf][addr_family]['nbr'].items(): + nbr_ip_data_list.update( { nbr_ip: nbr_data} ) + + return copy.deepcopy(nbr_ip_data_list) + + + @staticmethod + def bgp_sp_get_bgp_neigbour_ip_between_duts(from_dut, to_dut, addr_family, from_vrf='default', to_vrf='default'): + + if not BGPSP.bgp_sp_addr_family_valid(addr_family): + return [] + + from_asn = BGPSP.bgp_sp_get_bgp_asn(from_dut, from_vrf) + to_asn = BGPSP.bgp_sp_get_bgp_asn(to_dut, to_vrf) + to_dut_ip_list = BGPSP.bgp_sp_get_dut_ip_address_list(to_dut, addr_family, vrf=to_vrf) + + if from_asn == 0 or to_asn == 0 : + return [] + + nbr_ip_list = [] + for nbr_ip, nbr_data in bgp_topo[from_dut][from_vrf][addr_family]['nbr'].items(): + if nbr_data['rmt_asn'] == to_asn : + if nbr_ip in to_dut_ip_list : + nbr_ip_list.append(nbr_ip) + + return copy.deepcopy(nbr_ip_list) + + + @staticmethod + def bgp_sp_get_bgp_network_prefix_list(dut, addr_family, vrf='default'): + + if not BGPSP.bgp_sp_addr_family_valid(addr_family): + return {} + + if not BGPSP.bgp_sp_bgp_configured(dut, vrf): + return {} + + nbr_nwip_list = {} + for prefix, subnet in bgp_topo[dut][vrf][addr_family]['network'].items(): + nbr_nwip_list.update( {prefix: subnet} ) + + return copy.deepcopy(nbr_nwip_list) + + + @staticmethod + def bgp_sp_bgp_config_unconfig(dut, local_asn, router_id='', vrf='default', config='yes', cli_type="vtysh"): + """ + + :param dut + :param local_asn: + :param vrf: + :param config + :return: + """ + action_str = 'Config' if config == 'yes' else 'Unconfig' + st.log("{}uring BGP router.".format(action_str)) + + if not BGPSP.bgp_sp_topology_data_present() : + st.log("BGP SP Topology data not available") + return False + + if not BGPSP.bgp_sp_dut_present(dut): + st.log("BGP SP - Dut {} doesnt exist".format(dut)) + return False + + if not local_asn : + st.log("BGP SP - local asn not provided ") + return False + + result = True + + if dut not in bgp_topo.keys(): + if config != 'yes' : + st.log("BGP SP - {} BGP dut doesnt exist".format(dut)) + return False + bgp_topo[dut] = {} + + if vrf not in bgp_topo[dut].keys(): + if config != 'yes' : + st.log("BGP SP - {} vrf {} BGP router doesnt exist".format(dut, vrf)) + return True + + bgp_topo[dut][vrf] = {} + bgp_topo[dut][vrf]['asn'] = int(local_asn) + bgp_topo[dut][vrf]['rtrid'] = '0' + bgp_topo[dut][vrf]['ipv4']={} + bgp_topo[dut][vrf]['ipv4']['nbr']={} + bgp_topo[dut][vrf]['ipv4']['unicast']={} + bgp_topo[dut][vrf]['ipv4']['network'] = {} + bgp_topo[dut][vrf]['ipv6']={} + bgp_topo[dut][vrf]['ipv6']['nbr']={} + bgp_topo[dut][vrf]['ipv6']['unicast']={} + bgp_topo[dut][vrf]['ipv6']['network'] = {} + + if bgp_topo[dut][vrf]['asn'] != 0 : + if bgp_topo[dut][vrf]['asn'] != local_asn: + st.log("BGP SP - bgp asns {} {} dont match".format(bgp_topo[dut][vrf]['asn'], local_asn)) + return False + + tb_dut = BGPSP.bgp_sp_get_dut_device(dut) + + if config == 'yes' : + + if not router_id or router_id == '' : + router_id = BGPSP.bgp_sp_get_dut_loopback_ip(dut, 0, 'ipv4') + + st.log("BGP SP - {} vrf {} Configuring BGP with as {}".format(dut, vrf, local_asn)) + + result = bgpapi.config_bgp_router(tb_dut, local_asn, router_id=router_id, keep_alive=30, hold=60, config='yes') + if not result : + st.log("BGP SP - {} vrf {} Configuring BGP with as {} FAILED".format(dut, vrf, local_asn)) + return False + + bgp_topo[dut][vrf]['asn'] = int(local_asn) + bgp_topo[dut][vrf]['ipv4']['rtr_id'] = router_id + + bgpapi.config_bgp_default(tb_dut, local_asn, 'ipv4-unicast', config='no', cli_type=cli_type) + + else : + + st.log("BGP SP - {} vrf {} Unconfiguring BGP with as {}".format(dut, vrf, local_asn)) + + result = bgpapi.config_bgp_router(tb_dut, local_asn, config='no') + if not result : + st.log("BGP SP - {} vrf {} UnConfiguring BGP with as {} FAILED".format(dut, vrf, local_asn)) + return False + + del bgp_topo[dut][vrf] + + #st.log("BGP SP - Bgp topo after {} router bgpn: {}".format(action_str, bgp_topo)) + return result + + + @staticmethod + def bgp_sp_dut_bgp_redistribute_connected_config_unconfig(dut, addr_family='all', tr_type='unicast', vrf='default', config='yes'): + + action_str = 'Config' if config == 'yes' else 'Unconfig' + st.log("{}uring BGP redistribute connected route on {}".format(action_str, dut)) + + if not BGPSP.bgp_sp_dut_present(dut): + st.log("Dut {} not present".format(dut)) + return False + + result = True + afmly_list = BGPSP.bgp_sp_get_address_family_list(addr_family) + + tb_dut = BGPSP.bgp_sp_get_dut_device(dut) + dut_asn = BGPSP.bgp_sp_get_bgp_asn(dut, vrf) + + if dut_asn == 0 : + st.log("BGP SP - BGP bot configured in dut {}".format(dut)) + return False + + for afmly in afmly_list: + bgpapi.config_address_family_redistribute(tb_dut, dut_asn, afmly, tr_type, "connected", config=config) + + return result + + + @staticmethod + def bgp_sp_bgp_redistribute_connected_config_unconfig(dut_list, addr_family='all', tr_type='unicast', vrf='default', config='yes'): + + action_str = 'Config' if config == 'yes' else 'Unconfig' + st.log("{}uring BGP redistribute connected route ".format(action_str)) + + result = True + threaded_run = True + dut_thread = [] + + dut_list = list(dut_list) if isinstance(dut_list, list) else [dut_list] + if not dut_list or len(dut_list) < 2: threaded_run = False + + for dut in dut_list : + if threaded_run: + dut_thread.append([BGPSP.bgp_sp_dut_bgp_redistribute_connected_config_unconfig, + dut, addr_family, tr_type, vrf, config]) + else : + result = BGPSP.bgp_sp_dut_bgp_redistribute_connected_config_unconfig( + dut, addr_family, tr_type, vrf, config) + + if not result: + st.log("BGP SP - Redistribute connected at {} failed".format(dut)) + break + + if threaded_run: + [out, exceptions] = utils.exec_all(bgplib.fast_start, dut_thread) + st.log("BGP SP - Redistribute connected Threaded Run result {}".format([out, exceptions])) + if False in out : result = False + + if not result: + st.log("BGP SP - {}uring Redistribute connected Static FAILED".format(action_str)) + + return result + + + @staticmethod + def bgp_sp_dut_bgp_redistribute_static_config_unconfig(dut, addr_family='all', tr_type='unicast', vrf='default', config='yes'): + + action_str = 'Config' if config == 'yes' else 'Unconfig' + st.log("{}uring BGP redistribute static route on {}".format(action_str, dut)) + + if not BGPSP.bgp_sp_dut_present(dut): + st.log("Dut {} not present".format(dut)) + return False + + result = True + afmly_list = BGPSP.bgp_sp_get_address_family_list(addr_family) + tb_dut = BGPSP.bgp_sp_get_dut_device(dut) + + dut_asn = BGPSP.bgp_sp_get_bgp_asn(dut, vrf) + if dut_asn == 0 : + st.log("BGP SP - BGP bot configured in dut {}".format(dut)) + return False + + for afmly in afmly_list: + bgpapi.config_address_family_redistribute(tb_dut, dut_asn, afmly, tr_type, "static", config=config) + + return result + + + @staticmethod + def bgp_sp_bgp_redistribute_static_config_unconfig(dut_list, addr_family='all', tr_type='unicast', vrf='default', config='yes'): + + action_str = 'Config' if config == 'yes' else 'Unconfig' + st.log("{}uring BGP redistribute static route ".format(action_str)) + + result = True + threaded_run = True + dut_thread = [] + + dut_list = list(dut_list) if isinstance(dut_list, list) else [dut_list] + if not dut_list or len(dut_list) < 2: threaded_run = False + + for dut in dut_list : + if threaded_run: + dut_thread.append([BGPSP.bgp_sp_dut_bgp_redistribute_static_config_unconfig, + dut, addr_family, tr_type, vrf, config]) + else : + result = BGPSP.bgp_sp_dut_bgp_redistribute_static_config_unconfig( + dut, addr_family, tr_type, vrf, config) + + if not result: + st.log("BGP SP - Redistribute static at {} failed".format(dut)) + break + + if threaded_run: + [out, exceptions] = utils.exec_all(bgplib.fast_start, dut_thread) + st.log("BGP SP - Redistribute Static Threaded Run result {}".format([out, exceptions])) + if False in out : result = False + + if not result: + st.log("BGP SP - {}uring Redistribute Static FAILED".format(action_str)) + + return result + + + @staticmethod + def bgp_sp_dut_bgp_network_advertise_config_unconfig(dut, network_list=[], addr_family='ipv4', vrf='default', config='yes', cli_type="vtysh"): + + action_str = 'Config' if config == 'yes' else 'Unconfig' + st.log("{}uring BGP network advertise on {}".format(action_str, dut)) + + if not BGPSP.bgp_sp_dut_present(dut): + st.log("Dut {} not present".format(dut)) + return False + + if not BGPSP.bgp_sp_addr_family_valid(addr_family) : + st.log("BGP SP - Invalid address family {}".format(addr_family)) + return False + + result = True + tb_dut = BGPSP.bgp_sp_get_dut_device(dut) + + dut_asn = BGPSP.bgp_sp_get_bgp_asn(dut, vrf) + if dut_asn == 0 : + st.log("BGP SP - BGP bot configured in dut {}".format(dut)) + return False + + for network_ip in network_list: + result = bgpapi.config_bgp_network_advertise(tb_dut, dut_asn, network_ip, route_map='', + addr_family=addr_family, config=config, cli_type=cli_type) + + return result + + + @staticmethod + def bgp_sp_bgp_network_advertise_config_unconfig(dut_list, network_list=[], addr_family='ipv4', vrf='default', config='yes'): + + action_str = 'Config' if config == 'yes' else 'Unconfig' + st.log("{}uring BGP network advertise ".format(action_str)) + + result = True + threaded_run = True + dut_thread = [] + + dut_list = list(dut_list) if isinstance(dut_list, list) else [dut_list] + if not dut_list or len(dut_list) < 2: threaded_run = False + + for dut in dut_list : + if threaded_run: + dut_thread.append([BGPSP.bgp_sp_dut_bgp_network_advertise_config_unconfig, + dut, network_list, addr_family, vrf, config]) + else : + result = BGPSP.bgp_sp_dut_bgp_network_advertise_config_unconfig( + dut, network_list, addr_family, vrf, config) + + if not result: + st.log("BGP SP - Network advertise at {} failed".format(dut)) + break + + if threaded_run: + [out, exceptions] = utils.exec_all(bgplib.fast_start, dut_thread) + st.log("BGP SP - Network advertise Threaded Run result {}".format([out, exceptions])) + if False in out : result = False + + if not result: + st.log("BGP SP - {}uring Network advertise FAILED".format(action_str)) + + return result + + + @staticmethod + def bgp_sp_bgp_deterministic_med_config_unconfig(dut_list, vrf='default', config='yes'): + + action_str = 'Config' if config == 'yes' else 'Unconfig' + st.log("{}uring BGP deterministic Med".format(action_str)) + + result = True + threaded_run = True + dut_thread = [] + + dut_list = list(dut_list) if isinstance(dut_list, list) else [dut_list] + if not dut_list or len(dut_list) < 2: threaded_run = False + + for dut in dut_list : + tb_dut = BGPSP.bgp_sp_get_dut_device(dut) + dut_asn = BGPSP.bgp_sp_get_bgp_asn(dut, vrf) + + if dut_asn == 0 : + st.log("BGP SP - BGP not configured in dut {}".format(dut)) + return False + + if threaded_run: + dut_thread.append([bgpapi.config_bgp_deterministic_med, tb_dut, dut_asn, config]) + else : + result = bgpapi.config_bgp_deterministic_med(tb_dut, dut_asn, config=config) + + if not result: + st.log("BGP SP - deterministic med at {} failed".format(dut)) + break + + if threaded_run: + [out, exceptions] = utils.exec_all(bgplib.fast_start, dut_thread) + st.log("BGP SP - Deterministic med Threaded Run result {}".format([out, exceptions])) + if False in out : result = False + + if not result: + st.log("BGP SP - {}uring Deterministic med FAILED".format(action_str)) + + return result + + + @staticmethod + def bgp_sp_bgp_compare_med_config_unconfig(dut_list, vrf='default', config='yes'): + + action_str = 'Config' if config == 'yes' else 'Unconfig' + st.log("{}uring BGP Always compare Med".format(action_str)) + + result = True + threaded_run = True + dut_thread = [] + + dut_list = list(dut_list) if isinstance(dut_list, list) else [dut_list] + if not dut_list or len(dut_list) < 2: threaded_run = False + + for dut in dut_list : + tb_dut = BGPSP.bgp_sp_get_dut_device(dut) + dut_asn = BGPSP.bgp_sp_get_bgp_asn(dut, vrf) + + if dut_asn == 0 : + st.log("BGP SP - BGP not configured in dut {}".format(dut)) + return False + + if threaded_run: + dut_thread.append([bgpapi.config_bgp_always_compare_med, tb_dut, dut_asn, config]) + else : + result = bgpapi.config_bgp_always_compare_med(tb_dut, dut_asn, config=config) + + if not result: + st.log("BGP SP - compare med at {} failed".format(dut)) + break + + if threaded_run: + [out, exceptions] = utils.exec_all(bgplib.fast_start, dut_thread) + st.log("BGP SP - compare med Threaded Run result {}".format([out, exceptions])) + if False in out : result = False + + if not result: + st.log("BGP SP - {}uring Always compare med FAILED".format(action_str)) + + return result + + + @staticmethod + def bgp_sp_bgp_ctoc_reflection_config_unconfig(dut_list, vrf='default', config='yes', cli_type="vtysh"): + + action_str = 'Config' if config == 'yes' else 'Unconfig' + st.log("{}uring BGP Client to Client Route Reflection".format(action_str)) + + result = True + threaded_run = True + dut_thread = [] + + dut_list = list(dut_list) if isinstance(dut_list, list) else [dut_list] + if not dut_list or len(dut_list) < 2: threaded_run = False + + for dut in dut_list : + tb_dut = BGPSP.bgp_sp_get_dut_device(dut) + dut_asn = BGPSP.bgp_sp_get_bgp_asn(dut, vrf) + + if dut_asn == 0 : + st.log("BGP SP - BGP not configured in dut {}".format(dut)) + return False + + if threaded_run: + dut_thread.append([bgpapi.create_bgp_client_to_client_reflection, tb_dut, dut_asn, config, cli_type]) + else : + result = bgpapi.create_bgp_client_to_client_reflection(tb_dut, dut_asn, config=config, cli_type= cli_type) + + if not result: + st.log("BGP SP - Client to Client RR at {} failed".format(dut)) + break + + if threaded_run: + [out, exceptions] = utils.exec_all(bgplib.fast_start, dut_thread) + st.log("BGP SP - Client to Client RR Threaded Run result {}".format([out, exceptions])) + if False in out : result = False + + if not result: + st.log("BGP SP - {}uring Client to Client RR FAILED".format(action_str)) + + return result + + + @staticmethod + def bgp_sp_bgp_neighbor_route_reflector_config_unconfig(dut, nbr_list=[], addr_family='ipv4', vrf='default', config='yes', cli_type="vtysh"): + + action_str = 'Config' if config == 'yes' else 'Unconfig' + st.log("{}uring BGP Neighbor Route Reflector clients ".format(action_str)) + + result = True + + tb_dut = BGPSP.bgp_sp_get_dut_device(dut) + dut_asn = BGPSP.bgp_sp_get_bgp_asn(dut, vrf) + + if dut_asn == 0 : + st.log("BGP SP - dut {} doesnt have bgp configured".format(dut)) + return False + + if len(nbr_list) != 0: + if not BGPSP.bgp_sp_addr_family_valid(addr_family) : + st.log("BGP SP - Invalid address family {}".format(addr_family)) + return False + + afmly_list = BGPSP.bgp_sp_get_address_family_list(addr_family) + + for afmly in afmly_list : + dut_nbr_list = BGPSP.bgp_sp_get_bgp_neigbour_ip_list(dut, afmly, vrf=vrf) + if len(nbr_list) == 0 : + rr_nbr_list = dut_nbr_list + else : + rr_nbr_list = nbr_list + + for nbr_ip in rr_nbr_list : + if nbr_ip not in dut_nbr_list : + st.log("BGP SP - nbr {} not in ngr list {} Failed".format(nbr_ip, dut_nbr_list)) + continue + + st.log("BGP SP - {}uring {} route-reflector-client {}.".format(action_str, dut, nbr_ip)) + result = bgpapi.create_bgp_route_reflector_client(tb_dut, dut_asn, afmly, nbr_ip, config=config) + if not result : + st.log("BGP SP - Configuring client reflection on {} {} bgp {} Failed".format(dut, afmly, dut_asn)) + break + + return result + + + @staticmethod + def bgp_sp_route_map_config_unconfig(dut, rmap_name, condition='permit', sequence='', config='yes', **kwargs): + + action_str = 'Config' if config == 'yes' else 'Unconfig' + st.log("{}uring route map".format(action_str)) + + result = True + tb_dut = BGPSP.bgp_sp_get_dut_device(dut) + + no_params = True if not kwargs else False + cfg_action = "no" if config == 'no' else "" + cmd_str = '' + + if rmap_name == '' : + st.log("BGP SP - Routemap name must") + return False + + if no_params : + if config == 'yes' : + if sequence == '' : + st.log("BGP SP - Sequence value for rmap must".format(sequence)) + return False + else : + if condition == '': + st.log("BGP SP - routemap condition permit/deny is must") + return False + else : + cmd_str = "route-map {} {} {}".format(rmap_name, condition, sequence) + + elif config == 'no' : + if sequence == '' : + cmd_str = "no route-map {}".format(rmap_name) + else : + if condition == '': + st.log("BGP SP - routemap condition permit/deny is must") + return False + else : + cmd_str = "no route-map {} {} {}".format(rmap_name, condition, sequence) + + if no_params : + #st.log("BGP SP - Route Map cmd without params is\n{}\n".format(cmd_str)) + st.vtysh_config(tb_dut, cmd_str) + result = True + return result + + if condition == '': + st.log("BGP SP - routemap condition permit/deny is must") + return False + + cmd_str = "route-map {} {} {}".format(rmap_name, condition, sequence) + + if 'metric' in kwargs : + metric = kwargs['metric'] + cmd_str += "\n {} set metric {} ".format(cfg_action, metric) + + if 'community' in kwargs : + community = kwargs['metric'] + cmd_str += "\n {} set community {} ".format(cfg_action, community) + + #st.log("BGP SP - Route Map cmd is \n{}\n".format(cmd_str)) + st.vtysh_config(tb_dut, cmd_str) + return result + + + + @staticmethod + def bgp_sp_bgp_nexthop_self_config_unconfig(dut_list=[], addr_family='all', vrf='default', force='no', config='yes'): + + action_str = 'Config' if config == 'yes' else 'Unconfig' + st.log("{}uring BGP nexthop self ".format(action_str)) + + result = True + afmly_list = BGPSP.bgp_sp_get_address_family_list(addr_family) + + for dut in dut_list : + tb_dut = BGPSP.bgp_sp_get_dut_device(dut) + dut_asn = BGPSP.bgp_sp_get_bgp_asn(dut, vrf) + + if dut_asn == 0 : + st.log("BGP SP - BGP not configured on dut {}".format(dut)) + continue + + for afmly in afmly_list : + dut_nbr_list = BGPSP.bgp_sp_get_bgp_neigbour_ip_list(dut, afmly, vrf=vrf) + for bgp_nbr in dut_nbr_list : + st.log("BGP SP - {}uring {} nexthop self {}.".format(action_str, dut, bgp_nbr)) + result = bgpapi.create_bgp_next_hop_self(tb_dut, dut_asn, afmly, bgp_nbr, force, config=config) + if not result : + st.log("BGP SP - Configuring nexthop self on {} {} bgp {} Failed".format(dut, afmly, dut_asn)) + break + else : + if config == 'yes' : + bgp_topo[dut][vrf][afmly]['nbr'][bgp_nbr].update({'nh_self': True}) + else : + del bgp_topo[dut][vrf][afmly]['nbr'][bgp_nbr]['nh_self'] + + return result + + + @staticmethod + def bgp_sp_bgp_neighbor_route_map_config_unconfig(dut, nbr_list, route_map, direction, addr_family, vrf='default', config='yes'): + + action_str = 'Config' if config == 'yes' else 'Unconfig' + st.log("{}uring BGP neighbor route map".format(action_str)) + + result = True + if not BGPSP.bgp_sp_addr_family_valid(addr_family) : + st.log("BGP SP - Invalid address family {}".format(addr_family)) + return False + + if direction != 'in' and direction != 'out' : + st.log("BGP SP - Invalid rmap direction {}".format(direction)) + return False + + tb_dut = BGPSP.bgp_sp_get_dut_device(dut) + dut_asn = BGPSP.bgp_sp_get_bgp_asn(dut, vrf) + + if dut_asn == 0 : + st.log("BGP SP - dut {} doesnt have bgp configured".format(dut)) + return False + + dut_nbr_list = BGPSP.bgp_sp_get_bgp_neigbour_ip_list(dut, addr_family, vrf=vrf) + + for nbr_ip in nbr_list : + if nbr_ip in dut_nbr_list : + bgpapi.config_bgp(dut=tb_dut, local_as=dut_asn, neighbor= nbr_ip, + addr_family=addr_family, config_type_list =["routeMap"], + routeMap=route_map, diRection= direction, config ='yes') + + result = True + if result : + if config == 'yes': + if direction == 'out' : + bgp_topo[dut][vrf][addr_family]['nbr'][nbr_ip].update({'route_map_out': route_map}) + if direction == 'in' : + bgp_topo[dut][vrf][addr_family]['nbr'][nbr_ip].update({'route_map_in': route_map}) + else : + if direction == 'out' : + del bgp_topo[dut][vrf][addr_family]['nbr'][nbr_ip]['route_map_out'] + if direction == 'in' : + del bgp_topo[dut][vrf][addr_family]['nbr'][nbr_ip]['route_map_in'] + + return result + + + + @staticmethod + def bgp_sp_bgp_neighbor_config_unconfig(dut, nbr_ip, nbr_asn, addr_family, vrf='default', config='yes', cli_type="vtysh"): + """ + + :param dut + :param nbr_ip: + :param nbr_asn: + :param addr_family + :param vrf + :param config + :return: + """ + + action_str = 'Config' if config == 'yes' else 'Unconfig' + st.log("{}uring BGP neighbor ".format(action_str)) + + if not BGPSP.bgp_sp_topology_data_present() : + st.log("BGP SP Topology data not available") + st.log("SP topo:\n{}\n".format(sp_topo)) + return False + + if not nbr_ip or not nbr_asn : + st.log("BGP SP - nbr_ip or asn not provided ") + return False + + if not BGPSP.bgp_sp_addr_family_valid(addr_family): + return False + + result = True + + if dut not in bgp_topo.keys(): + st.log("BGP SP - {} BGP dut not configured".format(dut, vrf)) + return False + + if vrf not in bgp_topo[dut].keys(): + st.log("BGP SP - {} BGP on vrf {} not configured".format(dut, vrf)) + return False + + tb_dut = BGPSP.bgp_sp_get_dut_device(dut) + + lcl_asn = bgp_topo[dut][vrf]['asn'] + if lcl_asn == 0 : + st.log("BGP SP - {} {} BGP lcl asn not set".format(dut, vrf)) + return False + + if config == 'yes' : + + if nbr_ip in bgp_topo[dut][vrf][addr_family]['nbr'].keys(): + + st.log("BGP SP - {} vrf {} BGP nbr {} exists".format(dut, vrf, nbr_ip)) + + nbr_data = bgp_topo[dut][vrf][addr_family]['nbr'][nbr_ip] + if nbr_data['rmt_asn'] != nbr_asn : + st.log("BGP SP - {} vrf {} BGP nbr {} rmt asns {} wont match".format(dut, vrf, nbr_ip, nbr_asn)) + return False + + result = True + bgp_topo[dut][vrf][addr_family]['nbr'][nbr_ip].update({'nbr_ip' : nbr_ip}) + + else : + st.log("BGP SP - {} vrf {} Configuring BGP nbr {} asn {}".format(dut, vrf, nbr_ip, nbr_asn)) + + result = bgpapi.config_bgp_neighbor(tb_dut, lcl_asn, nbr_ip, nbr_asn, addr_family, 3, 9, config='yes', cli_type=cli_type, connect_retry=1) + if not result: + st.log("BGP SP - {} vrf {} Configuring BGP nbr {} asin {} FAILED".format(dut, vrf, nbr_ip, nbr_asn)) + return False + + nbr_data = {'lcl_asn': lcl_asn, 'rmt_asn': nbr_asn, 'rmt_ip': nbr_ip } + bgp_topo[dut][vrf][addr_family]['nbr'].update({nbr_ip : nbr_data}) + else : + + if nbr_ip not in bgp_topo[dut][vrf][addr_family]['nbr'].keys(): + st.log("BGP SP - {} vrf {} BGP nbr {} doesnt exists".format(dut, vrf, nbr_ip)) + return False + + st.log("BGP SP - {} vrf {} UnConfiguring BGP nbr {} asn {} ".format(dut, vrf, nbr_ip, nbr_asn)) + + result = bgpapi.config_bgp_neighbor(tb_dut, lcl_asn, nbr_ip, nbr_asn, addr_family, config='no', cli_type=cli_type) + if not result: + st.log("BGP SP - {} vrf {} UnConfiguring BGP nbr {} asn {} FAILED".format(dut, vrf, nbr_ip, nbr_asn)) + + del bgp_topo[dut][vrf][addr_family]['nbr'][nbr_ip] + + #st.log("BGP SP - Bgp topo after {} router bgp nbr: {}".format(action_str, bgp_topo)) + return result + + + @staticmethod + def bgp_sp_bgp_neighbor_segment_config_unconfig(segment_data={}, addr_family='all', config='yes'): + + action_str = 'Config' if config == 'yes' else 'Unconfig' + st.log("{}uring Bgp segment".format(action_str)) + st.log("Input BGP Segment data : {}".format(segment_data)) + + result = True + threaded_run = False + if config != 'yes' : threaded_run = True + + lcl_dut = segment_data['lcl_dut'] + lcl_asn = segment_data['lcl_asn'] + rmt_dut = segment_data['rmt_dut'] + rmt_asn = segment_data['rmt_asn'] + + if 'lcl_vrf' in segment_data.keys(): + lcl_vrf = segment_data['lcl_vrf'] + else: + lcl_vrf ='default' + + if 'rmt_vrf' in segment_data.keys(): + rmt_vrf = segment_data['rmt_vrf'] + else: + rmt_vrf ='default' + + if 'lcl_link' in segment_data.keys(): + link = segment_data['lcl_link'] + else: + link ='none' + + st.log("BGP SP - {}uring bgp nbr {}:{}--{}--{}:{}".format( + action_str, lcl_dut, lcl_asn, + link, rmt_asn, rmt_dut)) + + + for error_breaking_loop in range(0,1) : + + ibgp_session = True if lcl_asn == rmt_asn else False + + if not BGPSP.bgp_sp_dut_present(lcl_dut) : + st.log("BGP SP - Dut {} not in topology list ".format(lcl_dut)) + result = False + break + + if not BGPSP.bgp_sp_dut_present(rmt_dut) : + st.log("BGP SP - Dut {} not in topology list ".format(rmt_dut)) + result = False + break + + addr_family_list = BGPSP.bgp_sp_get_address_family_list(addr_family) + link_list = BGPSP.bgp_sp_dut_get_connected_links(lcl_dut, rmt_dut) + + if not link_list or len(link_list) == 0 : + st.log("BGP SP - no links available between {} {}".format(lcl_dut, rmt_dut)) + + bgp_configured = False + + for afmly in addr_family_list: + lcl_ip = '' + rmt_ip = '' + link_name = '' + + if link == 'none' : + lcl_ip = BGPSP.bgp_sp_get_dut_loopback_ip(lcl_dut, 0, afmly) + rmt_ip = BGPSP.bgp_sp_get_dut_loopback_ip(rmt_dut, 0, afmly) + elif link == 'any' : + if len(link_list) == 0 : + st.log("BGP SP - No link present between {} {}".format(lcl_dut, rmt_dut)) + lcl_ip = BGPSP.bgp_sp_get_dut_loopback_ip(lcl_dut, 0, afmly) + rmt_ip = BGPSP.bgp_sp_get_dut_loopback_ip(rmt_dut, 0, afmly) + else : + link_name = link_list[0] + else : + if link not in link_list : + st.log("BGP SP - Link {} not present between {} {}".format(link, lcl_dut, rmt_dut)) + result = False + break + + link_name = link + + lcl_ip = BGPSP.bgp_sp_dut_get_link_local_ip(lcl_dut, link_name, afmly) + rmt_ip = BGPSP.bgp_sp_dut_get_link_remote_ip(lcl_dut, link_name, afmly) + + if lcl_ip == '' or rmt_ip == '' : + st.log("BGP SP - {} Link {} no have lcl/rmt {} {} ip assigned".format(afmly, link, lcl_ip, rmt_ip)) + continue + #return False + + if not bgp_configured : + + bgp_configured = True + + dut_thread = [] + + if config == 'yes' : + if not BGPSP.bgp_sp_bgp_configured(lcl_dut, lcl_vrf): + st.log("BGP SP - {} BGP on vrf {} not configured".format(lcl_dut, lcl_vrf)) + + if threaded_run: + dut_thread.append([BGPSP.bgp_sp_bgp_config_unconfig, + lcl_dut, lcl_asn, '', lcl_vrf, config]) + else : + result = BGPSP.bgp_sp_bgp_config_unconfig( + lcl_dut, lcl_asn, router_id='', vrf=lcl_vrf, config=config) + + if not result : + st.log("BGP SP - bgp config for {} {} FAILED".format(lcl_dut, lcl_asn)) + result = False + break + + if not BGPSP.bgp_sp_bgp_configured(rmt_dut, rmt_vrf) : + st.log("BGP SP - {} BGP on vrf {} not configured".format(rmt_dut, rmt_vrf)) + + + if threaded_run: + dut_thread.append([BGPSP.bgp_sp_bgp_config_unconfig, + rmt_dut, rmt_asn, '', rmt_vrf, config]) + else : + result = BGPSP.bgp_sp_bgp_config_unconfig( + rmt_dut, rmt_asn, router_id='', vrf=rmt_vrf, config=config) + + if not result : + st.log("BGP SP - bgp config for {} {} FAILED".format(rmt_dut, rmt_asn)) + result = False + break + + if threaded_run: + [out, exceptions] = utils.exec_all(bgplib.fast_start, dut_thread) + st.log("BGP SP - Bgp config Threaded Run result {}".format([out, exceptions])) + if False in out : result = False + + if not result : + st.log("BGP SP - Neighbor BGP config FAILED") + return False + + + dut_thread = [] + + if threaded_run: + dut_thread.append([BGPSP.bgp_sp_bgp_neighbor_config_unconfig, + lcl_dut, rmt_ip, rmt_asn, afmly, lcl_vrf, config]) + else : + result = BGPSP.bgp_sp_bgp_neighbor_config_unconfig( + lcl_dut, rmt_ip, rmt_asn, afmly, vrf=lcl_vrf, config=config) + + if not result : + st.log("BGP SP - bgp nbr config for {} {} {} {} FAILED".format(lcl_dut, rmt_ip, rmt_asn, afmly)) + result = False + break + + if threaded_run: + dut_thread.append([BGPSP.bgp_sp_bgp_neighbor_config_unconfig, + rmt_dut, lcl_ip, lcl_asn, afmly, rmt_vrf, config]) + else : + result = BGPSP.bgp_sp_bgp_neighbor_config_unconfig( + rmt_dut, lcl_ip, lcl_asn, afmly, vrf=rmt_vrf, config=config) + + if not result : + st.log("BGP SP - bgp nbr config for {} {} {} {} FAILED".format(rmt_dut, lcl_ip, lcl_asn, afmly)) + result = False + break + + if threaded_run: + [out, exceptions] = utils.exec_all(bgplib.fast_start, dut_thread) + st.log("BGP SP - Bgp Neighbor config Threaded Run result {}".format([out, exceptions])) + if False in out : result = False + + if not result : + break + + if not bgp_configured : + result = False + break + + result_str = "Success" if result else "FAILED" + + st.log("BGP SP - {}uring bgp nbr {}:{}--{}--{}:{} {}".format( + action_str, lcl_dut, lcl_asn, + link, rmt_asn, rmt_dut, result_str)) + return result + + + + @staticmethod + def bgp_sp_bgp_asn_map_config_unconfig(dut_asn_map={}, config='yes', vrf='default', addr_family='all', max_adjacency='all', cli_type="vtysh"): + """ + + :param dut_asn_map + :param config: + :param vrf: + :param addr_family + :param max_adjacency + :return: + """ + action_str = 'Config' if config == 'yes' else 'Unconfig' + st.log("{}uring list of bgp AS nodes.".format(action_str)) + + if not BGPSP.bgp_sp_topology_data_present() : + st.log("BGP SP Topology data not available") + st.log("SP topo:\n{}\n".format(sp_topo)) + return False + + if not dut_asn_map : + st.log("BGP SP DUT to AS Map not provided ") + st.log("dut_asn_map:\n{}\n".format(dut_asn_map)) + return False + + threaded_run = False + debug_run = False + result = True + + addr_family_list = BGPSP.bgp_sp_get_address_family_list(addr_family) + dut_asn_map = {k: dut_asn_map[k] for k in sorted(dut_asn_map)} + adj_limit = 10 if max_adjacency == 'all' else int(max_adjacency) + + st.log("BGP Dut Asn map: {}".format(dut_asn_map)) + + for dut, as_num in dut_asn_map.items(): + if dut not in sp_topo['dut_list']: + st.log("BGP SP - Dut {} not in BGP SP topology {}".format(dut, sp_topo['dut_list'])) + return False + + nbr_count = {} + nbr_visited = {} + for dut, as_num in dut_asn_map.items(): + nbr_visited[dut] = False + + result = BGPSP.bgp_sp_bgp_config_unconfig(dut, as_num, router_id='', vrf=vrf, config=config, cli_type=cli_type) + if not result : + st.log("BGP SP - bgp config for {} {} FAILED".format(dut, as_num)) + return False + + for dut, lcl_asn in dut_asn_map.items(): + tb_dut = sp_topo[dut]['device'] + + for link_name, link_data in sp_topo[dut]['intf'].items(): + + if link_data['type'] == 'LBK': + continue + + rmt_dut = link_data['rmt_dut'] + if rmt_dut not in dut_asn_map.keys(): + continue + + if nbr_visited[rmt_dut] : + continue + + rmt_asn = dut_asn_map[rmt_dut] + + from_node_adj = "{}{}".format(dut, rmt_dut) + if from_node_adj not in nbr_count.keys(): + nbr_count[from_node_adj] = 0 + + to_node_adj = "{}{}".format(dut, rmt_dut) + if to_node_adj not in nbr_count.keys(): + nbr_count[to_node_adj] = 0 + + if nbr_count[from_node_adj] >= adj_limit : + continue + + if nbr_count[to_node_adj] >= adj_limit : + continue + + nbr_added = False + for afmly in addr_family_list: + if link_name in sp_topo[dut][afmly]['link'].keys(): + + ip_data = sp_topo[dut][afmly]['link'][link_name] + + if 'rmt_ip' in ip_data.keys() : + + lcl_ip = ip_data['ip'] + rmt_ip = ip_data['rmt_ip'] + + result = BGPSP.bgp_sp_bgp_neighbor_config_unconfig(dut, rmt_ip, rmt_asn, afmly, vrf=vrf, config=config, cli_type=cli_type) + if not result : + st.log("BGP SP - bgp nbr config for {} {} {} {} FAILED".format(dut, rmt_ip, rmt_asn, afmly)) + return False + + result = BGPSP.bgp_sp_bgp_neighbor_config_unconfig(rmt_dut, lcl_ip, lcl_asn, afmly, vrf=vrf, config=config, cli_type=cli_type) + if not result : + st.log("BGP SP - bgp nbr config for {} {} {} {} FAILED".format(rmt_dut, lcl_ip, lcl_asn, afmly)) + return False + + nbr_added = True + + if nbr_added : + nbr_count[to_node_adj] += 1 + nbr_count[from_node_adj] += 1 + + nbr_visited[dut] = True + + if debug_run: + BGPSP.bgp_sp_show_dut_bgp_cmd_logs(dut) + + return result + + + + @staticmethod + def bgp_sp_clear_bgp(dut_list, addr_family='all'): + + if len(dut_list) == 0: + dut_list = sp_topo['dut_list'] + + st.log("BGP SP - Clearing BGP sessions {}".format(dut_list)) + + result = True + threaded_run = True + dut_thread = [] + + dut_list = list(dut_list) if isinstance(dut_list, list) else [dut_list] + if not dut_list or len(dut_list) < 2: threaded_run = False + + addr_family_list = BGPSP.bgp_sp_get_address_family_list(addr_family) + + for afmly in addr_family_list: + dut_thread = [] + for dut in dut_list : + if dut not in bgp_topo.keys(): + continue + + if BGPSP.bgp_sp_dut_is_tg(dut) : + continue + + tb_dut = sp_topo[dut]['device'] + + st.log("BGP SP - clearing {} bgp on {}".format(afmly , dut)) + if threaded_run: + if afmly == 'ipv4' : + dut_thread.append([bgpapi.clear_ip_bgp_vtysh, tb_dut]) + else : + dut_thread.append([bgpapi.clear_ipv6_bgp_vtysh, tb_dut]) + else : + if afmly == 'ipv4' : + bgpapi.clear_ip_bgp_vtysh(tb_dut) + else : + bgpapi.clear_ipv6_bgp_vtysh(tb_dut) + + if threaded_run : + [out, exceptions] = utils.exec_all(bgplib.fast_start, dut_thread) + st.log("BGP SP - Clear BGP Threaded Run result {}".format([out, exceptions])) + if False in out : result = False + + return result + + + @staticmethod + def bgp_sp_cleanup_bgp_routers(dut_list = []): + + if len(dut_list) == 0: + dut_list = sp_topo['dut_list'] + + st.log("BGP SP - Unconfiguring BGP routers {}".format(dut_list)) + + result = True + threaded_run = True + device_list = [] + dut_thread = [] + + for dut in dut_list : + if dut not in bgp_topo.keys(): + st.log("BGP SP - BGP not in topo..force deleting bgp router on {}".format(dut)) + + tb_dut = sp_topo[dut]['device'] + if not BGPSP.bgp_sp_dut_is_tg(dut) : + device_list.append(tb_dut) + dut_thread.append([bgpapi.unconfig_router_bgp, tb_dut]) + + if dut in bgp_topo.keys(): + del bgp_topo[dut] + + if not device_list : return True + + st.log("BGP SP - clearing bgp on {}".format(device_list)) + + if threaded_run : + [out, exceptions] = utils.exec_all(bgplib.fast_start, dut_thread) + st.log("BGP SP - Threaded Run result {}".format([out, exceptions])) + if False in out : result = False + else : + result = bgpapi.cleanup_router_bgp(device_list) + + return result + + + @staticmethod + def bgp_sp_dut_verify_all_bgp_sessions(dut, addr_family='all', state='up'): + + st.log("BGP SP - Verify Bgp session {} on {}.".format(state, dut)) + + if not BGPSP.bgp_sp_dut_present(dut): + st.log("Dut {} not present".format(dut)) + return False + + if not bgp_topo[dut] : + st.log("BGP SP - BGP not configured in {}".format(dut)) + return False + + result = True + tb_dut = BGPSP.bgp_sp_get_dut_device(dut) + + addr_family_list = BGPSP.bgp_sp_get_address_family_list(addr_family) + + vrf_list = list(bgp_topo[dut].keys()) + + for vrf in vrf_list : + for afmly in addr_family_list: + nbr_list = bgp_topo[dut][vrf][afmly]['nbr'].keys() + + result = bgpapi.verify_bgp_summary(tb_dut, family=afmly, neighbor=nbr_list, state='Established') + if result : + if state == 'down' : + st.log("BGP SP - BGP session not down for nghbor {}".format(nbr_list)) + BGPSP.bgp_sp_show_dut_route_cmd_logs(dut) + break + + if not result : + if state == 'up' : + st.log("BGP SP - BGP session not up for nghbor {}".format(nbr_list)) + BGPSP.bgp_sp_show_dut_route_cmd_logs(dut) + break + + if not result : + break + + result_str = "Success" if result else "Failed" + st.log("BGP SP - BGP Session {} check {}".format(state, result_str)) + + return result + + + @staticmethod + def bgp_sp_verify_all_bgp_sessions(dut_list=[], addr_family='all', state='up', threaded_run=True): + """ + + :param config: + :param vrf: + :param addr_family: + :return: + """ + + if len(dut_list) == 0: + dut_list = BGPSP.bgp_sp_get_dut_list() + + st.log("BGP SP - Verify {} Bgp Session {} on {}.".format(addr_family, state, dut_list)) + + result = True + dut_thread = [] + + dut_list = list(dut_list) if isinstance(dut_list, list) else [dut_list] + if not dut_list or len(dut_list) < 2: threaded_run = False + + for dut in dut_list : + + dut_result = True + if threaded_run: + dut_thread.append([BGPSP.bgp_sp_dut_verify_all_bgp_sessions, dut, addr_family, state]) + else : + dut_result = BGPSP.bgp_sp_dut_verify_all_bgp_sessions(dut, addr_family, state) + + if not dut_result: + result = False + st.log("BGP SP - BGP session test at {} failed".format(dut)) + + if threaded_run: + [out, exceptions] = utils.exec_all(bgplib.fast_start, dut_thread) + st.log("BGP SP - BGP session test Threaded Run result {}".format([out, exceptions])) + if False in out : result = False + + result_str = "Success" if result else "Failed" + st.log("BGP SP - BGP Session {} check {}".format(state, result_str)) + + return result + + + @staticmethod + def bgp_sp_linear_topo_bgp_config_unconfig(sess_type='eBGP', addr_family='all', ring='no', config='yes'): + + action_str = 'Config' if config == 'yes' else 'Unconfig' + topology = "Linear" if ring == 'no' else "Ring" + st.banner("{}uring {} topo {} session".format(action_str, topology, sess_type)) + + if BGPSP.bgp_sp_get_dut_count() < 2 : + st.log("BGP SP - Testbed doesnt have two duts") + st.report_fail("test_case_passed") + return False + + if config == 'yes' : + if ring == 'no' : + dut_linear_path = BGPSP.bgp_sp_find_linear_topo_in_dut_list() + else : + dut_linear_path = BGPSP.bgp_sp_find_ring_topo_in_dut_list() + else : + if ring == 'no' : + dut_linear_path = BGPSP.bgp_sp_dut_get_saved_linear_topo() + else : + dut_linear_path = BGPSP.bgp_sp_dut_get_saved_ring_topo() + + BGPSP.bgp_sp_show_topo_path(dut_linear_path) + + if not dut_linear_path['found'] : + st.log("BGP SP - Get linear path Failed") + st.log("BGP SP - {} topo {} session test FAILED".format(sess_type, topology)) + st.report_fail("test_case_failed") + return False + + dut_list = dut_linear_path['dut_list'] + path_segts = dut_linear_path['segment'] + + result = True + base_asn = 65001 + asn_index = 0 + + segment_count = len(path_segts) + + form_ring_session = False + if segment_count >= 2 and ring == 'yes' : + form_ring_session = True + + for segt_idx, segt_data_links in path_segts.items(): + + segt_data = segt_data_links[0] + + if form_ring_session and segt_idx == (segment_count - 1): + # last node and first node segment + lcl_asn = base_asn + asn_index + rmt_asn = path_segts[0]['lcl_asn'] + + elif sess_type == 'iBGP' : + # all node i bgp + lcl_asn = base_asn + rmt_asn = base_asn + + elif sess_type == 'eBGP' : + # all node ebgp + lcl_asn = base_asn + asn_index + asn_index += 1 + rmt_asn = base_asn + asn_index + + elif sess_type == 'eBGPiBGPeBGP' : + # N--e--N--i--N--e--N... + lcl_asn = base_asn + asn_index + curr_sess = segt_idx % 3 + if curr_sess == 0 or curr_sess == 2: #0-e 1=i 2=e + asn_index += 1 + rmt_asn = base_asn + asn_index + + elif sess_type == 'eBGPiBGPiBGP' : + # N--e--N--i--N--i--N--i--N ...all i + lcl_asn = base_asn + asn_index + if segt_idx == 0: + asn_index += 1 + rmt_asn = base_asn + asn_index + + elif sess_type == 'eBGPeBGPiBGP' : + # N--e--N--e--N--i--N--i--N ...all i + lcl_asn = base_asn + asn_index + if segt_idx <= 1: + asn_index += 1 + rmt_asn = base_asn + asn_index + + elif sess_type == 'iBGPeBGPiBGP' : + # N--i--N--e--N--i--N--i--N ...all i + lcl_asn = base_asn + asn_index + if segt_idx == 1: + asn_index += 1 + rmt_asn = base_asn + asn_index + + else : + st.log("BGP SP - Invalid BGP session Type passed {}".format(sess_type)) + return False + + segt_data.update({'lcl_asn': lcl_asn}) + segt_data.update({'rmt_asn': rmt_asn}) + + result = BGPSP.bgp_sp_bgp_neighbor_segment_config_unconfig(segt_data, addr_family, config=config) + if not result : + break + + if result and config == 'yes': + st.wait(3) + result = BGPSP.bgp_sp_verify_all_bgp_sessions(dut_list, addr_family='all') + if not result : + st.log("BGP SP - Linear topo session {} check Failed".format(sess_type)) + + result_str = "Success" if result else "Failed" + st.banner("BGP SP - {}uring {} topo {} session {}".format(action_str, topology, sess_type, result_str)) + + return result + + + @staticmethod + def bgp_sp_star_topo_bgp_config_unconfig(bgp_asn=65008, sess_type='eBGP', addr_family='all', config='yes'): + + action_str = 'Config' if config == 'yes' else 'Unconfig' + st.banner("{}uring Star topology {} session".format(action_str, sess_type)) + + if BGPSP.bgp_sp_get_dut_count() < 3 : + st.log("BGP SP - Testbed doesnt have minimum 3 duts") + st.report_fail("test_case_passed") + return False + + if config == 'yes' : + dut_star_path = BGPSP.bgp_sp_find_star_topo_in_dut_list([],'', 0) + else : + dut_star_path = BGPSP.bgp_sp_dut_get_saved_star_topo() + BGPSP.bgp_sp_show_topo_path(dut_star_path) + + if not dut_star_path['found'] : + st.log("BGP SP - Get Star path Failed") + st.report_fail("test_case_failed") + return False + + dut_list = dut_star_path['dut_list'] + path_segts = dut_star_path['segment'] + + result = True + if len(path_segts) < 2 : + st.log("BGP SP - Testbed doesnt have 3 connected nodes") + st.report_fail("test_case_failed") + return False + + core_asn = bgp_asn + spoke_end_as = bgp_asn + for segt_idx, segt_data_links in path_segts.items(): + + segt_data = segt_data_links[0] + + if sess_type == 'eBGP' : + spoke_end_as += 1 + + segt_data.update({'lcl_asn': core_asn}) + segt_data.update({'rmt_asn': spoke_end_as}) + + result = BGPSP.bgp_sp_bgp_neighbor_segment_config_unconfig(segt_data, addr_family, config=config) + if not result : + break + + if result and config == 'yes': + st.wait(3) + result = BGPSP.bgp_sp_verify_all_bgp_sessions(dut_list, addr_family='all') + if not result : + st.log("BGP SP - Star topology {} session check Failed".format(sess_type)) + + result_str = "Success" if result else "Failed" + st.banner("BGP SP - {}uring Star topology {} session {}".format(action_str, sess_type, result_str)) + + return result + + + @staticmethod + def bgp_sp_spine_leaf_bgp_config_unconfig(spine_asn=65001, leaf_asn=65003, addr_family='all', config='yes', cli_type="vtysh"): + + action_str = 'Config' if config == 'yes' else 'Unconfig' + st.banner("{}uring Spine Leaf BGP session".format(action_str)) + + topo_dut_count = BGPSP.bgp_sp_get_dut_count() + if topo_dut_count < 2 : + st.log("BGP SP - Testbed doesnt have minimum 2 duts") + st.report_fail("test_case_passed") + return False + + topo_dut_list = BGPSP.bgp_sp_get_dut_list() + + st.log("BGP SP - dut count {} list {}".format(topo_dut_count, topo_dut_list)) + + spine_list = [] + leaf_list = [] + dut_mid_index = topo_dut_count / 2 + dut_idx = 1 + + for dut in topo_dut_list: + if dut_idx <= dut_mid_index : + spine_list.append(dut) + else : + leaf_list.append(dut) + dut_idx += 1 + + st.log("BGP SP - Spine List {} Leaf list {}".format(spine_list, leaf_list)) + + if config == 'yes' : + spine_leaf_path = BGPSP.bgp_sp_find_spine_leaf_topo_in_dut_list(spine_list, leaf_list, save_path='yes') + else : + spine_leaf_path = BGPSP.bgp_sp_dut_get_saved_spine_leaf_topo() + + st.log("BGP SP - Leaf Spine Path {}".format(spine_leaf_path)) + + spine_leaf_session_count = 0 + + for spine_dut, spine_path in spine_leaf_path['spine_path'].items(): + + st.log("BGP SP - Spine Path \n") + BGPSP.bgp_sp_show_topo_path(spine_path) + + if not spine_path['found'] : + st.log("BGP SP - Spine {} doesnot have any leafs connected".format(spine_dut)) + continue + + dut_list = spine_path['dut_list'] + path_segts = spine_path['segment'] + + result = True + if len(path_segts) < 1 : + st.log("BGP SP - Spine {} doesnt have connected leafs".format(spine_dut)) + continue + + for segt_idx, segt_data_links in path_segts.items(): + segt_data = segt_data_links[0] + segt_data.update({'lcl_asn': spine_asn}) + segt_data.update({'rmt_asn': leaf_asn}) + + result = BGPSP.bgp_sp_bgp_neighbor_segment_config_unconfig(segt_data, addr_family, config=config) + if not result : + break + + spine_leaf_session_count += 1 + + if result and spine_leaf_session_count < 1 : + result = False + st.log("BGP SP - Zero spine leaf sessions") + return False + + if result and config == 'yes' : + st.wait(3) + result = BGPSP.bgp_sp_verify_all_bgp_sessions(dut_list, addr_family='all') + if not result : + st.log("BGP SP - Spine Leaf BGP session check Failed") + + result_str = "Success" if result else "Failed" + st.banner("BGP SP - {}uring Spine Leaf BGP session {}".format(action_str, result_str)) + + return result + + diff --git a/spytest/tests/routing/BGP/resource.py b/spytest/tests/routing/BGP/resource.py new file mode 100644 index 00000000000..8a55a48c64d --- /dev/null +++ b/spytest/tests/routing/BGP/resource.py @@ -0,0 +1,70 @@ +from spytest.dicts import SpyTestDict + + +def resource_data(vars): + data = SpyTestDict() + ############################################################################################## + # For N:N topology, define the Leaf Spine mapping as example show below for 1:1 topology. + ############################################################################################## + data.leaf_routers = ['leaf1'] + data.spine_routers = ['spine1'] + + data.leaf1 = vars.D2 + data.spine1 = vars.D1 + + data['l3_max_links_leaf_spine'] = 1 + data['l3_max_tg_links_each_leaf_spine'] = 1 + + ''' + ############################################################################################## + # For N:N topology, define the Leaf Spine mapping as example show below for 2:2 topology. + ############################################################################################## + data.leaf_routers = ['leaf1','leaf2'] + data.spine_routers = ['spine1','spine2'] + + data.spine1 = vars.D1 + data.spine2 = vars.D2 + data.leaf1 = vars.D3 + data.leaf2 = vars.D4 + + data['l3_max_links_leaf_spine'] = 2 + data['l3_max_tg_links_each_leaf_spine'] = 1 + ''' + + + ############################################################################################## + # Independent variable w.r.t topology + ############################################################################################## + data['mac_addres_tg_src_spine'] = '00:00:01:01:00:01' + data['mac_addres_tg_src_leaf'] = '00:00:02:02:00:01' + + data['ipv4_addres_first_octet'] = '11' + data['ipv6_addres_first_octet'] = '67fe' + data['ipv4_nw_addres_first_octet'] = '12' + data['ipv6_nw_addres_first_octet'] = '6712' + data['ipv4_static_addres_first_octet'] = '13' + data['ipv6_static_addres_first_octet'] = '6713' + + data['ipv4_addres_first_octet_tg_spine'] = '192' + data['ipv6_addres_first_octet_tg_spine'] = '1092' + data['ipv4_addres_first_octet_tg_leaf'] = '193' + data['ipv6_addres_first_octet_tg_leaf'] = '1093' + + # starting vlanid for creating ve over phy, or ve over lag + data['start_vlan_id'] = 11 + + # start lag id for portchannel interfaces [For ve over lag or lag phy interfaces] + data['start_lag_id'] = 11 + + ############################################################################################## + # This is CLOS based Leaf-Spine topology, where all Spine nodes have same AS number + # and all Leaf nodes have the same AS numbers, this is inline with various deployments. + ############################################################################################## + data['spine_as'] = 65001 + # Fixing 4-byte value for leaf_as + data['leaf_as'] = 650002 + + data['spine_tg_as'] = 64001 + data['leaf_tg_as'] = 63001 + + return data diff --git a/spytest/tests/routing/BGP/test_bgp.py b/spytest/tests/routing/BGP/test_bgp.py new file mode 100644 index 00000000000..1a425153e14 --- /dev/null +++ b/spytest/tests/routing/BGP/test_bgp.py @@ -0,0 +1,2185 @@ +import pytest + +from spytest import st, tgapi, SpyTestDict + +import apis.routing.ip as ipapi +import apis.routing.bgp as bgpapi +import apis.switching.vlan as vlanapi +import apis.system.logging as slog_obj +import apis.switching.portchannel as poapi +import BGP.bgplib as bgplib + +import utilities.common as utils + +vtysh_cli_type = "vtysh" + +@pytest.fixture(scope="module", autouse=True) +def bgp_module_hooks(request): + global bgp_cli_type + st.ensure_min_topology('D1D2:1', 'D1T1:1', 'D2T1:1') + bgplib.init_resource_data(st.get_testbed_vars()) + #bgp_cli_type = st.get_ui_type() + bgp_cli_type = "click" + if bgp_cli_type == 'click': + bgp_cli_type = 'vtysh' + bgp_pre_config() + yield + bgp_pre_config_cleanup() + + +# bgp module level pre config function +def bgp_pre_config(): + global topo + st.banner("Running with {} CLI RUN".format(bgp_cli_type)) + st.banner("BGP MODULE CONFIG - START") + ipapi.clear_ip_configuration(st.get_dut_names(), family='all', thread=True) + vlanapi.clear_vlan_configuration(st.get_dut_names()) + poapi.clear_portchannel_configuration(st.get_dut_names()) + if not st.is_community_build(): + # loopback config + bgplib.l3tc_vrfipv4v6_address_leafspine_loopback_config_unconfig(config='yes', config_type='all') + # TG Configuration + bgplib.l3tc_vrfipv4v6_address_leafspine_tg_config_unconfig(config='yes', config_type='all') + bgplib.l3tc_vrfipv4v6_address_leafspine_tg_bgp_config(config='yes', config_type='all') + st.banner("BGP MODULE CONFIG - END") + + +# bgp module level pre config cleanup function +def bgp_pre_config_cleanup(): + st.banner("BGP MODULE CONFIG CLEANUP - START") + if not st.is_community_build(): + # loopback unconfig + bgplib.l3tc_vrfipv4v6_address_leafspine_loopback_config_unconfig(config='no') + # TG uconfiguration + bgplib.l3tc_vrfipv4v6_address_leafspine_tg_config_unconfig(config='no') + bgplib.l3tc_vrfipv4v6_address_leafspine_tg_bgp_config(config='no') + ipapi.clear_ip_configuration(st.get_dut_names(), family='all', thread=True) + vlanapi.clear_vlan_configuration(st.get_dut_names()) + poapi.clear_portchannel_configuration(st.get_dut_names()) + st.banner("BGP MODULE CONFIG CLEANUP - END") + + +@pytest.fixture(scope="function") +def bgp_func_hooks(request): + yield + + +""" +BGP common test cases class - START + +add common bgp test casesfunctions (common to TestBGPRif or other non rif test classes +pick specific cases in derived classes +this is a abstract class with test cases and test should be run from this class +only it has to be run from derived classes. + +**DONTs** +*dont* name member functions of TestBGPCommon starting test.have test member functions in derived class only +*dont* add fixtures in base class. add fixtures only in derived class. +""" + + +class TestBGPCommon: + def ft_bgp_clear(self): + """ + + Validate clear ip bgp & sonic-clear functionality + """ + st.log("Clearing bgp neighbors from sonic cli") + [out, exceptions] = utils.exec_foreach(bgplib.fast_start, topo.dut_list, bgpapi.clear_ip_bgp) + st.log([out, exceptions]) + if not utils.poll_wait(bgplib.l3tc_vrfipv4v6_address_leafspine_bgp_check, 20, config_type='all'): + st.error("Neighbour is failed to Establish between Spine - Leaf") + st.report_fail('test_case_failed') + st.log("Clearing bgp neighbors from FRR cli") + [out, exceptions] = utils.exec_foreach(bgplib.fast_start, topo.dut_list, bgpapi.clear_bgp_vtysh) + st.log([out, exceptions]) + if not utils.poll_wait(bgplib.l3tc_vrfipv4v6_address_leafspine_bgp_check, 20, config_type='all'): + st.error("Neighbour is failed to Establish between Spine - Leaf") + st.report_fail('test_case_failed') + st.report_pass("test_case_passed") + + def ft_bgp_peer_traffic_check(self): + """ + + Traffic validation between Leaf Routers. + """ + TG_D1 = topo.tg_dut_list_name[0] + TG_D2 = topo.tg_dut_list_name[1] + tg_ob = topo['T1{}P1_tg_obj'.format(TG_D1)] + tg_ob.tg_traffic_control(port_handle=topo["T1{}P1_ipv4_tg_ph".format(TG_D1)], action='clear_stats') + tg_ob.tg_traffic_control(port_handle=topo["T1{}P1_ipv4_tg_ph".format(TG_D2)], action='clear_stats') + bgp_handle = topo['T1{}P1_ipv4_tg_bh'.format(TG_D1)] + tc_fail_flag = 0 + spine_as = int(bgplib.data['spine_as']) + st.log("Advertising Routes from one of the Leaf Router") + bgp_route = tg_ob.tg_emulation_bgp_route_config(handle=bgp_handle['handle'], mode='add', num_routes='100', + prefix='121.1.1.0', as_path='as_seq:1') + bgp_ctrl = tg_ob.tg_emulation_bgp_control(handle=bgp_handle['handle'], mode='start') + st.log("Check for route count in neighbour, before update delay timer configuration") + bgp_summary_spine_before_timer = bgpapi.show_bgp_ipv4_summary(topo.dut_list[1]) + rib_entries_before_update_timer = bgp_summary_spine_before_timer[0]['ribentries'] + st.log('RIB entries before update delay configuration : {}'.format(rib_entries_before_update_timer)) + st.log("Configure Update delay timer on one of the Leaf router") + bgpapi.create_bgp_update_delay(topo.dut_list[0], spine_as, '60',cli_type=bgp_cli_type) + st.log("Do clear ip bgp to validate the update delay timer") + bgpapi.clear_bgp_vtysh(topo.dut_list[0], address_family="ipv4") + if not utils.poll_wait(bgplib.l3tc_vrfipv4v6_address_leafspine_bgp_check, 20, config_type='ipv4'): + st.error("Neighbour is failed to Establish between Spine - Leaf after clear ip bgp") + tc_fail_flag = 1 + bgp_summary_spine_before_timer = bgpapi.show_bgp_ipv4_summary(topo.dut_list[1]) + rib_entries_before_update_timer = bgp_summary_spine_before_timer[0]['ribentries'] + st.log('RIB entries before update delay timer expiry : {}'.format(rib_entries_before_update_timer)) + if int(rib_entries_before_update_timer) >= 100: + st.error('Routes advertised to peer DUT, proir to update delay timer expiry') + tc_fail_flag = 1 + + # Sleep for update delay timer and the check the route count in neighbour + st.wait(60) + bgp_summary_spine_after_update_timer = bgpapi.show_bgp_ipv4_summary(topo.dut_list[1]) + rib_entries_after_update_timer = bgp_summary_spine_after_update_timer[0]['ribentries'] + st.log('RIB Entries after update delay timer expiry : {}'.format(rib_entries_after_update_timer)) + if int(rib_entries_after_update_timer) < 100: + st.error('Routes are not advertised to peer DUT, even after the update delay timer expiry') + tc_fail_flag = 1 + st.log("Initiating the Ipv4 traffic for those Routes from another Leaf Router") + src_handle = 'handle' + if tg_ob.tg_type == 'ixia': + src_handle = 'ipv4_handle' + tr1 = tg_ob.tg_traffic_config(port_handle=topo['T1{}P1_ipv4_tg_ph'.format(TG_D2)], + emulation_src_handle=topo['T1{}P1_ipv4_tg_ih'.format(TG_D2)][src_handle], + emulation_dst_handle=bgp_route['handle'], circuit_endpoint_type='ipv4', + mode='create', + transmit_mode='single_burst', pkts_per_burst='2000', length_mode='fixed', + rate_pps=1000) + stream_id1 = tr1['stream_id'] + tg_ob.tg_traffic_control(action='run', handle=stream_id1) + tg_ob.tg_traffic_control(action='stop', port_handle=topo['T1{}P1_ipv4_tg_ph'.format(TG_D2)]) + st.wait(5) + tg1_stats = tgapi.get_traffic_stats(tg_ob, port_handle=topo["T1{}P1_ipv4_tg_ph".format(TG_D1)]) + tg2_stats = tgapi.get_traffic_stats(tg_ob, port_handle=topo["T1{}P1_ipv4_tg_ph".format(TG_D2)]) + if not (int(tg2_stats.tx.total_packets) and int(tg1_stats.rx.total_packets)): + st.error('Recieved ZERO stats.') + tc_fail_flag = 1 + else: + percent_rx = float(int(tg1_stats.rx.total_packets) - int(tg2_stats.tx.total_packets)) / int( + tg2_stats.tx.total_packets) * 100 + st.log('tg1_stats.rx.total_packets : {}'.format(tg1_stats.rx.total_packets)) + st.log('tg2_stats.tx.total_packets : {}'.format(tg2_stats.tx.total_packets)) + st.log('percent_rx : {}'.format(percent_rx)) + if int(tg1_stats.rx.total_packets) < int(tg2_stats.tx.total_packets)*0.95: + tc_fail_flag = 1 + tg_ob.tg_emulation_bgp_control(handle=bgp_handle['handle'], mode='stop') + bgpapi.create_bgp_update_delay(topo.dut_list[0], spine_as, '0', cli_type=bgp_cli_type) + if tc_fail_flag: + st.report_fail("traffic_verification_failed") + st.report_pass('test_case_passed') + + def ft_bgp_graceful_restart_and_aware_routers(self): + """ + Verify the BGP peering between a graceful restart capable and graceful restart aware routers. + """ + + st.banner("Verify the BGP peering between a graceful restart capable and graceful restart aware routers.") + # Getting topo info between an spine and leaf + info = SpyTestDict() + info = bgplib.get_tg_topology_leafspine_bgp(dut_type = 'spine-leaf', max_tg_links= '0', nodes='2') + # NOTE: D1 is spine and D2 is leaf by default + + leaf_name = info['D2'] + spine_name = info['D1'] + + # Configure graceful restart capability on the Leaf router + bgpapi.config_bgp_graceful_restart(leaf_name, local_asn=info['D2_as'], user_command='preserve-fw-state', + config='add', cli_type=bgp_cli_type) + + # Verify bgp neighbors + result = bgpapi.verify_bgp_summary(leaf_name, family='ipv4', neighbor=info['D1D2P1_ipv4'], state='Established') + if not result: + bgplib.show_bgp_neighbors([leaf_name, spine_name], af='ipv4') + + # Delete the graceful restart capability + bgpapi.config_bgp_graceful_restart(leaf_name, local_asn=info['D2_as'], user_command='preserve-fw-state', + config='delete', cli_type=bgp_cli_type) + + if result: + st.log("BGP adjacency verified between graceful restart capable and aware router") + st.report_pass("test_case_passed") + else: + st.log("Failed to form BGP peering between graceful restart capable and aware router") + st.report_fail("bgp_ip_peer_establish_fail", info['D1D2P1_ipv4']) + + def ft_bgp_ipv4_no_route_aggregation_for_exact_prefix_match(self): + """ + Verify that when the 'aggregate-address' command creates a summary address, incoming networks that + exactly match that prefix are not aggregated. + """ + st.banner("Verify that when the 'aggregate-address' command creates a summary address, " + "incoming networks that exactly match that prefix are not aggregated.") + # Getting topo info between an spine and leaf + aggr_route = "122.1.1.0/24" + info = SpyTestDict() + info = bgplib.get_tg_topology_leafspine_bgp(dut_type='spine-leaf', max_tg_links='1', nodes='2') + # NOTE: D1 is spine and D2 is leaf by default + + leaf_name = info['D2'] + spine_name = info['D1'] + TG_D2 = 'D2' + + # Verify bgp neighbors between leaf and Tg + result = bgpapi.verify_bgp_summary(leaf_name, family='ipv4', neighbor=info['T1D2P1_ipv4'], state='Established') + + if not result: + bgplib.show_bgp_neighbors([leaf_name, spine_name], af='ipv4') + st.report_fail("test_case_failed") + + # Configure the route aggregation on the Leaf router + bgpapi.create_bgp_aggregate_address(leaf_name, local_asn=info['D2_as'], address_range=aggr_route, + family="ipv4", config="add", cli_type=bgp_cli_type) + tg_ob = info['T1{}P1_tg_obj'.format(TG_D2)] + bgp_handle = info['T1{}P1_ipv4_tg_bh'.format(TG_D2)] + st.log("Advertising Routes from the Leaf Router") + bgp_route = tg_ob.tg_emulation_bgp_route_config(handle=bgp_handle['handle'], mode='add', num_routes='4', + prefix='122.1.1.0', as_path='as_seq:1') + + st.log("BGPROUTE: "+str(bgp_route)) + st.log("Advertise those routes from Ixia") + ctrl1=tg_ob.tg_bgp_routes_control(handle=bgp_handle['handle'], route_handle=bgp_route['handle'], + mode='readvertise') + st.log("TR_CTRL: "+str(ctrl1)) + st.wait(5) + + # Verify the prefix on spine + entries = bgpapi.get_ip_bgp_route(spine_name, family="ipv4", network=aggr_route) + + if not entries: + bgpapi.create_bgp_aggregate_address(leaf_name, local_asn=info['D2_as'], address_range=aggr_route, + family="ipv4", config="delete", cli_type=bgp_cli_type) + st.report_fail("bgp_route_info", aggr_route, "not found") + AS_PATH_STRING = str(entries[0]['as_path']) + asn = AS_PATH_STRING.split(" ") + + # If the route is aggregated the as_path will have only the peer-asn if not the whole as_path + if not int(asn[0]) == info['D2_as'] and len(asn) > 1: + bgpapi.create_bgp_aggregate_address(leaf_name, local_asn=info['D2_as'], address_range=aggr_route, + family="ipv4", config="delete", cli_type=bgp_cli_type) + st.report_fail("bgp_aggregation_pass", aggr_route) + bgpapi.create_bgp_aggregate_address(leaf_name, local_asn=info['D2_as'], address_range=aggr_route, + family="ipv4", config="delete", cli_type=bgp_cli_type) + bgp_route1 = tg_ob.tg_emulation_bgp_route_config(handle=bgp_handle['handle'], mode='remove', num_routes='4', + prefix='122.1.1.0', as_path='as_seq:1') + st.report_pass("test_case_passed") + + def ft_bgp_ipv4_route_aggregation_atomic_aggregate_without_as_set(self): + """ + Verify that the AGGREGATOR and ATOMIC AGGREGATE attribute is included when an AS_SET is not configured + in aggregation. + """ + st.banner("Verify that the AGGREGATOR and ATOMIC AGGREGATE attribute is included when an AS_SET " + "is not configured in aggregation.") + aggr_route = "123.1.0.0/16" + info = SpyTestDict() + info = bgplib.get_tg_topology_leafspine_bgp(dut_type = 'spine-leaf', max_tg_links='1', nodes='2') + # NOTE: D1 is spine and D2 is leaf by default + + leaf_name = info['D2'] + spine_name = info['D1'] + TG_D2 = 'D2' + + # Verify bgp neighbors between leaf and Tg + result = bgpapi.verify_bgp_summary(leaf_name, family='ipv4', neighbor=info['T1D2P1_ipv4'], state='Established') + + if not result: + bgplib.show_bgp_neighbors([leaf_name, spine_name], af='ipv4') + st.report_fail("test_case_failed") + + # Configure the route aggregation on the Leaf router + bgpapi.create_bgp_aggregate_address(leaf_name, local_asn=info['D2_as'], address_range=aggr_route, + summary="summary-only", family="ipv4", config="add", cli_type=bgp_cli_type) + st.log(" clear the syslog file") + slog_obj.clear_logging(spine_name) + # Enable zebra logs + bgpapi.bgp_debug_config(spine_name, message="updates", prefix=aggr_route) + string = "bgp#supervisord:" + + tg_ob=info['T1{}P1_tg_obj'.format(TG_D2)] + bgp_handle = info['T1{}P1_ipv4_tg_bh'.format(TG_D2)] + st.log("Configure routes to be advertised from Ixia") + bgp_route = tg_ob.tg_emulation_bgp_route_config(handle=bgp_handle['handle'], mode='add', num_routes='4', + prefix='123.1.1.0', as_path='as_seq:1') + st.log("Advertise those routes from Ixia") + ctrl1=tg_ob.tg_bgp_routes_control(handle=bgp_handle['handle'], route_handle=bgp_route['handle'], + mode='readvertise') + st.log("TR_CTRL: "+str(ctrl1)) + st.wait(5) + + st.log("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&") + st.log(slog_obj.show_logging(spine_name, lines=200)) + st.log("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&") + + st.log("Verify logs on spine to check if aggregator and atomic ") + log_msg = slog_obj.get_logging_count(spine_name, filter_list=['{}'.format(string), 'atomic-aggregate', + 'aggregated by {}'.format(info['D2_as']), + 'path {}'.format(info['D2_as'])]) + + st.log("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&") + st.log(log_msg) + st.log("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&") + + bgpapi.create_bgp_aggregate_address(leaf_name, local_asn=info['D2_as'], address_range=aggr_route, + summary="summary-only", family="ipv4", config="delete", cli_type=bgp_cli_type) + bgp_route1 = tg_ob.tg_emulation_bgp_route_config(handle=bgp_handle['handle'], mode='remove', num_routes='4', + prefix='123.1.1.0', as_path='as_seq:1') + if not log_msg: + st.report_fail("bgp_aggregation_fail", aggr_route) + st.report_pass("test_case_passed") + + def ft_bgp_ipv6_route_aggregation_with_as_set(self): + """ + Verify that aggregation of ipv6 prefixes occurs correctly with as-set keyword + """ + st.banner("Verify that aggregation of ipv6 prefixes occurs correctly with as-set keyword") + aggr_route = "6002:1::0/64" + info = SpyTestDict() + info = bgplib.get_tg_topology_leafspine_bgp(dut_type = 'spine-leaf', max_tg_links= '1', nodes='2') + # NOTE: D1 is spine and D2 is leaf by default + + leaf_name = info['D2'] + spine_name = info['D1'] + TG_D2 = 'D2' + + # Configure the route aggregation on the Leaf router + bgpapi.create_bgp_aggregate_address(leaf_name, local_asn=info['D2_as'], address_range=aggr_route, + summary="summary-only", as_set="as-set", family="ipv6", config="add", cli_type=bgp_cli_type) + tg_ob=info['T1{}P1_tg_obj'.format(TG_D2)] + bgp_handle = info['T1{}P1_ipv6_tg_bh'.format(TG_D2)] + + # Starting the BGP device. + bgp_ctrl=tg_ob.tg_emulation_bgp_control(handle=bgp_handle['handle'], mode='start') + st.log("BGPCTRL: "+str(bgp_ctrl)) + # Verified at neighbor. + # Verify bgp neighbors between leaf and Tg + result = bgpapi.verify_bgp_summary(leaf_name, family='ipv6', neighbor=info['T1D2P1_ipv6'], state='Established') + + if not result: + bgplib.show_bgp_neighbors([leaf_name, spine_name], af='ipv6') + st.report_fail("test_case_failed") + st.log("BGP neighbors established.") + st.log("Advertising Routes from the Leaf Router") + + bgp_route_ipv6 = tg_ob.tg_emulation_bgp_route_config(handle=bgp_handle['handle'], mode='add', ip_version='6', + num_routes='4', prefix='6002:1::0', as_path='as_seq:1') + st.log("BGPROUTE: "+str(bgp_route_ipv6)) + + bgp_ctrl = tg_ob.tg_emulation_bgp_control(handle=bgp_handle['handle'], mode='start') + + ctrl1=tg_ob.tg_bgp_routes_control(handle=bgp_handle['handle'], route_handle=bgp_route_ipv6['handle'], + mode='readvertise') + st.log("TR_CTRL: "+str(ctrl1)) + st.wait(10) + + # Verify the prefix on spine + st.log("Verify the prefix on spine") + entries = bgpapi.get_ip_bgp_route(spine_name, family="ipv6", network="6002:1::/64") + + if not entries: + bgpapi.create_bgp_aggregate_address(leaf_name, local_asn=info['D2_as'], address_range=aggr_route, + summary="summary-only", as_set="as-set", family="ipv6", + config="delete", cli_type=bgp_cli_type) + st.report_fail("bgp_route_info", aggr_route, "not found") + + AS_PATH_STRING = str(entries[0]['as_path']) + asn = AS_PATH_STRING.split(" ") + + # If the route is aggregated the as_path will have the whole as_path because of as-set configuration + if not int(asn[0]) == info['D2_as'] and len(asn) > 1: + bgpapi.create_bgp_aggregate_address(leaf_name, local_asn=info['D2_as'], address_range=aggr_route, + summary="summary-only", as_set="as-set", family="ipv6", + config="delete", cli_type=bgp_cli_type) + st.report_fail("bgp_aggregation_fail", aggr_route) + bgpapi.create_bgp_aggregate_address(leaf_name, local_asn=info['D2_as'], address_range=aggr_route, + summary="summary-only", as_set="as-set", family="ipv6", + config="delete", cli_type=bgp_cli_type) + bgp_route_ipv6_rem = tg_ob.tg_emulation_bgp_route_config(handle=bgp_handle['handle'], mode='remove', + ip_version='6', num_routes='4', prefix='6002:1::0', + as_path='as_seq:1') + st.report_pass("test_case_passed") + + def ft_bgp_route_aggregation_4byteASN(self): + """ + Validate AS4_Aggregate attribute w.r.to the BGP 4-byte ASN Feature + """ + aggr_route = "151.1.0.0/16" + info = SpyTestDict() + info = bgplib.get_tg_topology_leafspine_bgp(dut_type='spine-leaf', max_tg_links='1', nodes='2') + # NOTE: D1 is spine and D2 is leaf by default + + leaf_name = info['D2'] + spine_name = info['D1'] + TG_D2 = 'D2' + + # Verify bgp neighbors between leaf and Tg + if not utils.poll_wait(bgpapi.verify_bgp_summary, 30, leaf_name, family='ipv4', neighbor=info['T1D2P1_ipv4'], + state='Established'): + bgplib.show_bgp_neighbors([leaf_name, spine_name], af='ipv4') + st.error("Neighbour is failed to Establish between Leaf - TG") + st.report_fail('test_case_failed') + + # Configure the route aggregation on the Leaf router + bgpapi.create_bgp_aggregate_address(leaf_name, local_asn=info['D2_as'], address_range=aggr_route, + summary="summary-only", as_set="as-set", family="ipv4", config="add", cli_type=bgp_cli_type) + tg_ob=info['T1{}P1_tg_obj'.format(TG_D2)] + bgp_handle = info['T1{}P1_ipv4_tg_bh'.format(TG_D2)] + + st.log("Advertising Routes from the Leaf Router") + bgp_route = tg_ob.tg_emulation_bgp_route_config(handle=bgp_handle['handle'], mode='add', num_routes='4', + prefix='151.1.1.0', as_path='as_seq:1') + st.log("Advertise those routes from Ixia") + ctrl1=tg_ob.tg_bgp_routes_control(handle=bgp_handle['handle'], route_handle=bgp_route['handle'], + mode='readvertise') + st.log("TR_CTRL: "+str(ctrl1)) + st.wait(10) + + # Verify the prefix on spine + entries = bgpapi.get_ip_bgp_route(spine_name, family="ipv4", network=aggr_route) + + if not entries: + bgpapi.create_bgp_aggregate_address(leaf_name, local_asn=info['D2_as'], address_range=aggr_route, + summary="summary-only", as_set="as-set", family="ipv4", + config="delete", cli_type=bgp_cli_type) + st.report_fail("bgp_route_info", aggr_route, "not found") + AS_PATH_STRING = str(entries[0]['as_path']) + asn = AS_PATH_STRING.split(" ") + + # Since as-set is set, as-path will have the whole path for the aggregated route including the 4-byte AS. + if not int(asn[0]) == info['D2_as'] and len(asn)>1: + bgpapi.create_bgp_aggregate_address(leaf_name, local_asn=info['D2_as'], address_range=aggr_route, + summary="summary-only", as_set="as-set", family="ipv4", + config="delete", cli_type=bgp_cli_type) + st.report_fail("bgp_aggregation_fail", aggr_route) + bgpapi.create_bgp_aggregate_address(leaf_name, local_asn=info['D2_as'], address_range=aggr_route, + summary="summary-only", as_set="as-set", family="ipv4", + config="delete", cli_type=bgp_cli_type) + st.report_pass("test_case_passed") + + +""" +BGP common test cases class - END +""" + + +""" +BGP Neighbor over regular router interface fixture, class and test cases - START +""" + + +def bgp_rif_pre_config(): + global topo + st.banner("BGP RIF CLASS CONFIG - START") + # underlay config + bgplib.l3tc_underlay_config_unconfig(config='yes', config_type='phy') + bgplib.l3tc_vrfipv4v6_address_leafspine_config_unconfig(config='yes', config_type='all') + # Ping Verification + if not bgplib.l3tc_vrfipv4v6_address_leafspine_ping_test(config_type='all', ping_count=3): + st.error("Ping failed in between Spine - Leaf") + st.report_fail('test_case_failed') + bgplib.l3tc_vrfipv4v6_address_leafspine_bgp_config(config='yes') + + bgplib.l3tc_vrfipv4v6_address_leafspine_tg_bgp_config(config='yes', config_type='all', class_reconfig='Yes') + st.wait(10) + + # BGP Neighbour Verification + if not utils.poll_wait(bgplib.l3tc_vrfipv4v6_address_leafspine_bgp_check, 10, config_type='all'): + st.error("Neighbour is failed to Establish between Spine - Leaf") + st.report_fail('test_case_failed') + st.log("Getting all topology info related to connectivity / TG and other parameters between duts") + topo = bgplib.get_leaf_spine_topology_info() + st.banner("BGP RIF CLASS CONFIG - END") + + +def bgp_rif_pre_config_cleanup(): + st.banner("BGP RIF CLASS CONFIG CLEANUP - START") + bgplib.l3tc_vrfipv4v6_address_leafspine_bgp_config(config='no') + bgplib.l3tc_vrfipv4v6_address_leafspine_config_unconfig(config='no') + # cleanup underlay config + bgplib.l3tc_underlay_config_unconfig(config='no', config_type='phy') + st.banner("BGP RIF CLASS CONFIG CLEANUP - END") + + +@pytest.fixture(scope='class') +def bgp_rif_class_hook(request): + bgp_rif_pre_config() + yield + bgp_rif_pre_config_cleanup() + + +# TestBGPRif class +@pytest.mark.usefixtures('bgp_rif_class_hook') +class TestBGPRif(TestBGPCommon): + + @pytest.mark.bgp_ft + @pytest.mark.community + @pytest.mark.community_pass + def test_ft_bgp_v6_link_local_bgp(self): + """ + + Verify that BGP peer session is established with v6 link local address + """ + + # Getting topo info between an spine and leaf + info = SpyTestDict() + info = bgplib.get_tg_topology_leafspine_bgp(dut_type='spine-leaf', max_tg_links='0', nodes='2') + # NOTE: D1 is spine and D2 is leaf by default + + leaf_name = info['D2'] + spine_name = info['D1'] + + result = bgpapi.create_bgp_neighbor_interface(leaf_name, info['D2_as'], info['D2D1P1'], info['D1_as'], 'ipv6', cli_type=bgp_cli_type) + if not result: + st.error("Failed to enable BGP on interface {}".format(info['D2D1P1'])) + st.report_fail('test_case_failed') + + result = bgpapi.create_bgp_neighbor_interface(spine_name, info['D1_as'], info['D1D2P1'], info['D2_as'], 'ipv6', cli_type=bgp_cli_type) + if not result: + # Clear the previous config + bgpapi.create_bgp_neighbor_interface(leaf_name, info['D2_as'], info['D2D1P1'], info['D1_as'], 'ipv6', 'no', cli_type=bgp_cli_type) + st.error("Failed to enable BGP on interface {}".format(info['D1D2P1'])) + st.report_fail('test_case_failed') + + # Verify bgp session on interface + if not utils.poll_wait(bgpapi.verify_bgp_summary, 130, leaf_name, family='ipv6', neighbor=info['D2D1P1'], + state='Established'): + # show neighbors for debug in case of failure and Clear all config + utils.exec_all(True, [[bgpapi.show_bgp_ipv6_neighbor_vtysh, leaf_name], [bgpapi.show_bgp_ipv6_neighbor_vtysh, spine_name]]) + bgpapi.create_bgp_neighbor_interface(leaf_name, info['D2_as'], info['D2D1P1'], info['D1_as'], 'ipv6', 'no', cli_type=bgp_cli_type) + bgpapi.create_bgp_neighbor_interface(spine_name, info['D1_as'], info['D1D2P1'], info['D2_as'], 'ipv6', 'no', cli_type=bgp_cli_type) + st.error("BGP Neighbor failed to Establish between DUT and Partner") + st.report_fail('operation_failed') + utils.exec_all(True, [[bgpapi.show_bgp_ipv6_neighbor_vtysh, leaf_name], + [bgpapi.show_bgp_ipv6_neighbor_vtysh, spine_name]]) + bgpapi.create_bgp_neighbor_interface(leaf_name, info['D2_as'], info['D2D1P1'], info['D1_as'], 'ipv6', 'no', cli_type=bgp_cli_type) + bgpapi.create_bgp_neighbor_interface(spine_name, info['D1_as'], info['D1D2P1'], info['D2_as'], 'ipv6', 'no', cli_type=bgp_cli_type) + st.report_pass("test_case_passed") + + @pytest.mark.bgp_clear + @pytest.mark.bgp_ft + @pytest.mark.community + @pytest.mark.community_pass + def test_ft_bgp_clear(self): + TestBGPCommon.ft_bgp_clear(self) + + @pytest.mark.bgp_traffic + @pytest.mark.bgp_ft + @pytest.mark.community + @pytest.mark.community_fail + def test_ft_bgp_peer_traffic_check(self): + TestBGPCommon.ft_bgp_peer_traffic_check(self) + + @pytest.mark.bgp_ft + @pytest.mark.community + @pytest.mark.community_pass + def test_ft_bgp_graceful_restart_and_aware_routers(self): + TestBGPCommon.ft_bgp_graceful_restart_and_aware_routers(self) + + @pytest.mark.bgp_ft + @pytest.mark.community + @pytest.mark.community_fail + def test_ft_bgp_ipv4_no_route_aggregation_for_exact_prefix_match(self): + TestBGPCommon.ft_bgp_ipv4_no_route_aggregation_for_exact_prefix_match(self) + + @pytest.mark.bgp_ft + @pytest.mark.community + @pytest.mark.community_fail + def test_ft_bgp_ipv4_route_aggregation_atomic_aggregate_without_as_set(self): + TestBGPCommon.ft_bgp_ipv4_route_aggregation_atomic_aggregate_without_as_set(self) + + @pytest.mark.bgp_ft + @pytest.mark.community + @pytest.mark.community_fail + def test_bgp_route_aggregation_4byteASN(self): + TestBGPCommon.ft_bgp_route_aggregation_4byteASN(self) + + @pytest.mark.bgp_ft + def test_ft_bgp_ipv6_route_aggregation_with_as_set(self): + TestBGPCommon.ft_bgp_ipv6_route_aggregation_with_as_set(self) + + @pytest.mark.bgp_ft + def test_ft_bgp_v4_dyn_nbr(self): + """ + Verify that BGP peering is formed with dynamic neighbors having 4btye ASN + """ + + # Getting topo info between an spine and leaf + info = SpyTestDict() + info = bgplib.get_tg_topology_leafspine_bgp(dut_type='spine-leaf', max_tg_links='0', nodes='2') + # NOTE: D1 is spine D2 is leaf by default + + leaf_name = info['D2'] + spine_name = info['D1'] + + # Configure an ip address on Spine + spine_ipv4 = '45.45.45.45' + ipapi.config_ip_addr_interface(spine_name, info['D1D2P1'], spine_ipv4, 24) + + # Configure an ip address on Leaf + leaf_ipv4 = '45.45.45.46' + ipapi.config_ip_addr_interface(leaf_name, info['D2D1P1'], leaf_ipv4, 24) + # if bgp_cli_type == "klish": + # bgpapi.config_bgp_peer_group(leaf_name, info['D2_as'], 'leaf_spine', config="yes", cli_type=vtysh_cli_type) + # Add a listen range on Leaf + listen_range = '45.45.45.0' + bgpapi.config_bgp_listen(leaf_name, info['D2_as'], listen_range, 24, 'leaf_spine', 0, cli_type=bgp_cli_type) + + # Add neighbor on Spine + bgpapi.create_bgp_neighbor_use_peergroup(spine_name, info['D1_as'], 'spine_leaf', leaf_ipv4, cli_type=bgp_cli_type) + + # Verify bgp neighbors + result = bgpapi.verify_bgp_summary(leaf_name, family='ipv4', neighbor='*'+spine_ipv4, state='Established') + if not result: + bgplib.show_bgp_neighbors([leaf_name, spine_name], af='ipv4') + # Clear applied configs + + # Delete listen range + bgpapi.config_bgp_listen(leaf_name, info['D2_as'], listen_range, 24, 'leaf_spine', 0, 'no', cli_type=bgp_cli_type) + + # Delete the neighbor from Spine + bgpapi.delete_bgp_neighbor(spine_name, info['D1_as'], leaf_ipv4, info['D2_as'], cli_type=bgp_cli_type) + + # Delete ip address from Leaf + ipapi.delete_ip_interface(leaf_name, info['D2D1P1'], leaf_ipv4, 24) + + # Delete ip address from Spine + ipapi.delete_ip_interface(spine_name, info['D1D2P1'], spine_ipv4, 24) + # if bgp_cli_type == "klish": + # bgpapi.config_bgp_peer_group(leaf_name, info['D2_as'], 'leaf_spine', config="no", cli_type=bgp_cli_type) + if result: + st.log("BGP adjacency verified") + st.report_pass("test_case_passed") + else: + st.log("Failed to form BGP peering using dynamic ipv4 neighbors") + st.report_fail("test_case_failed") + + @pytest.mark.bgp_ft + def test_ft_bgp_v6_dyn_nbr(self): + """ + Verify that ipv6 BGP peering is formed with dynamic neighbors + """ + + # Getting topo info between an spine and leaf + info = SpyTestDict() + info = bgplib.get_tg_topology_leafspine_bgp(dut_type='spine-leaf', max_tg_links='0', nodes='2') + # NOTE: D1 is spine D2 is leaf by default + + leaf_name = info['D2'] + spine_name = info['D1'] + + # Configure an ip address on Spine + spine_ipv6 = '2001::1' + ipapi.config_ip_addr_interface(spine_name, info['D1D2P1'], spine_ipv6, 64, family='ipv6') + + # Configure an ip address on Leaf + leaf_ipv6 = '2001::2' + ipapi.config_ip_addr_interface(leaf_name, info['D2D1P1'], leaf_ipv6, 64, family='ipv6') + + # Add a listen range on Leaf + listen_range = '2001::0' + bgpapi.config_bgp_listen(leaf_name, info['D2_as'], listen_range, 64, 'leaf_spine6', 0, cli_type=bgp_cli_type) + + # Add neighbor on Spine + bgpapi.create_bgp_neighbor_use_peergroup(spine_name, info['D1_as'], 'spine_leaf6', leaf_ipv6, family='ipv6', cli_type=bgp_cli_type) + + # Verify dynamic bgp neighbors + result = bgpapi.verify_bgp_summary(leaf_name, family='ipv6', neighbor='*'+spine_ipv6, state='Established') + if not result: + bgplib.show_bgp_neighbors([leaf_name, spine_name], af='ipv6') + # Clear applied configs + + # Delete listen range + bgpapi.config_bgp_listen(leaf_name, info['D2_as'], listen_range, 64, 'leaf_spine6', 0, 'no', cli_type=bgp_cli_type) + + # Delete the neighbor from Spine + bgpapi.delete_bgp_neighbor(spine_name, info['D1_as'], leaf_ipv6, info['D2_as'], cli_type=bgp_cli_type) + + # Delete ip address from Leaf + ipapi.delete_ip_interface(leaf_name, info['D2D1P1'], leaf_ipv6, 64, family='ipv6') + + # Delete ip address from Spine + ipapi.delete_ip_interface(spine_name, info['D1D2P1'], spine_ipv6, 64, family='ipv6') + + if result: + st.log("BGP adjacency verified") + st.report_pass("test_case_passed") + else: + st.log("Failed to form BGP peering using dynamic ipv6 neighbors") + st.report_fail("test_case_failed") + + @pytest.mark.bgp_ft + def test_ft_bgp_v4_max_dyn_nbr(self): + """ + + Verify that BGP peering is established with maximum supported dynamic neighbors with maximum listen + ranges at once + """ + # Getting topo info between an spine and leaf + info = SpyTestDict() + info = bgplib.get_tg_topology_leafspine_bgp(dut_type='spine-leaf', max_tg_links='0', nodes='2') + # NOTE: D1 is spine D2 is leaf by default + + leaf_name = info['D2'] + spine_name = info['D1'] + + result = True + + # Set listen limit + # NOTE: Setting a limit to max dynamic neighbors. It can be set to any value, but the test case execution + # time increases + + limit = 5 + bgpapi.config_bgp_listen(leaf_name,info['D2_as'], 0, 0, 'leaf_spine', limit,cli_type=bgp_cli_type) + + # Apply Configs: + # Add IP addresses on leaf and spine + # Add neighbor on spine + # Add listen range on leaf + for i in range(1, limit+1): + leaf_ipaddr = '{}.0.5.1'.format(20+i) + spine_ipaddr = '{}.0.5.2'.format(20+i) + listen_range = '{}.0.5.0'.format(20+i) + ipapi.config_ip_addr_interface(spine_name, info['D1D2P1'], spine_ipaddr, 24) + ipapi.config_ip_addr_interface(leaf_name, info['D2D1P1'], leaf_ipaddr, 24) + bgpapi.config_bgp_listen(leaf_name, info['D2_as'], listen_range, 24, 'leaf_spine', 0,cli_type=bgp_cli_type) + bgpapi.create_bgp_neighbor_use_peergroup(spine_name, info['D1_as'], 'spine_leaf', leaf_ipaddr) + # Verify dynamic bgp neighbors + result = result & (bgpapi.verify_bgp_summary(leaf_name, family='ipv4', neighbor='*'+spine_ipaddr, + state='Established')) + if not result: + bgplib.show_bgp_neighbors([leaf_name, spine_name], af='ipv4') + + # Clear applied configs + + # Delete listen limit + bgpapi.config_bgp_listen(leaf_name, info['D2_as'], 0, 0, 'leaf_spine', limit, 'no',cli_type=bgp_cli_type) + + for i in range(1, limit+1): + leaf_ipaddr = '{}.0.5.1'.format(20+i) + spine_ipaddr = '{}.0.5.2'.format(20+i) + listen_range = '{}.0.5.0'.format(20+i) + # Delete listen range + bgpapi.config_bgp_listen(leaf_name, info['D2_as'], listen_range, 24, 'leaf_spine', 0, 'no',cli_type=bgp_cli_type) + # Delete the neighbor from Spine + bgpapi.delete_bgp_neighbor(spine_name, info['D1_as'], leaf_ipaddr, info['D2_as']) + # Delete ip address from Leaf + ipapi.delete_ip_interface(leaf_name, info['D2D1P1'], leaf_ipaddr, 24) + # Delete ip address from Spine + ipapi.delete_ip_interface(spine_name, info['D1D2P1'], spine_ipaddr, 24) + + if result: + st.log("BGP adjacency verified") + st.report_pass("test_case_passed") + else: + st.log("Failed to form BGP peering using max dynamic ipv4 neighbors") + st.report_fail("test_case_failed") + + + @pytest.mark.bgp_ft + @pytest.mark.community + @pytest.mark.community_pass + def test_ft_bgp_rmap(self): + """ + + Verify a route map application after route has been installed + """ + + # Getting topo info + info = SpyTestDict() + info = bgplib.get_tg_topology_leafspine_bgp(dut_type='spine-leaf', max_tg_links='0', nodes='2') + # NOTE: D1 is spine D2 is leaf by default + + leaf_name = info['D2'] + spine_name = info['D1'] + network1 = '134.5.6.0/24' + + # Advertise a network to peer + n1 = bgpapi.advertise_bgp_network(leaf_name, info['D2_as'], network1, cli_type=vtysh_cli_type) + n1 = ipapi.verify_ip_route(spine_name,ip_address=network1) + if n1: + st.log("Advertised route present") + + # Create a route-map to deny the network advertisement + ipapi.config_route_map_match_ip_address(leaf_name, 'test-rmap', 'deny', '10', 'test-access-list1') + # Create access-list test-access-list1 and deny the network + ipapi.config_access_list(leaf_name, 'test-access-list1', network1, 'deny') + # Add route-map to advertised network + bgpapi.advertise_bgp_network(leaf_name, info['D2_as'], network1, 'test-rmap', cli_type=vtysh_cli_type) + + # Verify the network on spine + n1 = ipapi.verify_ip_route(spine_name, ip_address=network1) + if not n1: + result = True + else: + result = False + # Clear applied configs + ipapi.config_access_list(leaf_name, 'test-access-list1', network1, 'deny', config='no') + ipapi.config_route_map_mode(leaf_name, 'test-rmap', 'permit', '10', config='no') + bgpapi.advertise_bgp_network(leaf_name, info['D2_as'], network1, 'test-rmap', config='no', cli_type=vtysh_cli_type) + + if result: + st.report_pass("test_case_passed") + else: + st.report_fail("test_case_failed") + + + @pytest.mark.bgp_ft + @pytest.mark.community + @pytest.mark.community_fail + def test_ft_bgp_rmap_out(self): + """ + + Verify a route map with multiple match and set option in out direction + """ + + # Getting topo info + info = SpyTestDict() + info = bgplib.get_tg_topology_leafspine_bgp(dut_type = 'spine-leaf', max_tg_links= '0', nodes='2') + # NOTE: D1 is spine D2 is leaf by default + + leaf_name = info['D2'] + spine_name = info['D1'] + + result = True + + network1 = '134.5.6.0/24' + network2 = '134.5.7.0/24' + network3 = '134.5.8.0' + + # Create route-map and permit network3 + ipapi.config_route_map_match_ip_address(leaf_name, 'test-rmap', 'permit', '10', 'test-access-list1') + # Add set option to prepend as-path 200 + ipapi.config_route_map_set_aspath(leaf_name, 'test-rmap', 'permit', '10', '200') + # Create access-list test-access-list1 + ipapi.config_access_list(leaf_name, 'test-access-list1', network3+'/24', 'permit') + + # Advertise two networks from leaf + bgpapi.advertise_bgp_network(leaf_name, info['D2_as'], network1, 'test-rmap', cli_type=vtysh_cli_type) + bgpapi.advertise_bgp_network(leaf_name, info['D2_as'], network2, 'test-rmap', cli_type=vtysh_cli_type) + bgpapi.advertise_bgp_network(leaf_name, info['D2_as'], network3+'/24', 'test-rmap', cli_type=vtysh_cli_type) + + # In route-map, deny network1 + ipapi.config_route_map_match_ip_address(leaf_name, 'test-rmap', 'deny', '20', 'test-access-list2') + # Create access-list test-access-list2 + ipapi.config_access_list(leaf_name, 'test-access-list2', network1, 'deny') + + # In route-map, permit network2 + ipapi.config_route_map_match_ip_address(leaf_name, 'test-rmap', 'permit', '30', 'test-access-list3') + # Create access-list test-access-list3 + ipapi.config_access_list(leaf_name, 'test-access-list3', network2, 'permit') + + # verify that the neighbor has the as-path prepended + output = bgpapi.show_bgp_ipvx_prefix(spine_name, prefix=network3, masklen=24) + st.log(output) + for x in output: # type: basestring + as_path = x['peerasn'] + as_path = as_path.split() + for each in as_path: + if each == "200": + result = True + + # verify that network1 is not present in bgp routes + n1 = ipapi.verify_ip_route(spine_name,ip_address=network1) + if not n1: + result = result & True + else: + result = result & False + + # verify that network2 is present in bgp routes + n2 = ipapi.verify_ip_route(spine_name,ip_address=network2) + if n2: + result = result & True + else: + result = result & False + + # CLear applied configs + ipapi.config_access_list(leaf_name, 'test-access-list3', network2, 'permit', config='no') + ipapi.config_access_list(leaf_name, 'test-access-list2', network1, 'deny', config='no') + ipapi.config_access_list(leaf_name, 'test-access-list1', network3+'/24', 'permit', config='no') + + ipapi.config_route_map_mode(leaf_name, 'test-rmap', 'permit', '10', config='no') + + bgpapi.advertise_bgp_network(leaf_name, info['D2_as'], network1, 'test-rmap', config='no', cli_type=vtysh_cli_type) + bgpapi.advertise_bgp_network(leaf_name, info['D2_as'], network2, 'test-rmap', config='no', cli_type=vtysh_cli_type) + bgpapi.advertise_bgp_network(leaf_name, info['D2_as'], network3+'/24', 'test-rmap', config='no', cli_type=vtysh_cli_type) + + if result: + st.report_pass("test_case_passed") + else: + st.report_fail("test_case_failed") + + @pytest.mark.regression + def test_ft_bgp_ebgp_confed(self): + """ + Author : seshareddy.koilkonda@broadcom.com + Verify the functionality of route-maps with confederation peers + """ + TG_D1 = topo.tg_dut_list_name[0] + tg_ob = topo['T1{}P1_tg_obj'.format(TG_D1)] + bgp_handle = topo['T1{}P1_ipv4_tg_bh'.format(TG_D1)] + info = SpyTestDict() + info = bgplib.get_tg_topology_leafspine_bgp(dut_type='spine-leaf', max_tg_links='1', nodes='2') + spine_name = info['D1'] + leaf_name = info['D2'] + spine_as = info['D1_as'] + leaf_as = info['D2_as'] + confed_identifier = 65000 + tc_fail_flag = 0 + + bgpapi.config_bgp(leaf_name, config='yes', config_type_list='', local_as=leaf_as, + conf_identf=confed_identifier,cli_type=vtysh_cli_type) + bgpapi.config_bgp(leaf_name, config='yes', config_type_list='', local_as=leaf_as, conf_peers=spine_as,cli_type=vtysh_cli_type) + bgpapi.config_bgp(spine_name, config='yes', config_type_list='', local_as=spine_as, + conf_identf=confed_identifier,cli_type=vtysh_cli_type) + bgpapi.config_bgp(spine_name, config='yes', config_type_list='', local_as=spine_as, conf_peers=leaf_as,cli_type=vtysh_cli_type) + + ipapi.config_route_map_match_ip_address(spine_name, 'confed-rmap', 'permit', '10', 'confed-access-list1') + ipapi.config_access_list(spine_name, 'confed-access-list1', '125.5.1.0/16', 'permit') + bgpapi.config_bgp(spine_name, local_as=spine_as, neighbor=info['D2D1P1_ipv4'], config_type_list=["routeMap"], + routeMap='confed-rmap', diRection='out',cli_type=vtysh_cli_type) + bgpapi.create_bgp_next_hop_self(spine_name, spine_as, 'ipv4', info['D2D1P1_ipv4']) + + st.log("Advertising the route map matching routes from the Spine DUT i.e. they should " + "be advertised on Leaf node") + tg_ob.tg_emulation_bgp_route_config(handle=bgp_handle['handle'], mode='add', num_routes='20', + prefix='125.5.1.0', as_path='as_seq:1') + tg_ob.tg_emulation_bgp_control(handle=bgp_handle['handle'], mode='start') + + n1 = ipapi.verify_ip_route(topo.dut_list[1], ip_address='125.5.5.0/24') + if not n1: + st.error('Route-map matching prefexis from the Spine DUT are not advertised to leaf DUT.') + tc_fail_flag = 1 + + st.log("Advertising the route-map non matching routes from the Spine DUT i.e. they should not be " + "advertised on Leaf node.") + tg_ob.tg_emulation_bgp_route_config(handle=bgp_handle['handle'], mode='add', num_routes='20', + prefix='126.5.1.0', as_path='as_seq:1') + tg_ob.tg_emulation_bgp_control(handle=bgp_handle['handle'], mode='start') + + n1 = ipapi.verify_ip_route(topo.dut_list[1], ip_address='126.5.5.0/24') + n2 = ipapi.verify_ip_route(topo.dut_list[0], ip_address='126.5.5.0/24') + if (n1 == True) or (n2 == False): + st.error('Route check failed for the scenario, route-map non matching prefexis from the Spine DUT') + tc_fail_flag = 1 + + # Unconfig section + tg_ob.tg_emulation_bgp_control(handle=bgp_handle['handle'], mode='stop') + ipapi.config_route_map_mode(topo.dut_list[0], 'confed-rmap', 'permit', '10', config='no') + ipapi.config_access_list(topo.dut_list[0], 'confed-access-list1', '121.5.1.0/16', 'permit', config='no') + bgpapi.config_bgp(topo.dut_list[0], local_as=spine_as, config='no', neighbor=info['D2D1P1_ipv4'], + config_type_list=["routeMap"], routeMap='confed-rmap', diRection='out',cli_type=vtysh_cli_type) + bgpapi.create_bgp_next_hop_self(topo.dut_list[0], spine_as, 'ipv4', info['D2D1P1_ipv4'], 'no', 'no') + + if tc_fail_flag: + st.report_fail('test_case_failed') + st.report_pass('test_case_passed') + + +""" +BGP Neighbor over regular router interface fixture, class and test cases - END +""" + + +""" +BGP IPv4 and IPv6 router distribution and filtering TCs: Start +""" +@pytest.fixture(scope='class') +def bgp_ipvx_route_adv_filter_fixture(request): + """ + Prepare base for router advertisement and filtering TCs + Pick first spine, first leaf and first link between them and create reduced topo + The following will be changed to API based eventually. + Currently implemented like this to progress on TC + """ + reduced_topo = dict() + reduced_topo['dut1'] = topo.spine_list[0] + reduced_topo['dut2'] = topo.leaf_list[0] + reduced_topo['dut1_index'] = 1 + topo.dut_list.index(topo.spine_list[0]) + reduced_topo['dut2_index'] = 1 + topo.dut_list.index(topo.leaf_list[0]) + reduced_topo['dut1_as'] = "{}".format(topo['D{}_as'.format(reduced_topo['dut1_index'])]) + reduced_topo['dut2_as'] = "{}".format(topo['D{}_as'.format(reduced_topo['dut2_index'])]) + reduced_topo['dut1_addr_ipv4'] = topo[ + 'D{}D{}P1_ipv4'.format(reduced_topo['dut1_index'], reduced_topo['dut2_index'])] + reduced_topo['dut2_addr_ipv4'] = topo[ + 'D{}D{}P1_ipv4'.format(reduced_topo['dut2_index'], reduced_topo['dut1_index'])] + reduced_topo['dut1_addr_ipv6'] = topo[ + 'D{}D{}P1_ipv6'.format(reduced_topo['dut1_index'], reduced_topo['dut2_index'])] + reduced_topo['dut2_addr_ipv6'] = topo[ + 'D{}D{}P1_ipv6'.format(reduced_topo['dut2_index'], reduced_topo['dut1_index'])] + reduced_topo['dut1_outif'] = topo[ + 'D{}D{}P1'.format(reduced_topo['dut1_index'], reduced_topo['dut2_index'])] + reduced_topo['dut2_outif'] = topo[ + 'D{}D{}P1'.format(reduced_topo['dut1_index'], reduced_topo['dut2_index'])] + + request.cls.local_topo = reduced_topo + + config_items = {} + + bgplib.configure_base_for_route_adv_and_filter(reduced_topo['dut1'], reduced_topo['dut2'], reduced_topo, + config_items) + + yield reduced_topo + + bgplib.unconfigure_base_for_route_adv_and_filter(reduced_topo['dut1'], reduced_topo['dut2'], reduced_topo, + config_items) + + +@pytest.mark.usefixtures('bgp_rif_class_hook', 'bgp_ipvx_route_adv_filter_fixture') +class TestBGPIPvxRouteAdvertisementFilter: + local_topo = dict() + + def configure_base_for_filter_prefix_on_community(self, peer_grp4_name, config, cli_type="vtysh"): + bgpapi.config_bgp(dut=self.local_topo['dut1'], local_as=self.local_topo['dut1_as'], config=config, + config_type_list=["redist"], redistribute='static',cli_type=cli_type) + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], + neighbor=peer_grp4_name, addr_family='ipv4', config=config, + config_type_list=["routeMap"], routeMap='rmap1', diRection='in',cli_type=cli_type) + + @pytest.mark.community + @pytest.mark.community_pass + def test_redistribute_connected_ipv4(self, bgp_ipvx_route_adv_filter_fixture): + + bgpapi.config_address_family_redistribute(self.local_topo['dut1'], self.local_topo['dut1_as'], + 'ipv4', 'unicast', "connected", config='yes', cli_type=bgp_cli_type) + + output = ipapi.fetch_ip_route(self.local_topo['dut1'], match={'type': 'C'}, select=['ip_address']) + list_of_connected_network_on_dut1 = list(x['ip_address'] for x in output) + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut2'], family='ipv4', + select=['network', 'as_path']) + + output = [x for x in output if "{}".format(self.local_topo['dut1_as']) in x['as_path']] + + list_of_learned_routes_on_dut2_from_dut1 = list(x['network'] for x in output) + + st.log('List of connected network on dut1:' + str(list_of_connected_network_on_dut1)) + st.log('List of network learnt on dut2 from dut1:' + str(list_of_learned_routes_on_dut2_from_dut1)) + + if set(list_of_connected_network_on_dut1).issubset(set(list_of_learned_routes_on_dut2_from_dut1)): + result = True + else: + result = False + + bgpapi.config_address_family_redistribute(self.local_topo['dut1'], self.local_topo['dut1_as'], + 'ipv4', 'unicast', "connected", config='no', cli_type=bgp_cli_type) + + if result: + st.report_pass("operation_successful") + else: + st.report_fail("operation_failed") + + @pytest.mark.community + @pytest.mark.community_pass + def test_redistribute_static_ipv4(self, bgp_ipvx_route_adv_filter_fixture): + + ipapi.create_static_route(self.local_topo['dut1'], self.local_topo['dut1_outif'], '100.1.1.1/32', family='ipv4') + + output = ipapi.fetch_ip_route(self.local_topo['dut1'], match={'type': 'S'}, select=['ip_address']) + list_of_static_network_on_dut1 = list(x['ip_address'] for x in output) + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut2'], family='ipv4', + select=['network', 'as_path']) + + output = [x for x in output if "{}".format(self.local_topo['dut1_as']) in x['as_path']] + + list_of_learned_routes_on_dut2_by_dut1 = list(x['network'] for x in output) + + st.log('List of static route on dut1' + str(list_of_static_network_on_dut1)) + st.log('List of network redistributed to dut2 from dut1' + str(list_of_learned_routes_on_dut2_by_dut1)) + + bgpapi.config_address_family_redistribute(self.local_topo['dut1'], self.local_topo['dut1_as'], + 'ipv4', 'unicast', "static", config='yes', cli_type=bgp_cli_type) + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut2'], family='ipv4', + select=['network', 'as_path']) + + output = [x for x in output if "{}".format(self.local_topo['dut1_as']) in x['as_path']] + + list_of_learned_routes_on_dut2_by_dut1 = list(x['network'] for x in output) + + st.log('List of static route on dut1' + str(list_of_static_network_on_dut1)) + st.log('List of network redistributed to dut2 from dut1' + str(list_of_learned_routes_on_dut2_by_dut1)) + + if set(list_of_static_network_on_dut1).issubset(set(list_of_learned_routes_on_dut2_by_dut1)): + st.log('static on dut1 is subset of dut1 learned route on dut2') + result = True + else: + st.log('static on dut1 is not a subset of dut1 learned route on dut2') + result = False + + bgpapi.config_address_family_redistribute(self.local_topo['dut1'], self.local_topo['dut1_as'], + 'ipv4', 'unicast', "static", config='no', cli_type=bgp_cli_type) + + ipapi.delete_static_route(self.local_topo['dut1'], self.local_topo['dut1_outif'], '100.1.1.1/32', family='ipv4') + + if result: + st.report_pass("operation_successful") + else: + st.report_fail("operation_failed") + + @pytest.mark.community + @pytest.mark.community_pass + def test_distribute_list_in_ipv4(self, bgp_ipvx_route_adv_filter_fixture): + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut2'], family='ipv4', + match={'next_hop': self.local_topo['dut1_addr_ipv4']}, + select=['network', 'as_path']) + + output = [x for x in output if "{}".format(self.local_topo['dut1_as']) in x['as_path']] + + list_of_learned_routes_on_dut2_by_dut1 = list(x['network'] for x in output) + if '102.1.1.0/24' in list_of_learned_routes_on_dut2_by_dut1: + st.log("route learnt") + else: + st.log("route not learnt") + + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], addr_family='ipv4', + config='yes', + neighbor=self.local_topo['dut1_addr_ipv4'], + config_type_list=["distribute_list"], distribute_list='11', diRection='in',cli_type=vtysh_cli_type) + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut2'], family='ipv4', + match={'next_hop': self.local_topo['dut1_addr_ipv4']}, + select=['network', 'as_path']) + + output = [x for x in output if "{}".format(self.local_topo['dut1_as']) in x['as_path']] + + list_of_learned_routes_on_dut2_by_dut1 = list(x['network'] for x in output) + if '102.1.1.0/24' in list_of_learned_routes_on_dut2_by_dut1: + st.log("route not suppressed") + result = False + else: + st.log("route suppressed") + result = True + + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], addr_family='ipv4', + config='no', + neighbor=self.local_topo['dut1_addr_ipv4'], + config_type_list=["distribute_list"], distribute_list='11', diRection='in',cli_type=vtysh_cli_type) + + if result: + st.report_pass("operation_successful") + else: + st.report_fail("operation_failed") + + @pytest.mark.community + @pytest.mark.community_pass + def test_filter_list_in_ipv4(self, bgp_ipvx_route_adv_filter_fixture): + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut2'], family='ipv4', + select=['network', 'as_path']) + + output = [x for x in output if "{}".format(self.local_topo['dut1_as']) in x['as_path']] + + list_of_learned_routes_on_dut2_by_dut1 = list(x['network'] for x in output) + if len(list_of_learned_routes_on_dut2_by_dut1): + st.log("route received for as {}".format(self.local_topo['dut1_as'])) + else: + st.log("route not learnt") + + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], addr_family='ipv4', + config='yes', + neighbor=self.local_topo['dut1_addr_ipv4'], + config_type_list=["filter_list"], filter_list='FILTER', diRection='in',cli_type=vtysh_cli_type) + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut2'], family='ipv4', + match={'next_hop': self.local_topo['dut1_addr_ipv4']}, + select=['network', 'as_path']) + + output = [x for x in output if "{}".format(self.local_topo['dut1_as']) in x['as_path']] + + list_of_learned_routes_on_dut2_by_dut1 = list(x['network'] for x in output) + if len(list_of_learned_routes_on_dut2_by_dut1) != 0: + st.log("still having routes from as {}".format(self.local_topo['dut1_as'])) + result = False + else: + st.log("no routes from as {}".format(self.local_topo['dut1_as'])) + result = True + + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], addr_family='ipv4', + config='no', + neighbor=self.local_topo['dut1_addr_ipv4'], + config_type_list=["filter_list"], filter_list='FILTER', diRection='in',cli_type=vtysh_cli_type) + + if result: + st.report_pass("operation_successful") + else: + st.report_fail("operation_failed") + + @pytest.mark.community + @pytest.mark.community_pass + def test_prefix_list_out_ipv4(self, bgp_ipvx_route_adv_filter_fixture): + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut1'], family='ipv4', + match={'next_hop': self.local_topo['dut2_addr_ipv4']}, + select=['network', 'as_path']) + + output = [x for x in output if "{}".format(self.local_topo['dut2_as']) in x['as_path']] + + list_of_learned_routes_on_dut1_by_dut2 = list(x['network'] for x in output) + if '202.1.1.0/24' in list_of_learned_routes_on_dut1_by_dut2: + st.log("route learnt") + else: + st.log("route not learnt") + if bgp_cli_type == "klish": + ipapi.config_ip_prefix_list(self.local_topo['dut2'], 'PREFIXOUT', '202.1.1.0/24', family="ipv4", action="deny", cli_type=bgp_cli_type) + ipapi.config_ip_prefix_list(self.local_topo['dut2'], 'PREFIXOUT', 'any', family="ipv4", action="permit", cli_type=bgp_cli_type) + + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], addr_family='ipv4', + config='yes', + neighbor=self.local_topo['dut1_addr_ipv4'], + config_type_list=["prefix_list"], prefix_list='PREFIXOUT', diRection='out',cli_type=bgp_cli_type) + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut1'], family='ipv4', + match={'next_hop': self.local_topo['dut2_addr_ipv4']}, + select=['network', 'as_path']) + + output = [x for x in output if "{}".format(self.local_topo['dut2_as']) in x['as_path']] + + list_of_learned_routes_on_dut1_by_dut2 = list(x['network'] for x in output) + if '202.1.1.0/24' in list_of_learned_routes_on_dut1_by_dut2: + st.log("route not suppressed") + result = False + else: + st.log("route suppressed") + result = True + + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], addr_family='ipv4', + config='no', + neighbor=self.local_topo['dut1_addr_ipv4'], + config_type_list=["prefix_list"], prefix_list='PREFIXOUT', diRection='out',cli_type=bgp_cli_type) + + if result: + st.report_pass("operation_successful") + else: + st.report_fail("operation_failed") + + @pytest.mark.community + @pytest.mark.community_pass + def test_default_originate_ipv4(self, bgp_ipvx_route_adv_filter_fixture): + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut1'], family='ipv4', + match={'next_hop': self.local_topo['dut2_addr_ipv4']}, + select=['network', 'as_path']) + + output = [x for x in output if "{}".format(self.local_topo['dut2_as']) in x['as_path']] + + list_of_learned_routes_on_dut1_by_dut2 = list(x['network'] for x in output) + if '0.0.0.0/0' in list_of_learned_routes_on_dut1_by_dut2: + st.log("route learnt") + else: + st.log("route not learnt") + + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], addr_family='ipv4', + config='yes', + neighbor=self.local_topo['dut1_addr_ipv4'], + config_type_list=["default_originate"], routeMap='UseGlobal',cli_type=vtysh_cli_type) + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut1'], family='ipv4', + match={'next_hop': self.local_topo['dut2_addr_ipv4']}, + select=['network', 'as_path']) + + output = [x for x in output if "{}".format(self.local_topo['dut2_as']) in x['as_path']] + + list_of_learned_routes_on_dut1_by_dut2 = list(x['network'] for x in output) + if '0.0.0.0/0' in list_of_learned_routes_on_dut1_by_dut2: + st.log("default route advertised") + result = True + else: + st.log("default route not advertised") + result = False + + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], addr_family='ipv4', + config='no', + neighbor=self.local_topo['dut1_addr_ipv4'], + config_type_list=["default_originate"], routeMap='UseGlobal',cli_type=vtysh_cli_type) + + if result: + st.report_pass("operation_successful") + else: + st.report_fail("operation_failed") + + @pytest.mark.community + @pytest.mark.community_pass + def test_route_map_in_ipv4(self, bgp_ipvx_route_adv_filter_fixture): + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut2'], family='ipv4', + match={'next_hop': self.local_topo['dut1_addr_ipv4']}, + select=['network', 'local_pref', 'metric']) + + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], addr_family='ipv4', + config='yes', + neighbor=self.local_topo['dut1_addr_ipv4'], + config_type_list=["routeMap"], routeMap='SETPROPS', diRection='in',cli_type=vtysh_cli_type) + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut2'], family='ipv4', + match={'next_hop': self.local_topo['dut1_addr_ipv4']}, + select=['network', 'local_pref', 'metric']) + + metric = [x for x in output if x['network'] == '102.1.1.0/24'][0]['metric'] + + local_pref = [x for x in output if x['network'] == '101.1.1.0/24'][0]['local_pref'] + + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], addr_family='ipv4', + config='no', + neighbor=self.local_topo['dut1_addr_ipv4'], + config_type_list=["routeMap"], routeMap='SETPROPS', diRection='in',cli_type=vtysh_cli_type) + + if metric == '400' and local_pref == '200': + st.report_pass("operation_successful") + else: + st.report_fail("operation_failed") + + @pytest.mark.community + @pytest.mark.community_pass + def test_redistribute_connected_ipv6(self, bgp_ipvx_route_adv_filter_fixture): + + bgpapi.config_address_family_redistribute(self.local_topo['dut1'], self.local_topo['dut1_as'], + 'ipv6', 'unicast', "connected", config='yes', cli_type=bgp_cli_type) + + output = ipapi.fetch_ip_route(self.local_topo['dut1'], family='ipv6', match={'type': 'C'}, + select=['ip_address']) + + output = [x for x in output if not x['ip_address'].startswith('fe80')] + list_of_connected_network_on_dut1 = list(x['ip_address'] for x in output) + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut2'], family='ipv6', + select=['network', 'as_path']) + + output = [x for x in output if "{}".format(self.local_topo['dut1_as']) in x['as_path']] + list_of_learned_routes_on_dut2_by_dut1 = list(x['network'] for x in output) + + st.log('List of connected network on dut1') + st.log(list_of_connected_network_on_dut1) + st.log('List of network redistributed to dut2 from dut1') + st.log(list_of_learned_routes_on_dut2_by_dut1) + + if set(list_of_connected_network_on_dut1).issubset(set(list_of_learned_routes_on_dut2_by_dut1)): + result = True + else: + result = False + + bgpapi.config_address_family_redistribute(self.local_topo['dut1'], self.local_topo['dut1_as'], + 'ipv6', 'unicast', "connected", config='no', cli_type=bgp_cli_type) + + if result: + st.report_pass("operation_successful") + else: + st.report_fail("operation_failed") + + def test_redistribute_static_ipv6(self, bgp_ipvx_route_adv_filter_fixture): + + ipapi.create_static_route(self.local_topo['dut1'], self.local_topo['dut1_outif'], '100:1::1:1/128', + family='ipv6') + + output = ipapi.fetch_ip_route(self.local_topo['dut1'], family='ipv6', match={'type': 'S'}, + select=['ip_address']) + list_of_static_network_on_dut1 = list(x['ip_address'] for x in output) + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut2'], family='ipv6', + select=['network', 'as_path']) + + output = [x for x in output if "{}".format(self.local_topo['dut1_as']) in x['as_path']] + list_of_learned_routes_on_dut2_by_dut1 = list(x['network'] for x in output) + + st.log('List of static route on dut1' + str(list_of_static_network_on_dut1)) + st.log('List of network redistributed to dut2 from dut1' + str(list_of_learned_routes_on_dut2_by_dut1)) + + bgpapi.config_address_family_redistribute(self.local_topo['dut1'], self.local_topo['dut1_as'], + 'ipv6', 'unicast', "static", config='yes', cli_type=bgp_cli_type) + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut2'], family='ipv6', + select=['network', 'as_path']) + + output = [x for x in output if "{}".format(self.local_topo['dut1_as']) in x['as_path']] + + list_of_learned_routes_on_dut2_by_dut1 = list(x['network'] for x in output) + + st.log('List of static route on dut1' + str(list_of_static_network_on_dut1)) + st.log('List of network redistributed to dut2 from dut1' + str(list_of_learned_routes_on_dut2_by_dut1)) + + if set(list_of_static_network_on_dut1).issubset(set(list_of_learned_routes_on_dut2_by_dut1)): + st.log('static on dut1 is subset of dut1 learned route on dut2') + result = True + else: + st.log('static on dut1 is not a subset of dut1 learned route on dut2') + result = False + + bgpapi.config_address_family_redistribute(self.local_topo['dut1'], self.local_topo['dut1_as'], + 'ipv6', 'unicast', "static", config='no', cli_type=bgp_cli_type) + + ipapi.delete_static_route(self.local_topo['dut1'], self.local_topo['dut1_outif'], '100:1::1:1/128', + family='ipv6') + + if result: + st.report_pass("operation_successful") + else: + st.report_fail("operation_failed") + + @pytest.mark.community + @pytest.mark.community_pass + def test_distribute_list_in_ipv6(self, bgp_ipvx_route_adv_filter_fixture): + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut2'], family='ipv6', + match={'next_hop': self.local_topo['dut1_addr_ipv6']}, + select=['network', 'as_path']) + + output = [x for x in output if "{}".format(self.local_topo['dut1_as']) in x['as_path']] + + list_of_learned_routes_on_dut2_by_dut1 = list(x['network'] for x in output) + if '102:1::/64' in list_of_learned_routes_on_dut2_by_dut1: + st.log("route learnt") + else: + st.log("route not learnt") + + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], addr_family='ipv6', + config='yes', + neighbor=self.local_topo['dut1_addr_ipv6'], + config_type_list=["distribute_list"], distribute_list='12', diRection='in',cli_type=vtysh_cli_type) + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut2'], family='ipv6', + match={'next_hop': self.local_topo['dut1_addr_ipv6']}, + select=['network', 'as_path']) + + output = [x for x in output if "{}".format(self.local_topo['dut1_as']) in x['as_path']] + + list_of_learned_routes_on_dut2_by_dut1 = list(x['network'] for x in output) + if '102:1::/64' in list_of_learned_routes_on_dut2_by_dut1: + st.log("route not suppressed") + result = False + else: + st.log("route suppressed") + result = True + + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], addr_family='ipv6', + config='no', + neighbor=self.local_topo['dut1_addr_ipv6'], + config_type_list=["distribute_list"], distribute_list='12', diRection='in',cli_type=vtysh_cli_type) + + if result: + st.report_pass("operation_successful") + else: + st.report_fail("operation_failed") + + @pytest.mark.community + @pytest.mark.community_pass + def test_filter_list_in_ipv6(self, bgp_ipvx_route_adv_filter_fixture): + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut2'], family='ipv6', + select=['network', 'as_path']) + + output = [x for x in output if "{}".format(self.local_topo['dut1_as']) in x['as_path']] + + list_of_learned_routes_on_dut2_by_dut1 = list(x['network'] for x in output) + if len(list_of_learned_routes_on_dut2_by_dut1): + st.log("route received for as {}".format(self.local_topo['dut1_as'])) + else: + st.log("route not learnt") + + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], addr_family='ipv6', + config='yes', + neighbor=self.local_topo['dut1_addr_ipv6'], + config_type_list=["filter_list"], filter_list='FILTER', diRection='in',cli_type=vtysh_cli_type) + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut2'], family='ipv6', + match={'next_hop': self.local_topo['dut1_addr_ipv6']}, + select=['network', 'as_path']) + + output = [x for x in output if "{}".format(self.local_topo['dut1_as']) in x['as_path']] + + list_of_learned_routes_on_dut2_by_dut1 = list(x['network'] for x in output) + if len(list_of_learned_routes_on_dut2_by_dut1) != 0: + st.log("still having routes from as {}".format(self.local_topo['dut1_as'])) + result = False + else: + st.log("no routes from as {}".format(self.local_topo['dut1_as'])) + result = True + + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], addr_family='ipv6', + config='no', + neighbor=self.local_topo['dut1_addr_ipv6'], + config_type_list=["filter_list"], filter_list='FILTER', diRection='in',cli_type=vtysh_cli_type) + + if result: + st.report_pass("operation_successful") + else: + st.report_fail("operation_failed") + + @pytest.mark.community + @pytest.mark.community_pass + def test_prefix_list_out_ipv6(self, bgp_ipvx_route_adv_filter_fixture): + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut1'], family='ipv6', + match={'next_hop': self.local_topo['dut2_addr_ipv6']}, + select=['network', 'as_path']) + + output = [x for x in output if "{}".format(self.local_topo['dut2_as']) in x['as_path']] + + list_of_learned_routes_on_dut1_by_dut2 = list(x['network'] for x in output) + if '202:1::/64' in list_of_learned_routes_on_dut1_by_dut2: + st.log("route learnt") + else: + st.log("route not learnt") + if bgp_cli_type == "klish": + ipapi.config_ip_prefix_list(self.local_topo['dut2'], 'PREFIXOUT6', '202:1::/64', family="ipv6", action="deny", cli_type=bgp_cli_type) + ipapi.config_ip_prefix_list(self.local_topo['dut2'], 'PREFIXOUT6', 'any', family="ipv6", action="permit", cli_type=bgp_cli_type) + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], addr_family='ipv6', + config='yes', + neighbor=self.local_topo['dut1_addr_ipv6'], + config_type_list=["prefix_list"], prefix_list='PREFIXOUT6', diRection='out',cli_type=bgp_cli_type) + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut1'], family='ipv6', + match={'next_hop': self.local_topo['dut2_addr_ipv6']}, + select=['network', 'as_path']) + + output = [x for x in output if "{}".format(self.local_topo['dut2_as']) in x['as_path']] + + list_of_learned_routes_on_dut1_by_dut2 = list(x['network'] for x in output) + if '202:1::/64' in list_of_learned_routes_on_dut1_by_dut2: + st.log("route not suppressed") + result = False + else: + st.log("route suppressed") + result = True + + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], addr_family='ipv6', + config='no', + neighbor=self.local_topo['dut1_addr_ipv6'], + config_type_list=["prefix_list"], prefix_list='PREFIXOUT6', diRection='out',cli_type=bgp_cli_type) + + if result: + st.report_pass("operation_successful") + else: + st.report_fail("operation_failed") + + @pytest.mark.community + @pytest.mark.community_pass + def test_filter_list_out_ipv6(self, bgp_ipvx_route_adv_filter_fixture): + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut1'], family='ipv6', + select=['network', 'as_path']) + + output = [x for x in output if "{}".format(self.local_topo['dut2_as']) in x['as_path']] + + list_of_learned_routes_on_dut2_by_dut1 = list(x['network'] for x in output) + if len(list_of_learned_routes_on_dut2_by_dut1): + st.log("route received for as {}".format(self.local_topo['dut2_as'])) + else: + st.log("route not learnt") + + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], addr_family='ipv6', + config='yes', + neighbor=self.local_topo['dut1_addr_ipv6'], + config_type_list=["filter_list"], filter_list='FILTER6', diRection='out',cli_type=vtysh_cli_type) + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut1'], family='ipv6', + match={'next_hop': self.local_topo['dut2_addr_ipv6']}, + select=['network', 'as_path']) + + output = [x for x in output if "{}".format(self.local_topo['dut2_as']) in x['as_path']] + + list_of_learned_routes_on_dut2_by_dut1 = list(x['network'] for x in output) + if len(list_of_learned_routes_on_dut2_by_dut1) != 0: + st.log("still having routes from as {}".format(self.local_topo['dut2_as'])) + result = False + else: + st.log("no routes from as {}".format(self.local_topo['dut2_as'])) + result = True + + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], addr_family='ipv6', + config='no', + neighbor=self.local_topo['dut1_addr_ipv6'], + config_type_list=["filter_list"], filter_list='FILTER6', diRection='out',cli_type=vtysh_cli_type) + + if result: + st.report_pass("operation_successful") + else: + st.report_fail("operation_failed") + + def test_default_originate_ipv6(self, bgp_ipvx_route_adv_filter_fixture): + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut1'], family='ipv6', + match={'next_hop': self.local_topo['dut2_addr_ipv6']}, + select=['network', 'as_path']) + + output = [x for x in output if "{}".format(self.local_topo['dut2_as']) in x['as_path']] + list_of_learned_routes_on_dut1_by_dut2 = list(x['network'] for x in output) + if '::/0' in list_of_learned_routes_on_dut1_by_dut2: + st.log("route learnt") + else: + st.log("route not learnt") + + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], addr_family='ipv6', + config='yes', + neighbor=self.local_topo['dut1_addr_ipv6'], + config_type_list=["default_originate"], routeMap='UseGlobal',cli_type=bgp_cli_type) + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut1'], family='ipv6', + match={'next_hop': self.local_topo['dut2_addr_ipv6']}, + select=['network', 'as_path']) + + output = [x for x in output if "{}".format(self.local_topo['dut2_as']) in x['as_path']] + list_of_learned_routes_on_dut1_by_dut2 = list(x['network'] for x in output) + if '::/0' in list_of_learned_routes_on_dut1_by_dut2: + st.log("default route advertised") + result = True + else: + st.log("default route not advertised") + result = False + + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], addr_family='ipv6', + config='no', + neighbor=self.local_topo['dut1_addr_ipv6'], + config_type_list=["default_originate"], routeMap='UseGlobal',cli_type=bgp_cli_type) + + if result: + st.report_pass("operation_successful") + else: + st.report_fail("operation_failed") + + @pytest.mark.community + @pytest.mark.community_pass + def test_route_map_in_ipv6(self, bgp_ipvx_route_adv_filter_fixture): + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut2'], family='ipv6', + match={'next_hop': self.local_topo['dut1_addr_ipv6']}, + select=['network', 'local_pref', 'metric']) + + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], addr_family='ipv6', + config='yes', + neighbor=self.local_topo['dut1_addr_ipv6'], + config_type_list=["routeMap"], routeMap='SETPROPS6', diRection='in',cli_type=vtysh_cli_type) + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut2'], family='ipv6', + match={'next_hop': self.local_topo['dut1_addr_ipv6']}, + select=['network', 'local_pref', 'metric']) + metric = bgplib.get_route_attribute(output, 'metric', network='102:1::/64') + local_pref = bgplib.get_route_attribute(output, 'local_pref', network='101:1::/64') + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], addr_family='ipv6', + config='yes', + neighbor=self.local_topo['dut1_addr_ipv6'], + config_type_list=["routeMap"], routeMap='UseGlobal', diRection='in',cli_type=vtysh_cli_type) + + if metric == '6400' and local_pref == '6200': + st.report_pass("operation_successful") + else: + st.report_fail("operation_failed") + + # testcase: FtOtSoRtBgp4Fn016, To verify functioning of route-map to filter incoming IPv4 prefix(s) + # on community from dynamic neighbors + @pytest.mark.bgp_rtmap_comm + @pytest.mark.community + @pytest.mark.community_pass + def test_bgp_route_map_with_community(self, bgp_ipvx_route_adv_filter_fixture): + result = True + ipapi.config_route_map(dut=self.local_topo['dut2'], route_map='rmap1', config='yes', + sequence='10', community='100:100') + ipapi.create_static_route(dut=self.local_topo['dut1'], next_hop='blackhole', static_ip='40.1.1.1/32') + self.configure_base_for_filter_prefix_on_community('leaf_spine', 'yes') + # Check the show command in leaf + output = bgpapi.show_bgp_ipvx_prefix(self.local_topo['dut2'], prefix="40.1.1.1", + masklen=32, family='ipv4') + st.log(output) + # there is only one record + for x in output: # type: basestring + if ((x['peerip'].find('11.1.1.2')) != -1) and (x['community'] == '100:100'): + result = True + else: + result = False + self.configure_base_for_filter_prefix_on_community('leaf_spine', 'no') + ipapi.config_route_map(dut=self.local_topo['dut2'], route_map='rmap1', config='no', + community='100:100') + ipapi.delete_static_route(dut=self.local_topo['dut1'], next_hop='blackhole', static_ip='40.1.1.1/32') + if result: + st.report_pass("operation_successful") + else: + st.report_fail("operation_failed") + + # testcase: FtOtSoRtBgp4Fn014, Verify that BGP peering with dynamic neighbors established with update source option. + @pytest.mark.bgp_nbr_updsrc + @pytest.mark.community + @pytest.mark.community_pass + def test_bgp_ebgp4_nbr_update_source(self, bgp_ipvx_route_adv_filter_fixture): + result = True + # configure update source for both the duts + # Note: Currently, leaf spine topology has a fixed neighbor formation (peer-group leaf_spine and spine_leaf) + # Since in sonic, we must have neighbor which is same as update-source, we will use this nbr as the source. + # basically, we will use update-source on the same neighbor, which has been created using leaf spine topology. + bgpapi.config_bgp(dut=self.local_topo['dut1'], local_as=self.local_topo['dut1_as'], + neighbor=self.local_topo['dut2_addr_ipv4'], config='yes', + update_src=self.local_topo['dut1_addr_ipv4'], config_type_list=["update_src"],cli_type=bgp_cli_type) + bgpapi.config_bgp(dut=self.local_topo['dut1'], local_as=self.local_topo['dut1_as'], + neighbor=self.local_topo['dut2_addr_ipv4'], config='yes', + config_type_list=["ebgp_mhop"], ebgp_mhop='2',cli_type=bgp_cli_type) + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], + neighbor=self.local_topo['dut1_addr_ipv4'], config='yes', + update_src=self.local_topo['dut2_addr_ipv4'], + config_type_list=["update_src"],cli_type=bgp_cli_type) + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], + neighbor=self.local_topo['dut1_addr_ipv4'], config='yes', + config_type_list=["ebgp_mhop"], ebgp_mhop='2',cli_type=bgp_cli_type) + # clear bgp neighbors before checking for neighbor state again. + bgpapi.clear_ip_bgp_vtysh(dut=self.local_topo['dut1'], value="*") + bgpapi.clear_ip_bgp_vtysh(dut=self.local_topo['dut2'], value="*") + if not utils.poll_wait(bgpapi.verify_bgp_summary, 30, self.local_topo['dut1'], family='ipv4', + neighbor=self.local_topo['dut2_addr_ipv4'], state='Established'): + bgplib.show_bgp_neighbors([self.local_topo['dut1'], self.local_topo['dut2']], af='ipv4') + st.error("BGP Neighbor failed to Establish between DUT1 and DUT2") + st.log("{} - Neighbor {} is failed to Establish".format(self.local_topo['dut1'], + self.local_topo['dut2_addr_ipv4'])) + result = False + + # cleanup the testcase + bgpapi.config_bgp(dut=self.local_topo['dut1'], local_as=self.local_topo['dut1_as'], + neighbor=self.local_topo['dut2_addr_ipv4'], config='no', + update_src=self.local_topo['dut1_addr_ipv4'], + config_type_list=["update_src"],cli_type=bgp_cli_type) + bgpapi.config_bgp(dut=self.local_topo['dut1'], local_as=self.local_topo['dut1_as'], + neighbor=self.local_topo['dut2_addr_ipv4'], config='no', + config_type_list=["ebgp_mhop"], ebgp_mhop='2',cli_type=bgp_cli_type) + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], + neighbor=self.local_topo['dut1_addr_ipv4'], config='no', + update_src=self.local_topo['dut2_addr_ipv4'], + config_type_list=["update_src"],cli_type=bgp_cli_type) + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], + neighbor=self.local_topo['dut1_addr_ipv4'], config='no', + config_type_list=["ebgp_mhop"], ebgp_mhop='2',cli_type=bgp_cli_type) + if result: + st.report_pass("operation_successful") + else: + st.report_fail("operation_failed") + + # testcase: FtOtSoRtBgp4Fn015, Verify eBGP authentication. + @pytest.mark.bgp_nbr_auth + def test_bgp_ebgp4_nbr_authentication(self, bgp_ipvx_route_adv_filter_fixture): + result = True + # configure password for both the duts + bgpapi.config_bgp(dut=self.local_topo['dut1'], local_as=self.local_topo['dut1_as'], + neighbor=self.local_topo['dut2_addr_ipv4'], config='yes', password='broadcom', + config_type_list=["pswd"],cli_type=bgp_cli_type) + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], + neighbor=self.local_topo['dut1_addr_ipv4'], config='yes', password='broadcom', + config_type_list=["pswd"],cli_type=bgp_cli_type) + + # clear bgp neighbors before checking for neighbor state again. + bgpapi.clear_ip_bgp_vtysh(dut=self.local_topo['dut1'], value="*") + bgpapi.clear_ip_bgp_vtysh(dut=self.local_topo['dut2'], value="*") + + if not utils.poll_wait(bgpapi.verify_bgp_summary, 30, self.local_topo['dut1'], family='ipv4', + neighbor=self.local_topo['dut2_addr_ipv4'], state='Established'): + bgplib.show_bgp_neighbors([self.local_topo['dut1'], self.local_topo['dut2']], af='ipv4') + st.error("BGP Neighbor failed to Establish between DUT1 and DUT2") + st.log("{} - Neighbor {} is failed to Establish".format(self.local_topo['dut1'], + self.local_topo['dut2_addr_ipv4'])) + result = False + # Verify neighbors formation after rebooting Dut1 + st.log("Verification of neighbor formation after reboot.") + # below API will change routing mode to split and save the sonic config. + bgpapi.enable_docker_routing_config_mode(dut=self.local_topo['dut1']) + st.vtysh(self.local_topo['dut1'], "copy running-config startup-config") + st.reboot(self.local_topo['dut1'], 'fast') + st.wait(3) + if not utils.poll_wait(bgpapi.verify_bgp_summary, 30, self.local_topo['dut1'], family='ipv4', + neighbor=self.local_topo['dut2_addr_ipv4'], state='Established'): + bgplib.show_bgp_neighbors([self.local_topo['dut1'], self.local_topo['dut2']], af='ipv4') + st.error("BGP Neighbor failed to Establish between DUT1 and DUT2") + st.log("{} - Neighbor {} is failed to Establish".format(self.local_topo['dut1'], + self.local_topo['dut2_addr_ipv4'])) + result = False + # cleanup the testcase + bgpapi.config_bgp(dut=self.local_topo['dut1'], local_as=self.local_topo['dut1_as'], + neighbor=self.local_topo['dut2_addr_ipv4'], config='no', password='broadcom', + config_type_list=["pswd"],cli_type=bgp_cli_type) + bgpapi.config_bgp(dut=self.local_topo['dut2'], local_as=self.local_topo['dut2_as'], + neighbor=self.local_topo['dut1_addr_ipv4'], config='no', password='broadcom', + config_type_list=["pswd"],cli_type=bgp_cli_type) + if result: + st.report_pass("operation_successful") + else: + st.report_fail("operation_failed") + + # testcase: FtOtSoRtBgp4Fn015, Verify eBGP traffic for ipv6. + @pytest.mark.bgp_ebgp6_traffic + def test_bgp_ebgp6_traffic(self, bgp_ipvx_route_adv_filter_fixture): + result = True + TG_D1 = topo.tg_dut_list_name[0] + TG_D2 = topo.tg_dut_list_name[1] + tg_ob = topo['T1{}P1_tg_obj'.format(TG_D1)] + bgp_handle = topo['T1{}P1_ipv6_tg_bh'.format(TG_D1)] + tg_d1_ip = topo['T1{}P1_ipv6'.format(TG_D1)] + tg_d2_ip = topo['T1{}P1_ipv6'.format(TG_D2)] + tc_fail_flag = 0 + spine_as = int(bgplib.data['spine_as']) + st.log("Advertising 500 IPv6 Routes from TG connected to DUT1") + bgp_route = tg_ob.tg_emulation_bgp_route_config(handle=bgp_handle['handle'], mode='add', ip_version='6', + num_routes='500', prefix='1001::1', as_path='as_seq:1') + bgp_ctrl = tg_ob.tg_emulation_bgp_control(handle=bgp_handle['handle'], mode='start') + # Sleep for update delay timer and the check the route count in neighbour + st.wait(15) + if not utils.poll_wait(bgpapi.verify_bgp_neighborship, 120, topo.dut_list[0], family="ipv6", shell="sonic", + neighbor=self.local_topo['dut2_addr_ipv6'], state='Established', asn=self.local_topo['dut1_as']): + utils.exec_all(True, [[bgpapi.show_bgp_ipv6_neighbor_vtysh, topo.dut_list[0]], + [bgpapi.show_bgp_ipv6_neighbor_vtysh, topo.dut_list[1]]]) + st.error("BGP Neighbor failed to Establish between DUT1 and TG") + st.log("{} - Neighbor {} is failed to Establish".format(topo.dut_list[0], + self.local_topo['dut2_addr_ipv6'])) + result = False + bgp_summary_spine_after_update_timer = bgpapi.show_bgp_ipv6_summary(topo.dut_list[1]) + rib_entries_after_update_timer = bgp_summary_spine_after_update_timer[0]['ribentries'] + st.log('RIB Entries after update delay timer expiry : {}'.format(rib_entries_after_update_timer)) + if int(rib_entries_after_update_timer) < 500: + st.error('Routes are not advertised to peer DUT, even after the update delay timer expiry') + tc_fail_flag = 1 + st.log("Initiating the Ipv6 traffic for those Routes from TG connected to DUT2") + src_handle = 'handle' + if tg_ob.tg_type == 'ixia': + src_handle = 'ipv6_handle' + tr1 = tg_ob.tg_traffic_config(port_handle=topo['T1{}P1_ipv6_tg_ph'.format(TG_D2)], + emulation_src_handle=topo['T1{}P1_ipv6_tg_ih'.format(TG_D2)][src_handle], + emulation_dst_handle=bgp_route['handle'], circuit_endpoint_type='ipv6', + mode='create', + transmit_mode='single_burst', pkts_per_burst='2000', length_mode='fixed', + rate_pps=1000) + stream_id1 = tr1['stream_id'] + tg_ob.tg_traffic_control(action='run', handle=stream_id1) + st.wait(20) + tg1_stats = tgapi.get_traffic_stats(tg_ob, port_handle=topo["T1{}P1_ipv6_tg_ph".format(TG_D1)]) + tg2_stats = tgapi.get_traffic_stats(tg_ob, port_handle=topo["T1{}P1_ipv6_tg_ph".format(TG_D2)]) + if not (int(tg2_stats.tx.total_packets) and int(tg1_stats.rx.total_packets)): + st.error('Received ZERO stats.') + tc_fail_flag = 1 + else: + percent_rx = float(int(tg1_stats.rx.total_packets) - int(tg2_stats.tx.total_packets)) / int( + tg2_stats.tx.total_packets) * 100 + st.log('tg1_stats.rx.total_packets : {}'.format(tg1_stats.rx.total_packets)) + st.log('tg2_stats.tx.total_packets : {}'.format(tg2_stats.tx.total_packets)) + st.log('percent_rx : {}'.format(percent_rx)) + if int(tg1_stats.rx.total_packets) < int(tg2_stats.tx.total_packets)*0.95: + tc_fail_flag = 1 + tg_ob.tg_emulation_bgp_control(handle=bgp_handle['handle'], mode='stop') + if tc_fail_flag: + st.report_fail("traffic_verification_failed") + st.report_pass('test_case_passed') + + TG_D1 = topo.tg_dut_list_name[0] + TG_D2 = topo.tg_dut_list_name[1] + tg_ob = topo['T1{}P1_tg_obj'.format(TG_D2)] + bgp_handle = topo['T1{}P1_ipv6_tg_bh'.format(TG_D2)] + tc_fail_flag = 0 + leaf_as = int(bgplib.data['leaf_as']) + st.log("Advertising 500 IPv6 Routes from TG connected to DUT2") + bgp_route = tg_ob.tg_emulation_bgp_route_config(handle=bgp_handle['handle'], mode='add', ip_version='6', + num_routes='500', prefix='1002::1', as_path='as_seq:2') + bgp_ctrl = tg_ob.tg_emulation_bgp_control(handle=bgp_handle['handle'], mode='start') + # Check for route count in neighbour, before update delay timer expiry + # Sleep for update delay timer and the check the route count in neighbour + st.wait(15) + if not utils.poll_wait(bgpapi.verify_bgp_neighborship, 120, topo.dut_list[0], family="ipv6", shell="sonic", + neighbor=self.local_topo['dut2_addr_ipv6'], state='Established', + asn=self.local_topo['dut1_as']): + utils.exec_all(True, [[bgpapi.show_bgp_ipv6_neighbor_vtysh, topo.dut_list[0]], + [bgpapi.show_bgp_ipv6_neighbor_vtysh, topo.dut_list[1]]]) + st.error("BGP Neighbor failed to Establish between DUT1 and TG") + st.log("{} - Neighbor {} is failed to Establish".format(topo.dut_list[0], + self.local_topo['dut2_addr_ipv6'])) + result = False + bgp_summary_spine_after_update_timer = bgpapi.show_bgp_ipv6_summary(topo.dut_list[0]) + rib_entries_after_update_timer = bgp_summary_spine_after_update_timer[0]['ribentries'] + st.log('RIB Entries after update delay timer expiry : {}'.format(rib_entries_after_update_timer)) + if int(rib_entries_after_update_timer) < 1000: + st.error('Routes are not advertised to peer DUT, even after the update delay timer expiry') + tc_fail_flag = 1 + st.log("Initiating the Ipv6 traffic for those Routes from TG connected to DUT1") + src_handle = 'handle' + if tg_ob.tg_type == 'ixia': + src_handle = 'ipv6_handle' + tr1 = tg_ob.tg_traffic_config(port_handle=topo['T1{}P1_ipv6_tg_ph'.format(TG_D1)], + emulation_src_handle=topo['T1{}P1_ipv6_tg_ih'.format(TG_D1)][src_handle], + emulation_dst_handle=bgp_route['handle'], circuit_endpoint_type='ipv6', + mode='create', + transmit_mode='single_burst', pkts_per_burst='2000', length_mode='fixed', + rate_pps=1000) + stream_id1 = tr1['stream_id'] + tg_ob.tg_traffic_control(action='run', handle=stream_id1) + st.wait(20) + tg1_stats = tgapi.get_traffic_stats(tg_ob, port_handle=topo["T1{}P1_ipv6_tg_ph".format(TG_D2)]) + tg2_stats = tgapi.get_traffic_stats(tg_ob, port_handle=topo["T1{}P1_ipv6_tg_ph".format(TG_D1)]) + if not (int(tg2_stats.tx.total_packets) and int(tg1_stats.rx.total_packets)): + st.error('Received ZERO stats.') + tc_fail_flag = 1 + else: + percent_rx = float(int(tg1_stats.rx.total_packets) - int(tg2_stats.tx.total_packets)) / int( + tg2_stats.tx.total_packets) * 100 + st.log('tg1_stats.rx.total_packets : {}'.format(tg1_stats.rx.total_packets)) + st.log('tg2_stats.tx.total_packets : {}'.format(tg2_stats.tx.total_packets)) + st.log('percent_rx : {}'.format(percent_rx)) + if int(tg1_stats.rx.total_packets) < int(tg2_stats.tx.total_packets)*0.95: + tc_fail_flag = 1 + tg_ob.tg_emulation_bgp_control(handle=bgp_handle['handle'], mode='stop') + if tc_fail_flag: + st.report_fail("traffic_verification_failed") + st.report_pass('test_case_passed') + # below API will change routing mode to split and save the sonic config. + bgpapi.enable_docker_routing_config_mode(dut=topo.dut_list[0]) + st.vtysh(topo.dut_list[0], "copy running-config startup-config") + st.reboot(topo.dut_list[0], 'fast') + st.wait(3) + if not utils.poll_wait(bgpapi.verify_ipv6_bgp_summary, 120, topo.dut_list[0], + neighbor=self.local_topo['dut2_addr_ipv6'], state='500'): + utils.exec_all(True, [[bgpapi.show_bgp_ipv6_neighbor_vtysh, topo.dut_list[0]], + [bgpapi.show_bgp_ipv6_neighbor_vtysh, topo.dut_list[1]]]) + st.error("BGP Neighbor failed to Establish between DUT1 and DUT2") + st.log("{} - Neighbor {} is failed to Establish".format(topo.dut_list[0], + self.local_topo['dut2_addr_ipv6'])) + result = False + bgp_summary_spine_after_update_timer = bgpapi.show_bgp_ipv6_summary(topo.dut_list[0]) + rib_entries_after_update_timer = bgp_summary_spine_after_update_timer[0]['ribentries'] + st.log('RIB Entries after reboot : {}'.format(rib_entries_after_update_timer)) + # without BGP helper, after reboot, no routes sent by DUT2 will be seen in dut1. + if int(rib_entries_after_update_timer) < 500: + st.error('Routes are not advertised to peer DUT, even after the update delay timer expiry') + tc_fail_flag = 1 + if tc_fail_flag: + st.report_fail("traffic_verification_failed") + st.report_pass('test_case_passed') + + # testcase: FtOtSoRtBgpPlFn002, Verify ipv6 route aggregation. + def test_route_aggregate_ipv6(self, bgp_ipvx_route_adv_filter_fixture): + limit = 3 + ip6_rt_list = ["2018:3:1::/64", "2018:3:2::/64", "2018:3:3::/64", "2018:3:4::/64"] + ip6_adr_list = ["2019:1::1", "2019:2::1", "2019:3::1", "2019:4::1"] + aggr_addr = "2018:3::/32" + + for i in range(0, limit): + ipapi.create_static_route(self.local_topo['dut1'], 'blackhole', ip6_rt_list[i], family='ipv6') + + # configure aggregate address prefix + bgpapi.create_bgp_aggregate_address(self.local_topo['dut1'], local_asn=self.local_topo['dut1_as'], + address_range=aggr_addr, summary=True, family="ipv6", config="add",cli_type=bgp_cli_type) + + my_cmd = 'router bgp\n' + my_cmd += 'address-family ipv6 unicast\n' + my_cmd += 'redistribute static\n' + my_cmd += 'end' + st.vtysh_config(self.local_topo['dut1'], my_cmd) + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut1'], family='ipv6') + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut2'], family='ipv6', + select=['network', 'as_path', 'next_hop']) + + list_of_learned_routes_on_dut2_by_dut = list(x['network'] for x in output) + + if set(ip6_rt_list).isdisjoint(set(list_of_learned_routes_on_dut2_by_dut)): + st.log("Routes falling under aggregate prefix are not distributed") + aggregation = True + else: + st.log("Routes falling under aggregate prefix are distributed") + aggregation = False + + if (aggr_addr in list_of_learned_routes_on_dut2_by_dut) and aggregation: + st.log("Aggregation happened") + result = True + else: + st.log("Aggregation not happened") + result = False + + bgpapi.create_bgp_aggregate_address(self.local_topo['dut1'], local_asn=self.local_topo['dut1_as'], + address_range=aggr_addr, summary=True, family="ipv6", config="delete",cli_type=bgp_cli_type) + + for i in range(0, limit): + ipapi.delete_static_route(self.local_topo['dut1'], 'blackhole', ip6_rt_list[i], family='ipv6') + + my_cmd = 'router bgp\n' + my_cmd += 'address-family ipv6 unicast\n' + my_cmd += 'no redistribute static\n' + my_cmd += 'end' + st.vtysh_config(self.local_topo['dut1'], my_cmd) + + if result: + st.report_pass("test_case_passed") + else: + st.report_fail("test_case_failed") + + # testcase: FtOtSoRtBgpPlFn005, verify static blackhole route redistribution with metric set in route-map + def test_static_blackhole_rt_redistribute_with_routemap_ipv6(self, bgp_ipvx_route_adv_filter_fixture): + ipapi.create_static_route(self.local_topo['dut1'], 'Null0', '2012:1::/64', family='ipv6') + ipapi.config_route_map(dut=self.local_topo['dut1'], route_map='rmap_blackhole', config='yes', sequence='10', + metric='50') + my_cmd = 'router bgp\n' + my_cmd += 'address-family ipv6 unicast\n' + my_cmd += 'redistribute static route-map rmap_blackhole\n' + my_cmd += 'end' + st.vtysh_config(self.local_topo['dut1'], my_cmd) + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['dut2'], family='ipv6', + select=['network', 'as_path', 'metric']) + + metric = bgplib.get_route_attribute(output, 'metric', network = '2012:1::/64') + if metric == '50': + st.log('static blackhole route with metric 50 redistributed from dut1 to dut2') + result = True + else: + st.log('static blackhole route is not learned on dut2') + result = False + + my_cmd = 'router bgp\n' + my_cmd += 'address-family ipv6 unicast\n' + my_cmd += 'no redistribute static route-map rmap_blackhole\n' + my_cmd += 'end' + st.vtysh_config(self.local_topo['dut1'], my_cmd) + + ipapi.config_route_map(dut=self.local_topo['dut1'], route_map='rmap_blackhole', config='no', sequence='10', metric='50') + ipapi.delete_static_route(self.local_topo['dut1'], 'Null0', '2012:1::/64', family='ipv6') + + if result: + st.report_pass("test_case_passed") + else: + st.report_fail("test_case_failed") + + +""" +BGP IPv4 and IPv6 router distribution and filtering TCs: End + +""" + + +""" +BGP Neighbor over VE over LAG fixture, class and test cases - START + +""" + + +def bgp_ve_lag_pre_config(): + global topo + st.banner("BGP VE LAG CLASS CONFIG - START") + + # underlay config - configure ve over lag + bgplib.l3tc_underlay_config_unconfig(config='yes', config_type='veLag') + + # config ip on underlay interface + bgplib.l3tc_vrfipv4v6_address_leafspine_config_unconfig(config='yes', config_type='all') + + # Ping Verification + if not bgplib.l3tc_vrfipv4v6_address_leafspine_ping_test(config_type='all', ping_count=3): + st.error("Ping failed in between Spine - Leaf") + st.report_fail('test_case_failed') + bgplib.l3tc_vrfipv4v6_address_leafspine_bgp_config(config='yes') + + bgplib.l3tc_vrfipv4v6_address_leafspine_tg_bgp_config(config='yes', config_type='all', class_reconfig='Yes') + st.wait(10) + + # BGP Neighbour Verification + if not utils.poll_wait(bgplib.l3tc_vrfipv4v6_address_leafspine_bgp_check, 10, config_type='all'): + st.error("Neighbour is failed to Establish between Spine - Leaf") + st.report_fail('test_case_failed') + + st.log("Getting all topology info related to connectivity / TG and other parameters between duts") + topo = bgplib.get_leaf_spine_topology_info() + st.banner("BGP VE LAG CLASS CONFIG - END") + + +def bgp_ve_lag_pre_config_cleanup(): + st.banner("BGP VE LAG CLASS CONFIG CLEANUP - START") + bgplib.l3tc_vrfipv4v6_address_leafspine_bgp_config(config='no', config_type='veLag') + bgplib.l3tc_vrfipv4v6_address_leafspine_config_unconfig(config='no') + bgplib.l3tc_underlay_config_unconfig(config='no', config_type='veLag') + st.banner("BGP VE LAG CLASS CONFIG CLEANUP - END") + + +@pytest.fixture(scope='class') +def bgp_ve_lag_class_hook(request): + bgp_ve_lag_pre_config() + yield + bgp_ve_lag_pre_config_cleanup() + + +# TestBGPVeLag Class +@pytest.mark.usefixtures('bgp_ve_lag_class_hook') +class TestBGPVeLag(TestBGPCommon): + + # test v4 and v6 neighbors + @pytest.mark.bgp_clear + @pytest.mark.bgp_ft + # tests both v4 and v6 neighbors + def test_ft_bgp_clear(self): + TestBGPCommon.ft_bgp_clear(self) + # tests both v4 and v6 neighbors + + @pytest.mark.bgp_traffic + @pytest.mark.bgp_ft + def test_ft_bgp_peer_traffic_check(self): + TestBGPCommon.ft_bgp_peer_traffic_check(self) + + +""" +BGP Neighbor over VE over LAG fixture, class and test cases - END +""" + + +""" +BGP Neighbor over L3 over LAG fixture, class and test cases - START +""" + + +def bgp_l3_lag_pre_config(): + global topo + st.banner("BGP L3 OVER LAG CLASS CONFIG - START") + + # underlay config - configure ve over lag + bgplib.l3tc_underlay_config_unconfig(config='yes', config_type='l3Lag') + + # config ip on underlay interface + bgplib.l3tc_vrfipv4v6_address_leafspine_config_unconfig(config='yes', config_type='all') + + # Ping Verification + if not bgplib.l3tc_vrfipv4v6_address_leafspine_ping_test(config_type='all', ping_count=3): + st.error("Ping failed in between Spine - Leaf") + st.report_fail('test_case_failed') + bgplib.l3tc_vrfipv4v6_address_leafspine_bgp_config(config='yes') + + bgplib.l3tc_vrfipv4v6_address_leafspine_tg_bgp_config(config='yes', config_type='all', class_reconfig='Yes') + st.wait(10) + + # BGP Neighbour Verification + if not utils.poll_wait(bgplib.l3tc_vrfipv4v6_address_leafspine_bgp_check, 10, config_type='all'): + st.error("Neighbour is failed to Establish between Spine - Leaf") + st.report_fail('test_case_failed') + st.log("Getting all topology info related to connectivity / TG and other parameters between duts") + topo = bgplib.get_leaf_spine_topology_info() + + st.banner("BGP L3 LAG CLASS CONFIG - END") + + +def bgp_l3_lag_pre_config_cleanup(): + st.banner("BGP L3 OVER LAG CLASS CONFIG CLEANUP - START") + bgplib.l3tc_vrfipv4v6_address_leafspine_bgp_config(config='no') + bgplib.l3tc_vrfipv4v6_address_leafspine_config_unconfig(config='no') + bgpapi.cleanup_bgp_config(st.get_dut_names()) + ipapi.clear_ip_configuration(st.get_dut_names(), family='all', thread=True) + bgplib.l3tc_underlay_config_unconfig(config='no', config_type='l3Lag') + st.banner("BGP L3 OVER LAG CLASS CONFIG CLEANUP - END") + + +@pytest.fixture(scope='class') +def bgp_l3_lag_class_hook(request): + bgp_l3_lag_pre_config() + yield + bgp_l3_lag_pre_config_cleanup() + +# TestBGPVeLag Class +@pytest.mark.usefixtures('bgp_l3_lag_class_hook') +class TestBGPL3Lag(TestBGPCommon): + @pytest.mark.bgp_l3lag_traffic + def test_ft_bgp_l3lag_peer_traffic_check(self): + TestBGPCommon.ft_bgp_peer_traffic_check(self) + + +""" +BGP Neighbor In L3 Over LAG fixture, class and test cases - END +""" + diff --git a/spytest/tests/routing/BGP/test_bgp_4node.py b/spytest/tests/routing/BGP/test_bgp_4node.py new file mode 100644 index 00000000000..7ab793a3529 --- /dev/null +++ b/spytest/tests/routing/BGP/test_bgp_4node.py @@ -0,0 +1,256 @@ +# BGP 4 node topology test cases +import pytest + +from spytest import st + +import apis.routing.ip as ipapi +import apis.routing.bgp as bgpapi +import BGP.bgp4nodelib as bgp4nodelib + +from utilities.common import poll_wait + +@pytest.fixture(scope="module", autouse=True) +def bgp_module_hooks(request): + bgp_pre_config() + yield + bgp_pre_config_cleanup() + +# bgp module level pre config function +def bgp_pre_config(): + global topo + st.banner("BGP MODULE CONFIG - START") + st.log("Ensure minimum linear 4-node topology") + st.ensure_min_topology('D1D2:1', 'D2D3:1', 'D3D4:1') + bgp4nodelib.l3_ipv4v6_address_config_unconfig(config='yes', config_type='all') + # Ping Verification + if not bgp4nodelib.l3tc_vrfipv4v6_address_ping_test(config_type='all', ping_count=3): + st.error("Ping failed in between DUTs") + st.report_fail('test_case_failed') + topo = bgp4nodelib.get_confed_topology_info() + st.log(topo) + st.banner("BGP MODULE CONFIG - END") + +# bgp module level pre config cleanup function +def bgp_pre_config_cleanup(): + st.banner("BGP MODULE CONFIG CLEANUP - START") + bgp4nodelib.l3_ipv4v6_address_config_unconfig(config='no') + st.banner("BGP MODULE CONFIG CLEANUP - END") + + +@pytest.fixture(scope="function") +def bgp_func_hooks(request): + yield + +@pytest.mark.bgp_ft +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_bgp_ebgp_multihop_4byteASN(): + """ + + Verify the functioning of ebgp multihop command with 4 byte ASN + """ + #On DUT1 and DUT3, create BGP with 4byte ASN + dut1_as = 6500001 + dut1 = topo['dut_list'][0] + dut3_as = 6500002 + dut3 = topo['dut_list'][2] + + #Configure bgp on DUT1 and add DUT3 as neighbor with ebgp-multihop ttl set to 5 + bgpapi.config_bgp(dut1, local_as= dut1_as, neighbor=topo['D3D2P1_ipv4'], remote_as =dut3_as, config_type_list=["neighbor","ebgp_mhop"], ebgp_mhop='5') + #Add static route to DUT3 neighbor + ipapi.create_static_route(dut1, topo['D1D2P1_neigh_ipv4'], "{}/24".format(topo['D3D2P1_ipv4'])) + #Configure bgp on DUT3 and add DUT1 as neighbor with ebgp-multihop ttl set to 5 + bgpapi.config_bgp(dut3, local_as= dut3_as, neighbor=topo['D1D2P1_ipv4'], remote_as =dut1_as, config_type_list=["neighbor","ebgp_mhop"], ebgp_mhop='5') + #Add static route to DUT1 neighbor + ipapi.create_static_route(dut3, topo['D3D2P1_neigh_ipv4'], "{}/24".format(topo['D1D2P1_ipv4'])) + + result = bgpapi.verify_bgp_summary(dut1, family='ipv4', neighbor=topo['D3D2P1_ipv4'], state='Established') + + #Clear applied configs + bgpapi.cleanup_router_bgp(dut1) + bgpapi.cleanup_router_bgp(dut3) + ipapi.delete_static_route(dut1, topo['D1D2P1_neigh_ipv4'], "{}/24".format(topo['D3D2P1_ipv4'])) + ipapi.delete_static_route(dut3, topo['D3D2P1_neigh_ipv4'], "{}/24".format(topo['D1D2P1_ipv4'])) + + if result: + st.report_pass("test_case_passed") + else: + st.report_fail("test_case_failed") + +################################################################################ +#BGP Confederation test cases - START + +def bgp_confed_pre_config(): + st.banner("BGP CONFED CLASS CONFIG - START") + bgp4nodelib.l3tc_vrfipv4v6_confed_bgp_config(config='yes') + # BGP Neighbour Verification + if not poll_wait(bgp4nodelib.l3tc_vrfipv4v6_address_confed_bgp_check, 10, config_type='all'): + st.error("Neighbour is failed to Establish between DUTs") + st.report_fail('test_case_failed') + st.log("Getting all topology info related to connectivity / TG and other parameters between duts") + st.banner("BGP CONFED CLASS CONFIG - END") + +def bgp_confed_pre_config_cleanup(): + st.banner("BGP CONFED CLASS CONFIG CLEANUP - START") + bgp4nodelib.l3tc_vrfipv4v6_confed_bgp_config(config='no') + st.banner("BGP RIF CLASS CONFIG CLEANUP - END") + +@pytest.fixture(scope='class') +def bgp_confed_class_hook(request): + bgp_confed_pre_config() + yield + bgp_confed_pre_config_cleanup() + +#TestBGPConfed class +@pytest.mark.usefixtures('bgp_confed_class_hook') +class TestBGPConfed(): + + @pytest.mark.bgp_ft + @pytest.mark.community + @pytest.mark.community_pass + def test_ipv6_confed_route_distribution(self): + st.log("Verify the config of BGP v6 confederation and router advertisement") + + st.log("Advertise a network from DUT1 and check if it's learnt on confed peer DUT3") + dut1_name = topo['dut_list'][0] + dut3_name = topo['dut_list'][2] + network_ipv4 = '131.5.6.0/24' + network_ipv6 = '2000:1::0/64' + + #Advertise a network to peer + + bgpapi.config_bgp_network_advertise(dut1_name, topo['D1_as'], network_ipv4) + bgpapi.config_bgp_network_advertise(dut1_name, topo['D1_as'], network_ipv6, addr_family='ipv6', config='yes') + entries = bgpapi.get_ip_bgp_route(dut3_name, family="ipv4", network=network_ipv4) + entries1 = bgpapi.get_ip_bgp_route(dut3_name, family="ipv6", network="2000:1::/64") + + bgpapi.config_bgp_network_advertise(dut1_name, topo['D1_as'], network_ipv4, config='no' ) + bgpapi.config_bgp_network_advertise(dut1_name, topo['D1_as'], network_ipv6, addr_family='ipv6', config='no') + if entries and entries1: + st.log("Advertised route present on DUT3") + else: + st.log("Advertised route not present on DUT3") + st.report_fail("test_case_failed") + + st.report_pass("test_case_passed") + + @pytest.mark.bgp_ft + @pytest.mark.community + @pytest.mark.community_pass + def test_ipv6_confed_with_rr(self): + st.log("Verify the behavior of route-reflector within a confederation of BGPv6 peers") + st.log("Consider the right confed iBGP_as and check RR functionality between the 3 iBGP routers") + + network_ipv4 = '131.6.6.0/24' + network_ipv6 = '3000:1::0/64' + #IBGP as is one of D2/D3/D4 asn + iBGP_as=topo['D2_as'] + + bgpapi.config_bgp_network_advertise(topo['dut_list'][1], iBGP_as, network_ipv4) + bgpapi.config_bgp_network_advertise(topo['dut_list'][1], iBGP_as, network_ipv6, addr_family='ipv6', config='yes') + + st.log("Check the network on the 3rd IBGP peer is not learnt becaue RR is not configured") + entries = bgpapi.get_ip_bgp_route(topo['dut_list'][3], family="ipv4", network=network_ipv4) + entries1 = bgpapi.get_ip_bgp_route(topo['dut_list'][3], family="ipv6", network="3000:1::/64") + + if entries and entries1: + st.log("Routes learnt on the 3rd IBGP peer withour configuring RR") + bgpapi.config_bgp_network_advertise(topo['dut_list'][1], iBGP_as, network_ipv4, config='no' ) + bgpapi.config_bgp_network_advertise(topo['dut_list'][1], iBGP_as, network_ipv6, addr_family='ipv6', config='no') + st.report_fail("test_case_failed") + + st.log(" Now configure RR") + bgpapi.create_bgp_route_reflector_client(topo.dut_list[2], iBGP_as, 'ipv4', topo['D3D4P1_neigh_ipv4'], 'yes') + bgpapi.create_bgp_route_reflector_client(topo.dut_list[2], iBGP_as, 'ipv6', topo['D3D4P1_neigh_ipv6'], 'yes') + + st.wait(10) + st.log("Now the routes should be learnt on the 3rd IBGP peer") + entries2 = bgpapi.get_ip_bgp_route(topo['dut_list'][3], family="ipv4", network=network_ipv4) + entries3 = bgpapi.get_ip_bgp_route(topo['dut_list'][3], family="ipv6", network="3000:1::/64") + + bgpapi.config_bgp_network_advertise(topo['dut_list'][1], iBGP_as, network_ipv4, config='no' ) + bgpapi.config_bgp_network_advertise(topo['dut_list'][1], iBGP_as, network_ipv6, addr_family='ipv6', config='no') + bgpapi.create_bgp_route_reflector_client(topo.dut_list[2], iBGP_as, 'ipv4', topo['D3D4P1_neigh_ipv4'], 'no') + bgpapi.create_bgp_route_reflector_client(topo.dut_list[2], iBGP_as, 'ipv6', topo['D3D4P1_neigh_ipv6'], 'no') + + if not entries2 and not entries3: + st.report_fail("test_case_failed") + + st.report_pass("test_case_passed") + + @pytest.mark.bgp_ft + @pytest.mark.community + @pytest.mark.community_fail + def test_confed_route_distribution_with_rmap(self): + st.log("Verify the behavior of route-maps over confederation peers") + result = True + + network1 = '134.5.6.0/24' + network2 = '134.5.7.0/24' + network3 = '134.5.8.0' + + #Create route-map and permit network3 + ipapi.config_route_map_match_ip_address(topo['dut_list'][0], 'test-rmap', 'permit', '10', 'test-access-list1') + #Add set option to prepend as-path 200 + ipapi.config_route_map_set_aspath(topo['dut_list'][0], 'test-rmap', 'permit', '10', '200') + #Create access-list test-access-list1 + ipapi.config_access_list(topo['dut_list'][0], 'test-access-list1', network3+'/24', 'permit') + + #Advertise two networks from leaf + bgpapi.advertise_bgp_network(topo['dut_list'][0], topo['D1_as'], network1, 'test-rmap') + bgpapi.advertise_bgp_network(topo['dut_list'][0], topo['D1_as'], network2, 'test-rmap') + bgpapi.advertise_bgp_network(topo['dut_list'][0], topo['D1_as'], network3+'/24', 'test-rmap') + + #In route-map, deny network1 + ipapi.config_route_map_match_ip_address(topo['dut_list'][0], 'test-rmap', 'deny', '20', 'test-access-list2') + #Create access-list test-access-list2 + ipapi.config_access_list(topo['dut_list'][0], 'test-access-list2', network1, 'deny') + + #In route-map, permit network2 + ipapi.config_route_map_match_ip_address(topo['dut_list'][0], 'test-rmap', 'permit', '30', 'test-access-list3') + #Create access-list test-access-list3 + ipapi.config_access_list(topo['dut_list'][0], 'test-access-list3', network2, 'permit') + + #verify that the neighbor has the as-path prepended + output = bgpapi.show_bgp_ipvx_prefix(topo['dut_list'][1], prefix=network3, masklen=topo['D1_as']) + st.log(output) + for x in output: # type: basestring + as_path = x['peerasn'] + as_path = as_path.split() + for each in as_path: + if each == "200": + result = True + + #verify that network1 is not present in bgp routes + n1 = ipapi.verify_ip_route(topo['dut_list'][1],ip_address=network1) + if (n1 == False): + result = result & True + else: + result = result & False + + #verify that network2 is present in bgp routes + n2 = ipapi.verify_ip_route(topo['dut_list'][1],ip_address=network2) + if (n2): + result = result & True + else: + result = result & False + + #CLear applied configs + ipapi.config_access_list(topo['dut_list'][0], 'test-access-list3', network2, 'permit', config='no') + ipapi.config_access_list(topo['dut_list'][0], 'test-access-list2', network1, 'deny', config='no') + ipapi.config_access_list(topo['dut_list'][0], 'test-access-list1', network3+'/24', 'permit', config='no') + + ipapi.config_route_map_mode(topo['dut_list'][0], 'test-rmap', 'permit', '10', config='no') + + bgpapi.advertise_bgp_network(topo['dut_list'][0], topo['D1_as'], network1, 'test-rmap', config='no') + bgpapi.advertise_bgp_network(topo['dut_list'][0], topo['D1_as'], network2, 'test-rmap', config='no') + bgpapi.advertise_bgp_network(topo['dut_list'][0], topo['D1_as'], network3+'/24', 'test-rmap', config='no') + + if result: + st.report_pass("test_case_passed") + else: + st.report_fail("test_case_failed") + +#BGP Confederation test cases - END +################################################################################ + diff --git a/spytest/tests/routing/BGP/test_bgp_fast_reboot.py b/spytest/tests/routing/BGP/test_bgp_fast_reboot.py new file mode 100644 index 00000000000..9eff4c67982 --- /dev/null +++ b/spytest/tests/routing/BGP/test_bgp_fast_reboot.py @@ -0,0 +1,285 @@ +import pytest + +from spytest import st, tgapi, SpyTestDict + +import apis.system.reboot as reboot_obj +import apis.routing.ip as ip_obj +import apis.routing.bgp as bgp_obj +import apis.switching.portchannel as portchannel_obj +import apis.switching.vlan as vlan_obj + +vars = dict() + +@pytest.fixture(scope="module", autouse=True) +def bgp_fast_reboot_module_hooks(request): + global vars + vars = st.ensure_min_topology("D1D2:1", "D1T1:2") + st.log("Enabling IPv6 mode globally") + ip_obj.config_ipv6(vars.D1,action='enable') + ip_obj.config_ipv6(vars.D2,action='enable') + st.log("Configuring ipv4 addresses on routing interfaces") + ipv4_ip_address_config() + st.log("Verifying ipv4 addresses on routing interfaces") + verify_ipv4_address_config() + st.log("Configuring ipv6 addresses on routing interfaces") + ipv6_address_config() + st.log("Verifying ipv6 addresses on routing interfaces") + verify_ipv6_address_config() + st.log("Configuring IPV6 eBGP config between DUT1 and DUT2,iBGP config between DUT1 and TG2") + ipv6_bgp_config() + st.log("Configuring IPV4 eBGP config between DUT1 and DUT2,iBGP config between DUT1 and TG1") + ipv4_bgp_config() + st.log("Configuring TG2 V6 iBGP config") + tg_bgpv6_config(vars, data.local_asn4, data.remote_asn4) + st.log("Configuring TG1 V4 iBGP config") + tg_bgp_config(vars, data.local_asn4, data.remote_asn4) + st.log("Verify IPV4 eBGP neighborship between D1 and D2 and iBGP neighborship between D1 and TG1") + verify_v4_bgp_neigborship() + st.log("Verify IPV6 eBGP neighborship between D1 and D2 and iBGP neighborship between D1 and TG2") + verify_v6_bgp_neigborship() + yield + bgp_obj.cleanup_router_bgp(st.get_dut_names()) + ip_obj.clear_ip_configuration(st.get_dut_names()) + ip_obj.clear_ip_configuration(st.get_dut_names(), 'ipv6') + vlan_obj.clear_vlan_configuration(st.get_dut_names()) + portchannel_obj.clear_portchannel_configuration(st.get_dut_names()) + + +@pytest.fixture(scope="function", autouse=True) +def bgp_fast_reboot_func_hooks(request): + yield + + +data = SpyTestDict() +data.local_ip_addr = "12.12.12.1" +data.neigh_ip_addr = "12.12.12.2" +data.local_ip6_addr = "3241::1" +data.neigh_ip6_addr = "3241::2" +data.router_id_1 = "110.110.110.1" +data.router_id_2 = "120.120.120.1" +data.loopback_1 = "66.66.66.66" +data.loopback_2 = "77.77.77.77" +data.af_ipv4 = "ipv4" +data.af_ipv6 = "ipv6" +data.shell_sonic = "sonic" +data.shell_vtysh = "vtysh" +data.d1t1_ip_addr = "192.168.0.1" +data.t1d1_ip_addr = "192.168.0.2" +data.d1t1_ip_addr_mask = "16" +data.d1t1_ip6_addr = '2001::1' +data.t1d1_ip6_addr = '2001::100' +data.d1t1_ip6_addr_mask = '64' +data.af_ipv4 = "ipv4" +data.af_ipv6 = "ipv6" +data.local_asn = "4294966195" +data.tg_bgp_route_prfix = "157.1.0.0" +data.local_asn4 = "4294966195" +data.remote_asn4 = "65001" + + +def ipv4_ip_address_config(): + st.log("Creating the ipv4 routing interfaces in {}".format(vars.D1)) + ip_obj.config_ip_addr_interface(vars.D1, vars.D1D2P1, data.local_ip_addr, 30, family=data.af_ipv4) + + st.log("Creating the ipv4 routing interfaces in {}".format(vars.D2)) + ip_obj.config_ip_addr_interface(vars.D2, vars.D2D1P1, data.neigh_ip_addr, 30, family=data.af_ipv4) + + st.log("Creating the ipv4 routing interfaces on TG1 {}".format(vars.D1T1P1)) + ip_obj.config_ip_addr_interface(vars.D1, vars.D1T1P1, data.d1t1_ip_addr, + data.d1t1_ip_addr_mask, family=data.af_ipv4) + + +def verify_ipv4_address_config(): + st.log("Verify ipv4 address on routing interface {} in D1".format(vars.D1D2P1)) + if not ip_obj.verify_interface_ip_address(vars.D1, vars.D1D2P1, "{}/30".format(data.local_ip_addr), data.af_ipv4): + st.report_fail('ip_routing_int_create_fail', vars.D1D2P1) + else: + st.log(" IPV4 Addres Config is successful") + st.log("Verify ipv4 address on routing interface {} in D2".format(vars.D1D2P1)) + if not ip_obj.verify_interface_ip_address(vars.D2, vars.D2D1P1, "{}/30".format(data.neigh_ip_addr), data.af_ipv4): + st.report_fail('ip_routing_int_create_fail', vars.D2D1P1) + else: + st.log(" IPV4 Addres Config is successful") + st.log("Verify ipv4 address on routing interface {} in D1".format(vars.D1T1P1)) + if not ip_obj.verify_interface_ip_address(vars.D1, vars.D1T1P1, + "{}/{}".format(data.d1t1_ip_addr, data.d1t1_ip_addr_mask), + data.af_ipv4): + st.report_fail('ip_routing_int_create_fail', vars.D1T1P1) + else: + st.log(" IPV4 Addres Config is successful") + + +def ipv6_address_config(): + st.log("Creating the ipv6 routing interfaces in {}".format(vars.D1D2P1)) + ip_obj.config_ip_addr_interface(vars.D1, vars.D1D2P1, data.local_ip6_addr, 96, family=data.af_ipv6) + + st.log("Creating the ipv6 routing interfaces in {}".format(vars.D2D1P1)) + ip_obj.config_ip_addr_interface(vars.D2, vars.D2D1P1, data.neigh_ip6_addr, 96, family=data.af_ipv6) + + st.log("Creating the ipv6 routing interfaces on TG2 interface {}".format(vars.D1T1P2)) + ip_obj.config_ip_addr_interface(vars.D1, vars.D1T1P2, data.d1t1_ip6_addr, data.d1t1_ip6_addr_mask, + family=data.af_ipv6) + + +def verify_ipv6_address_config(): + st.log("Verify ipv6 address on routing interface {} in D1".format(vars.D1D2P1)) + if not ip_obj.verify_interface_ip_address(vars.D1, vars.D1D2P1, "{}/96".format(data.local_ip6_addr), data.af_ipv6): + st.report_fail('ip6_routing_int_create_fail', vars.D1D2P1) + else: + st.log(" IPV6 Addres Config is successful") + st.log("Verify ipv6 address on routing interface {} in D2".format(vars.D2D1P1)) + if not ip_obj.verify_interface_ip_address(vars.D2, vars.D2D1P1, "{}/96".format(data.neigh_ip6_addr), data.af_ipv6): + st.report_fail('ip6_routing_int_create_fail', vars.D2D1P1) + else: + st.log(" IPV6 Addres Config is successful") + st.log("Verify ipv6 address on routing interface {} in D1".format(vars.D1T1P2)) + if not ip_obj.verify_interface_ip_address(vars.D1, vars.D1T1P2, + "{}/{}".format(data.d1t1_ip6_addr, data.d1t1_ip6_addr_mask), + data.af_ipv6): + st.report_fail('ipv6_routing_int_create_fail', vars.D1T1P2) + else: + st.log(" IPV6 Addres Config is successful") + + +def ipv4_bgp_config(): + st.log("Creating the eBGP ipv4 neighbors in {}".format(vars.D1)) + bgp_obj.create_bgp_router(vars.D1, data.local_asn4, data.router_id_1) + bgp_obj.config_address_family_redistribute(vars.D1, data.local_asn4, data.af_ipv4, "unicast", "connected") + bgp_obj.create_bgp_neighbor(dut=vars.D1, local_asn=data.local_asn4, neighbor_ip=data.neigh_ip_addr, + remote_asn=data.remote_asn4, family=data.af_ipv4) + + st.log("Creating the eBGP ipv4 neighbors in {}".format(vars.D2)) + bgp_obj.create_bgp_router(vars.D2, data.remote_asn4, data.router_id_2) + bgp_obj.config_address_family_redistribute(vars.D2, data.remote_asn4, data.af_ipv4, "unicast", "connected") + bgp_obj.create_bgp_neighbor(dut=vars.D2, local_asn=data.remote_asn4, neighbor_ip=data.local_ip_addr, + remote_asn=data.local_asn4, family=data.af_ipv4) + + st.log("Creating the iBGP ipv4 neighbors with TG1 {}".format(vars.D1T1P1)) + bgp_obj.create_bgp_router(vars.D1, data.local_asn4, data.router_id_1) + bgp_obj.config_address_family_redistribute(vars.D1, data.local_asn4, data.af_ipv4, "unicast", "connected") + bgp_obj.create_bgp_neighbor(dut=vars.D1, local_asn=data.local_asn4, neighbor_ip=data.t1d1_ip_addr, + remote_asn=data.local_asn4, family=data.af_ipv4) + + +def ipv6_bgp_config(): + st.log("Creating the eBGP ipv6 neighbors in {}".format(vars.D1)) + bgp_obj.create_bgp_router(vars.D1, data.local_asn4, data.router_id_1) + bgp_obj.config_address_family_redistribute(vars.D1, data.local_asn4, data.af_ipv6, "unicast", "connected") + bgp_obj.create_bgp_neighbor(dut=vars.D1, local_asn=data.local_asn4, neighbor_ip=data.neigh_ip6_addr, + remote_asn=data.remote_asn4, family=data.af_ipv6) + st.log("Creating the eBGP ipv6 neighbors in {}".format(vars.D2)) + bgp_obj.create_bgp_router(vars.D2, data.remote_asn4, data.router_id_2) + bgp_obj.config_address_family_redistribute(vars.D2, data.remote_asn4, data.af_ipv6, "unicast", "connected") + bgp_obj.create_bgp_neighbor(dut=vars.D2, local_asn=data.remote_asn4, neighbor_ip=data.local_ip6_addr, + remote_asn=data.local_asn4, family=data.af_ipv6) + + st.log("Creating the iBGP ipv6 neighbors with TG2 {}".format(vars.D1)) + bgp_obj.create_bgp_router(vars.D1, data.local_asn4, data.router_id_1) + bgp_obj.create_bgp_neighbor(dut=vars.D1, local_asn=data.local_asn4, neighbor_ip='2001::100', + remote_asn=data.local_asn4, family=data.af_ipv6) + + +def tg_bgp_config(vars, local_asn, remote_asn): + tg1, tg_ph_1 = tgapi.get_handle_byname("T1D1P1") + tg1.tg_traffic_control(action='reset', port_handle=tg_ph_1) + + h1 = tg1.tg_interface_config(port_handle=tg_ph_1, mode='config', intf_ip_addr=data.t1d1_ip_addr, + gateway=data.d1t1_ip_addr, + src_mac_addr='00:0a:01:00:00:01', arp_send_req='1') + st.log("INTFCONF: " + str(h1)) + # Configuring BGP device on top of interface. + bgp_conf = tg1.tg_emulation_bgp_config(handle=h1['handle'], mode='enable', active_connect_enable='1', + local_as=data.local_asn4, + remote_as=data.local_asn4, remote_ip_addr=data.d1t1_ip_addr, + enable_4_byte_as='1') + st.log("BGPCONF: " + str(bgp_conf)) + # Adding routes to BGP device. + bgp_route = tg1.tg_emulation_bgp_route_config(handle=bgp_conf['handle'], mode='add', num_routes='100', + prefix='172.168.1.0') + st.log("BGPROUTE: " + str(bgp_route)) + return True + + +def tg_bgpv6_config(vars, local_asn, remote_asn): + tg2, tg_ph_2 = tgapi.get_handle_byname("T1D1P2") + tg2.tg_traffic_control(action='reset', port_handle=tg_ph_2) + h1 = tg2.tg_interface_config(port_handle=tg_ph_2, mode='config', ipv6_intf_addr=data.t1d1_ip6_addr, \ + ipv6_prefix_length='64', ipv6_gateway=data.d1t1_ip6_addr, + src_mac_addr='00:0a:01:00:00:01', \ + arp_send_req='1') + st.log("INTFCONF: " + str(h1)) + st.log("Configuring BGP device on top of interface") + bgp_conf = tg2.tg_emulation_bgp_config(handle=h1['handle'], mode='enable', ip_version='6', \ + active_connect_enable='1', local_as=data.local_asn4, \ + remote_as=data.local_asn4, remote_ipv6_addr=data.d1t1_ip6_addr, + enable_4_byte_as='1') + st.log("BGPCONF: " + str(bgp_conf)) + st.log("Adding routes to BGP device") + bgp_route = tg2.tg_emulation_bgp_route_config(handle=bgp_conf['handle'], mode='add', ip_version='6', + num_routes='100', prefix='1001::0') + st.log("BGPROUTE: " + str(bgp_route)) + return True + + +def verify_v4_bgp_neigborship(): + st.wait(10) + st.log("Waiting for the eBGP neighbors to get Established") + if not bgp_obj.verify_bgp_neighborship(vars.D1, family=data.af_ipv4, shell=data.shell_sonic, + neighbor=data.neigh_ip_addr, state='Established', asn=data.remote_asn4): + st.report_fail('bgp_ip_peer_establish_fail', data.neigh_ip_addr) + else: + st.log("eBGP peer neigborship is successful") + if not bgp_obj.verify_bgp_neighborship(vars.D2, family=data.af_ipv4, shell=data.shell_sonic, + neighbor=data.local_ip_addr, state='Established', asn=data.local_asn4): + st.report_fail('bgp_ip_peer_establish_fail', data.local_ip_addr) + else: + st.log("eBGP V4 peer neigborship is successful") + + st.log("Waiting for the iBGP neighbors to get Established wit TG1") + if not bgp_obj.verify_bgp_neighborship(vars.D1, family=data.af_ipv4, shell=data.shell_sonic, + neighbor=data.t1d1_ip_addr, state='Established', asn=data.local_asn4): + st.report_fail('bgp_ip_peer_establish_fail', data.t1d1_ip_addr) + else: + st.log("iBGP V4 peer neigborship is successful") + +def verify_v6_bgp_neigborship(): + st.wait(10) + st.log("Waiting for the eBGP neighbors to get Established with peer DUT") + if not bgp_obj.verify_bgp_neighborship(vars.D1, family=data.af_ipv6, shell=data.shell_sonic, + neighbor=data.neigh_ip6_addr, state='Established', asn=data.remote_asn4): + st.report_fail('bgp_ip6_peer_establish_fail', data.neigh_ip6_addr) + else: + st.log("eBGP V6 peer neigborship is successful") + if not bgp_obj.verify_bgp_neighborship(vars.D2, family=data.af_ipv6, shell=data.shell_sonic, + neighbor=data.local_ip6_addr, state='Established', asn=data.local_asn4): + st.report_fail('bgp_ip6_peer_establish_fail', data.local_ip6_addr) + else: + st.log("eBGP V6 peer neigborship is successful") + + st.log("Waiting for the iBGPV6 neighbors to get Established with TG2") + if not bgp_obj.verify_bgp_neighborship(vars.D1, family=data.af_ipv6, shell=data.shell_sonic, + neighbor=data.t1d1_ip6_addr, state='Established', asn=data.local_asn4): + st.report_fail('bgp_ip_peer_establish_fail', data.t1d1_ip6_addr) + else: + st.log("iBGP V6 peer neigborship is successful") + + +@pytest.mark.bgp_fast_reboot +def test_ft_bgp_fast_reboot(): + st.log("Enabling docker routing config mode in D1 and D2") + bgp_obj.enable_docker_routing_config_mode(vars.D1) + bgp_obj.enable_docker_routing_config_mode(vars.D2) + st.log("saving the BGP config in vtysh shell") + command = "copy run start" + st.vtysh(vars.D1, command) + st.vtysh(vars.D2, command) + st.log("config save in D1 and D2") + reboot_obj.config_save([vars.D1, vars.D2]) + st.log("Performing fast reboot") + st.reboot(vars.D1,"fast") + st.log("Verifying BGP is established after fast reboot") + verify_v4_bgp_neigborship() + st.log("Verifying BGPV6 is established after fast reboot") + verify_v6_bgp_neigborship() + st.report_pass('test_case_passed') + diff --git a/spytest/tests/routing/BGP/test_bgp_rr_traffic.py b/spytest/tests/routing/BGP/test_bgp_rr_traffic.py new file mode 100644 index 00000000000..a584521c0c1 --- /dev/null +++ b/spytest/tests/routing/BGP/test_bgp_rr_traffic.py @@ -0,0 +1,247 @@ +import pytest + +from spytest import st, tgapi +from spytest.utils import poll_wait + +import apis.routing.ip as ipapi +import apis.routing.bgp as bgpapi +import BGP.bgplib as bgplib + +bgp_cli_type = 'vtysh' + +@pytest.fixture(scope="module", autouse=True) +def bgp_module_hooks(request): + st.ensure_min_topology('D1D2:1', 'D1T1:1', 'D2T1:1') + bgplib.init_resource_data(st.get_testbed_vars()) + bgp_pre_config() + yield + bgp_pre_config_cleanup() + + +# bgp module level pre config function +def bgp_pre_config(): + global topo + st.banner("BGP MODULE CONFIG - START") + # loopback config + bgplib.l3tc_vrfipv4v6_address_leafspine_loopback_config_unconfig(config='yes', config_type='all') + # TG Configuration + bgplib.l3tc_vrfipv4v6_address_leafspine_tg_config_unconfig(config='yes', config_type='all') + st.banner("BGP MODULE CONFIG - END") + +# bgp module level pre config cleanup function +def bgp_pre_config_cleanup(): + st.banner("BGP MODULE CONFIG CLEANUP - START") + + # loopback unconfig + bgplib.l3tc_vrfipv4v6_address_leafspine_loopback_config_unconfig(config='no') + + # TG uconfiguration + bgplib.l3tc_vrfipv4v6_address_leafspine_tg_config_unconfig(config='no') + + st.banner("BGP MODULE CONFIG CLEANUP - END") + +@pytest.fixture(scope="function") +def bgp_func_hooks(request): + yield + + + +################################################################################ +# BGP Route Reflector with traffic fixture, class and test cases - START +def bgp_rr_traffic_pre_config(): + global topo + st.banner("BGP RR WITH TRAFFIC CLASS CONFIG - START") + + # underlay config - configure physical interfaces + bgplib.l3tc_underlay_config_unconfig(config='yes') + + # config ip on underlay interface + bgplib.l3tc_vrfipv4v6_address_leafspine_config_unconfig(config='yes', config_type='all') + + # Ping Verification + if not bgplib.l3tc_vrfipv4v6_address_leafspine_ping_test(config_type='all', ping_count=3): + st.error("Ping failed in between Spine - Leaf") + st.report_fail('test_case_failed') + ibgp_as = bgplib.data['spine_as'] + + bgplib.l3tc_vrfipv4v6_address_leafspine_rr_tg_bgp_config(config='yes', rr_enable='true') + bgplib.l3tc_vrfipv4v6_address_leafspine_bgp_config(config='yes', rr_enable='true') + # BGP Neighbor Verification + if not poll_wait(bgplib.l3tc_vrfipv4v6_address_leafspine_bgp_check, 10, config_type='all'): + st.error("Neighbour is failed to Establish between Spine - Leaf") + st.report_fail('test_case_failed') + + st.log("Getting all topology info related to connectivity / TG and other parameters between duts") + topo = bgplib.get_leaf_spine_topology_info() + st.banner("BGP RR WITH TRAFFIC CLASS CONFIG - END") + + +def bgp_rr_traffic_pre_config_cleanup(): + st.banner("BGP RR WITH TRAFFIC CLASS CONFIG CLEANUP - START") + ibgp_as = bgplib.data['spine_as'] + bgplib.l3tc_vrfipv4v6_address_leafspine_bgp_config(config='no', rr_enable='true') + bgplib.l3tc_vrfipv4v6_address_leafspine_config_unconfig(config='no') + bgpapi.cleanup_router_bgp(st.get_dut_names()) + ipapi.clear_ip_configuration(st.get_dut_names(), family='all', thread=True) + bgplib.l3tc_underlay_config_unconfig(config='no') + bgplib.l3tc_vrfipv4v6_address_leafspine_rr_tg_bgp_config(config='no', rr_enable='true') + st.banner("BGP RR WITH TRAFFIC CLASS CONFIG CLEANUP - END") + + +@pytest.fixture(scope='class') +def bgp_rr_traffic_class_hook(request): + bgp_rr_traffic_pre_config() + yield + bgp_rr_traffic_pre_config_cleanup() + + +# Route Reflector with traffic Class +@pytest.mark.usefixtures('bgp_rr_traffic_class_hook') +class TestBGPRrTraffic(): + + @pytest.mark.bgp_rr_traffic + def test_ft_bgp_rr_traffic_check(self): + TG_D1 = topo.tg_dut_list_name[0] + TG_D2 = topo.tg_dut_list_name[1] + tg_ob = topo['T1{}P1_tg_obj'.format(TG_D1)] + bgp_handle = topo['T1{}P1_ipv4_tg_bh'.format(TG_D1)] + tc_fail_flag = 0 + spine_as = int(bgplib.data['spine_as']) + bgp_ctrl = tg_ob.tg_emulation_bgp_control(handle=bgp_handle['handle'], mode='stop') + st.wait(10) + st.log("Advertising Routes from one of the Leaf Router") + bgp_route = tg_ob.tg_emulation_bgp_route_config(handle=bgp_handle['handle'], mode='add', num_routes='100', + prefix='121.1.1.0', as_path='as_seq:1') + bgp_ctrl = tg_ob.tg_emulation_bgp_control(handle=bgp_handle['handle'], mode='start') + + # Sleep for some time and the check the route count in neighbour + st.wait(10) + bgp_summary = bgpapi.show_bgp_ipv4_summary(topo.dut_list[1]) + rib_entries = bgp_summary[0]['ribentries'] + st.log('RIB Entries : {}'.format(rib_entries)) + # when route-reflector is not configured at server(spine), we should not learn anything at + # route-reflector-client (leaf node), ideally, route count should be 0. + if int(rib_entries) > 10: + st.error('iBGP Routes are advertised to iBGP peer DUT, even when route-reflector-client is not configured') + tc_fail_flag = 1 + # now configure route-reflector-client at spine node + result = bgpapi.create_bgp_route_reflector_client(topo.dut_list[0], spine_as, 'ipv4', 'spine_leaf', 'yes') + if not result: + st.log("Configuring client reflection on {} {} bgp {} Failed".format(topo.dut_list[0], 'ipv4', spine_as)) + tc_fail_flag = 1 + bgpapi.create_bgp_next_hop_self(topo.dut_list[0], spine_as, 'ipv4', 'spine_leaf', 'yes', 'yes',cli_type=bgp_cli_type) + st.wait(15) + bgp_summary = bgpapi.show_bgp_ipv4_summary(topo.dut_list[1]) + rib_entries = bgp_summary[0]['ribentries'] + st.log('RIB Entries : {}'.format(rib_entries)) + if int(rib_entries) < 100: + st.error('iBGP Routes are not advertised to route-reflector-client') + tc_fail_flag = 1 + + st.log("Initiating the Ipv4 traffic for those Routes from another Leaf Router") + src_handle = 'handle' + dst_handle = 'handles' + if tg_ob.tg_type == 'ixia': + src_handle = 'ipv4_handle' + dst_handle = 'handle' + tr1 = tg_ob.tg_traffic_config(port_handle=topo['T1{}P1_ipv4_tg_ph'.format(TG_D2)], + emulation_src_handle=topo['T1{}P1_ipv4_tg_ih'.format(TG_D2)][src_handle], + emulation_dst_handle=bgp_route[dst_handle], circuit_endpoint_type='ipv4', + mode='create', + transmit_mode='single_burst', pkts_per_burst='2000', length_mode='fixed', + rate_pps=1000) + stream_id1 = tr1['stream_id'] + tg_ob.tg_traffic_control(action='run', handle=stream_id1) + st.wait(20) + tg1_stats = tgapi.get_traffic_stats(tg_ob, port_handle=topo["T1{}P1_ipv4_tg_ph".format(TG_D1)]) + tg2_stats = tgapi.get_traffic_stats(tg_ob, port_handle=topo["T1{}P1_ipv4_tg_ph".format(TG_D2)]) + if not (int(tg2_stats.tx.total_packets) and int(tg1_stats.rx.total_packets)): + st.error('Received ZERO stats.') + tc_fail_flag = 1 + else: + percent_rx = float(int(tg1_stats.rx.total_packets) - int(tg2_stats.tx.total_packets)) / int( + tg2_stats.tx.total_packets) * 100 + st.log('tg1_stats.rx.total_packets : {}'.format(tg1_stats.rx.total_packets)) + st.log('tg2_stats.tx.total_packets : {}'.format(tg2_stats.tx.total_packets)) + st.log('percent_rx : {}'.format(percent_rx)) + if percent_rx > 0.5: + tc_fail_flag = 1 + tg_ob.tg_emulation_bgp_control(handle=bgp_handle['handle'], mode='stop') + if tc_fail_flag: + st.report_fail("traffic_verification_failed") + st.report_pass('test_case_passed') + + @pytest.mark.bgp6_rr_traffic + def test_ft_bgp6_rr_traffic_check(self): + TG_D1 = topo.tg_dut_list_name[0] + TG_D2 = topo.tg_dut_list_name[1] + tg_ob = topo['T1{}P1_tg_obj'.format(TG_D1)] + bgp_handle = topo['T1{}P1_ipv6_tg_bh'.format(TG_D1)] + tc_fail_flag = 0 + spine_as = int(bgplib.data['spine_as']) + st.log("Advertising Routes from one of the Leaf Router") + bgp_route = tg_ob.tg_emulation_bgp_route_config(handle=bgp_handle['handle'], mode='add', ip_version='6', + num_routes='100', + prefix='1001::1', as_path='as_seq:1') + bgp_ctrl = tg_ob.tg_emulation_bgp_control(handle=bgp_handle['handle'], mode='start') + + # Sleep for some time and the check the route count in neighbour + st.wait(10) + bgp_summary = bgpapi.show_bgp_ipv6_summary(topo.dut_list[1]) + rib_entries = bgp_summary[0]['ribentries'] + st.log('RIB Entries : {}'.format(rib_entries)) + # when route-reflector is not configured at server(spine), we should not learn anything at + # route-reflector-client (leaf node), ideally, route count should be 0. + if int(rib_entries) > 10: + st.error('iBGP Routes are advertised to iBGP peer DUT, even when route-reflector-client is not configured') + tc_fail_flag = 1 + # now configure route-reflector-client at spine node + result = bgpapi.create_bgp_route_reflector_client(topo.dut_list[0], spine_as, 'ipv6', 'spine_leaf6', 'yes') + if not result: + st.log("Configuring client reflection on {} {} bgp {} Failed".format(topo.dut_list[0], 'ipv6', spine_as)) + tc_fail_flag = 1 + bgpapi.create_bgp_next_hop_self(topo.dut_list[0], spine_as, 'ipv6', 'spine_leaf6', 'yes', 'yes',cli_type=bgp_cli_type) + st.wait(15) + bgp_summary = bgpapi.show_bgp_ipv6_summary(topo.dut_list[1]) + rib_entries = bgp_summary[0]['ribentries'] + st.log('RIB Entries : {}'.format(rib_entries)) + if int(rib_entries) < 100: + st.error('iBGP Routes are not advertised to route-reflector-client') + tc_fail_flag = 1 + + st.log("Initiating the Ipv6 traffic for those Routes from another Leaf Router") + src_handle = 'handle' + dst_handle = 'handles' + if tg_ob.tg_type == 'ixia': + src_handle = 'ipv6_handle' + dst_handle = 'handle' + tr1 = tg_ob.tg_traffic_config(port_handle=topo['T1{}P1_ipv6_tg_ph'.format(TG_D2)], + emulation_src_handle=topo['T1{}P1_ipv6_tg_ih'.format(TG_D2)][src_handle], + emulation_dst_handle=bgp_route[dst_handle], circuit_endpoint_type='ipv6', + mode='create', + transmit_mode='single_burst', pkts_per_burst='2000', length_mode='fixed', + rate_pps=1000) + stream_id1 = tr1['stream_id'] + tg_ob.tg_traffic_control(action='run', handle=stream_id1) + st.wait(20) + tg1_stats = tgapi.get_traffic_stats(tg_ob, port_handle=topo["T1{}P1_ipv6_tg_ph".format(TG_D1)]) + tg2_stats = tgapi.get_traffic_stats(tg_ob, port_handle=topo["T1{}P1_ipv6_tg_ph".format(TG_D2)]) + if not (int(tg2_stats.tx.total_packets) and int(tg1_stats.rx.total_packets)): + st.error('Received ZERO stats.') + tc_fail_flag = 1 + else: + percent_rx = float(int(tg2_stats.tx.total_packets) - int(tg1_stats.rx.total_packets)) / int( + tg2_stats.tx.total_packets) * 100 + st.log('tg1_stats.rx.total_packets : {}'.format(tg1_stats.rx.total_packets)) + st.log('tg2_stats.tx.total_packets : {}'.format(tg2_stats.tx.total_packets)) + st.log('percent_rx : {}'.format(percent_rx)) + if percent_rx > 0.5: + tc_fail_flag = 1 + tg_ob.tg_emulation_bgp_control(handle=bgp_handle['handle'], mode='stop') + if tc_fail_flag: + st.report_fail("traffic_verification_failed") + st.report_pass('test_case_passed') + + + # BGP Neighbor In L3 Over LAG fixture, class and test cases - END +################################################################################ diff --git a/spytest/tests/routing/BGP/test_bgp_save_reboot.py b/spytest/tests/routing/BGP/test_bgp_save_reboot.py new file mode 100644 index 00000000000..71dc14fd424 --- /dev/null +++ b/spytest/tests/routing/BGP/test_bgp_save_reboot.py @@ -0,0 +1,281 @@ +import pytest +from spytest import st, tgapi, SpyTestDict +import apis.system.reboot as reboot_obj +import apis.routing.ip as ip_obj +import apis.routing.bgp as bgp_obj +import apis.switching.portchannel as portchannel_obj +import apis.switching.vlan as vlan_obj +from utilities.parallel import exec_all,ensure_no_exception + +vars = dict() + +@pytest.fixture(scope="module", autouse=True) +def bgp_save_reboot_module_hooks(request): + global vars + vars = st.ensure_min_topology("D1D2:1","D1T1:2") + st.log("Enabling IPv6 mode globally") + ip_obj.config_ipv6(vars.D1,action='enable') + ip_obj.config_ipv6(vars.D2,action='enable') + st.log("Configuring ipv4 addresses on routing interfaces") + ipv4_ip_address_config() + st.log("Verifying ipv4 addresses on routing interfaces") + verify_ipv4_address_config() + st.log("Configuring ipv6 addresses on routing interfaces") + ipv6_address_config() + st.log("Verifying ipv6 addresses on routing interfaces") + verify_ipv6_address_config() + st.log("Configuring IPV6 eBGP config between DUT1 and DUT2,iBGP config between DUT1 and TG2") + ipv6_bgp_config() + st.log("Configuring IPV4 eBGP config between DUT1 and DUT2,iBGP config between DUT1 and TG1") + ipv4_bgp_config() + st.log("Configuring TG2 V6 iBGP config") + tg_bgpv6_config(vars, data.local_asn4, data.remote_asn4) + st.log("Configuring TG1 V4 iBGP config") + tg_bgp_config(vars, data.local_asn4, data.remote_asn4) + yield + bgp_obj.cleanup_router_bgp(st.get_dut_names()) + ip_obj.clear_ip_configuration(st.get_dut_names()) + ip_obj.clear_ip_configuration(st.get_dut_names(), 'ipv6') + vlan_obj.clear_vlan_configuration(st.get_dut_names()) + portchannel_obj.clear_portchannel_configuration(st.get_dut_names()) + + +@pytest.fixture(scope="function", autouse=True) +def bgp_save_reboot_func_hooks(request): + yield + + + + +data = SpyTestDict() +data.local_ip_addr = "12.12.12.1" +data.neigh_ip_addr = "12.12.12.2" +data.local_ip6_addr = "3241::1" +data.neigh_ip6_addr = "3241::2" +data.router_id_1 = "110.110.110.1" +data.router_id_2 = "120.120.120.1" +data.loopback_1 = "66.66.66.66" +data.loopback_2 = "77.77.77.77" +data.af_ipv4 = "ipv4" +data.af_ipv6 = "ipv6" +data.shell_sonic = "sonic" +data.shell_vtysh = "vtysh" +data.d1t1_ip_addr = "192.168.0.1" +data.t1d1_ip_addr = "192.168.0.2" +data.d1t1_ip_addr_mask = "16" +data.d1t1_ip6_addr = '2001::1' +data.t1d1_ip6_addr = '2001::100' +data.d1t1_ip6_addr_mask = '64' +data.af_ipv4 = "ipv4" +data.af_ipv6 = "ipv6" +data.local_asn = "4294966195" +data.tg_bgp_route_prfix = "157.1.0.0" +data.local_asn4 = "4294966195" +data.remote_asn4 = "65001" + + +def ipv4_ip_address_config(): + st.log("Creating the ipv4 routing interfaces in {}".format(vars.D1)) + ip_obj.config_ip_addr_interface(vars.D1, vars.D1D2P1, data.local_ip_addr, 30, family=data.af_ipv4) + + st.log("Creating the ipv4 routing interfaces in {}".format(vars.D2)) + ip_obj.config_ip_addr_interface(vars.D2, vars.D2D1P1, data.neigh_ip_addr, 30, family=data.af_ipv4) + + st.log("Creating the ipv4 routing interfaces on TG1 {}".format(vars.D1T1P1)) + ip_obj.config_ip_addr_interface(vars.D1, vars.D1T1P1, data.d1t1_ip_addr, + data.d1t1_ip_addr_mask, family=data.af_ipv4) + +def verify_ipv4_address_config(): + st.log("Verify ipv4 address on routing interface {} in D1".format(vars.D1D2P1)) + if not ip_obj.verify_interface_ip_address(vars.D1, vars.D1D2P1, "{}/30".format(data.local_ip_addr), data.af_ipv4): + st.report_fail('ip_routing_int_create_fail', vars.D1D2P1) + else: + st.log(" IPV4 Addres Config is successful") + st.log("Verify ipv4 address on routing interface {} in D2".format(vars.D1D2P1)) + if not ip_obj.verify_interface_ip_address(vars.D2, vars.D2D1P1, "{}/30".format(data.neigh_ip_addr), data.af_ipv4): + st.report_fail('ip_routing_int_create_fail', vars.D2D1P1) + else: + st.log(" IPV4 Addres Config is successful") + st.log("Verify ipv4 address on routing interface {} in D1".format(vars.D1T1P1)) + if not ip_obj.verify_interface_ip_address(vars.D1, vars.D1T1P1, + "{}/{}".format(data.d1t1_ip_addr, data.d1t1_ip_addr_mask), + data.af_ipv4): + st.report_fail('ip_routing_int_create_fail', vars.D1T1P1) + else: + st.log(" IPV4 Addres Config is successful") + +def ipv6_address_config(): + st.log("Creating the ipv6 routing interfaces in {}".format(vars.D1D2P1)) + ip_obj.config_ip_addr_interface(vars.D1, vars.D1D2P1, data.local_ip6_addr, 96, family=data.af_ipv6) + + + st.log("Creating the ipv6 routing interfaces in {}".format(vars.D2D1P1)) + ip_obj.config_ip_addr_interface(vars.D2, vars.D2D1P1, data.neigh_ip6_addr, 96, family=data.af_ipv6) + + + st.log("Creating the ipv6 routing interfaces on TG2 interface {}".format(vars.D1T1P2)) + ip_obj.config_ip_addr_interface(vars.D1, vars.D1T1P2, data.d1t1_ip6_addr, data.d1t1_ip6_addr_mask, + family=data.af_ipv6) + +def verify_ipv6_address_config(): + st.log("Verify ipv6 address on routing interface {} in D1".format(vars.D1D2P1)) + if not ip_obj.verify_interface_ip_address(vars.D1, vars.D1D2P1, "{}/96".format(data.local_ip6_addr), data.af_ipv6): + st.report_fail('ip6_routing_int_create_fail', vars.D1D2P1) + else: + st.log(" IPV6 Addres Config is successful") + st.log("Verify ipv6 address on routing interface {} in D2".format(vars.D2D1P1)) + if not ip_obj.verify_interface_ip_address(vars.D2, vars.D2D1P1, "{}/96".format(data.neigh_ip6_addr), data.af_ipv6): + st.report_fail('ip6_routing_int_create_fail', vars.D2D1P1) + else: + st.log(" IPV6 Addres Config is successful") + st.log("Verify ipv6 address on routing interface {} in D1".format(vars.D1T1P2)) + if not ip_obj.verify_interface_ip_address(vars.D1, vars.D1T1P2, + "{}/{}".format(data.d1t1_ip6_addr, data.d1t1_ip6_addr_mask), + data.af_ipv6): + st.report_fail('ipv6_routing_int_create_fail', vars.D1T1P2) + else: + st.log(" IPV6 Addres Config is successful") + +def ipv4_bgp_config(): + st.log("Creating the eBGP ipv4 neighbors in {}".format(vars.D1)) + bgp_obj.create_bgp_router(vars.D1, data.local_asn4, data.router_id_1) + bgp_obj.config_address_family_redistribute(vars.D1, data.local_asn4, data.af_ipv4, "unicast", "connected") + bgp_obj.create_bgp_neighbor(dut=vars.D1, local_asn=data.local_asn4, neighbor_ip=data.neigh_ip_addr, + remote_asn=data.remote_asn4, family=data.af_ipv4) + + st.log("Creating the eBGP ipv4 neighbors in {}".format(vars.D2)) + bgp_obj.create_bgp_router(vars.D2, data.remote_asn4, data.router_id_2) + bgp_obj.config_address_family_redistribute(vars.D2, data.remote_asn4, data.af_ipv4, "unicast", "connected") + bgp_obj.create_bgp_neighbor(dut=vars.D2, local_asn=data.remote_asn4, neighbor_ip=data.local_ip_addr, + remote_asn=data.local_asn4, family=data.af_ipv4) + + st.log("Creating the iBGP ipv4 neighbors with TG1 {}".format(vars.D1T1P1)) + bgp_obj.create_bgp_router(vars.D1, data.local_asn4, data.router_id_1) + bgp_obj.config_address_family_redistribute(vars.D1, data.local_asn4, data.af_ipv4, "unicast", "connected") + bgp_obj.create_bgp_neighbor(dut=vars.D1, local_asn=data.local_asn4, neighbor_ip=data.t1d1_ip_addr, + remote_asn=data.local_asn4, family=data.af_ipv4) + + +def ipv6_bgp_config(): + st.log("Creating the eBGP ipv6 neighbors in {}".format(vars.D1)) + bgp_obj.create_bgp_router(vars.D1, data.local_asn4, data.router_id_1) + bgp_obj.config_address_family_redistribute(vars.D1, data.local_asn4, data.af_ipv6, "unicast", "connected") + bgp_obj.create_bgp_neighbor(dut=vars.D1, local_asn=data.local_asn4, neighbor_ip=data.neigh_ip6_addr, + remote_asn=data.remote_asn4, family=data.af_ipv6) + st.log("Creating the eBGP ipv6 neighbors in {}".format(vars.D2)) + bgp_obj.create_bgp_router(vars.D2, data.remote_asn4, data.router_id_2) + bgp_obj.config_address_family_redistribute(vars.D2, data.remote_asn4, data.af_ipv6, "unicast", "connected") + bgp_obj.create_bgp_neighbor(dut=vars.D2, local_asn=data.remote_asn4, neighbor_ip=data.local_ip6_addr, + remote_asn=data.local_asn4, family=data.af_ipv6) + + st.log("Creating the iBGP ipv6 neighbors with TG2 {}".format(vars.D1)) + bgp_obj.create_bgp_router(vars.D1, data.local_asn4, data.router_id_1) + bgp_obj.create_bgp_neighbor(dut=vars.D1, local_asn=data.local_asn4, neighbor_ip='2001::100', + remote_asn=data.local_asn4, family=data.af_ipv6) + +def tg_bgp_config(vars, local_asn, remote_asn): + tg1, tg_ph_1 = tgapi.get_handle_byname("T1D1P1") + tg1.tg_traffic_control(action='reset', port_handle=tg_ph_1) + + h1 = tg1.tg_interface_config(port_handle=tg_ph_1, mode='config', intf_ip_addr=data.t1d1_ip_addr, + gateway=data.d1t1_ip_addr, + src_mac_addr='00:0a:01:00:00:01', arp_send_req='1') + st.log("INTFCONF: " + str(h1)) + # Configuring BGP device on top of interface. + bgp_conf = tg1.tg_emulation_bgp_config(handle=h1['handle'], mode='enable', active_connect_enable='1', + local_as=data.local_asn4, + remote_as=data.local_asn4, remote_ip_addr=data.d1t1_ip_addr, + enable_4_byte_as='1') + st.log("BGPCONF: " + str(bgp_conf)) + # Adding routes to BGP device. + bgp_route = tg1.tg_emulation_bgp_route_config(handle=bgp_conf['handle'], mode='add', num_routes='100', + prefix='172.168.1.0') + st.log("BGPROUTE: " + str(bgp_route)) + return True + +def tg_bgpv6_config(vars, local_asn, remote_asn): + tg2, tg_ph_2 = tgapi.get_handle_byname("T1D1P2") + tg2.tg_traffic_control(action='reset', port_handle=tg_ph_2) + h1 = tg2.tg_interface_config(port_handle=tg_ph_2, mode='config', ipv6_intf_addr=data.t1d1_ip6_addr,\ + ipv6_prefix_length='64',ipv6_gateway=data.d1t1_ip6_addr, + src_mac_addr='00:0a:01:00:00:01',\ + arp_send_req='1') + st.log("INTFCONF: " + str(h1)) + st.log("Configuring BGP device on top of interface") + bgp_conf = tg2.tg_emulation_bgp_config(handle=h1['handle'], mode='enable', ip_version='6',\ + active_connect_enable='1', local_as= data.local_asn4,\ + remote_as=data.local_asn4, remote_ipv6_addr=data.d1t1_ip6_addr, + enable_4_byte_as='1') + st.log("BGPCONF: " + str(bgp_conf)) + st.log("Adding routes to BGP device") + bgp_route = tg2.tg_emulation_bgp_route_config(handle=bgp_conf['handle'], mode='add', ip_version='6', + num_routes='100', prefix='1001::0') + st.log("BGPROUTE: " + str(bgp_route)) + return True + +def config_dut1_verify(): + st.log("Enabling docker routing config mode in D1 ") + bgp_obj.enable_docker_routing_config_mode(vars.D1) + st.log("saving the BGP config in vtysh shell") + st.log("config save in D1") + reboot_obj.config_save(vars.D1,shell='vtysh') + st.log("Performing reboot") + st.reboot(vars.D1) + st.log("Verifying BGP is established after save and reload") + st.log("Waiting for the eBGP neighbors to get Established") + bgp_obj.verify_bgp_neighborship(vars.D1, family=data.af_ipv4, shell=data.shell_sonic, neighbor=data.neigh_ip_addr, + state='Established') + if not bgp_obj.verify_bgp_neighborship(vars.D1, family=data.af_ipv4, shell=data.shell_sonic, + neighbor=data.neigh_ip_addr, state='Established', asn=data.remote_asn4): + st.report_fail('bgp_ip_peer_establish_fail', data.neigh_ip_addr) + else: + st.log("eBGP peer neigborship is successful") + st.log("Waiting for the iBGP neighbors to get Established wit TG1") + if not bgp_obj.verify_bgp_neighborship(vars.D1, family=data.af_ipv4, shell=data.shell_sonic, + neighbor=data.t1d1_ip_addr, state='Established', asn=data.local_asn4): + st.report_fail('bgp_ip_peer_establish_fail', data.t1d1_ip_addr) + else: + st.log("iBGP V4 peer neigborship is successful") + st.log("Verifying BGPV6 is established after save and reload") + st.log("Waiting for the eBGP neighbors to get Established with peer DUT") + if not bgp_obj.verify_bgp_neighborship(vars.D1, family=data.af_ipv6, shell=data.shell_sonic, + neighbor=data.neigh_ip6_addr, state='Established', asn=data.remote_asn4): + st.report_fail('bgp_ip6_peer_establish_fail', data.neigh_ip6_addr) + else: + st.log("eBGP V6 peer neigborship is successful") + + st.log("Waiting for the iBGPV6 neighbors to get Established with TG2") + if not bgp_obj.verify_bgp_neighborship(vars.D1, family=data.af_ipv6, shell=data.shell_sonic, + neighbor=data.t1d1_ip6_addr, state='Established', asn=data.local_asn4): + st.report_fail('bgp_ip_peer_establish_fail', data.t1d1_ip6_addr) + else: + st.log("iBGP V6 peer neigborship is successful") + + +def config_dut2_verify(): + st.log("Enabling docker routing config mode in D2 ") + bgp_obj.enable_docker_routing_config_mode(vars.D2) + st.log("saving the BGP config in vtysh shell") + st.log("config save in D2") + reboot_obj.config_save(vars.D2,shell='vtysh') + st.log("Performing reboot") + st.reboot(vars.D2) + st.log("Verifying BGP is established after save and reload") + if not bgp_obj.verify_bgp_neighborship(vars.D2, family=data.af_ipv4, shell=data.shell_sonic, + neighbor=data.local_ip_addr, state='Established', asn=data.local_asn4): + st.report_fail('bgp_ip_peer_establish_fail', data.local_ip_addr) + else: + st.log("eBGP V4 peer neigborship is successful") + st.log("Verifying BGPV6 is established after save and reload") + if not bgp_obj.verify_bgp_neighborship(vars.D2, family=data.af_ipv6, shell=data.shell_sonic, + neighbor=data.local_ip6_addr, state='Established', asn=data.local_asn4): + st.report_fail('bgp_ip6_peer_establish_fail', data.local_ip6_addr) + else: + st.log("eBGP V6 peer neigborship is successful") + + +@pytest.mark.bgp_save_reboot +def test_ft_bgp_save_reboot(): + [out, exceptions] = exec_all(True, [[config_dut1_verify], [config_dut2_verify]], first_on_main=True) + ensure_no_exception(exceptions) + st.report_pass('test_case_passed') diff --git a/spytest/tests/routing/BGP/test_bgp_sp.py b/spytest/tests/routing/BGP/test_bgp_sp.py new file mode 100644 index 00000000000..dbe332cea9d --- /dev/null +++ b/spytest/tests/routing/BGP/test_bgp_sp.py @@ -0,0 +1,1416 @@ +# BGP SP Topology Test cases +# Author: Naveena Suvarna (naveen.suvarna@broadcom.com) + +import pytest +from spytest import st +from spytest.dicts import SpyTestDict +import apis.routing.ip as ipapi +import apis.routing.bgp as bgpapi +import apis.routing.route_map as rmapapi +import copy +from BGP.bgpsplib import BGPSP + +bgp_cli_type="vtysh" + +@pytest.fixture(scope="module", autouse=True) +def bgp_sp_module_hooks(request): + st.ensure_min_topology('D1D2:4', 'D1D3:4', 'D1D4:4', 'D2D3:4', 'D2D4:4', 'D3D4:4') + BGPSP.bgp_sp_setup_testbed_topology() + #BGPSP.bgp_sp_setup_testbed_topology(per_node_nw='no', nw_ip_octet='192') + #BGPSP.bgp_sp_setup_testbed_topology(per_node_nw='yes', nw_ip_octet='10') + + pre_config = True + if pre_config : + BGPSP.bgp_sp_config_ip_topology_on_testbed() + + BGPSP.bgp_sp_cleanup_bgp_routers() + + yield + + pre_config = True + if pre_config : + BGPSP.bgp_sp_cleanup_bgp_routers() + BGPSP.bgp_sp_unconfig_ip_topology_on_testbed() + + BGPSP.bgp_sp_clear_testbed_topology() + + +@pytest.fixture(scope="function") +def bgp_sp_func_hooks(request): + # + yield + # + + +def test_bgp_sp_dummy(): + + #tb_dut = BGPSP.bgp_sp_get_dut_device("D3") + #result = bgpapi.verify_bgp_ip_route(tb_dut, network="209.92.2.0/24") + #result = bgpapi.verify_bgp_ip_route(tb_dut, network="209:92:3::/64", family='ipv6') + + #BGPSP.bgp_sp_find_linear_topo_in_dut_list() + #BGPSP.bgp_sp_find_linear_topo_in_dut_list(node_limit=2) + #BGPSP.bgp_sp_find_linear_topo_in_dut_list(node_limit=3) + #BGPSP.bgp_sp_find_linear_topo_in_dut_list(node_limit=4) + #BGPSP.bgp_sp_find_linear_topo_in_dut_list(node_limit=5) + + #BGPSP.bgp_sp_find_ring_topo_in_dut_list() + #BGPSP.bgp_sp_find_ring_topo_in_dut_list(node_limit=2) + #BGPSP.bgp_sp_find_ring_topo_in_dut_list(node_limit=3) + #BGPSP.bgp_sp_find_ring_topo_in_dut_list(node_limit=4) + #BGPSP.bgp_sp_find_ring_topo_in_dut_list(node_limit=5) + + #tb_dut = BGPSP.bgp_sp_get_dut_device("D1") + #bgpapi.config_bgp_neighbor(tb_dut, 65001, "11.1.1.2", 65002, 'ipv4', 30, 60, config='yes') + + #BGPSP.bgp_sp_tg_interface_ip_all_config_unconfig() + #BGPSP.bgp_sp_tg_interface_ip_all_config_unconfig(config='no') + + #BGPSP.bgp_sp_interface_address_all_config_unconfig() + #BGPSP.bgp_sp_interface_address_all_config_unconfig(config='no') + + #BGPSP.bgp_sp_loopback_interface_config_unconfig() + #BGPSP.bgp_sp_loopback_interface_config_unconfig(config='no') + + st.report_pass("test_case_passed") + + +#--------------------------------------- BASE TEST BASE ------------------------------------------- + +@pytest.fixture(scope='class') +def bgp_sp_base_class_hook(request): + BGPSP.bgp_sp_cleanup_bgp_routers() + yield + BGPSP.bgp_sp_cleanup_bgp_routers() + + +@pytest.mark.usefixtures('bgp_sp_base_class_hook') +class TestBGPSP: + + def test_bgp_sp_topolog_interface_ip_ping(self): + + st.banner("BGP SP - SP Topology interface ping test") + + result = True + result = BGPSP.bgp_sp_interface_address_ping_test(vrf='default', addr_family='all', ping_count=3) + + result_str = "PASSED" if result else "FAILED" + st.banner("BGP SP - SP Topology interface ping test {}".format(result_str)) + + if result: + st.report_pass("test_case_passed") + else: + st.report_fail("test_case_failed") + + + def test_bgp_sp_n_node_one_link_e_bgp_session(self): + + st.banner("BGP SP - N node One link ipv4/6 E-BGP session Test") + + result = True + + dut_asn_map = {} + + dut_count = BGPSP.bgp_sp_get_dut_count() + + for count in range (1, dut_count+1): + dut_name = "D{}".format(count) + asn = 6500 + count + dut_asn_map.update({ dut_name: asn }) + + #dut_asn_map = { 'D1': 65001, 'D2': 65002, 'D3': 65003, 'D4': 65004 } + + dut_list = list(dut_asn_map.keys()) + dut_list.sort() + + st.log("BGP SP - Dut asn map {}.".format(dut_asn_map)) + + if BGPSP.bgp_sp_dut_list_present(dut_list): + + BGPSP.bgp_sp_cleanup_bgp_routers(dut_list) + + result = BGPSP.bgp_sp_bgp_asn_map_config_unconfig(dut_asn_map, config='yes', + vrf='default', addr_family='all', + max_adjacency=1, cli_type=bgp_cli_type) + if result : + result = BGPSP.bgp_sp_verify_all_bgp_sessions(dut_asn_map.keys(), addr_family='all') + if result : + BGPSP.bgp_sp_bgp_asn_map_config_unconfig(dut_asn_map, config='no', cli_type=bgp_cli_type) + + BGPSP.bgp_sp_cleanup_bgp_routers(dut_list) + + + result_str = "PASSED" if result else "FAILED" + st.banner("BGP SP - N node One link ipv4/6 E-BGP session test {}".format(result_str)) + + if result: + st.report_pass("test_case_passed") + else: + st.report_fail("test_case_failed") + + + def test_bgp_sp_n_node_one_link_i_bgp_session(self): + + st.banner("BGP SP - N node One link ipv4/6 I-BGP session Test") + + result = True + + dut_asn_map = {} + + dut_count = BGPSP.bgp_sp_get_dut_count() + + for count in range (1, dut_count+1): + dut_name = "D{}".format(count) + asn = 6501 + dut_asn_map.update({ dut_name: asn }) + + #dut_asn_map = { 'D1': 65001, 'D2': 65001, 'D3': 65001, 'D4': 65001 } + + dut_list = list(dut_asn_map.keys()) + dut_list.sort() + + st.log("BGP SP - Dut asn map {}.".format(dut_asn_map)) + + if BGPSP.bgp_sp_dut_list_present(dut_list): + + BGPSP.bgp_sp_cleanup_bgp_routers(dut_list) + + result = BGPSP.bgp_sp_bgp_asn_map_config_unconfig(dut_asn_map, config='yes', + vrf='default', addr_family='all', + max_adjacency=1, cli_type=bgp_cli_type) + if result : + result = BGPSP.bgp_sp_verify_all_bgp_sessions(dut_asn_map.keys(), addr_family='all') + if result : + BGPSP.bgp_sp_bgp_asn_map_config_unconfig(dut_asn_map, config='no', cli_type=bgp_cli_type) + + BGPSP.bgp_sp_cleanup_bgp_routers(dut_list) + + + result_str = "PASSED" if result else "FAILED" + st.banner("BGP SP - N node One link ipv4/6 I-BGP session test {}".format(result_str)) + + if result: + st.report_pass("test_case_passed") + else: + st.report_fail("test_case_failed") + + +#-------------------------------------- SPINE LEAF TOPO TEST CLASS ------------------------------------------ + +# Spine Leaf BGP topo +@pytest.fixture(scope='class') +def bgp_sp_spine_leaf_ibgp_class_hook(request): + + pre_config = False + if pre_config : + BGPSP.bgp_sp_cleanup_bgp_routers() + + if BGPSP.bgp_sp_get_dut_count() >= 2 : + result = BGPSP.bgp_sp_spine_leaf_bgp_config_unconfig(65002, 65002, addr_family='all', config='yes', cli_type=bgp_cli_type) + if not result : + st.log("BGP SP - SpineLeaf Topo bgp config and neighbor session test failed") + st.report_fail("operation_failed") + + yield + + pre_config = False + if pre_config : + if BGPSP.bgp_sp_get_dut_count() >= 2 : + BGPSP.bgp_sp_spine_leaf_bgp_config_unconfig(65002, 65002, addr_family='all', config='no', cli_type=bgp_cli_type) + + BGPSP.bgp_sp_cleanup_bgp_routers() + + +@pytest.mark.usefixtures('bgp_sp_spine_leaf_ibgp_class_hook') +class TestBGP_SPINE_LEAF_IBGP: + + def test_bgp_sp_spine_leaf_bgp_session(self): + + #class not used for any tests for now, return s passed + st.report_pass("test_case_passed") + return + + st.banner("BGP SP - Spine Leaf topology BGP session test START ") + + result = True + + if BGPSP.bgp_sp_get_dut_count() < 2 : + st.log("BGP SP - Needs minumum two nodes in testbed") + st.report_env_fail("test_case_not_executed") + return + + spine_leaf_path = BGPSP.bgp_sp_dut_get_saved_spine_leaf_topo() + st.log("BGP SP - Topo Spine Leaf saved path {}.\n".format(spine_leaf_path)) + + if not spine_leaf_path['found'] : + st.log("BGP SP - Testbed doesnt have minimum 2 connected nodes") + result = False + + if result : + + spine_list = spine_leaf_path['spine_list'] + leaf_list = spine_leaf_path['leaf_list'] + + #for dut in spine_list : BGPSP.bgp_sp_show_dut_bgp_cmd_logs(dut) + #for dut in leaf_list : BGPSP.bgp_sp_show_dut_bgp_cmd_logs(dut) + + #write your test case here + + result_str = "PASSED" if result else "FAILED" + st.banner("BGP SP - Spine Leaf topology BGP session test {}".format(result_str)) + + if result: + st.report_pass("test_case_passed") + else: + st.report_fail("test_case_failed") + + +#------------------------------------- LINEAR TOPO eBGP iBGP iBGP eBGP TEST CLASS ----------------------------------- + +@pytest.fixture(scope='class') +def bgp_sp_linear_topo_ebgp_ibgp_class_hook(request): + + pre_config = True + if pre_config : + BGPSP.bgp_sp_cleanup_bgp_routers() + result = BGPSP.bgp_sp_linear_topo_bgp_config_unconfig(sess_type='eBGPiBGPeBGP', addr_family='all', config='yes') + if not result : + st.log("BGP SP - Linear Topo bgp config and neighbor session test failed") + st.report_fail("operation_failed") + + yield + + if pre_config : + BGPSP.bgp_sp_linear_topo_bgp_config_unconfig(sess_type='eBGPiBGPeBGP', addr_family='all', config='no') + BGPSP.bgp_sp_cleanup_bgp_routers() + + +@pytest.mark.usefixtures('bgp_sp_linear_topo_ebgp_ibgp_class_hook') +class TestBGP_LINEAR_EBGP_IBGP: + + def test_bgp_sp_four_node_linear_ebgp_ibgp_session(self): + + st.banner("BGP SP - 4 Node Linear EBGP IBGP EBGP session test START") + result = True + + linear_topo = BGPSP.bgp_sp_dut_get_saved_linear_topo() + BGPSP.bgp_sp_show_topo_path(linear_topo) + + if not BGPSP.bgp_sp_test_topo_present(topo_path=linear_topo, dut_count=4, segment_count=3) : + st.log("BGP SP - Test case topo requirement FAILED") + st.report_env_fail("test_case_not_executed") + return + + start_dut = linear_topo['start_dut'] + dut_list = linear_topo['dut_list'] + + #for dut in dut_list : BGPSP.bgp_sp_show_dut_bgp_cmd_logs(dut) + + #write your test case here + + + result_str = "PASSED" if result else "FAILED" + st.banner("BGP SP - 4 Node Linear EBGP IBGP EBGP session test {}".format(result_str)) + + if result: + st.report_pass("test_case_passed") + else: + st.report_fail("test_case_failed") + + + def test_bgp_linear_ebgp_ibgp_route_advanced(self): + + st.banner("BGP SP - 4 Node Linear EBGP IBGP EBGP session advanced test START") + result = True + + linear_topo = BGPSP.bgp_sp_dut_get_saved_linear_topo() + BGPSP.bgp_sp_show_topo_path(linear_topo) + + if not BGPSP.bgp_sp_test_topo_present(topo_path=linear_topo, dut_count=4, segment_count=3) : + st.log("BGP SP - Test case topo requirement FAILED") + st.report_env_fail("test_case_not_executed") + return + + dut_list = linear_topo['dut_list'] + #BGPSP.bgp_sp_show_dut_bgp_running_config(dut_list) + + reduced_topo = SpyTestDict() + reduced_topo['leftebgpdut'] = BGPSP.bgp_sp_get_dut_device(dut_list[0]) + reduced_topo['leftibgpdut'] = BGPSP.bgp_sp_get_dut_device(dut_list[1]) + reduced_topo['rightibgpdut'] = BGPSP.bgp_sp_get_dut_device(dut_list[2]) + reduced_topo['rightebgpdut'] = BGPSP.bgp_sp_get_dut_device(dut_list[3]) + reduced_topo['linear_path'] = linear_topo + + #request.cls.local_topo = reduced_topo + self.local_topo = reduced_topo + + #Common config items + prefix_list_201 = ipapi.PrefixList("201_network") + prefix_list_201.add_match_permit_sequence('201.1.1.0/24') + prefix_list_202 = ipapi.PrefixList("202_network") + prefix_list_202.add_match_permit_sequence('202.1.1.0/24') + prefix_list_203 = ipapi.PrefixList("203_network") + prefix_list_203.add_match_permit_sequence('203.1.1.0/24') + prefix_list_204 = ipapi.PrefixList("204_network") + prefix_list_204.add_match_permit_sequence('204.1.1.0/24') + prefix_list_205 = ipapi.PrefixList("205_network") + prefix_list_205.add_match_permit_sequence('205.1.1.0/24') + prefix_list_100 = ipapi.PrefixList("100_network") + prefix_list_100.add_match_permit_sequence('100.1.1.0/24') + + aspath_acl = bgpapi.ASPathAccessList("ASPATH") + aspath_acl.add_match_permit_sequence(['(_3000_)+']) + aspath_acl.add_match_permit_sequence(['(_4000_)+']) + + #Left eBGP node config items and commands + leftebgprmap = rmapapi.RouteMap("test_community") + leftebgprmap.add_permit_sequence('10') + leftebgprmap.add_sequence_match_prefix_list('10', '202_network') + leftebgprmap.add_sequence_set_community('10', ['55:5555']) + leftebgprmap.add_permit_sequence('15') + leftebgprmap.add_sequence_match_prefix_list('15', '204_network') + leftebgprmap.add_sequence_set_as_path_prepend('15', ['2000', '3000']) + leftebgprmap.add_permit_sequence('18') + leftebgprmap.add_sequence_match_prefix_list('18', '205_network') + leftebgprmap.add_sequence_set_as_path_prepend('18', ['2000', '4000']) + leftebgprmap.add_permit_sequence('20') + + leftebgpdutcmd = prefix_list_201.config_command_string() + leftebgpdutcmd += prefix_list_202.config_command_string() + leftebgpdutcmd += prefix_list_204.config_command_string() + leftebgpdutcmd += prefix_list_205.config_command_string() + leftebgpdutcmd += leftebgprmap.config_command_string() + + leftebgpnetworklistipv4 = ['200.1.1.0/24', '201.1.1.0/24', '202.1.1.0/24', '203.1.1.0/24', '204.1.1.0/24', '205.1.1.0/24'] + leftebgpnetworklistipv6 = ['200:1::/64'] + + #Left iBGP node config items and commands + leftibgprmap1 = rmapapi.RouteMap("test_community") + leftibgprmap1.add_permit_sequence('10') + leftibgprmap1.add_sequence_match_prefix_list('10', '201_network') + leftibgprmap1.add_sequence_set_community('10', ['no-advertise']) + leftibgprmap1.add_permit_sequence('15') + leftibgprmap1.add_sequence_match_prefix_list('15', '202_network') + leftibgprmap1.add_sequence_set_community('15', ['44:4444 additive']) + #FIXME: Make additive keyword into proper API + leftibgprmap1.add_permit_sequence('18') + leftibgprmap1.add_sequence_match_prefix_list('18', '100_network') + leftibgprmap1.add_sequence_set_as_path_prepend('18', ['65111']) + leftibgprmap1.add_permit_sequence('20') + + leftibgprmap2 = rmapapi.RouteMap("test_community_1") + leftibgprmap2.add_permit_sequence('10') + leftibgprmap2.add_sequence_match_prefix_list('10', '203_network') + leftibgprmap2.add_sequence_set_community('10', ['no-export']) + leftibgprmap2.add_permit_sequence('20') + + leftibgpdutcmd = prefix_list_201.config_command_string() + leftibgpdutcmd += prefix_list_202.config_command_string() + leftibgpdutcmd += prefix_list_203.config_command_string() + leftibgpdutcmd += prefix_list_100.config_command_string() + leftibgpdutcmd += leftibgprmap1.config_command_string() + leftibgpdutcmd += leftibgprmap2.config_command_string() + + #Right eBGP node config items and commands + rightebgprmap = rmapapi.RouteMap("ASP") + rightebgprmap.add_deny_sequence('10') + rightebgprmap.add_sequence_match_bgp_aspath_list('10', 'ASPATH') + rightebgprmap.add_permit_sequence('20') + + rightebgpdutcmd = aspath_acl.config_command_string() + rightebgpdutcmd += rightebgprmap.config_command_string() + + BGPSP.bgp_sp_dut_bgp_network_advertise_config_unconfig(dut_list[0], leftebgpnetworklistipv4) + BGPSP.bgp_sp_dut_bgp_network_advertise_config_unconfig(dut_list[0], leftebgpnetworklistipv6, addr_family='ipv6') + BGPSP.bgp_sp_dut_bgp_network_advertise_config_unconfig(dut_list[1], ['100.1.1.0/24']) + BGPSP.bgp_sp_dut_bgp_network_advertise_config_unconfig(dut_list[3], ['100.1.1.0/24']) + + st.vtysh_config(reduced_topo['leftebgpdut'], leftebgpdutcmd) + st.vtysh_config(reduced_topo['leftibgpdut'], leftibgpdutcmd) + #st.vtysh_config(reduced_topo['rightibgpdut'], rightibgpdutcmd) + st.vtysh_config(reduced_topo['rightebgpdut'], rightebgpdutcmd) + + + result = True + bgpapi.show_ip_bgp_route(self.local_topo['leftebgpdut']) + bgpapi.show_ip_bgp_route(self.local_topo['leftibgpdut']) + bgpapi.show_ip_bgp_route(self.local_topo['rightibgpdut']) + bgpapi.show_ip_bgp_route(self.local_topo['rightebgpdut']) + + link_idx = 0 + #configure route-maps and other commands on relevat DUTs + seg0_data = self.local_topo['linear_path']['segment'][0][link_idx] + left_ebgp_asn = BGPSP.bgp_sp_get_bgp_asn(seg0_data['lcl_dut']) + left_ebgp_ip = BGPSP.bgp_sp_dut_get_link_local_ip(seg0_data['lcl_dut'], seg0_data['lcl_link'], 'ipv4') + left_ebgp_nbr_ip = BGPSP.bgp_sp_dut_get_link_remote_ip(seg0_data['lcl_dut'], seg0_data['lcl_link'], 'ipv4') + bgpapi.config_bgp(dut=self.local_topo['leftebgpdut'], local_as=left_ebgp_asn, neighbor=left_ebgp_nbr_ip, config='yes', + config_type_list=["routeMap"], routeMap='test_community', diRection='out') + + seg1_data = self.local_topo['linear_path']['segment'][1][link_idx] + left_ibgp_asn = BGPSP.bgp_sp_get_bgp_asn(seg1_data['lcl_dut']) + left_ibgp_ipv6 = BGPSP.bgp_sp_dut_get_link_local_ip(seg1_data['lcl_dut'], seg1_data['lcl_link'], 'ipv6') + left_ibgp_nbr_ip = BGPSP.bgp_sp_dut_get_link_remote_ip(seg1_data['lcl_dut'], seg1_data['lcl_link'], 'ipv4') + route_20x_correct_nhop = BGPSP.bgp_sp_dut_get_link_local_ip(seg1_data['lcl_dut'], seg1_data['lcl_link'], 'ipv4') + bgpapi.create_bgp_next_hop_self(self.local_topo['leftibgpdut'], left_ibgp_asn, 'ipv4', left_ibgp_nbr_ip, config='yes') + bgpapi.config_bgp(dut=self.local_topo['leftibgpdut'], local_as=left_ibgp_asn, neighbor=left_ibgp_nbr_ip, config='yes', + config_type_list=["routeMap"], routeMap='test_community', diRection='out') + bgpapi.config_bgp(dut=self.local_topo['leftibgpdut'], local_as=left_ibgp_asn, neighbor=left_ebgp_ip, config='yes', + config_type_list=["routeMap"], routeMap='test_community_1', diRection='in') + + seg2_data = self.local_topo['linear_path']['segment'][2][link_idx] + right_ibgp_asn = BGPSP.bgp_sp_get_bgp_asn(seg2_data['lcl_dut']) + right_ebgp_asn = BGPSP.bgp_sp_get_bgp_asn(seg2_data['rmt_dut']) + right_ebgp_nbr_ip = BGPSP.bgp_sp_dut_get_link_local_ip(seg2_data['lcl_dut'], seg2_data['lcl_link'], 'ipv4') + right_ebgp_ip = BGPSP.bgp_sp_dut_get_link_remote_ip(seg2_data['lcl_dut'], seg2_data['lcl_link'], 'ipv4') + bgpapi.config_bgp(dut=self.local_topo['rightibgpdut'], local_as=right_ibgp_asn, neighbor=right_ebgp_ip, config='yes', + config_type_list=["removePrivateAs"]) + bgpapi.config_bgp(dut=self.local_topo['rightebgpdut'], local_as=right_ebgp_asn, neighbor=right_ebgp_nbr_ip, config='yes', + config_type_list=["routeMap"], routeMap='ASP', diRection='in') + route_100_correct_nhop = BGPSP.bgp_sp_dut_get_link_remote_ip(seg2_data['lcl_dut'], seg2_data['lcl_link'], 'ipv4') + + prefix_list_v6200 = ipapi.PrefixList("v6_200_network", family='ipv6') + prefix_list_v6200.add_match_permit_sequence('200:1::/64') + test_v6_nhop_rmap = rmapapi.RouteMap("test_v6_nhop") + test_v6_nhop_rmap.add_permit_sequence('10') + test_v6_nhop_rmap.add_sequence_match_prefix_list('10', 'v6_200_network', family='ipv6') + test_v6_nhop_rmap.add_sequence_set_ipv6_next_hop_global('10', left_ibgp_ipv6) + + rightibgpdutcmd = prefix_list_v6200.config_command_string() + test_v6_nhop_rmap.config_command_string() + st.vtysh_config(self.local_topo['rightibgpdut'], rightibgpdutcmd) + bgpapi.config_bgp(dut=self.local_topo['rightibgpdut'], local_as=right_ibgp_asn, neighbor=left_ibgp_ipv6, + addr_family ='ipv6', config='yes', + config_type_list=["routeMap"], routeMap='test_v6_nhop', diRection='in') + #end + + st.wait(2) + + res = ipapi.verify_ip_route(self.local_topo['rightibgpdut'], + type='B', ip_address='100.1.1.0/24', nexthop=route_100_correct_nhop) + if not res: + st.log("100 network nexthop did not match") + result = False + else: + st.log("Test to verify eBGP preferred over iBGP passed") + + res = ipapi.verify_ip_route(self.local_topo['rightibgpdut'], + type='B', ip_address='200.1.1.0/24', nexthop=route_20x_correct_nhop) + if not res: + st.log("200 network nexthop did not match") + result = False + else: + st.log("Test to verify next hop self passed") + + res = ipapi.verify_ip_route(self.local_topo['rightebgpdut'], + type='B', ip_address='201.1.1.0/24') + if res: + st.log("201 still advertised, fail") + result = False + else: + st.log("Test to verify NO_ADVERTISE community passed") + + + st.wait(2) + output = bgpapi.show_bgp_ipvx_prefix(self.local_topo['rightibgpdut'], prefix="202.1.1.0", + masklen=24, family='ipv4') + if not output or '55:5555' not in output[0]['community']: + st.log('community not appended') + result = False + else: + st.log("Test to verify community append passed") + + res = ipapi.verify_ip_route(self.local_topo['rightebgpdut'], + type='B', ip_address='203.1.1.0/24') + if res: + st.log("203 still advertised, fail") + result = False + else: + st.log("Test to verify NO_EXPORT community passed") + + output = bgpapi.fetch_ip_bgp_route(self.local_topo['rightebgpdut'], family='ipv4', + match={'network': '200.1.1.0/24'}, + select=['network', 'as_path']) + if not output or str(left_ebgp_asn) in output[0]['as_path']: + st.log('private as not removed') + result = False + else: + st.log("Test to verify remove_private_as passed") + + res = bgpapi.verify_ip_bgp_route(self.local_topo['rightebgpdut'], network="204.1.1.0/24") + if res: + st.log("204 still advertised, fail") + result = False + else: + st.log("Test to verify as path filter passed") + + res = bgpapi.verify_ip_bgp_route(self.local_topo['rightibgpdut'], family='ipv6', + network="200:1::/64", next_hop=left_ibgp_ipv6) + if not res: + st.log("200:1::/64 next hop did not change, fail") + result = False + else: + st.log("Test to verify IPv6 nhop through route-map in passed") + + # unconfigure route-maps and other commands on relevat DUTs + bgpapi.config_bgp(dut=self.local_topo['leftebgpdut'], local_as=left_ebgp_asn, neighbor=left_ebgp_nbr_ip, + config='no', + config_type_list=["routeMap"], routeMap='test_community', diRection='out', cli_type=bgp_cli_type) + + bgpapi.create_bgp_next_hop_self(self.local_topo['leftibgpdut'], left_ibgp_asn, 'ipv4', left_ibgp_nbr_ip, + config='no', cli_type=bgp_cli_type) + bgpapi.config_bgp(dut=self.local_topo['leftibgpdut'], local_as=left_ibgp_asn, neighbor=left_ibgp_nbr_ip, + config='no', + config_type_list=["routeMap"], routeMap='test_community', diRection='out', cli_type=bgp_cli_type) + bgpapi.config_bgp(dut=self.local_topo['leftibgpdut'], local_as=left_ibgp_asn, neighbor=left_ebgp_ip, + config='no', + config_type_list=["routeMap"], routeMap='test_community_1', diRection='in', cli_type=bgp_cli_type) + + bgpapi.config_bgp(dut=self.local_topo['rightibgpdut'], local_as=right_ibgp_asn, neighbor=right_ebgp_ip, + config='no', + config_type_list=["removePrivateAs"], cli_type=bgp_cli_type) + bgpapi.config_bgp(dut=self.local_topo['rightebgpdut'], local_as=right_ebgp_asn, neighbor=right_ebgp_nbr_ip, + config='no', + config_type_list=["routeMap"], routeMap='ASP', diRection='in', cli_type=bgp_cli_type) + + bgpapi.config_bgp(dut=self.local_topo['rightibgpdut'], local_as=right_ibgp_asn, neighbor=left_ibgp_ipv6, + addr_family='ipv6', config='no', + config_type_list=["routeMap"], routeMap='test_v6_nhop', diRection='in', cli_type=bgp_cli_type) + rightibgpdutcmd = prefix_list_v6200.unconfig_command_string() + test_v6_nhop_rmap.unconfig_command_string() + st.vtysh_config(self.local_topo['rightibgpdut'], rightibgpdutcmd) + + # end + + # Unconfigure networks from DUTs + BGPSP.bgp_sp_dut_bgp_network_advertise_config_unconfig(dut_list[0], leftebgpnetworklistipv4, config='no') + BGPSP.bgp_sp_dut_bgp_network_advertise_config_unconfig(dut_list[0], leftebgpnetworklistipv6, addr_family='ipv6', config='no') + BGPSP.bgp_sp_dut_bgp_network_advertise_config_unconfig(dut_list[1], ['100.1.1.0/24'], config='no') + BGPSP.bgp_sp_dut_bgp_network_advertise_config_unconfig(dut_list[3], ['100.1.1.0/24'], config='no') + + + #Left eBGP node config items and commands + leftebgpdutcmd = prefix_list_201.unconfig_command_string() + leftebgpdutcmd += prefix_list_202.unconfig_command_string() + leftebgpdutcmd += prefix_list_204.unconfig_command_string() + leftebgpdutcmd += prefix_list_205.unconfig_command_string() + leftebgpdutcmd += leftebgprmap.unconfig_command_string() + + #Left iBGP node config items and commands + leftibgpdutcmd = prefix_list_201.unconfig_command_string() + leftibgpdutcmd += prefix_list_202.unconfig_command_string() + leftibgpdutcmd += prefix_list_203.unconfig_command_string() + leftibgpdutcmd += prefix_list_100.unconfig_command_string() + leftibgpdutcmd += leftibgprmap1.unconfig_command_string() + leftibgpdutcmd += leftibgprmap2.unconfig_command_string() + + #Right eBGP node config items and commands + rightebgpdutcmd = aspath_acl.unconfig_command_string() + rightebgpdutcmd += rightebgprmap.unconfig_command_string() + + st.vtysh_config(reduced_topo['leftebgpdut'], leftebgpdutcmd) + st.vtysh_config(reduced_topo['leftibgpdut'], leftibgpdutcmd) + #st.vtysh_config(reduced_topo['rightibgpdut'], rightibgpdutcmd) + st.vtysh_config(reduced_topo['rightebgpdut'], rightebgpdutcmd) + + + if result: + st.report_pass("test_case_passed") + else: + st.report_fail("test_case_failed") + + + +#------------------------------------ LINEAR TOPO eBGP TEST CLASS -------------------------------- + +@pytest.fixture(scope='class') +def bgp_sp_linear_topo_ebgp_class_hook(request): + + pre_config = True + if pre_config : + BGPSP.bgp_sp_cleanup_bgp_routers() + result = BGPSP.bgp_sp_linear_topo_bgp_config_unconfig(sess_type='eBGP', addr_family='all', config='yes') + if not result : + st.log("BGP SP - Linear Topo ebgp config and neighbor session test failed") + st.report_fail("operation_failed") + + yield + + pre_config = True + if pre_config : + BGPSP.bgp_sp_linear_topo_bgp_config_unconfig(sess_type='eBGP', addr_family='all', config='no') + BGPSP.bgp_sp_cleanup_bgp_routers() + + +@pytest.mark.usefixtures('bgp_sp_linear_topo_ebgp_class_hook') +class TestBGP_LINEAR_EBGP: + + def test_bgp_sp_three_node_linear_ebgp_med_rmap(self): + + st.banner("BGP SP - 3 Node Linear EBGP session MED test START") + result = True + + linear_topo = BGPSP.bgp_sp_dut_get_saved_linear_topo() + BGPSP.bgp_sp_show_topo_path(linear_topo) + + if not BGPSP.bgp_sp_test_topo_present(topo_path=linear_topo, dut_count=3, segment_count=2) : + st.log("BGP SP - Test case topo requirement FAILED") + st.report_env_fail("test_case_not_executed") + return + + start_dut = linear_topo['start_dut'] + dut_list = linear_topo['dut_list'] + + link_idx = 0 + left_dut = linear_topo['segment'][0][link_idx]['lcl_dut'] + mid_dut = linear_topo['segment'][1][link_idx]['lcl_dut'] + right_dut = linear_topo['segment'][1][link_idx]['rmt_dut'] + lr_dut_list = [left_dut, right_dut] + rmap_name = 'rmap_med_metric' + + tb_left_dut = BGPSP.bgp_sp_get_dut_device(left_dut) + tb_mid_dut = BGPSP.bgp_sp_get_dut_device(mid_dut) + tb_right_dut = BGPSP.bgp_sp_get_dut_device(right_dut) + + #BGPSP.bgp_sp_show_dut_bgp_running_config(dut_list) + + if result : + st.log("BGP SP - Configure route map {} in {}".format(left_dut, rmap_name)) + result = BGPSP.bgp_sp_route_map_config_unconfig(left_dut, rmap_name, 'permit', '10', metric='111') + + if result : + st.log("BGP SP - Configure route map {} in {}".format(right_dut, rmap_name)) + result = BGPSP.bgp_sp_route_map_config_unconfig(right_dut, rmap_name, 'permit', '10', metric='333') + + if result : + st.log("BGP SP - Configure deterministic med in {}".format(mid_dut)) + result = BGPSP.bgp_sp_bgp_deterministic_med_config_unconfig(list([mid_dut])) + + if result : + st.log("BGP SP - Configure always compare med in {}".format(mid_dut)) + result = BGPSP.bgp_sp_bgp_compare_med_config_unconfig(list([mid_dut])) + + nw_prefixes = { 'ipv4': [], 'ipv6': []} + addr_family_list = BGPSP.bgp_sp_get_address_family_list("all") + selected_metric ={'metric' : '0', 'status_code': '*>'} + + for afmly in addr_family_list: + + bgp_nw_prefixes = BGPSP.bgp_sp_get_dut_static_network_prefixes(left_dut, afmly) + nw_prefixes[afmly] = bgp_nw_prefixes + dest_list = BGPSP.bgp_sp_ip_prefix_list_to_route_prefix_list(bgp_nw_prefixes, afmly) + + if len(dest_list) == 0 : + st.log("BGP SP - Route List for {} empty for prefix {}".format(afmly, dest_list)) + result = False + + if result : + st.log("BGP SP - Configure {} network on nodes".format(afmly, lr_dut_list)) + result = BGPSP.bgp_sp_bgp_network_advertise_config_unconfig(lr_dut_list, bgp_nw_prefixes, addr_family=afmly) + + if result : + st.log("BGP SP - Verify {} routes {} show 0 metric".format(mid_dut, dest_list)) + result = BGPSP.bgp_sp_bgp_verify_routes_in_dut_list([mid_dut], dest_list, afmly, present='yes') + + selected_metric['metric'] = '0' + result = BGPSP.bgp_sp_bgp_ip_routes_matching([mid_dut], dest_list, afmly, selected_metric) + + if result : + nbr_list = BGPSP.bgp_sp_get_bgp_neigbour_ip_between_duts(left_dut, mid_dut, afmly) + st.log("BGP SP - Configure rmap {} to {} nbrs {}".format(rmap_name, left_dut, nbr_list)) + result = BGPSP.bgp_sp_bgp_neighbor_route_map_config_unconfig(left_dut, nbr_list, rmap_name, 'out', afmly) + + if result : + st.log("BGP SP - Verify {} routes {} show rmap metric".format(mid_dut, dest_list)) + selected_metric['metric'] = '0' + result = BGPSP.bgp_sp_bgp_ip_routes_matching([mid_dut], dest_list, afmly, selected_metric) + + if result : + st.log("BGP SP - change metric in {} rmap {}".format(right_dut, rmap_name)) + result = BGPSP.bgp_sp_route_map_config_unconfig(right_dut, rmap_name, 'permit', '10', metric='33') + + if result : + nbr_list = BGPSP.bgp_sp_get_bgp_neigbour_ip_between_duts(right_dut, mid_dut, afmly) + st.log("BGP SP - Configure rmap {} to {} nbrs {}".format(rmap_name, right_dut, nbr_list)) + result = BGPSP.bgp_sp_bgp_neighbor_route_map_config_unconfig(right_dut, nbr_list, rmap_name, 'out', afmly) + + if result : + st.wait(5) + st.log("BGP SP - Verify {} routes {} show rmap metric".format(mid_dut, dest_list)) + selected_metric['metric'] = '33' + result = BGPSP.bgp_sp_bgp_ip_routes_matching([mid_dut], dest_list, afmly, selected_metric) + + if result : + st.wait(5) + st.log("BGP SP - change metric in {} rmap {}".format(right_dut, rmap_name)) + result = BGPSP.bgp_sp_route_map_config_unconfig(right_dut, rmap_name, 'permit', '10', metric='333') + + if result : + st.wait(5) + st.log("BGP SP - Verify {} routes {} show rmap metric".format(mid_dut, dest_list)) + selected_metric['metric'] = '111' + result = BGPSP.bgp_sp_bgp_ip_routes_matching([mid_dut], dest_list, afmly, selected_metric) + + if result : + st.log("BGP SP - change metric in {} rmap {}".format(right_dut, rmap_name)) + result = BGPSP.bgp_sp_route_map_config_unconfig(right_dut, rmap_name, 'permit', '10', metric='3') + + if result : + st.wait(5) + st.log("BGP SP - Verify {} routes {} show rmap metric".format(mid_dut, dest_list)) + selected_metric['metric'] = '3' + result = BGPSP.bgp_sp_bgp_ip_routes_matching([mid_dut], dest_list, afmly, selected_metric) + + if not result : + break + + + BGPSP.bgp_sp_bgp_deterministic_med_config_unconfig(list([mid_dut]), config='no') + BGPSP.bgp_sp_bgp_compare_med_config_unconfig(list([mid_dut]), config='no') + BGPSP.bgp_sp_route_map_config_unconfig(left_dut, rmap_name, config='no') + BGPSP.bgp_sp_route_map_config_unconfig(right_dut, rmap_name, config='no') + + for afmly in addr_family_list: + if len(nw_prefixes[afmly]) == 0 : continue + BGPSP.bgp_sp_bgp_network_advertise_config_unconfig(lr_dut_list, nw_prefixes[afmly], addr_family=afmly, config='no') + + result_str = "PASSED" if result else "FAILED" + st.banner("BGP SP - 3 Node Linear EBGP session MED test {}".format(result_str)) + + if result: + st.report_pass("test_case_passed") + else: + st.report_fail("test_case_failed") + + + +#------------------------------------ LINEAR TOPO iBGP TEST CLASS -------------------------------- + +@pytest.fixture(scope='class') +def bgp_sp_linear_topo_ibgp_class_hook(request): + + pre_config = True + if pre_config : + BGPSP.bgp_sp_cleanup_bgp_routers() + result = BGPSP.bgp_sp_linear_topo_bgp_config_unconfig(sess_type='iBGP', addr_family='all', config='yes') + if not result : + st.log("BGP SP - Linear Topo ibgp config and neighbor session test failed") + st.report_fail("operation_failed") + + yield + + pre_config = True + if pre_config : + BGPSP.bgp_sp_linear_topo_bgp_config_unconfig(sess_type='iBGP', addr_family='all', config='no') + BGPSP.bgp_sp_cleanup_bgp_routers() + + +@pytest.mark.usefixtures('bgp_sp_linear_topo_ibgp_class_hook') +class TestBGP_LINEAR_IBGP: + + def test_bgp_sp_four_node_bgp_cluster_route(self): + + st.banner("BGP SP - Four Node Linear iBGP Clustor Route Test START") + result = True + + linear_topo = BGPSP.bgp_sp_dut_get_saved_linear_topo() + BGPSP.bgp_sp_show_topo_path(linear_topo) + + if not BGPSP.bgp_sp_test_topo_present(topo_path=linear_topo, dut_count=4, segment_count=3) : + st.log("BGP SP - Test case topo requirement FAILED") + st.report_env_fail("test_case_not_executed") + return + + start_dut = linear_topo['start_dut'] + dut_list = linear_topo['dut_list'] + + #BGPSP.bgp_sp_show_dut_bgp_running_config(dut_list) + + # c1--r1---r2---c2 + link_idx = 0 + c1_dut = linear_topo['segment'][0][link_idx]['lcl_dut'] + r1_dut = linear_topo['segment'][0][link_idx]['rmt_dut'] + + r2_dut = linear_topo['segment'][2][link_idx]['lcl_dut'] + c2_dut = linear_topo['segment'][2][link_idx]['rmt_dut'] + + addr_family_list = BGPSP.bgp_sp_get_address_family_list("all") + + bgp_nw_prefixes = { c1_dut : { 'ipv4': [], 'ipv6': []}, + c2_dut : { 'ipv4': [], 'ipv6': []} } + + bgp_rt_prefixes = { c1_dut : { 'ipv4': [], 'ipv6': []}, + c2_dut : { 'ipv4': [], 'ipv6': []} } + + bgp_rr_nbr = { r1_dut : { 'ipv4': [], 'ipv6': []}, + r2_dut : { 'ipv4': [], 'ipv6': []} } + + if result : + for dut in [c1_dut, c2_dut] : + for afmly in addr_family_list: + nw_prefixes = BGPSP.bgp_sp_get_dut_static_network_prefixes(dut, afmly) + bgp_nw_prefixes[dut][afmly] = nw_prefixes + rt_prefixes = BGPSP.bgp_sp_ip_prefix_list_to_route_prefix_list(nw_prefixes, afmly) + bgp_rt_prefixes[dut][afmly] = rt_prefixes + st.log("BGP SP - Configure {} network on nodes".format(dut, bgp_nw_prefixes[dut][afmly])) + result = BGPSP.bgp_sp_bgp_network_advertise_config_unconfig([dut], nw_prefixes, addr_family=afmly) + + if result : + st.log("BGP SP - Configure {} client to client reflection".format(r1_dut)) + result = BGPSP.bgp_sp_bgp_ctoc_reflection_config_unconfig(r1_dut, cli_type=bgp_cli_type) + + if result : + st.log("BGP SP - Configure {} client to client reflection".format(r2_dut)) + result = BGPSP.bgp_sp_bgp_ctoc_reflection_config_unconfig(r2_dut, cli_type=bgp_cli_type) + + if result : + for afmly in addr_family_list: + rt_prefixes = bgp_rt_prefixes[c1_dut][afmly] + + if result : + st.log("BGP SP - Verify {} has routes {}".format(r1_dut, rt_prefixes)) + result = BGPSP.bgp_sp_bgp_ip_routes_matching([r1_dut], rt_prefixes, afmly) + + if result : + st.log("BGP SP - Verify {} doesnt have routes {}".format(r2_dut, rt_prefixes)) + result = BGPSP.bgp_sp_bgp_ip_routes_not_matching([r2_dut], rt_prefixes, afmly) + + if result : + st.log("BGP SP - Verify {} doesnt have routes {}".format(c2_dut, rt_prefixes)) + result = BGPSP.bgp_sp_bgp_ip_routes_not_matching([c2_dut], rt_prefixes, afmly) + + rt_prefixes = bgp_rt_prefixes[c2_dut][afmly] + if result : + st.log("BGP SP - Verify {} has routes {}".format(r2_dut, rt_prefixes)) + result = BGPSP.bgp_sp_bgp_ip_routes_matching([r2_dut], rt_prefixes, afmly) + + if result : + st.log("BGP SP - Verify {} doesnt have routes {}".format(r1_dut, rt_prefixes)) + result = BGPSP.bgp_sp_bgp_ip_routes_not_matching([r1_dut], rt_prefixes, afmly) + + if result : + st.log("BGP SP - Verify {} doesnt have routes {}".format(c1_dut, rt_prefixes)) + result = BGPSP.bgp_sp_bgp_ip_routes_not_matching([c1_dut], rt_prefixes, afmly) + + if result : + st.log("BGP SP - Configure redistribute connected in {}".format(r1_dut)) + result = BGPSP.bgp_sp_bgp_redistribute_connected_config_unconfig([r1_dut], afmly) + + if result : + nbr_list = BGPSP.bgp_sp_get_bgp_neigbour_ip_between_duts(r1_dut, c1_dut, afmly) + bgp_rr_nbr[r1_dut][afmly] = nbr_list + st.log("BGP SP - Configure {} {} nbrs {} as reflector client".format(r1_dut, c1_dut, nbr_list)) + result = BGPSP.bgp_sp_bgp_neighbor_route_reflector_config_unconfig(r1_dut, nbr_list, afmly, cli_type=bgp_cli_type) + + if result : + nbr_list = BGPSP.bgp_sp_get_bgp_neigbour_ip_between_duts(r2_dut, c2_dut, afmly) + bgp_rr_nbr[r2_dut][afmly] = nbr_list + st.log("BGP SP - Configure {} {} nbrs {} as reflector client".format(r2_dut, c2_dut, nbr_list)) + result = BGPSP.bgp_sp_bgp_neighbor_route_reflector_config_unconfig(r2_dut, nbr_list, afmly, cli_type=bgp_cli_type) + + rt_prefixes = bgp_rt_prefixes[c1_dut][afmly] + if result : + st.wait(5) + st.log("BGP SP - Verify {} {} {} has routes {}".format(r1_dut, r2_dut, c2_dut, rt_prefixes)) + result = BGPSP.bgp_sp_bgp_ip_routes_matching([r1_dut, r2_dut, c2_dut], rt_prefixes, afmly) + + if result : + for ip_prefix in rt_prefixes: + tb_dut = BGPSP.bgp_sp_get_dut_device(c2_dut) + entries = bgpapi.show_bgp_ip_prefix(tb_dut, ip_prefix, afmly) + st.log("BGP SP - {} ".format(entries)) + + if not result : + break + + for dut in [c1_dut, c2_dut] : + for afmly in addr_family_list: + nw_prefixes = bgp_nw_prefixes[dut][afmly] + if len (nw_prefixes) == 0 : continue + BGPSP.bgp_sp_bgp_network_advertise_config_unconfig([dut], nw_prefixes, addr_family=afmly, config='no') + + for dut in [r1_dut, r2_dut] : + for afmly in addr_family_list: + nbr_list = bgp_rr_nbr[dut][afmly] + if len (nbr_list) == 0 : continue + BGPSP.bgp_sp_bgp_neighbor_route_reflector_config_unconfig(dut, nbr_list, addr_family=afmly, config='no', cli_type=bgp_cli_type) + BGPSP.bgp_sp_bgp_redistribute_connected_config_unconfig([r1_dut], afmly, config='no') + #BGPSP.bgp_sp_bgp_ctoc_reflection_config_unconfig(dut, config='no') + + result_str = "PASSED" if result else "FAILED" + st.banner("BGP SP - Four Node Linear iBGP Clustor Route Test {}".format(result_str)) + + if result: + st.report_pass("test_case_passed") + else: + st.report_fail("test_case_failed") + + +#--------------------------------- STAR TOPO eBGP TEST CLASS --------------------------------- + +@pytest.fixture(scope='class') +def bgp_sp_star_topo_ebgp_class_hook(request): + + pre_config = False + if pre_config : + BGPSP.bgp_sp_cleanup_bgp_routers() + if BGPSP.bgp_sp_get_dut_count() >= 3 : + result = BGPSP.bgp_sp_star_topo_bgp_config_unconfig(sess_type='eBGP', addr_family='all', config='yes') + if not result : + st.log("BGP SP - Star Topo ebgp config and neighbor session test failed") + st.report_fail("operation_failed") + + yield + + pre_config = False + if pre_config : + if BGPSP.bgp_sp_get_dut_count() >= 3 : + BGPSP.bgp_sp_star_topo_bgp_config_unconfig(sess_type='eBGP', addr_family='all', config='no') + BGPSP.bgp_sp_cleanup_bgp_routers() + + +@pytest.mark.usefixtures('bgp_sp_star_topo_ebgp_class_hook') +class TestBGP_STAR_EBGP: + + def test_bgp_sp_star_node_ebgp_session(self): + + st.banner("BGP SP - Star topology eBGP session test START ") + result = True + + #class not used for any tests for now, return s passed + st.report_pass("test_case_passed") + return + + star_topo = BGPSP.bgp_sp_dut_get_saved_star_topo() + BGPSP.bgp_sp_show_topo_path(star_topo) + + if not BGPSP.bgp_sp_test_topo_present(topo_path=star_topo, dut_count=3, segment_count=2) : + st.log("BGP SP - Test case topo requirement FAILED") + st.report_env_fail("test_case_not_executed") + return + + if result : + + central_dut = star_topo['start_dut'] + dut_list = star_topo['dut_list'] + + #for dut in dut_list : BGPSP.bgp_sp_show_dut_bgp_cmd_logs(dut) + + #write your test case here + + result_str = "PASSED" if result else "FAILED" + st.banner("BGP SP - Star topology eBGP session test {}".format(result_str)) + + if result: + st.report_pass("test_case_passed") + else: + st.report_fail("test_case_failed") + + +#----------------------------------- STAR TOPO iBGP TEST CLASS --------------------------- + +@pytest.fixture(scope='class') +def bgp_sp_star_topo_ibgp_class_hook(request): + + pre_config = True + if pre_config : + BGPSP.bgp_sp_cleanup_bgp_routers() + if BGPSP.bgp_sp_get_dut_count() >= 3 : + result = BGPSP.bgp_sp_star_topo_bgp_config_unconfig(sess_type='iBGP', addr_family='all', config='yes') + if not result : + st.log("BGP SP - Star Topo ibgp config and neighbor session test failed") + st.report_fail("operation_failed") + + yield + + pre_config = True + if pre_config : + if BGPSP.bgp_sp_get_dut_count() >= 3 : + BGPSP.bgp_sp_star_topo_bgp_config_unconfig(sess_type='iBGP', addr_family='all', config='no') + BGPSP.bgp_sp_cleanup_bgp_routers() + + +@pytest.mark.usefixtures('bgp_sp_star_topo_ibgp_class_hook') +class TestBGP_STAR_IBGP: + + def test_bgp_sp_star_ibgp_route_reflector_ipv46(self): + + st.banner("BGP SP - Star topology iBGP route reflector IPv4 IPv6 Test START ") + result = True + + star_topo = BGPSP.bgp_sp_dut_get_saved_star_topo() + BGPSP.bgp_sp_show_topo_path(star_topo) + + if not BGPSP.bgp_sp_test_topo_present(topo_path=star_topo, dut_count=3, segment_count=2) : + st.log("BGP SP - Test case topo requirement FAILED") + st.report_env_fail("test_case_not_executed") + return + + core_dut = star_topo['start_dut'] + dut_list = star_topo['dut_list'] + spoke_dut_list = copy.deepcopy(dut_list) + spoke_dut_list.remove(core_dut) + + st.log("BGP SP - Core dut {} and spokes {}".format(core_dut, spoke_dut_list)) + + core_asn = BGPSP.bgp_sp_get_bgp_asn(core_dut) + tb_core_dut = BGPSP.bgp_sp_get_dut_device(core_dut) + + afmly_list = BGPSP.bgp_sp_get_address_family_list("all") + rt_prefixes = { 'ipv4' :[], 'ipv6' :[]} + + for spoke_dut in spoke_dut_list : + for afmly in afmly_list: + prefixes = BGPSP.bgp_sp_get_dut_null_nhop_static_route_prefixes(spoke_dut, afmly) + rt_prefixes[afmly] += BGPSP.bgp_sp_ip_prefix_list_to_route_prefix_list(prefixes, afmly) + + if result : + st.log("BGP SP - Configure redistribute static on all spoke nodes") + result = BGPSP.bgp_sp_bgp_redistribute_connected_config_unconfig([core_dut], 'all', 'unicast', 'default', 'yes') + + if result : + result = BGPSP.bgp_sp_bgp_redistribute_static_config_unconfig(spoke_dut_list, 'all', 'unicast', 'default', 'yes') + + if result : + st.log("BGP SP - verify spokes does not have other spokes network") + for afmly in afmly_list: + result = BGPSP.bgp_sp_verify_no_bgp_ip_routes(spoke_dut_list, rt_prefixes[afmly], afmly) + if not result : + st.log("BGP SP - routei no check failed ") + break + + if result : + st.log("BGP SP - Configure {} client to client reflection".format(core_dut)) + result = BGPSP.bgp_sp_bgp_ctoc_reflection_config_unconfig(core_dut) + + if result : + st.log("BGP SP - Configuring client reflection on {} bgp asn {}".format(core_dut, core_asn)) + result = BGPSP.bgp_sp_bgp_neighbor_route_reflector_config_unconfig(core_dut, nbr_list=[], addr_family='all' ) + + if result : + st.wait(5) + st.log("BGP SP - verify every spoke has other spokes network due to root reflection") + for afmly in afmly_list: + result = BGPSP.bgp_sp_verify_bgp_ip_routes(spoke_dut_list, rt_prefixes[afmly], afmly) + if not result : + st.log("BGP SP - Route reflector iBGP session check Failed") + break + + BGPSP.bgp_sp_bgp_ctoc_reflection_config_unconfig(core_dut, vrf='default', config='no') + BGPSP.bgp_sp_bgp_neighbor_route_reflector_config_unconfig(core_dut, nbr_list=[], addr_family='all', config='no') + BGPSP.bgp_sp_bgp_redistribute_connected_config_unconfig([core_dut], 'all', 'unicast', 'default', 'no') + BGPSP.bgp_sp_bgp_redistribute_static_config_unconfig(spoke_dut_list, 'all', 'unicast', 'default', 'no') + + result_str = "PASSED" if result else "FAILED" + st.banner("BGP SP - Star topology iBGP route reflector test {}".format(result_str)) + + if result: + st.report_pass("test_case_passed") + else: + st.report_fail("test_case_failed") + + + + def test_bgp_sp_star_ibgp_route_reflector_bgp_clear(self): + + st.banner("BGP SP - Star topology iBGP route reflector bgp clear test START ") + result = True + + star_topo = BGPSP.bgp_sp_dut_get_saved_star_topo() + BGPSP.bgp_sp_show_topo_path(star_topo) + + if not BGPSP.bgp_sp_test_topo_present(topo_path=star_topo, dut_count=3, segment_count=2) : + st.log("BGP SP - Test case topo requirement FAILED") + st.report_env_fail("test_case_not_executed") + return + + core_dut = star_topo['start_dut'] + dut_list = star_topo['dut_list'] + spoke_dut_list = copy.deepcopy(dut_list) + spoke_dut_list.remove(core_dut) + + st.log("BGP SP - Core dut {} and spokes {}".format(core_dut, spoke_dut_list)) + + core_asn = BGPSP.bgp_sp_get_bgp_asn(core_dut) + tb_core_dut = BGPSP.bgp_sp_get_dut_device(core_dut) + + afmly_list = BGPSP.bgp_sp_get_address_family_list("all") + rt_prefixes = { 'ipv4' :[], 'ipv6' :[]} + + for spoke_dut in spoke_dut_list : + for afmly in afmly_list: + prefixes = BGPSP.bgp_sp_get_dut_null_nhop_static_route_prefixes(spoke_dut, afmly) + rt_prefixes[afmly] += BGPSP.bgp_sp_ip_prefix_list_to_route_prefix_list(prefixes, afmly) + + if result : + st.log("BGP SP - Configure redistribute static on all spoke nodes") + result = BGPSP.bgp_sp_bgp_redistribute_connected_config_unconfig([core_dut], 'all', 'unicast', 'default', 'yes') + + if result : + result = BGPSP.bgp_sp_bgp_redistribute_static_config_unconfig(spoke_dut_list, 'all', 'unicast', 'default', 'yes') + + if result : + st.log("BGP SP - Configure {} client to client reflection".format(core_dut)) + result = BGPSP.bgp_sp_bgp_ctoc_reflection_config_unconfig(core_dut) + + if result : + st.log("BGP SP - Configuring client reflection on {} bgp asn {}".format(core_dut, core_asn)) + result = BGPSP.bgp_sp_bgp_neighbor_route_reflector_config_unconfig(core_dut, nbr_list=[], addr_family='all' ) + + if result : + st.wait(5) + st.log("BGP SP - verify every spoke has other spokes network due to root reflection") + for afmly in afmly_list: + result = BGPSP.bgp_sp_verify_bgp_ip_routes(spoke_dut_list, rt_prefixes[afmly], afmly) + if not result : + st.log("BGP SP - Route reflector iBGP session check Failed") + break + + if result : + st.log("BGP SP - Clear BGP on all Nodes") + result = BGPSP.bgp_sp_clear_bgp(dut_list, addr_family="all") + + if result : + st.wait(5) + result = BGPSP.bgp_sp_verify_all_bgp_sessions(dut_list, addr_family='all') + if not result : + st.log("BGP SP - BGP sessions not up upon clear BGP") + + if result : + st.log("BGP SP - verify spoke has other spokes network after bgp clear") + for afmly in afmly_list: + result = BGPSP.bgp_sp_verify_bgp_ip_routes(spoke_dut_list, rt_prefixes[afmly], afmly) + if not result : + st.log("BGP SP - Route reflector iBGP session check Failed") + break + + BGPSP.bgp_sp_bgp_ctoc_reflection_config_unconfig(core_dut, vrf='default', config='no') + BGPSP.bgp_sp_bgp_neighbor_route_reflector_config_unconfig(core_dut, nbr_list=[], addr_family='all', config='no') + BGPSP.bgp_sp_bgp_redistribute_connected_config_unconfig([core_dut], 'all', 'unicast', 'default', 'no') + BGPSP.bgp_sp_bgp_redistribute_static_config_unconfig(spoke_dut_list, 'all', 'unicast', 'default', 'no') + + result_str = "PASSED" if result else "FAILED" + st.banner("BGP SP - Star topology iBGP route reflector test {}".format(result_str)) + + if result: + st.report_pass("test_case_passed") + else: + st.report_fail("test_case_failed") + + + + def test_bgp_sp_star_ibgp_route_reflector_bgp_sess_flap(self): + + st.banner("BGP SP - Star topology iBGP route reflector bgp session flap test START ") + result = True + + star_topo = BGPSP.bgp_sp_dut_get_saved_star_topo() + BGPSP.bgp_sp_show_topo_path(star_topo) + + if not BGPSP.bgp_sp_test_topo_present(topo_path=star_topo, dut_count=3, segment_count=2) : + st.log("BGP SP - Test case topo requirement FAILED") + st.report_env_fail("test_case_not_executed") + return + + core_dut = star_topo['start_dut'] + dut_list = star_topo['dut_list'] + spoke_dut_list = copy.deepcopy(dut_list) + spoke_dut_list.remove(core_dut) + + st.log("BGP SP - Core dut {} and spokes {}".format(core_dut, spoke_dut_list)) + + core_asn = BGPSP.bgp_sp_get_bgp_asn(core_dut) + tb_core_dut = BGPSP.bgp_sp_get_dut_device(core_dut) + + afmly_list = BGPSP.bgp_sp_get_address_family_list("all") + rt_prefixes = { 'ipv4' :[], 'ipv6' :[]} + + for spoke_dut in spoke_dut_list : + for afmly in afmly_list: + prefixes = BGPSP.bgp_sp_get_dut_null_nhop_static_route_prefixes(spoke_dut, afmly) + rt_prefixes[afmly] += BGPSP.bgp_sp_ip_prefix_list_to_route_prefix_list(prefixes, afmly) + + if result : + st.log("BGP SP - Configure redistribute static on all spoke nodes") + result = BGPSP.bgp_sp_bgp_redistribute_connected_config_unconfig([core_dut], 'all', 'unicast', 'default', 'yes') + + if result : + result = BGPSP.bgp_sp_bgp_redistribute_static_config_unconfig(spoke_dut_list, 'all', 'unicast', 'default', 'yes') + + if result : + st.log("BGP SP - Configure {} client to client reflection".format(core_dut)) + result = BGPSP.bgp_sp_bgp_ctoc_reflection_config_unconfig(core_dut) + + if result : + st.log("BGP SP - Configuring client reflection on {} bgp asn {}".format(core_dut, core_asn)) + result = BGPSP.bgp_sp_bgp_neighbor_route_reflector_config_unconfig(core_dut, nbr_list=[], addr_family='all' ) + + if result : + st.wait(5) + st.log("BGP SP - verify every spoke has other spokes network due to root reflection") + for afmly in afmly_list: + result = BGPSP.bgp_sp_verify_bgp_ip_routes(spoke_dut_list, rt_prefixes[afmly], afmly) + if not result : + st.log("BGP SP - Route reflector iBGP session check Failed") + break + + if result : + st.log("BGP SP - Flap BGP BGP session by interface down") + dut_int_shut = True + for segt_idx, segt_data_links in star_topo['segment'].items(): + segt_data = segt_data_links[0] + lcl_dut = segt_data['lcl_dut'] + dut_link_list = BGPSP.bgp_sp_dut_get_all_links(lcl_dut) + for lcl_link in dut_link_list: + result = BGPSP.bgp_sp_dut_interface_shut_noshut(lcl_dut, lcl_link, shut='yes') + #BGPSP.bgp_sp_show_dut_if_cmd_logs(lcl_dut) + if not result : + st.log("BGP SP - {} {} shutdown Failed".format(lcl_dut, lcl_link)) + break + break + + if result : + st.log("BGP SP - Verify All sesions are down due to timeout") + st.log("BGP SP - waiting for session timeout.....") + st.wait(70) + result = BGPSP.bgp_sp_verify_all_bgp_sessions(dut_list, addr_family='all', state='down') + if result : + st.log("BGP SP - Route reflector bgp session down check Failed") + result = False + result = True + + if result : + st.log("BGP SP - verify spokes do not have other spokes network after interface down") + for afmly in afmly_list: + result = BGPSP.bgp_sp_verify_no_bgp_ip_routes(spoke_dut_list, rt_prefixes[afmly], afmly, threaded_run=False) + if not result : + st.log("BGP SP - routei no check failed ") + break + + if result : + st.log("BGP SP - Bring up all interfaces again after session down") + for segt_idx, segt_data_links in star_topo['segment'].items(): + segt_data = segt_data_links[0] + lcl_dut = segt_data['lcl_dut'] + dut_link_list = BGPSP.bgp_sp_dut_get_all_links(lcl_dut) + for lcl_link in dut_link_list: + result = BGPSP.bgp_sp_dut_interface_shut_noshut(lcl_dut, lcl_link, shut='no') + #BGPSP.bgp_sp_show_dut_if_cmd_logs(lcl_dut) + if not result : + st.log("BGP SP - {} {} shutdown Failed".format(lcl_dut, lcl_link)) + break + if not result : dut_int_shut = False + break + + if result : + st.log("BGP SP - verify bgp sessions are up after interfaces up....") + st.wait(10) + result = BGPSP.bgp_sp_verify_all_bgp_sessions(dut_list, addr_family='all') + if not result : + st.log("BGP SP - Route reflector bgp session up check Failed") + + if result : + st.log("BGP SP - verify every spoke has other spokes network due to root reflection") + for afmly in afmly_list: + result = BGPSP.bgp_sp_verify_bgp_ip_routes(spoke_dut_list, rt_prefixes[afmly], afmly, threaded_run=False) + if not result : + st.log("BGP SP - Route reflector iBGP session check Failed") + break + + BGPSP.bgp_sp_bgp_ctoc_reflection_config_unconfig(core_dut, vrf='default', config='no') + BGPSP.bgp_sp_bgp_neighbor_route_reflector_config_unconfig(core_dut, nbr_list=[], addr_family='all', config='no') + BGPSP.bgp_sp_bgp_redistribute_connected_config_unconfig([core_dut], 'all', 'unicast', 'default', 'no') + BGPSP.bgp_sp_bgp_redistribute_static_config_unconfig(spoke_dut_list, 'all', 'unicast', 'default', 'no') + + if dut_int_shut : + st.log("BGP SP - Flap BGP BGP session by interface up") + for segt_idx, segt_data_links in star_topo['segment'].items(): + segt_data = segt_data_links[0] + lcl_dut = segt_data['lcl_dut'] + dut_link_list = BGPSP.bgp_sp_dut_get_all_links(lcl_dut) + for lcl_link in dut_link_list: + temp_result = BGPSP.bgp_sp_dut_interface_shut_noshut(lcl_dut, lcl_link, shut='no') + #BGPSP.bgp_sp_show_dut_if_cmd_logs(lcl_dut) + if not temp_result : + st.log("BGP SP - {} {} shutdown Failed".format(lcl_dut, lcl_link)) + break + dut_int_shut = False + break + + result_str = "PASSED" if result else "FAILED" + st.banner("BGP SP - Star topology iBGP route reflector bgp session flap test {}".format(result_str)) + + if result: + st.report_pass("test_case_passed") + else: + st.report_fail("test_case_failed") + + + +#--------------------------- RING TOPO eBGP iBGP eBGP CLASS ----------------------------------- + +@pytest.fixture(scope='class') +def bgp_sp_ring_topo_ebgp_ibgp_class_hook(request): + + pre_config = False + if pre_config : + BGPSP.bgp_sp_cleanup_bgp_routers() + if BGPSP.bgp_sp_get_dut_count() >= 3 : + result = BGPSP.bgp_sp_linear_topo_bgp_config_unconfig(sess_type='eBGPiBGPeBGP', addr_family='all', ring='yes', config='yes') + if not result : + st.log("BGP SP - Ring Topo eibgp config and neighbor session test failed") + st.report_fail("operation_failed") + + yield + + pre_config = False + if pre_config : + if BGPSP.bgp_sp_get_dut_count() >= 3 : + BGPSP.bgp_sp_linear_topo_bgp_config_unconfig(sess_type='eBGPiBGPeBGP', addr_family='all', ring='yes', config='no') + BGPSP.bgp_sp_cleanup_bgp_routers() + + +@pytest.mark.usefixtures('bgp_sp_ring_topo_ebgp_ibgp_class_hook') +class TestBGP_RING_EBGP_IBGP: + + def test_bgp_sp_four_node_ring_ebgp_ibgp_session(self): + + #class not used for any tests for now, return s passed + st.report_pass("test_case_passed") + return + + st.banner("BGP SP - 4 Node Ring eBGP iBGP session test START") + result = True + + ring_topo = BGPSP.bgp_sp_dut_get_saved_ring_topo() + BGPSP.bgp_sp_show_topo_path(ring_topo) + + if not BGPSP.bgp_sp_test_topo_present(topo_path=ring_topo, dut_count=4, segment_count=3) : + st.log("BGP SP - Test case topo requirement FAILED") + st.report_env_fail("test_case_not_executed") + return + + start_dut = ring_topo['start_dut'] + dut_list = ring_topo['dut_list'] + + #BGPSP.bgp_sp_show_dut_bgp_running_config(dut_list) + + #if result : + # st.log("BGP SP - Configure redistribute connected on all spoke nodes") + # result = BGPSP.bgp_sp_bgp_redistribute_connected_config_unconfig(dut_list, 'all', 'unicast', 'default', 'yes') + + if result : + st.log("BGP SP - Configure redistribute static on all spoke nodes") + result = BGPSP.bgp_sp_bgp_redistribute_static_config_unconfig(dut_list, 'all', 'unicast', 'default', 'yes') + + + #BGPSP.bgp_sp_bgp_redistribute_connected_config_unconfig(spoke_dut_list, 'all', 'unicast', 'default', 'no') + #BGPSP.bgp_sp_bgp_redistribute_static_config_unconfig(spoke_dut_list, 'all', 'unicast', 'default', 'no') + + result_str = "PASSED" if result else "FAILED" + st.banner("BGP SP - Ring topology eBGP iBGP test {}".format(result_str)) + + if result: + st.report_pass("test_case_passed") + else: + st.report_fail("test_case_failed") + + + diff --git a/spytest/tests/routing/test_arp.py b/spytest/tests/routing/test_arp.py new file mode 100644 index 00000000000..f508a3b4b68 --- /dev/null +++ b/spytest/tests/routing/test_arp.py @@ -0,0 +1,343 @@ +import pytest + +from spytest import st, tgapi, SpyTestDict + +import apis.routing.arp as arp_obj +import apis.routing.ip as ip_obj +import apis.system.interface as interface_obj +import apis.switching.vlan as vlan_obj +import apis.switching.mac as mac + +data = SpyTestDict() +data.d1t1_ip_addr = "192.168.11.1" +data.t1d1_ip_addr = "192.168.11.2" +data.t1d1_mac_addr = "00:00:00:00:00:01" +data.d1t2_ip_addr = "192.168.12.1" +data.t2d1_ip_addr = "192.168.12.2" +data.t2d1_mac_addr = "00:00:00:00:00:02" +data.static_arp_ip_1 = "192.168.11.4" +data.static_arp_mac = "00:00:00:00:00:66" +data.static_arp_ip = "192.168.12.3" +data.static_arp_mac_1 = "00:00:00:00:00:77" +data.mask = "24" +data.vlan_1 = 64 +data.vlan_int_1 = "Vlan{}".format(data.vlan_1) +data.clear_parallel = False + +@pytest.fixture(scope="module", autouse=True) +def arp_module_hooks(request): + global vars, tg_handler, tg, dut1, d1_mac_addr, h1, h2 + + # Min topology verification + vars = st.ensure_min_topology("D1T1:2") + + # Initialize TG and TG port handlers + tg_handler = tgapi.get_handles_byname("T1D1P1", "T1D1P2") + tg = tg_handler["tg"] + + # Test setup details + data.my_dut_list = st.get_dut_names() + dut1 = data.my_dut_list[0] + + # Test variables + d1_mac_addr = mac.get_sbin_intf_mac(dut1, "eth0") + + # ARP module configuration + st.log("ARP module configuration.") + ip_obj.config_ip_addr_interface(dut1, vars.D1T1P1, data.d1t1_ip_addr, data.mask) + vlan_obj.create_vlan(dut1, data.vlan_1) + vlan_obj.add_vlan_member(dut1, data.vlan_1, vars.D1T1P2, True) + ip_obj.config_ip_addr_interface(dut1, data.vlan_int_1, data.d1t2_ip_addr, data.mask) + + # TG ports reset + st.log("Resetting the TG ports") + tg.tg_traffic_control(action="reset", port_handle=tg_handler["tg_ph_list"]) + + # TG protocol interface creation + st.log("TG protocol interface creation") + h1 = tg.tg_interface_config(port_handle=tg_handler["tg_ph_1"], mode='config', + intf_ip_addr=data.t1d1_ip_addr,gateway=data.d1t1_ip_addr, + src_mac_addr=data.t1d1_mac_addr,arp_send_req='1') + st.log("INTFCONF: "+str(h1)) + h2 = tg.tg_interface_config(port_handle=tg_handler["tg_ph_2"], mode='config', + intf_ip_addr=data.t2d1_ip_addr,gateway=data.d1t2_ip_addr, + src_mac_addr=data.t2d1_mac_addr,arp_send_req='1',vlan_id=data.vlan_1,vlan=1) + st.log("INTFCONF: "+str(h2)) + + yield + # ARP module cleanup + st.log("ARP module cleanup.") + ip_obj.clear_ip_configuration(dut1,family="ipv4",thread=data.clear_parallel) + vlan_obj.clear_vlan_configuration(dut1,thread= data.clear_parallel) + +@pytest.fixture(scope="function", autouse=True) +def arp_func_hooks(request): + # ARP function configuration + yield + # ARP function cleanup + +def test_ft_arp_entry_link_failure(): + ################# Author Details ################ + # Name: Rakesh Kumar Vooturi + # Email: rakesh-kumar.vooturi@broadcom.com + ################################################# + # + # Objective - Verify an ARP table entry learned on port based routing interface is + # removed from ARP table after link failure on which that entry is learned. + # Objective - Verify an ARP table entry learned on vlan based routing interface is + # removed from ARP table after link failure on which that entry is learned + # + ############### Test bed details ################ + # DUT-----TG + ################################################# + # Ping from tgen to DUT. + res = tgapi.verify_ping(src_obj=tg, port_handle=tg_handler["tg_ph_1"], dev_handle=h1['handle'], + dst_ip=data.d1t1_ip_addr,ping_count='1', exp_count='1') + if res: + st.log("Ping succeeded.") + else: + st.warn("Ping failed.") + + res = tgapi.verify_ping(src_obj=tg, port_handle=tg_handler["tg_ph_2"], dev_handle=h2['handle'], + dst_ip=data.d1t2_ip_addr,ping_count='1', exp_count='1') + st.log("PING_RES: " + str(res)) + if res: + st.log("Ping succeeded.") + else: + st.warn("Ping failed.") + + # Verify dynamic arp entries + st.log("Verifying the arp entries on the DUT.") + if not arp_obj.verify_arp(dut1,data.t1d1_ip_addr,data.t1d1_mac_addr,vars.D1T1P1): + st.report_fail("ARP_entry_dynamic_entry_fail", data.t1d1_ip_addr, dut1) + st.log("Verifying the arp entries on the DUT") + if not arp_obj.verify_arp(dut1,data.t2d1_ip_addr,data.t2d1_mac_addr,vars.D1T1P2,data.vlan_1): + st.report_fail("ARP_entry_dynamic_entry_fail", data.t2d1_ip_addr, dut1) + + # Shutdown the routing interface link. + st.log("Shutdown the routing interface links.") + if not interface_obj.interface_operation(dut1, [vars.D1T1P1, vars.D1T1P2] , "shutdown"): + st.report_fail('interface_admin_shut_down_fail', [vars.D1T1P1, vars.D1T1P2]) + + # Verify dynamic arp entries + st.log("Verifying the arp entries on the DUT.") + if arp_obj.verify_arp(dut1,data.t1d1_ip_addr,data.t1d1_mac_addr,vars.D1T1P1): + st.report_fail("ARP_dynamic_entry_removal_fail", data.t1d1_ip_addr, vars.D1T1P1) + st.log("Verifying the arp entries on the DUT") + if arp_obj.verify_arp(dut1,data.t2d1_ip_addr): + st.report_fail("ARP_dynamic_entry_removal_fail", data.t2d1_ip_addr, vars.D1T1P2) + + # Startup the routing interface link. + st.log("Startup the routing interface link.") + if not interface_obj.interface_operation(dut1, [vars.D1T1P1, vars.D1T1P2], "startup"): + st.report_fail('interface_admin_startup_fail', [vars.D1T1P1, vars.D1T1P2]) + + st.wait(5) + + # Verify dynamic arp entries + st.log("Verifying the arp entries on the DUT.") + if not arp_obj.verify_arp(dut1,data.t1d1_ip_addr,data.t1d1_mac_addr,vars.D1T1P1): + st.report_fail("ARP_entry_dynamic_entry_fail", data.t1d1_ip_addr, dut1) + st.log("Verifying the arp entries on the DUT") + if not arp_obj.verify_arp(dut1,data.t2d1_ip_addr,data.t2d1_mac_addr,vars.D1T1P2,data.vlan_1): + st.report_fail("ARP_entry_dynamic_entry_fail", data.t2d1_ip_addr, dut1) + + st.report_pass("test_case_passed") + +@pytest.fixture(scope="function") +def fixture_ft_arp_dynamic_renew_traffic_test(request): + yield + arp_obj.set_arp_ageout_time(dut1,60) + +def test_ft_arp_dynamic_renew_traffic_test(fixture_ft_arp_dynamic_renew_traffic_test): + ################## Author Details ################ + # Name: Rakesh Kumar Vooturi + # Email: rakesh-kumar.vooturi@broadcom.com + ################################################## + # + # Objective - Verify a dynamic ARP table entry can be created. + # Objective - Verify that there is no data traffic loss during ARP dynamic review. + # + ############### Test bed details ################ + # TG-----DUT-----TG + ################################################# + # Set DUT values + st.log("Setting the DUT values") + arp_obj.set_arp_ageout_time(dut1,5) + + # Ping from tgen to DUT. + res = tgapi.verify_ping(src_obj=tg, port_handle=tg_handler["tg_ph_1"], dev_handle=h1['handle'], + dst_ip=data.d1t1_ip_addr,ping_count='1', exp_count='1') + if res: + st.log("Ping succeeded.") + else: + st.warn("Ping failed.") + + res = tgapi.verify_ping(src_obj=tg, port_handle=tg_handler["tg_ph_2"], dev_handle=h2['handle'], + dst_ip=data.d1t2_ip_addr,ping_count='1', exp_count='1') + if res: + st.log("Ping succeeded.") + else: + st.warn("Ping failed.") + + # TG ports reset + st.log("Resetting the TG ports") + tg.tg_traffic_control(action="clear_stats", port_handle=tg_handler["tg_ph_list"]) + + #TG stream formation + s1 = tg.tg_traffic_config(port_handle= tg_handler["tg_ph_2"],mode='create',rate_pps=10, + mac_src=data.t2d1_mac_addr,transmit_mode="continuous",mac_dst=d1_mac_addr, + l2_encap='ethernet_ii_vlan',l3_protocol="ipv4",ip_dst_addr=data.t1d1_ip_addr, + ip_src_addr=data.t2d1_ip_addr,vlan_id=data.vlan_1,vlan="enable") + tg.tg_traffic_control(action="run", stream_handle=s1['stream_id']) + + # Waiting for more than arp ageout time + st.wait(10) + tg.tg_traffic_control(action="stop", stream_handle=s1['stream_id']) + + # Adding sleep + st.wait(2) + + st.log("Checking the stats and verifying the traffic flow") + traffic_details = { + '1': { + 'tx_ports': [vars.T1D1P2], + 'tx_obj': [tg], + 'exp_ratio': [1], + 'rx_ports': [vars.T1D1P1], + 'rx_obj': [tg], + } + } + if not tgapi.validate_tgen_traffic(traffic_details=traffic_details, mode='aggregate', comp_type='packet_count'): + st.report_fail("traffic_verification_failed") + else: + st.log("traffic verification passed") + st.report_pass("test_case_passed") + + +@pytest.fixture(scope="function") +def fixture_ft_arp_clear_cache_static_and_dynamic_entries(request): + yield + arp_obj.delete_static_arp(dut1, data.static_arp_ip) + arp_obj.delete_static_arp(dut1, data.static_arp_ip_1) + +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_arp_clear_cache_static_and_dynamic_entries(fixture_ft_arp_clear_cache_static_and_dynamic_entries): + ################## Author Details ############### + # Name: Rakesh Kumar Vooturi + # Email: rakesh-kumar.vooturi@broadcom.com + ################################################# + # + # Objective - Verify that the clearing the ARP cache, + # all dynamic entries are removed and static entries are not cleared. + # + ############### Test bed details ################ + # TG-----DUT-----TG + ################################################# + # Adding static arp entries + arp_obj.add_static_arp(dut1, data.static_arp_ip_1, data.static_arp_mac_1, vars.D1T1P1) + arp_obj.add_static_arp(dut1, data.static_arp_ip, data.static_arp_mac, data.vlan_int_1) + + res = tgapi.verify_ping(src_obj=tg, port_handle=tg_handler["tg_ph_1"], dev_handle=h1['handle'], + dst_ip=data.d1t1_ip_addr,ping_count='1', exp_count='1') + if res: + st.log("Ping succeeded.") + else: + st.warn("Ping failed.") + + res = tgapi.verify_ping(src_obj=tg, port_handle=tg_handler["tg_ph_2"], dev_handle=h2['handle'], + dst_ip=data.d1t2_ip_addr,ping_count='1', exp_count='1') + if res: + st.log("Ping succeeded.") + else: + st.warn("Ping failed.") + + # Verify static and dynamic arp entries + st.log("Verifying the arp entries on the DUT") + if not arp_obj.verify_arp(dut1,data.t1d1_ip_addr,data.t1d1_mac_addr,vars.D1T1P1): + st.report_fail("ARP_entry_dynamic_entry_fail", data.t1d1_ip_addr, dut1) + if not arp_obj.verify_arp(dut1,data.t2d1_ip_addr,data.t2d1_mac_addr,vars.D1T1P2,data.vlan_1): + st.report_fail("ARP_entry_dynamic_entry_fail", data.t2d1_ip_addr, dut1) + if not arp_obj.verify_arp(dut1,data.static_arp_ip_1,data.static_arp_mac_1,vars.D1T1P1): + st.report_fail("static_arp_create_fail", dut1) + if not arp_obj.verify_arp(dut1,data.static_arp_ip,data.static_arp_mac,"",data.vlan_1): + st.report_fail("static_arp_create_fail", dut1) + + st.banner("Start - Verifying dynamic and static arp entries behavior on issuing sonic-clear arp command *WITH* traffic flowing") + + # TG ports reset + st.log("Resetting the TG ports") + tg.tg_traffic_control(action="clear_stats", port_handle=tg_handler["tg_ph_list"]) + + #TG stream formation + s1=tg.tg_traffic_config(port_handle= tg_handler["tg_ph_2"],mode='create',rate_pps=10, + mac_src=data.t2d1_mac_addr,transmit_mode="continuous",mac_dst=d1_mac_addr, + l2_encap='ethernet_ii_vlan',l3_protocol="ipv4",ip_dst_addr=data.t1d1_ip_addr, + ip_src_addr=data.t2d1_ip_addr,vlan_id=data.vlan_1,vlan="enable") + s2=tg.tg_traffic_config(port_handle= tg_handler["tg_ph_1"],mode='create',rate_pps=10, + mac_src=data.t1d1_mac_addr,transmit_mode="continuous",mac_dst=d1_mac_addr, + l2_encap='ethernet_ii_vlan',l3_protocol="ipv4",ip_dst_addr=data.t2d1_ip_addr, + ip_src_addr=data.t1d1_ip_addr) + + tg.tg_traffic_control(action="run",stream_handle=[s1['stream_id'], s2['stream_id']]) + + # Adding wait for traffic to flow. + st.wait(5) + + # Issuing sonic-clear arp command and veryfying the entries behavior. + arp_obj.clear_arp_table(dut1) + + # Adding wait for traffic to flow. + st.wait(5) + + # Verify static and dynamic arp entries after clear arp + st.log("Verifying the arp entries on the DUT after issuing sonic-clear arp command with traffic flowing.") + if not arp_obj.verify_arp(dut1,data.t1d1_ip_addr,data.t1d1_mac_addr,vars.D1T1P1): + st.report_fail("ARP_entry_dynamic_entry_fail", data.t1d1_ip_addr, dut1) + if not arp_obj.verify_arp(dut1,data.t2d1_ip_addr,data.t2d1_mac_addr,vars.D1T1P2,data.vlan_1): + st.report_fail("ARP_entry_dynamic_entry_fail", data.t2d1_ip_addr, dut1) + if not arp_obj.verify_arp(dut1,data.static_arp_ip_1,data.static_arp_mac_1,vars.D1T1P1): + st.report_fail("static_arp_delete_fail", dut1) + if not arp_obj.verify_arp(dut1,data.static_arp_ip,data.static_arp_mac,"",data.vlan_1): + st.report_fail("static_arp_delete_fail", dut1) + + # Stop the traffic + tg.tg_traffic_control(action="stop",stream_handle=[s1['stream_id'], s2['stream_id']]) + + st.log("Verifying the TG stats") + + traffic_details = { + '1': { + 'tx_ports': [vars.T1D1P2], + 'tx_obj': [tg], + 'exp_ratio': [1], + 'rx_ports': [vars.T1D1P1], + 'rx_obj': [tg], + } + } + if not tgapi.validate_tgen_traffic(traffic_details=traffic_details, mode='aggregate', comp_type='packet_count'): + st.report_fail("traffic_verification_failed") + else: + st.log("traffic verification passed") + + st.banner("End - Verified dynamic and static arp entries behavior on issuing sonic-clear arp command *WITH* traffic flowing") + + st.banner("Start - Verifying dynamic and static arp entries behavior on issuing sonic-clear arp command *WITHOUT* traffic flowing") + # Issuing sonic-clear arp command and veryfying the entries behavior. + arp_obj.clear_arp_table(dut1) + + # Verify static and dynamic arp entries after clear arp + st.log("Verifying the arp entries on the DUT after issuing sonic-clear arp command") + if arp_obj.verify_arp(dut1,data.t1d1_ip_addr,data.t1d1_mac_addr,vars.D1T1P1): + st.report_fail("ARP_dynamic_entry_clear_arp_fail", data.t1d1_ip_addr, dut1) + if arp_obj.verify_arp(dut1,data.t2d1_ip_addr,data.t2d1_mac_addr,vars.D1T1P2,data.vlan_1): + st.report_fail("ARP_dynamic_entry_clear_arp_fail", data.t2d1_ip_addr, dut1) + if not arp_obj.verify_arp(dut1,data.static_arp_ip_1,data.static_arp_mac_1,vars.D1T1P1): + st.report_fail("static_arp_delete_fail", dut1) + if not arp_obj.verify_arp(dut1,data.static_arp_ip,data.static_arp_mac,"",data.vlan_1): + st.report_fail("static_arp_delete_fail", dut1) + + st.banner("End - Verified dynamic and static arp entries behavior on issuing sonic-clear arp command *WITHOUT* traffic flowing") + + st.report_pass("test_case_passed") diff --git a/spytest/tests/routing/test_arp_static_route_long_run.py b/spytest/tests/routing/test_arp_static_route_long_run.py new file mode 100644 index 00000000000..72c89a5bc9a --- /dev/null +++ b/spytest/tests/routing/test_arp_static_route_long_run.py @@ -0,0 +1,187 @@ +import pytest + +from spytest import st, tgapi, SpyTestDict + +import apis.routing.arp as arp_obj +import apis.system.reboot as rb_obj +import apis.system.basic as basic_obj +import apis.routing.ip as ip_obj +import apis.routing.bgp as bgp_obj + +from utilities.common import poll_wait + +def init_vars(): + global vars + vars = st.ensure_min_topology("D1T1:2") + +def initialize_variables(): + global data + data = SpyTestDict() + data.static_arp_mac = "00:00:00:00:00:66" + data.static_arp_ip = "192.168.12.2" + data.ipv4_address_ixia = "10.10.10.2" + data.ipv4_address = "10.10.10.1" + data.ipv4_address_network = "20.20.20.0/24" + data.mask = "24" + data.src_mac_addr = "00:00:01:02:03:04" + data.ipv4_address_1 = "192.168.12.1" + +def get_parms(): + data.platform = basic_obj.get_hwsku(vars.D1) + data.constants = st.get_datastore(vars.D1, "constants", "default") + +@pytest.fixture(scope="module", autouse=True) +def arp_static_route_reboot_module_hooks(request): + # add things at the start of this module + init_vars() + initialize_variables() + get_parms() + + global tg_handler + tg_handler = tgapi.get_handles_byname("T1D1P1", "T1D1P2") + global tg + tg = tg_handler["tg"] + tg_ph_list = [tg_handler["tg_ph_1"], tg_handler["tg_ph_2"]] + st.log("configuring static route") + adding_static_route() + st.log("Getting ARP entry dynamically") + adding_dynamic_arp() + ip_obj.config_ip_addr_interface(vars.D1, vars.D1T1P2, data.ipv4_address_1, data.mask, family="ipv4", config='add') + st.log("Configuring static ARP") + arp_obj.add_static_arp(vars.D1, data.static_arp_ip, data.static_arp_mac, vars.D1T1P2) + st.log("Verifying static route entries before save and reboot/fast-reboot/warm-reboot") + static_route_verify() + st.log("Verifying dynamic ARP entries before save and reboot/fast-reboot/warm-reboot") + if not arp_obj.verify_arp(vars.D1, data.ipv4_address_ixia, data.src_mac_addr, vars.D1T1P1): + st.report_fail("ARP_entry_dynamic_entry_fail", data.ipv4_address_ixia, vars.D1) + else: + st.log("Verified that dynamic ARP entry is present in arp table") + st.log("Verifying static ARP entries before save and reboot/fast-reboot/warm-reboot") + if not arp_obj.verify_arp(vars.D1, data.static_arp_ip, data.static_arp_mac, ""): + st.report_fail("static_arp_create_fail", vars.D1) + else: + st.log("Verified that static ARP entry is present in arp table") + st.log("Save the config on the DUT") + rb_obj.config_save(vars.D1) + st.log("saving config in vtysh mode to save static route") + rb_obj.config_save(vars.D1, shell="vtysh") + yield + # Below step will clear IP adresses configured on different interfaces in the device + ip_obj.clear_ip_configuration(st.get_dut_names()) + #Below step will clear static route configured in the device + ip_obj.delete_static_route(vars.D1, data.ipv4_address_ixia, data.ipv4_address_network, family='ipv4', shell="vtysh") + #Below step will delete static arp entries configured in the device + arp_obj.delete_static_arp(vars.D1, data.static_arp_ip, vars.D1T1P2) + +@pytest.fixture(scope="function", autouse=True) +def arp_static_route_reboot_func_hooks(request): + # add things at the start every test case + # use 'request.function.func_name' to compare + # if any thing specific a particular test case + yield + # add things at the end every test case + # use 'request.function.func_name' to compare + # if any thing specific a particular test case + +def adding_static_route(): + st.log("About to add ipv4 address on ixia connected interface") + ip_obj.config_ip_addr_interface(vars.D1, vars.D1T1P1, data.ipv4_address, data.mask, family="ipv4", config='add') + st.log("Enabling docker routing config mode to split") + bgp_obj.enable_docker_routing_config_mode(vars.D1) + st.log("configuring static route via vtysh mode") + ip_obj.create_static_route(vars.D1, data.ipv4_address_ixia, data.ipv4_address_network, shell="vtysh", family="ipv4") + +def static_route_verify(): + st.log("Ip address configuration verification") + if not poll_wait(ip_obj.verify_interface_ip_address, 10, vars.D1, vars.D1T1P1, "{}/{}".format(data.ipv4_address, data.mask), + family="ipv4"): + st.report_fail("ip_routing_int_create_fail", vars.D1T1P1) + else: + st.log("Successfully added ipv4 address on ixia connected interface") + + st.log("static route configuration verification") + if not ip_obj.verify_ip_route(vars.D1, "ipv4", ip_address=data.ipv4_address_network, type="S"): + st.error("Static route - {} information not exists.".format(data.ipv4_address_network)) + st.report_fail("ip_static_route_create_fail", data.ipv4_address_network) + else: + st.log("creation of static route is successful") + +def adding_dynamic_arp(): + data.h1 = tg.tg_interface_config(port_handle=tg_handler["tg_ph_1"], mode='config', intf_ip_addr=data.ipv4_address_ixia, + gateway=data.ipv4_address, src_mac_addr=data.src_mac_addr, arp_send_req='1') + st.log("INTFCONF: " + str(data.h1)) + st.log("Pinging from tgen to DUT's ixia connected IPV4 interface") + res = tgapi.verify_ping(src_obj=tg, port_handle=tg_handler["tg_ph_1"], dev_handle=data.h1['handle'], dst_ip=data.ipv4_address, + ping_count='1', exp_count='1') + st.log("PING_RES: " + str(res)) + if res: + st.log("Ping succeeded.") + else: + st.log("Ping failed.") + st.wait(5) + if not arp_obj.show_arp(vars.D1, data.ipv4_address_ixia): + st.report_fail("ARP_entry_dynamic_entry_fail", data.ipv4_address_ixia, vars.D1) + +def test_ft_arp_static_route_config_mgmt_verifying_config_with_warm_reboot(): + ''' + Author: Surendra Kumar Vella(surendrakumar.vella@broadcom.com) + Verify static ARP route config after warm-reboot + ''' + + st.log("Checking whether the platform supports warm-reboot") + if not data.platform.lower() in data.constants['WARM_REBOOT_SUPPORTED_PLATFORMS']: + st.report_unsupported('test_case_unsupported') + st.log("Performing warm-reboot on DUT") + st.reboot(vars.D1, "warm") + st.log("Verifying static route entries after save and warm-reboot") + st.wait(5) + static_route_verify() + st.log("Verifying dynamic ARP entries after save and warm-reboot") + if not arp_obj.verify_arp(vars.D1, data.ipv4_address_ixia, data.src_mac_addr, vars.D1T1P1): + st.report_fail("ARP_entry_dynamic_entry_fail", data.ipv4_address_ixia, vars.D1) + else: + st.log("Verified that dynamic ARP entry is present in arp table") + st.log("Verifying static ARP entries after save and warm-reboot") + if not arp_obj.verify_arp(vars.D1, data.static_arp_ip, data.static_arp_mac, ""): + st.report_fail("static_arp_create_fail", vars.D1) + else: + st.log("Verified that static ARP entry is present in arp table") + st.report_pass("test_case_passed") + +def test_ft_arp_static_route_config_mgmt_verifying_config_with_save_fast_reboot(): + ''' + Author: Surendra Kumar Vella(surendrakumar.vella@broadcom.com) + Verify static ARP route config after save fast-reboot + ''' + st.log("Performing fast-reboot on DUT") + st.reboot(vars.D1, "fast") + st.log("Verifying static route entries after save and fast-reboot") + st.wait(5) + static_route_verify() + adding_dynamic_arp() + st.log("Verifying dynamic ARP entries after save and fast-reboot") + if not arp_obj.verify_arp(vars.D1, data.ipv4_address_ixia, data.src_mac_addr, vars.D1T1P1): + st.report_fail("ARP_entry_dynamic_entry_fail", data.ipv4_address_ixia, vars.D1) + else: + st.log("Verified that dynamic ARP entry is present in arp table") + st.report_pass("test_case_passed") + + +def test_ft_arp_static_route_config_mgmt_verifying_config_with_save_reboot(): + ''' + Author: Surendra Kumar Vella(surendrakumar.vella@broadcom.com) + Verify static ARP route config after save cold-reboot + ''' + st.log("Performing reboot on DUT") + st.reboot(vars.D1) + st.log("Verifying static route entries after save and reboot") + st.wait(5) + static_route_verify() + adding_dynamic_arp() + st.log("Verifying dynamic ARP entries after save and reboot") + if not arp_obj.verify_arp(vars.D1, data.ipv4_address_ixia, data.src_mac_addr, vars.D1T1P1): + st.report_fail("ARP_entry_dynamic_entry_fail", data.ipv4_address_ixia, vars.D1) + else: + st.log("Verified that dynamic ARP entry is present in arp table") + st.report_pass("test_case_passed") + diff --git a/spytest/tests/routing/test_ip.py b/spytest/tests/routing/test_ip.py new file mode 100644 index 00000000000..3efbc88e076 --- /dev/null +++ b/spytest/tests/routing/test_ip.py @@ -0,0 +1,638 @@ + +import random +import math +import re +import pytest + +from spytest import st, tgapi, SpyTestDict +from spytest.utils import random_vlan_list + +import apis.routing.ip as ipfeature +import apis.switching.vlan as vlan_obj +import apis.switching.portchannel as pc_obj +import apis.system.basic as basic_obj +import apis.switching.portchannel as portchannel_obj +import apis.common.asic as asicapi +import apis.routing.bgp as bgpfeature + +vars = dict() +data = SpyTestDict() +data.ip4_addr = ["192.168.1.1", "192.168.1.2", "192.168.2.1", "192.168.2.2", "192.168.3.1", "192.168.3.3", + "192.168.4.1", "192.168.4.2", "192.168.5.1", "192.168.5.2", "192.168.6.1", "192.168.6.2"] +data.ip4_addr_rt = ["192.168.1.0", "192.168.2.0", "192.168.3.0", "192.168.4.0", "192.168.5.0", "192.168.6.0"] +data.ip6_addr = ["2001::1", "2001::2", "3301::1", "3301::2", "4441::1", "4441::2", "5551::1", "5551::2", "6661::1", + "6661::2", "7771::1", "7771::2"] +data.ip6_addr_rt = ["2001::", "3301::", "4441::", "5551::", "6661::", "7771::"] +data.loopback_1 = ["11.11.11.1", "22.22.22.1", "33.33.33.1"] +data.loopback6_1 = ["7767:12::2", "6671:230f:12::f", "9109:2cd1:341::3"] +data.af_ipv4 = "ipv4" +data.af_ipv6 = "ipv6" +data.shell_sonic = "sonic" +data.shell_vtysh = "vtysh" +data.vlan_1 = str(random_vlan_list()[0]) +data.vlan_2 = str(random_vlan_list()[0]) +data.vlan_int_1 = "Vlan{}".format(data.vlan_1) +data.vlan_int_2 = "Vlan{}".format(data.vlan_2) +data.port_channel = "PortChannel100" +data.tg_mac1 = "00:00:00:EA:23:0F" +data.tg_mac2 = "00:00:11:0A:45:33" +data.rate_pps = 2000 +data.static_ip6_rt_drop = "blackhole" +data.static_ip6_rt = "6661::/64" +data.static_ip_rt = "192.168.5.0/24" +data.as_num = 100 +data.remote_as_num = 200 +data.routemap = "preferGlobal" +data.wait_tgstats = 2 +data.no_of_ports = 8 +data.ipv4_mask = '24' +data.ipv6_mask = '96' + +@pytest.fixture(scope="module", autouse=True) +def ip_module_hooks(request): + global vars, tg_handler, tg + # Min topology verification + st.log("Ensuring minimum topology") + vars = st.ensure_min_topology("D1T1:2", "D2T1:2", "D1D2:4") + + # Initialize TG and TG port handlers + tg_handler = tgapi.get_handles_byname("T1D1P1", "T1D1P2", "T1D2P1", "T1D2P2") + tg = tg_handler["tg"] + + # IP module configuration + st.log("Vlan routing configuration on D1D2P1,D2D1P1") + vlan_obj.create_vlan(vars.D1, data.vlan_1) + vlan_obj.add_vlan_member(vars.D1, data.vlan_1, [vars.D1D2P1], tagging_mode=True) + vlan_obj.create_vlan(vars.D2, data.vlan_1) + vlan_obj.add_vlan_member(vars.D2, data.vlan_1, [vars.D2D1P1], tagging_mode=True) + ipfeature.config_ip_addr_interface(vars.D1, data.vlan_int_1, data.ip4_addr[2], 24, family=data.af_ipv4) + ipfeature.config_ip_addr_interface(vars.D1, data.vlan_int_1, data.ip6_addr[2], 96, family=data.af_ipv6) + ipfeature.config_ip_addr_interface(vars.D2, data.vlan_int_1, data.ip4_addr[3],24, family = data.af_ipv4) + ipfeature.config_ip_addr_interface(vars.D2, data.vlan_int_1, data.ip6_addr[3], 96, family=data.af_ipv6) + st.log("Port routing configuration on port-channel") + data.dut1_pc_members = [vars.D1D2P2, vars.D1D2P3] + data.dut2_pc_members = [vars.D2D1P2, vars.D2D1P3] + pc_obj.create_portchannel(vars.D1, data.port_channel) + pc_obj.add_portchannel_member(vars.D1, data.port_channel, data.dut1_pc_members) + pc_obj.create_portchannel(vars.D2, data.port_channel) + pc_obj.add_portchannel_member(vars.D2, data.port_channel, data.dut2_pc_members) + ipfeature.config_ip_addr_interface(vars.D1, data.port_channel, data.ip4_addr[4], 24, family=data.af_ipv4) + ipfeature.config_ip_addr_interface(vars.D2, data.port_channel, data.ip4_addr[5], 24, family=data.af_ipv4) + ipfeature.config_ip_addr_interface(vars.D1, data.port_channel, data.ip6_addr[4], 96, family=data.af_ipv6) + ipfeature.config_ip_addr_interface(vars.D2, data.port_channel, data.ip6_addr[5], 96, family=data.af_ipv6) + st.log("port routing configuration on D1D2P4,D2D1P4") + ipfeature.config_ip_addr_interface(vars.D1, vars.D1D2P4, data.ip4_addr[6], 24, family=data.af_ipv4) + ipfeature.config_ip_addr_interface(vars.D2, vars.D2D1P4, data.ip4_addr[7], 24, family=data.af_ipv4) + ipfeature.config_ip_addr_interface(vars.D1, vars.D1D2P4, data.ip6_addr[6], 96, family=data.af_ipv6) + ipfeature.config_ip_addr_interface(vars.D2, vars.D2D1P4, data.ip6_addr[7], 96, family=data.af_ipv6) + st.log("configuring the dut1 ports connected to ixias with ip addresses") + ipfeature.config_ip_addr_interface(vars.D1, vars.D1T1P1, data.ip4_addr[1], 24, family=data.af_ipv4) + ipfeature.config_ip_addr_interface(vars.D1, vars.D1T1P2, data.ip6_addr[1], 96, family=data.af_ipv6) + ipfeature.create_static_route(vars.D1, data.ip6_addr[7], data.static_ip6_rt, shell=data.shell_vtysh, + family=data.af_ipv6) + ipfeature.create_static_route(vars.D1, data.ip4_addr[7], data.static_ip_rt, shell=data.shell_vtysh, + family=data.af_ipv4) + st.log("configuring the dut2 ports connected to ixias with ip addresses") + ipfeature.config_ip_addr_interface(vars.D2, vars.D2T1P1, data.ip4_addr[8], 24, family=data.af_ipv4) + ipfeature.config_ip_addr_interface(vars.D2, vars.D2T1P2, data.ip6_addr[8], 96, family=data.af_ipv6) + + yield + ipfeature.clear_ip_configuration(st.get_dut_names()) + ipfeature.clear_ip_configuration(st.get_dut_names(), 'ipv6') + vlan_obj.clear_vlan_configuration(st.get_dut_names()) + portchannel_obj.clear_portchannel_configuration(st.get_dut_names()) + ipfeature.delete_static_route(vars.D1, data.ip4_addr[7], data.static_ip_rt, shell=data.shell_vtysh, + family=data.af_ipv4) + ipfeature.delete_static_route(vars.D1, data.static_ip6_rt_drop, data.static_ip6_rt, shell=data.shell_vtysh, + family=data.af_ipv6) + +@pytest.fixture(scope="function", autouse=True) +def ip_func_hooks(request): + yield + +def delete_bgp_router(dut, router_id, as_num): + """ + :param router_id: + :type router_id: + :param as_num: + :type as_num: + :param dut: + :type dut: + :return: + :rtype: + """ + st.log("delete bgp router info") + my_cmd = "router bgp {}".format(as_num) + st.vtysh_config(dut, my_cmd) + my_cmd = "no bgp router-id {}".format(router_id) + +def create_bgp_neighbor_route_map_config(dut, local_asn, neighbor_ip, routemap): + command = "route-map {} permit 10".format(routemap) + st.vtysh_config(dut, command) + command = "set ipv6 next-hop prefer-global" + st.vtysh_config(dut, command) + command = "router bgp {}".format(local_asn) + st.vtysh_config(dut, command) + command = "address-family ipv6 unicast" + st.vtysh_config(dut, command) + command = "neighbor {} route-map {} in".format(neighbor_ip, routemap) + st.vtysh_config(dut, command) + command = "neighbor {} route-map {} out".format(neighbor_ip, routemap) + return + +def create_v4_route(route_count): + vars = st.get_testbed_vars() + dut = vars.D1 + + st.show(dut, "show ip interfaces") + st.show(dut, "show ip route") + st.show(dut, "show interface status",skip_tmpl=True) + + bgpfeature.create_bgp_router(dut, data.as_num, '') + bgpfeature.create_bgp_neighbor(dut, data.as_num, data.ip4_addr[0], data.remote_as_num) + + tg_handler = tgapi.get_handles_byname("T1D1P1", "T1D2P1") + tg = tg_handler["tg"] + tg.tg_traffic_control(action="reset", port_handle=tg_handler["tg_ph_list"]) + tg.tg_traffic_control(action="clear_stats", port_handle=tg_handler["tg_ph_list"]) + + dut_rt_int_mac1 = basic_obj.get_ifconfig_ether(vars.D1, vars.D1T1P1) + h1 = tg.tg_interface_config(port_handle=tg_handler["tg_ph_1"], mode='config', intf_ip_addr=data.ip4_addr[0], \ + gateway=data.ip4_addr[1], src_mac_addr=data.tg_mac1, arp_send_req='1') + st.log("INTFCONF: " + str(h1)) + h2 = tg.tg_interface_config(port_handle=tg_handler["tg_ph_2"], mode='config', intf_ip_addr=data.ip4_addr[9], \ + gateway=data.ip4_addr[8], src_mac_addr=data.tg_mac2, arp_send_req='1') + st.log("INTFCONF: " + str(h2)) + # Ping from tgen to DUT. + res = tgapi.verify_ping(src_obj=tg, port_handle=tg_handler["tg_ph_1"], dev_handle=h1['handle'], dst_ip=data.ip4_addr[1], \ + ping_count='1', exp_count='1') + if res: + st.log("Ping succeeded.") + else: + st.warn("Ping failed.") + + conf_var = { 'mode' : 'enable', + 'active_connect_enable' : '1', + 'local_as' : '200', + 'remote_as' : '100', + 'remote_ip_addr' : data.ip4_addr[1] + } + route_var = { 'mode' : 'add', + 'num_routes' : route_count, + 'prefix' : '121.1.1.0', + 'as_path' : 'as_seq:1' + } + ctrl_start = { 'mode' : 'start'} + + # Configuring the BGP router. + bgp_rtr1 = tgapi.tg_bgp_config(tg = tg, + handle = h1['handle'], + conf_var = conf_var, + route_var = route_var, + ctrl_var = ctrl_start) + + st.log("BGP_HANDLE: "+str(bgp_rtr1)) + st.log("waiting for 10 sec to get the BGP neighbor started before going for another TG operation") + st.wait(10) + # Verified at neighbor. + tr1 = tg.tg_traffic_config(port_handle=tg_handler["tg_ph_1"], mode='create', transmit_mode='single_burst', + pkts_per_burst=2000, \ + length_mode='fixed', rate_pps=2000, l3_protocol='ipv4', mac_src=data.tg_mac1, \ + mac_dst=dut_rt_int_mac1, ip_src_addr=data.ip4_addr[0], + ip_dst_addr=data.ip4_addr[9]) + st.log("TRAFCONF: " + str(tr1)) + res = tg.tg_traffic_control(action='run', stream_handle=tr1['stream_id']) + st.log("TR_CTRL: " + str(res)) + tg.tg_traffic_control(action='stop', stream_handle=tr1['stream_id']) + st.log("Checking the stats and verifying the traffic flow") + traffic_details = { + '1': { + 'tx_ports' : [vars.T1D1P1], + 'tx_obj' : [tg_handler["tg"]], + 'exp_ratio' : [1], + 'rx_ports' : [vars.T1D2P1], + 'rx_obj' : [tg_handler["tg"]], + } + } + #verify statistics + aggrResult = tgapi.validate_tgen_traffic(traffic_details=traffic_details, mode='aggregate', comp_type='packet_count') + if not aggrResult: + return False + + return True + +def test_l3_v4_route_po_1(): + dut = vars.D1 + asicapi.dump_vlan(dut) + asicapi.dump_l2(dut) + asicapi.dump_trunk(dut) + + ret = create_v4_route(30000) + if (ret): + st.report_pass("test_case_passed") + else: + st.report_fail("test_case_failed") + +def create_v6_route(route_count): + vars = st.get_testbed_vars() + dut = vars.D1 + + st.show(dut, "show ipv6 interfaces") + st.show(dut, "show ipv6 route") + + bgpfeature.create_bgp_router(dut, data.as_num, '') + bgpfeature.create_bgp_neighbor(dut, data.as_num, data.ip6_addr[0], data.remote_as_num, family="ipv6") + create_bgp_neighbor_route_map_config(dut, data.as_num, data.ip6_addr[1], data.routemap) + + tg_handler = tgapi.get_handles_byname("T1D1P2", "T1D2P2") + tg = tg_handler["tg"] + tg.tg_traffic_control(action="reset", port_handle=tg_handler["tg_ph_list"]) + tg.tg_traffic_control(action="clear_stats", port_handle=tg_handler["tg_ph_list"]) + + dut_rt_int_mac1 = basic_obj.get_ifconfig_ether(vars.D1, vars.D1T1P1) + h1 = tg.tg_interface_config(port_handle=tg_handler["tg_ph_1"], mode='config', ipv6_intf_addr=data.ip6_addr[0], \ + ipv6_prefix_length='64', ipv6_gateway=data.ip6_addr[1], + src_mac_addr=data.tg_mac1, arp_send_req='1') + st.log("INTFCONF: " + str(h1)) + h2 = tg.tg_interface_config(port_handle=tg_handler["tg_ph_2"], mode='config', ipv6_intf_addr=data.ip6_addr[9], \ + ipv6_prefix_length='64', ipv6_gateway=data.ip6_addr[8], + src_mac_addr=data.tg_mac2, arp_send_req='1') + st.log("INTFCONF: " + str(h2)) + + # Ping from tgen to DUT. + res = tgapi.verify_ping(src_obj=tg, port_handle=tg_handler["tg_ph_1"], dev_handle=h1['handle'], dst_ip=data.ip6_addr[1], \ + ping_count='1', exp_count='1') + if res: + st.log("Ping succeeded.") + else: + st.warn("Ping failed.") + + bgp_conf=tg.tg_emulation_bgp_config(handle=h1['handle'], mode='enable', ip_version='6', + active_connect_enable='1', local_as=data.as_num, remote_as=data.remote_as_num, remote_ipv6_addr=data.ip6_addr[1]) + + tg.tg_emulation_bgp_route_config(handle=bgp_conf['handle'], mode='add', ip_version='6', + num_routes=route_count, prefix='3300:1::', as_path='as_seq:1') + tg.tg_emulation_bgp_control(handle=bgp_conf['handle'], mode='start') + + # Configuring the BGP router. + st.log("BGP neighborship established.") + tr1 = tg.tg_traffic_config(port_handle=tg_handler["tg_ph_1"], mode='create', transmit_mode='single_burst', + pkts_per_burst=2000, \ + length_mode='fixed', rate_pps=2000, l3_protocol='ipv6', mac_src=data.tg_mac1, \ + mac_dst=dut_rt_int_mac1, ipv6_src_addr=data.ip6_addr[0], + ipv6_dst_addr=data.ip6_addr[9]) + st.log("TRAFCONF: " + str(tr1)) + + res = tg.tg_traffic_control(action='run', stream_handle=tr1['stream_id']) + st.log("TR_CTRL: " + str(res)) + tg.tg_traffic_control(action='stop', stream_handle=tr1['stream_id']) + st.log("Checking the stats and verifying the traffic flow") + traffic_details = { + '1': { + 'tx_ports' : [vars.T1D1P2], + 'tx_obj' : [tg_handler["tg"]], + 'exp_ratio' : [1], + 'rx_ports' : [vars.T1D2P2], + 'rx_obj' : [tg_handler["tg"]], + } + } + # verify statistics + aggrResult = tgapi.validate_tgen_traffic(traffic_details=traffic_details, mode='aggregate', comp_type='packet_count') + if not aggrResult: + return False + else: + return True + +def test_l3_v6_route_po_1(): + dut = vars.D1 + asicapi.dump_vlan(dut) + asicapi.dump_l2(dut) + asicapi.dump_trunk(dut) + + ret = create_v6_route(30000) + if (ret): + st.report_pass("test_case_passed") + else: + st.report_fail("test_case_failed") + +@pytest.mark.ip_basic_ping +@pytest.mark.community +@pytest.mark.community_fail +def test_ft_ping_v4_v6_vlan(): + # Objective - Verify that IPv6 & Ipv4 ping is successful over vlan routing interfaces. + st.log("Checking IPv4 ping from {} to {} over vlan routing interface".format(vars.D1, vars.D2)) + if not ipfeature.ping(vars.D1, data.ip4_addr[3], family=data.af_ipv4, count=1): + st.report_fail("ping_fail",data.ip4_addr[2], data.ip4_addr[3]) + st.log("Checking IPv6 ping from {} to {} over vlan routing interface".format(vars.D1, vars.D2)) + if not ipfeature.ping(vars.D2, data.ip6_addr[2], family=data.af_ipv6, count=1): + st.report_fail("ping_fail",data.ip6_addr[3], data.ip6_addr[2]) + st.report_pass("test_case_passed") + + +@pytest.mark.ip_basic_ping +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_ping__v4_v6_after_ip_change_pc(): + # Objective - Verify that ping is successful between L3 interfaces when Ip address is removed and new ip + # is assigned + st.log("In {} check portchannel is UP or not".format(vars.D2)) + if not pc_obj.verify_portchannel_state(vars.D2, data.port_channel, state="up"): + st.report_fail("portchannel_state_fail", data.port_channel, vars.D2, "Up") + st.log("Checking IPv4 ping from {} to {} over portchannel routing interface".format(vars.D1, vars.D2)) + if not ipfeature.ping(vars.D1, data.ip4_addr[5], family=data.af_ipv4, count=1): + st.report_fail("ping_fail",data.ip4_addr[4], data.ip4_addr[5]) + st.log("Checking IPv6 ping from {} to {} over portchannel routing interface".format(vars.D1, vars.D2)) + if not ipfeature.ping(vars.D2, data.ip6_addr[4], family=data.af_ipv6, count=1): + st.report_fail("ping_fail",data.ip6_addr[5], data.ip6_addr[4]) + st.log("Removing the Ipv4 address on portchannel") + ipfeature.delete_ip_interface(vars.D1, data.port_channel, data.ip4_addr[4],24, family = data.af_ipv4) + ipfeature.delete_ip_interface(vars.D2, data.port_channel, data.ip4_addr[5], 24, family = data.af_ipv4) + st.log("Removing the Ipv6 address on portchannel") + ipfeature.delete_ip_interface(vars.D1, data.port_channel, data.ip6_addr[4], 96, family = data.af_ipv6) + ipfeature.delete_ip_interface(vars.D2, data.port_channel, data.ip6_addr[5], 96, family = data.af_ipv6) + st.log("configuring new Ipv4 address on portchannel") + ipfeature.config_ip_addr_interface(vars.D1, data.port_channel, data.ip4_addr[10], 24, family=data.af_ipv4) + ipfeature.config_ip_addr_interface(vars.D2, data.port_channel, data.ip4_addr[11], 24, family=data.af_ipv4) + st.log("configuring new Ipv6 address on portchannel") + ipfeature.config_ip_addr_interface(vars.D1, data.port_channel, data.ip6_addr[10], + 96, family = data.af_ipv6) + ipfeature.config_ip_addr_interface(vars.D2, data.port_channel, data.ip6_addr[11], 96, family=data.af_ipv6) + st.log("After Ipv4 address change, checking IPv4 ping from {} to {} over portchannel " + "routing interface".format(vars.D1, vars.D2)) + if not ipfeature.ping(vars.D1, data.ip4_addr[11], family=data.af_ipv4, count=1): + st.report_fail("ping_fail",data.ip4_addr[10],data.ip4_addr[11]) + st.log("After Ipv6 address change, checking IPv6 ping from {} to {} over portchannel " + "routing interface".format(vars.D1, vars.D2)) + if not ipfeature.ping(vars.D1, data.ip6_addr[11], family=data.af_ipv6, count=1): + st.report_fail("ping_fail",data.ip6_addr[10], data.ip6_addr[11]) + st.report_pass("test_case_passed") + +@ pytest.mark.ip6_basic +def test_ft_ip6_static_route_traffic_forward_blackhole(): + # Objective - Verify the Ipv6 traffic forwarding over static route. + tg_handler = tgapi.get_handles_byname("T1D1P2", "T1D2P2") + tg = tg_handler["tg"] + tg.tg_traffic_control(action="reset", port_handle=tg_handler["tg_ph_list"]) + tg.tg_traffic_control(action="clear_stats", port_handle=tg_handler["tg_ph_list"]) + + dut_rt_int_mac1 = basic_obj.get_ifconfig_ether(vars.D1, vars.D1T1P2) + h1 = tg.tg_interface_config(port_handle=tg_handler["tg_ph_1"], mode='config', ipv6_intf_addr=data.ip6_addr[0], \ + ipv6_prefix_length='64', ipv6_gateway=data.ip6_addr[1], + src_mac_addr=data.tg_mac1, arp_send_req='1') + st.log("INTFCONF: " + str(h1)) + h2 = tg.tg_interface_config(port_handle=tg_handler["tg_ph_2"], mode='config', ipv6_intf_addr=data.ip6_addr[9], \ + ipv6_prefix_length='64', ipv6_gateway=data.ip6_addr[8], + src_mac_addr=data.tg_mac2, arp_send_req='1') + st.log("INTFCONF: " + str(h2)) + + # Ping from tgen to DUT. + res = tgapi.verify_ping(src_obj=tg, port_handle=tg_handler["tg_ph_1"], dev_handle=h1['handle'], dst_ip=data.ip6_addr[1], \ + ping_count='1', exp_count='1') + if res: + st.log("Ping succeeded.") + else: + st.warn("Ping failed.") + + tr1 = tg.tg_traffic_config(port_handle=tg_handler["tg_ph_1"], mode='create', transmit_mode='single_burst', + pkts_per_burst=2000, \ + length_mode='fixed', rate_pps=2000, l3_protocol='ipv6', mac_src=data.tg_mac1, \ + mac_dst=dut_rt_int_mac1, ipv6_src_addr=data.ip6_addr[0], + ipv6_dst_addr=data.ip6_addr[9]) + st.log("TRAFCONF: " + str(tr1)) + + res = tg.tg_traffic_control(action='run', stream_handle=tr1['stream_id']) + st.log("TR_CTRL: " + str(res)) + tg.tg_traffic_control(action='stop', stream_handle=tr1['stream_id']) + st.log("Checking the stats and verifying the traffic flow") + traffic_details = { + '1': { + 'tx_ports' : [vars.T1D1P2], + 'tx_obj' : [tg_handler["tg"]], + 'exp_ratio' : [1], + 'rx_ports' : [vars.T1D2P2], + 'rx_obj' : [tg_handler["tg"]], + } + } + # verify statistics + aggrResult = tgapi.validate_tgen_traffic(traffic_details=traffic_details, mode='aggregate', comp_type='packet_count') + if not aggrResult: + st.report_fail("traffic_verification_failed") + ipfeature.delete_static_route(vars.D1, data.ip6_addr[7], data.static_ip6_rt, shell=data.shell_vtysh, + family=data.af_ipv6) + st.log("Create a static route with nexthop as blackhole") + ipfeature.create_static_route(vars.D1, data.static_ip6_rt_drop, data.static_ip6_rt, shell=data.shell_vtysh, + family=data.af_ipv6) + tg.tg_traffic_control(action="clear_stats", port_handle=tg_handler["tg_ph_list"]) + + res = tg.tg_traffic_control(action='run', stream_handle=tr1['stream_id']) + st.log("TR_CTRL: " + str(res)) + tg.tg_traffic_control(action='stop', stream_handle=tr1['stream_id']) + st.log("Checking the stats and verifying the traffic flow") + traffic_details = { + '1': { + 'tx_ports' : [vars.T1D1P2], + 'tx_obj' : [tg_handler["tg"]], + 'exp_ratio' : [1], + 'rx_ports' : [vars.T1D2P2], + 'rx_obj' : [tg_handler["tg"]], + } + } + + # verify statistics + aggrResult = tgapi.validate_tgen_traffic(traffic_details=traffic_details, mode='aggregate', comp_type='packet_count') + if aggrResult: + st.report_fail("traffic_verification_failed") + st.report_pass("test_case_passed") + + +@pytest.mark.ip_basic13 +def test_ft_ip_static_route_traffic_forward(): + # Objective - Verify the Ipv4 traffic forwarding over IPv4 static route. + tg_handler = tgapi.get_handles_byname("T1D1P1", "T1D2P1") + tg = tg_handler["tg"] + tg.tg_traffic_control(action="reset", port_handle=tg_handler["tg_ph_list"]) + tg.tg_traffic_control(action="clear_stats", port_handle=tg_handler["tg_ph_list"]) + + dut_rt_int_mac1 = basic_obj.get_ifconfig_ether(vars.D1, vars.D1T1P1) + h1 = tg.tg_interface_config(port_handle=tg_handler["tg_ph_1"], mode='config', intf_ip_addr=data.ip4_addr[0], \ + gateway=data.ip4_addr[1], src_mac_addr=data.tg_mac1, arp_send_req='1') + st.log("INTFCONF: " + str(h1)) + h2 = tg.tg_interface_config(port_handle=tg_handler["tg_ph_2"], mode='config', intf_ip_addr=data.ip4_addr[9], \ + gateway=data.ip4_addr[8], src_mac_addr=data.tg_mac2, arp_send_req='1') + st.log("INTFCONF: " + str(h2)) + # Ping from tgen to DUT. + res = tgapi.verify_ping(src_obj=tg, port_handle=tg_handler["tg_ph_1"], dev_handle=h1['handle'], dst_ip=data.ip4_addr[1], \ + ping_count='1', exp_count='1') + if res: + st.log("Ping succeeded.") + else: + st.warn("Ping failed.") + tr1 = tg.tg_traffic_config(port_handle=tg_handler["tg_ph_1"], mode='create', transmit_mode='single_burst', + pkts_per_burst=2000, \ + length_mode='fixed', rate_pps=2000, l3_protocol='ipv4', mac_src=data.tg_mac1, \ + mac_dst=dut_rt_int_mac1, ip_src_addr=data.ip4_addr[0], + ip_dst_addr=data.ip4_addr[9]) + st.log("TRAFCONF: " + str(tr1)) + res = tg.tg_traffic_control(action='run', stream_handle=tr1['stream_id']) + st.log("TR_CTRL: " + str(res)) + tg.tg_traffic_control(action='stop', stream_handle=tr1['stream_id']) + st.log("Checking the stats and verifying the traffic flow") + traffic_details = { + '1': { + 'tx_ports' : [vars.T1D1P1], + 'tx_obj' : [tg_handler["tg"]], + 'exp_ratio' : [1], + 'rx_ports' : [vars.T1D2P1], + 'rx_obj' : [tg_handler["tg"]], + } + } + #verify statistics + aggrResult = tgapi.validate_tgen_traffic(traffic_details=traffic_details, mode='aggregate', comp_type='packet_count') + if not aggrResult: + st.report_fail("traffic_verification_failed") + st.report_pass("test_case_passed") + + +@pytest.mark.ip_basic_L2_L3_translation +def test_ft_ip_v4_v6_L2_L3_translation(): + # Objective - Verify that L2 port to IPv4 L3 port transition and vice-versa is successful. + st.log("Checking IPv4 ping from {} to {} over routing interface".format(vars.D1, vars.D2)) + if not ipfeature.ping(vars.D1, data.ip4_addr[7], family=data.af_ipv4, count=1): + st.report_fail("ping_fail",data.ip4_addr[6], data.ip4_addr[7]) + st.log("Checking IPv6 ping from {} to {} over vlan routing interface".format(vars.D1, vars.D2)) + if not ipfeature.ping(vars.D2, data.ip6_addr[6], family=data.af_ipv6, count=1): + st.report_fail("ping_fail",data.ip6_addr[7], data.ip6_addr[6]) + st.log("L3 to L2 port transition") + st.log("Removing ipv4,ipv6 address from interface") + ipfeature.delete_ip_interface(vars.D1, vars.D1D2P4, data.ip4_addr[6], 24, family=data.af_ipv4) + ipfeature.delete_ip_interface(vars.D2, vars.D2D1P4, data.ip4_addr[7], 24, family=data.af_ipv4) + ipfeature.delete_ip_interface(vars.D1, vars.D1D2P4, data.ip6_addr[6], 96, family=data.af_ipv6) + ipfeature.delete_ip_interface(vars.D2, vars.D2D1P4, data.ip6_addr[7], 96, family=data.af_ipv6) + ipfeature.delete_ip_interface(vars.D1, vars.D1T1P1, data.ip4_addr[1], 24, family=data.af_ipv4) + ipfeature.delete_ip_interface(vars.D2, vars.D2T1P1, data.ip4_addr[8], 24, family=data.af_ipv4) + st.log("Removing the static routes") + ipfeature.delete_static_route(vars.D1, data.ip4_addr[7], data.static_ip_rt, shell=data.shell_vtysh, family=data.af_ipv4) + ipfeature.delete_static_route(vars.D1, data.static_ip6_rt_drop, data.static_ip6_rt, shell=data.shell_vtysh, family=data.af_ipv6) + st.log("Vlan creation and port association configuration") + vlan_obj.create_vlan(vars.D1, data.vlan_2) + st.log("Adding back to back connecting ports to vlan {}".format(data.vlan_2)) + vlan_obj.add_vlan_member(vars.D1, data.vlan_2, [vars.D1D2P4], tagging_mode=True) + vlan_obj.create_vlan(vars.D2, data.vlan_2) + vlan_obj.add_vlan_member(vars.D2, data.vlan_2, [vars.D2D1P4], tagging_mode=True) + st.log("Adding TG connecting ports to vlan {}".format(data.vlan_1)) + vlan_obj.add_vlan_member(vars.D1, data.vlan_2, vars.D1T1P1, tagging_mode=True) + vlan_obj.add_vlan_member(vars.D2, data.vlan_2, vars.D2T1P1, tagging_mode=True) + tg_handler = tgapi.get_handles_byname("T1D1P1", "T1D2P1") + tg.tg_traffic_control(action="reset", port_handle=tg_handler["tg_ph_list"]) + tg.tg_traffic_control(action="clear_stats", port_handle=tg_handler["tg_ph_list"]) + + tr2 = tg.tg_traffic_config(port_handle=tg_handler["tg_ph_2"], mode='create', rate_pps="2000", + mac_src_mode="fixed", + transmit_mode="single_burst", pkts_per_burst=2000, + length_mode='fixed', l2_encap='ethernet_ii_vlan', + vlan_id=data.vlan_2, mac_dst_mode="fixed", + vlan="enable", + mac_src="00:a1:bb:cc:dd:01", + mac_dst="00:b1:bb:cc:dd:01") + st.log("TRAFCONF: " + str(tr2)) + res = tg.tg_traffic_control(action='run', stream_handle=tr2['stream_id']) + tg.tg_traffic_control(action='stop', stream_handle=tr2['stream_id']) + st.wait(data.wait_tgstats) + st.log("TR_CTRL: " + str(res)) + st.log("Fetching IXIA statistics") + stats_tg1 = tgapi.get_traffic_stats(tg_handler["tg"], mode="aggregate", port_handle=tg_handler["tg_ph_2"]) + total_tx_tg1 = stats_tg1.tx.total_packets + stats_tg2 = tgapi.get_traffic_stats(tg_handler["tg"], mode="aggregate", port_handle=tg_handler["tg_ph_1"]) + total_rx_tg2 = stats_tg2.rx.total_packets + st.log("total_tx_tg1 = {}".format(total_tx_tg1)) + total_tx_tg1_95_percentage = int(total_tx_tg1) * 0.95 + st.log("total_tx_tg1_95_percentage= {}".format(total_tx_tg1_95_percentage)) + st.log("total_rx_tg2 = {}".format(total_rx_tg2)) + if not int(total_tx_tg1_95_percentage) <= int(total_rx_tg2): + st.report_fail("traffic_verification_failed") + st.log("Removing vlan configuration") + vlan_obj.delete_vlan_member(vars.D1, data.vlan_2, [vars.D1D2P4, vars.D1T1P1]) + vlan_obj.delete_vlan_member(vars.D2, data.vlan_2, [vars.D2D1P4, vars.D2T1P1]) + st.log("L2 to L3 port transition") + ipfeature.config_ip_addr_interface(vars.D1, vars.D1D2P4, data.ip4_addr[6], 24, family=data.af_ipv4) + ipfeature.config_ip_addr_interface(vars.D2, vars.D2D1P4, data.ip4_addr[7], 24, family=data.af_ipv4) + ipfeature.create_static_route(vars.D1, data.ip4_addr[7], data.static_ip_rt, shell=data.shell_vtysh, family=data.af_ipv4) + st.log("Checking IPv4 ping from {} to {} over routing interface".format(vars.D1, vars.D2)) + if not ipfeature.ping(vars.D1, data.ip4_addr[7], family=data.af_ipv4, count=1): + st.report_fail("ping_fail",data.ip4_addr[6], data.ip4_addr[7]) + ipfeature.config_ip_addr_interface(vars.D1, vars.D1D2P4, data.ip6_addr[6], 96, family=data.af_ipv6) + ipfeature.config_ip_addr_interface(vars.D2, vars.D2D1P4, data.ip6_addr[7], 96, family=data.af_ipv6) + ipfeature.create_static_route(vars.D1, data.static_ip6_rt_drop, data.static_ip6_rt, shell=data.shell_vtysh, family=data.af_ipv6) + st.log("Checking IPv6 ping from {} to {} over vlan routing interface".format(vars.D1, vars.D2)) + if not ipfeature.ping(vars.D2, data.ip6_addr[6], family=data.af_ipv6, count=1): + st.report_fail("ping_fail",data.ip6_addr[7], data.ip6_addr[6]) + st.report_pass("test_case_passed") + + +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_verify_interfaces_order(): + ''' + @author: Ramprakash Reddy (ramprakash-reddy.kanala@broadcom.com) + ipv4_intf_order : Verify order of interfaces in "show ip interfaces" + ipv6_intf_order : Verify order of interfaces in "show ipv6 interfaces' + Verify order of interfaces in "show ip/ipv6 interfaces" in sorted order or not + + :return: + ''' + flag = 1 + st.log("This test is to ensure that interfaces are listed in sorted order by 'interface name' in 'show ip/ipv6 " + "interfaces'") + free_ports = st.get_free_ports(vars.D1) + if len(free_ports) < data.no_of_ports: + data.no_of_ports = len(free_ports) + req_ports = random.sample(free_ports, data.no_of_ports) + ipv4_addr = data.ip4_addr[11]+'/'+data.ipv4_mask + ipv6_addr = data.ip6_addr[0]+'/'+data.ipv6_mask + intf_list = [] + for i in range(int(math.ceil(float(data.no_of_ports)/2))): + _, ipv4_addr = ipfeature.increment_ip_addr(ipv4_addr, "network") + ipfeature.config_ip_addr_interface(vars.D1, interface_name=req_ports[i], ip_address=ipv4_addr.split('/')[0], + subnet=data.ipv4_mask, family="ipv4") + for i in range(int(math.floor(float(data.no_of_ports)/2))): + _, ipv6_addr = ipfeature.increment_ip_addr(ipv6_addr, "network", family="ipv6") + ipfeature.config_ip_addr_interface(vars.D1, interface_name=req_ports[i+int(math.ceil(float(data.no_of_ports)/2))], + ip_address=ipv6_addr.split('/')[0], subnet=data.ipv6_mask, family="ipv6") + output = ipfeature.get_interface_ip_address(vars.D1) + for each in output: + intf_list.append(each['interface']) + temp = lambda text: int(text) if text.isdigit() else text + alphanum_key = lambda key: [temp(c) for c in re.split('([0-9]+)', key)] + intf_list_sorted = sorted(intf_list, key=alphanum_key) + if intf_list == intf_list_sorted: + st.log("Ipv4 interfaces are in sorted order") + else: + st.error("Ipv4 interfaces are not in soretd order") + flag = 0 + del intf_list[:] + del intf_list_sorted[:] + output = ipfeature.get_interface_ip_address(vars.D1, family="ipv6") + for each in output: + intf_list.append(each['interface']) + temp = lambda text: int(text) if text.isdigit() else text + alphanum_key = lambda key: [temp(c) for c in re.split('([0-9]+)', key)] + intf_list_sorted = sorted(intf_list, key=alphanum_key) + if intf_list == intf_list_sorted: + st.log("Ipv6 interfaces are in sorted order") + else: + st.error("Ipv6 interfaces are not in soretd order") + flag = 0 + #Unconfig + ipv4_addr = data.ip4_addr[11] + '/' + data.ipv4_mask + ipv6_addr = data.ip6_addr[0] + '/' + data.ipv6_mask + for i in range(int(math.ceil(float(data.no_of_ports)/2))): + _, ipv4_addr = ipfeature.increment_ip_addr(ipv4_addr, "network") + ipfeature.delete_ip_interface(vars.D1, interface_name=req_ports[i], ip_address=ipv4_addr.split('/')[0], + subnet=data.ipv4_mask, family="ipv4") + for i in range(int(math.floor(float(data.no_of_ports)/2))): + _, ipv6_addr = ipfeature.increment_ip_addr(ipv6_addr, "network", family="ipv6") + ipfeature.delete_ip_interface(vars.D1, interface_name=req_ports[i+int(math.ceil(float(data.no_of_ports)/2))], + ip_address=ipv6_addr.split('/')[0], subnet=data.ipv6_mask, family="ipv6") + if flag == 0: + st.report_fail("test_case_failed") + st.report_pass("test_case_passed") diff --git a/spytest/tests/routing/test_l3_scale_ecmp_paths.py b/spytest/tests/routing/test_l3_scale_ecmp_paths.py new file mode 100644 index 00000000000..ce353c48e6e --- /dev/null +++ b/spytest/tests/routing/test_l3_scale_ecmp_paths.py @@ -0,0 +1,1084 @@ +import os +import pytest +from collections import OrderedDict + +from spytest import st, tgapi, SpyTestDict +from spytest.utils import filter_and_select + +import apis.switching.vlan as vapi +import apis.routing.ip as ipfeature +import apis.switching.mac as macapi +import apis.system.port as papi +import apis.routing.bgp as bgpfeature + +def clear_arp_entries(dut): + """ + This proc is to clear arp entries of the dut. + :param dut: DUT Number + :return: + """ + st.config(dut, "sonic-clear arp".format()) + return + +def verify_ip_from_vlan_interface( dut, port): + """ + + :param port: + :type port: + :param ipaddr: + :type ipaddr: + :param dut: + :type dut: + :return: + :rtype: + """ + output = st.show(dut, "show vlan brief") + match = {"VLAN ID": port} + entries = filter_and_select(output, ["IP Address"], match) + return entries + +def trigger_link_flap(dut, port): + """ + + :param dut: + :type dut: + :param port: + :type port: + :return: + :rtype: + """ + st.config(dut, "config interface {} {}".format("shutdown", port)) + st.wait(5) + st.config(dut, "config interface {} {}".format("startup", port)) + st.wait(5) + +def verify_ping(src_obj,port_handle,dev_handle,dst_ip,ping_count=5,exp_count=5): + ping_count,exp_count = int(ping_count),int(exp_count) + if src_obj.tg_type == 'stc': + result = src_obj.tg_emulation_ping(handle=dev_handle,host=dst_ip,count=ping_count) + print("ping output: %s" % (result)) + return True if int(result['tx']) == ping_count and int(result['rx']) == exp_count else False + return True + +def get_handles(): + tg1, tg_ph_1 = tgapi.get_handle_byname("T1D1P3") + tg2, tg_ph_2 = tgapi.get_handle_byname("T1D1P4") + return (tg1, tg_ph_1, tg2, tg_ph_2) + +def get_handles_1(): + tg1, tg_ph_1 = tgapi.get_handle_byname("T1D1P1") + tg2, tg_ph_2 = tgapi.get_handle_byname("T1D1P2") + tg3, tg_ph_3 = tgapi.get_handle_byname("T1D1P3") + tg4, tg_ph_4 = tgapi.get_handle_byname("T1D1P4") + return (tg1, tg_ph_1, tg2, tg_ph_2, tg_ph_3, tg_ph_4) + +def get_handles_2(): + tg1, tg_ph_1 = tgapi.get_handle_byname("T1D1P1") + tg2, tg_ph_2 = tgapi.get_handle_byname("T1D2P1") + return (tg1, tg_ph_1, tg2, tg_ph_2) + +data = SpyTestDict() + +@pytest.fixture(scope="module", autouse=True) +def l3_scale_ecmp_paths_module_hooks(request): + #add things at the start of this module + global vars + vars = st.ensure_min_topology("D1D2:4","D1T1:4","D2T1:1") + data.start_ip_addr = "10.2.100.1/24" + data.vlans = [] + data.dut = vars.D1 + data.dut1_start_ip_addr = "10.2.2.1/24" + data.dut2_start_ip_addr = "10.2.2.2/24" + data.v6_start_ip_addr = "2100:0:2::1/64" + data.v6_dut2_start_ip_addr = "2100:0:2::2/64" + data.v6_new_dut2_start_ip_addr = "2200:0:2::2/64" + data.neigh_ip_addr = "10.2.2.2/24" + data.start_ip_addr2 = "11.11.1.2/24" + data.nexthop_start_ip_addr = "10.2.100.10/32" + #data.nexthop_start_ip_addr = "10.2.101.10/32" + #data.static_route = "200.1.0.0/24" + #data.static_route = ["200.1.0.0/16"] + data.static_route = "200.1.0.0/16" + data.vlan_count = 16 + data.vlan_val = 100 + data.max_ecmp = 128 + data.base_val = 101 + data.src_ip_addr = "10.2.100.1" + data.edit_index = 4 + data.ip_prefixlen = 24 + data.all_ports = st.get_all_ports(data.dut) + data.free_member_ports = OrderedDict() + data.tg_member_ports = OrderedDict() + data.d1t1_ip_addr = "10.2.106.1" + data.t1d1_ip_addr = "10.2.106.2" + data.d1_ip_addr = "11.11.6.1/24" + data.d2_ip_addr = "11.11.6.2/24" + data.tg_start_ip_addr = "10.2.101.10/24" + data.thresh = 12 + data.dut1_ports = [vars.D1D2P1,vars.D1D2P2, vars.D1D2P3, vars.D1D2P4] + data.dut2_ports = [vars.D2D1P1,vars.D2D1P2, vars.D2D1P3, vars.D2D1P4] + data.as_num = 100 + data.remote_as_num = 200 + data.new_as_num = 300 + data.routemap = "preferGlobal" + data.vrf = "Vrf-Green" + + # create required random vlans excluding existing vlans + + # create required random vlans excluding existing vlans + + yield + +def check_end_to_end_intf_traffic_counters(): + dut1 = vars.D1 + DUT_tx_value = papi.get_interface_counters(dut1, vars.D1T1P1, "tx_ok") + for i in DUT_tx_value: + p1_tx = i['tx_ok'] + p1_tx = p1_tx.replace(",","") + st.log("tx_ok xounter value on DUT Inress port : {}".format(p1_tx)) + if (abs(int(float(p1_tx))) > 0): + output = papi.get_interface_counters_all(dut1) + entry1 = filter_and_select(output, ["tx_bps"], {'iface': vars.D1T1P1}) + for i in entry1: + p1_txmt = i['tx_bps'] + p1_txmt = p1_txmt.replace(" MB/s","") + p1_txmt = p1_txmt.replace(" KB/s","") + p1_txmt = p1_txmt.replace(" B/s","") + if (abs(int(float(p1_txmt))) == 0): + output = st.show(dut1, "show arp") + return False + else: + st.log("End to End traffic is fine") + return True + else: + return False + +def intf_traffic_stats(entry_tx): + for i in entry_tx: + p_txmt = i['tx_bps'] + p_txmt = p_txmt.replace(" MB/s","") + p_txmt = p_txmt.replace(" KB/s","") + p_txmt = p_txmt.replace(" B/s","") + + p_tx = abs(int(float(p_txmt))) + return p_tx + + + +def check_inter_dut_intf_traffic_counters(): + dut2 = vars.D2 + (dut1) = (data.dut) + papi.clear_interface_counters(dut2) + papi.clear_interface_counters(dut1) + st.wait(5) + output = papi.get_interface_counters_all(dut2) + p1_tx = intf_traffic_stats(filter_and_select(output, ["tx_bps"], {'iface': vars.D2D1P1})) + p2_tx = intf_traffic_stats(filter_and_select(output, ["tx_bps"], {'iface': vars.D2D1P2})) + p3_tx = intf_traffic_stats(filter_and_select(output, ["tx_bps"], {'iface': vars.D2D1P3})) + p4_tx = intf_traffic_stats(filter_and_select(output, ["tx_bps"], {'iface': vars.D2D1P4})) + st.log("Inter Dut port stats tx_ok xounter value on DUT Egress ports : {} {} {} {}".format(p1_tx, p2_tx, p3_tx, p4_tx)) + if (p1_tx == 0) | (p2_tx == 0) | (p3_tx == 0) | (p4_tx == 0): + st.log("Error:Inter Dut port stats tx_ok xounter value on DUT Egress ports : {} {} {} {}".format(p1_tx, p2_tx, p3_tx, p4_tx)) + else: + return True + + DUT_rx_value = papi.get_interface_counters(dut2, vars.D2T1P1, "rx_ok") + for i in DUT_rx_value: + p1_rcvd = i['rx_ok'] + p1_rcvd = p1_rcvd.replace(",","") + + st.log("rx_ok xounter value on DUT Inress port : {}".format(p1_rcvd)) + + if (abs(int(float(p1_rcvd))) > 0): + output = papi.get_interface_counters_all(dut2) + entry1 = filter_and_select(output, ["tx_bps"], {'iface': vars.D2D1P1}) + entry2 = filter_and_select(output, ["tx_bps"], {'iface': vars.D2D1P2}) + entry3 = filter_and_select(output, ["tx_bps"], {'iface': vars.D2D1P3}) + entry4 = filter_and_select(output, ["tx_bps"], {'iface': vars.D2D1P4}) + for i in entry1: + p1_txmt = i['tx_bps'] + p1_txmt = p1_txmt.replace(" MB/s","") + p1_txmt = p1_txmt.replace(" KB/s","") + p1_txmt = p1_txmt.replace(" B/s","") + for i in entry2: + p2_txmt = i['tx_bps'] + p2_txmt = p2_txmt.replace(" MB/s","") + p2_txmt = p2_txmt.replace(" KB/s","") + p2_txmt = p2_txmt.replace(" B/s","") + for i in entry3: + p3_txmt = i['tx_bps'] + p3_txmt = p3_txmt.replace(" MB/s","") + p3_txmt = p3_txmt.replace(" KB/s","") + p3_txmt = p3_txmt.replace(" B/s","") + for i in entry4: + p4_txmt = i['tx_bps'] + p4_txmt = p4_txmt.replace(" MB/s","") + p4_txmt = p4_txmt.replace(" KB/s","") + p4_txmt = p4_txmt.replace(" B/s","") + + st.log("Inter Dut port stats tx_ok xounter value on DUT Egress ports : {} {} {} {}".format(p1_txmt, p2_txmt, p3_txmt, p4_txmt)) + if (abs(int(float(p1_txmt))) == 0) | (abs(int(float(p2_txmt))) == 0) | (abs(int(float(p3_txmt))) == 0) | (abs(int(float(p4_txmt))) == 0): + output = st.show(dut1, "show arp") + return False + else: + st.log("All ECMP paths are utilized") + return True + else: + return False + +def create_bgp_neighbor_route_map_config(dut, local_asn, neighbor_ip, routemap, vrf_flag): + command = "route-map {} permit 10".format(routemap) + st.vtysh_config(dut, command) + command = "set ipv6 next-hop prefer-global" + st.vtysh_config(dut, command) + +def create_bgp_neighbor_config(dut, local_asn, neighbor_ip, remote_asn, routemap, keep_alive=60, hold=180, password=None, family="ipv6"): + """ + + :param dut: + :param local_asn: + :param neighbor_ip: + :param remote_asn: + :param keep_alive: + :param hold: + :param password: + :param family: + :return: + """ + st.log("Creating BGP neighbor ..") + # Add validation for IPV4 / IPV6 address + # config_router_bgp_mode(dut, local_asn) + + command = "neighbor {} remote-as {}".format(neighbor_ip, remote_asn) + st.vtysh_config(dut, command) + command = "neighbor {} timers {} {}".format(neighbor_ip, keep_alive, hold) + st.vtysh_config(dut, command) + if password: + command = " neighbor {} password {}".format(neighbor_ip, password) + st.vtysh_config(dut, command) + # Gather the IP type using the validation result + # ipv6 = False + if family == "ipv6": + command = "address-family ipv6 unicast" + st.vtysh_config(dut, command) + command = "neighbor {} activate".format(neighbor_ip) + st.vtysh_config(dut, command) + command = "neighbor {} route-map {} in".format(neighbor_ip, routemap) + st.vtysh_config(dut, command) + command = "neighbor {} route-map {} out".format(neighbor_ip, routemap) + return True + + + + +def check_intf_traffic_counters(): + (dut1) = (data.dut) + papi.clear_interface_counters(dut1) + st.wait(5) + DUT_tx_value = papi.get_interface_counters(dut1, vars.D1T1P4, "tx_bps") + + for i in DUT_tx_value: + p2_txmt = i['tx_bps'] + p2_txmt = p2_txmt.replace(" MB/s","") + p2_txmt = p2_txmt.replace(" KB/s","") + p2_txmt = p2_txmt.replace(" B/s","") + + st.log("tx_ok xounter value on DUT Egress port : {}".format(p2_txmt)) + + if (abs(int(float(p2_txmt))) == 0): + output = papi.get_interface_counters_all(dut1) + entry1 = filter_and_select(output, ["tx_bps"], {'iface': vars.D1T1P4}) + for i in entry1: + p2_txmt = i['tx_bps'] + p2_txmt = p2_txmt.replace(" MB/s","") + p2_txmt = p2_txmt.replace(" KB/s","") + p2_txmt = p2_txmt.replace(" B/s","") + st.log("RETRY tx_ok xounter value on DUT Egress port : {}".format(p2_txmt)) + if (abs(int(float(p2_txmt))) == 0): + output = st.show(dut1, "show arp") + command1 = "bcmcmd 'l3 ecmp egress show'" + rv = st.config(dut1, command1) + return False + else: + return True + else: + return True + +def check_intf_traffic_bo_counters(): + (dut1) = (data.dut) + papi.clear_interface_counters(dut1) + st.wait(5) + output = papi.get_interface_counters_all(dut1) + p1_tx = intf_traffic_stats(filter_and_select(output, ["tx_bps"], {'iface': vars.D1T1P1})) + p2_tx = intf_traffic_stats(filter_and_select(output, ["tx_bps"], {'iface': vars.D1T1P2})) + p3_tx = intf_traffic_stats(filter_and_select(output, ["tx_bps"], {'iface': vars.D1T1P3})) + p4_tx = intf_traffic_stats(filter_and_select(output, ["tx_bps"], {'iface': vars.D1T1P4})) + st.log("Inter Dut port stats tx_ok xounter value on DUT Egress ports : {} {} {} {}".format(p1_tx, p2_tx, p3_tx, p4_tx)) + if (p2_tx == 0) | (p3_tx == 0) | (p4_tx == 0): + st.log("Error:Inter Dut port stats tx_ok xounter value on DUT Egress ports : {} {} {}".format(p2_tx, p3_tx, p4_tx)) + else: + return True + + DUT_rx_value = papi.get_interface_counters(dut1, vars.D1T1P4, "rx_ok") + for i in DUT_rx_value: + p1_rcvd = i['rx_ok'] + p1_rcvd = p1_rcvd.replace(",","") + + st.log("rx_ok xounter value on DUT Inress port : {}".format(p1_rcvd)) + + if (abs(int(float(p1_rcvd))) > 0): + output = papi.get_interface_counters_all(dut1) + entry1 = filter_and_select(output, ["tx_bps"], {'iface': vars.D1T1P1}) + entry2 = filter_and_select(output, ["tx_bps"], {'iface': vars.D1T1P2}) + entry3 = filter_and_select(output, ["tx_bps"], {'iface': vars.D1T1P3}) + entry4 = filter_and_select(output, ["tx_bps"], {'iface': vars.D1T1P4}) + for i in entry1: + p1_txmt = i['tx_bps'] + p1_txmt = p1_txmt.replace(" MB/s","") + p1_txmt = p1_txmt.replace(" KB/s","") + p1_txmt = p1_txmt.replace(" B/s","") + for i in entry2: + p2_txmt = i['tx_bps'] + p2_txmt = p2_txmt.replace(" MB/s","") + p2_txmt = p2_txmt.replace(" KB/s","") + p2_txmt = p2_txmt.replace(" B/s","") + for i in entry3: + p3_txmt = i['tx_bps'] + p3_txmt = p3_txmt.replace(" MB/s","") + p3_txmt = p3_txmt.replace(" KB/s","") + p3_txmt = p3_txmt.replace(" B/s","") + for i in entry4: + p4_txmt = i['tx_bps'] + p4_txmt = p4_txmt.replace(" MB/s","") + p4_txmt = p4_txmt.replace(" KB/s","") + p4_txmt = p4_txmt.replace(" B/s","") + + st.log("RETRY tx_ok xounter value on DUT Egress ports : {} {} {} {}".format(p1_txmt, p2_txmt, p3_txmt, p4_txmt)) + if (abs(int(float(p2_txmt))) == 0) | (abs(int(float(p3_txmt))) == 0) | (abs(int(float(p4_txmt))) == 0): + output = st.show(dut1, "show arp") + return False + else: + return True + else: + return False + + + +def l3_max_route_max_path_scaling_tc(max_paths, max_routes, use_config_file, family="ipv4"): + (dut) = (data.dut) + count = 0 + data.my_dut_list = st.get_dut_names() + dut1 = data.my_dut_list[0] + dut2 = data.my_dut_list[1] + intf_ip_addr = data.dut1_start_ip_addr + intf_ip_addr2 = data.start_ip_addr2 + nexthop = data.nexthop_start_ip_addr + vrf_flag = False + member_dut1 = vars.D1T1P1 + member_dut2 = vars.D2T1P1 + # L3 INTF SCALING TEST CASE 1.1 START + json_path = os.getcwd() + apply_file = False + + if apply_file == False: + ipfeature.clear_ip_configuration(st.get_dut_names()) + vapi.clear_vlan_configuration(st.get_dut_names()) + cmd = "config vlan range add 2 129" + st.config(dut, cmd) + st.config(dut2, cmd) + command = "config vlan member add 2 {}".format(member_dut1) + st.config(dut, command) + command = "config vlan member add 2 {}".format(member_dut2) + st.config(dut2, command) + max_vlan = max_paths/4 + base_vlan = 3 + max_vlan = max_vlan - base_vlan + v_range_t = str(base_vlan) + " " + str(base_vlan + max_vlan ) + vapi.config_vlan_range_members(dut1, v_range_t, data.dut1_ports[0]) + vapi.config_vlan_range_members(dut2, v_range_t, data.dut2_ports[0]) + base_range = 1 + max_range = 4 + max_vlan = max_paths/4 + incr_vlan = max_paths/4 + for index in range(base_range, max_range): + base_vlan = max_vlan + 1 + max_vlan = max_vlan + incr_vlan + #max_vlan = max_vlan + 32 + v_range_t = str(base_vlan) + " " + str(max_vlan) + vapi.config_vlan_range_members(dut1, v_range_t, data.dut1_ports[index]) + vapi.config_vlan_range_members(dut2, v_range_t, data.dut2_ports[index]) + + ip_addr = data.dut1_start_ip_addr + ip_addr2 = data.dut2_start_ip_addr + v6_ip_addr = data.v6_start_ip_addr + v6_ip_addr2 = data.v6_new_dut2_start_ip_addr + ix_vlan_val = 2 + #command = "config interface ip add "+ "Vlan" + str(data.vlan_val) + " " + ip_addr+'/24' + if family == "ipv4": + command1 = "config interface ip add "+ "Vlan" + str(ix_vlan_val) + " " + ip_addr + command2 = "config interface ip add "+ "Vlan" + str(ix_vlan_val) + " " + ip_addr2 + else: + command1 = "config interface ip add "+ "Vlan" + str(ix_vlan_val) + " " + v6_ip_addr + command2 = "config interface ip add "+ "Vlan" + str(ix_vlan_val) + " " + v6_ip_addr2 + rv = st.config(dut1, command1) + rv = st.config(dut2, command2) + ip_addr2 = data.dut2_start_ip_addr + base_vlan = 3 + max_vlan = max_paths + 1 + #max_vlan = 130 + for index in range(base_vlan, max_vlan): + if family == "ipv4": + (is_valid, ip_addr) = ipfeature.increment_ip_addr(ip_addr, "network") + (is_valid, ip_addr2) = ipfeature.increment_ip_addr(ip_addr2, "network") + command = "config interface ip add "+ "Vlan" + str(index) + " " + ip_addr + command_dut2 = "config interface ip add "+ "Vlan" + str(index) + " " + ip_addr2 + else: + v6_tok = str(hex(index)[2:]) + v6_ip_addr = "2100:0:" + v6_tok + "::1/64" + v6_ip_addr2 = "2100:0:" + v6_tok + "::2/64" + command = "config interface ip add "+ "Vlan" + str(index) + " " + v6_ip_addr + command_dut2 = "config interface ip add "+ "Vlan" + str(index) + " " + v6_ip_addr2 + rv = st.config(dut, command) + rv = st.config(dut2, command_dut2) + + + (tg1, tg_ph_1, tg2, tg_ph_2) = get_handles_2() + + tg1.tg_traffic_control(action='reset',port_handle=tg_ph_1) + tg2.tg_traffic_control(action='reset',port_handle=tg_ph_2) + + #h1=tg1.tg_interface_config(port_handle=tg_ph_1, mode='config', intf_ip_addr='10.2.2.2', gateway='10.2.2.1', arp_send_req='1') + #h1=tg1.tg_interface_config(port_handle=tg_ph_1, mode='config', intf_ip_addr='10.2.2.2', gateway='10.2.2.1', src_mac_addr='00:0a:01:00:00:01', vlan='1', vlan_id='2', arp_send_req='1', gateway_step='0.0.1.0', intf_ip_addr_step='0.0.1.0', vlan_id_step='1') + if family == "ipv4": + h1=tg1.tg_interface_config(port_handle=tg_ph_1, mode='config', intf_ip_addr='10.2.2.3', gateway='10.2.2.1', src_mac_addr='00:0a:01:00:00:01', vlan='1', vlan_id='2', arp_send_req='1') + output = st.show(dut, "show arp") + h2=tg1.tg_interface_config(port_handle=tg_ph_2, mode='config', intf_ip_addr='10.2.2.4', gateway='10.2.2.2', src_mac_addr='00:0b:01:00:00:01', vlan='1', vlan_id='2', arp_send_req='1') + output = st.show(dut2, "show arp") + else: + h1=tg1.tg_interface_config(port_handle=tg_ph_1, mode='config', ipv6_intf_addr='2100:0:2::3', ipv6_gateway='2100:0:2::1', src_mac_addr='00:0a:01:00:00:01', vlan='1', vlan_id='2', arp_send_req='1') + output = st.show(dut, "show ndp") + h2=tg1.tg_interface_config(port_handle=tg_ph_2, mode='config', ipv6_intf_addr='2200:0:2::4', ipv6_gateway='2200:0:2::2', src_mac_addr='00:0b:01:00:00:01', vlan='1', vlan_id='2', arp_send_req='1') + output = st.show(dut2, "show ndp") + + if family == "ipv4": + bgpfeature.create_bgp_router(dut, data.as_num, '') + bgpfeature.create_bgp_router(dut2, data.new_as_num, '') + dut1_neigh_ip_addr = data.neigh_ip_addr + dut2_neigh_ip_addr = data.dut1_start_ip_addr + formatted_dut1_neigh_ip_addr = dut1_neigh_ip_addr.replace("/24","") + formatted_dut2_neigh_ip_addr = dut2_neigh_ip_addr.replace("/24","") + formatted_dut1_neigh_ip_addr = "10.2.2.3" + bgpfeature.create_bgp_neighbor(dut, data.as_num, formatted_dut1_neigh_ip_addr, data.remote_as_num) + + (is_valid, dut1_neigh_ip_addr) = ipfeature.increment_ip_addr(dut1_neigh_ip_addr, "network") + (is_valid, dut2_neigh_ip_addr) = ipfeature.increment_ip_addr(dut2_neigh_ip_addr, "network") + base_vlan = 3 + max_vlan = max_paths + 1 + #max_vlan = 130 + # The below neighbor config is for inter dut links ibgp + for index in range(base_vlan, max_vlan): + formatted_dut1_neigh_ip_addr = dut1_neigh_ip_addr.replace("/24","") + bgpfeature.create_bgp_neighbor(dut, data.as_num, formatted_dut1_neigh_ip_addr, data.new_as_num) + (is_valid, dut1_neigh_ip_addr) = ipfeature.increment_ip_addr(dut1_neigh_ip_addr, "network") + formatted_dut2_neigh_ip_addr = dut2_neigh_ip_addr.replace("/24","") + bgpfeature.create_bgp_neighbor(dut2, data.new_as_num, formatted_dut2_neigh_ip_addr, data.as_num) + (is_valid, dut2_neigh_ip_addr) = ipfeature.increment_ip_addr(dut2_neigh_ip_addr, "network") + + conf_var = { 'mode' : 'enable', + 'active_connect_enable' : '1', + 'local_as' : '200', + 'remote_as' : '100', + 'remote_ip_addr' : '10.2.2.1' + } + max_route_str = str(max_routes) + route_var = { 'mode' : 'add', + 'num_routes' : max_route_str, + 'prefix' : '121.1.1.0', + 'as_path' : 'as_seq:1' + } + #'num_routes' : '30000', + ctrl_start = { 'mode' : 'start'} + ctrl_stop = { 'mode' : 'stop'} + + + # Configuring the BGP router. + bgp_rtr1 = tgapi.tg_bgp_config(tg = tg1, + handle = h1['handle'], + conf_var = conf_var, + route_var = route_var, + ctrl_var = ctrl_start) + + st.log("BGP_HANDLE: "+str(bgp_rtr1)) + # Verified at neighbor. + st.log("BGP neighborship established.") + st.wait(10) + command = "show bgp ipv4 summary" + st.config(dut, command) + st.config(dut2, command) + command = "show interface status" + st.config(dut, command) + command = "show ip route | head -1000" + st.config(dut, command) + st.config(dut2, command) + + output = st.show(dut, "show arp") + output = st.show(dut2, "show arp") + #Port Counters + + + tr1=tg2.tg_traffic_config(port_handle=tg_ph_2, emulation_src_handle=h2['handle'], emulation_dst_handle=bgp_rtr1['route'][0]['handle'], circuit_endpoint_type='ipv4', mode='create', transmit_mode='continuous', length_mode='fixed', rate_pps=512000, enable_stream_only_gen='0') + else: + v6_dut1_neigh_ip_addr = "2100:0:2::3" + create_bgp_neighbor_route_map_config(dut, data.as_num, v6_dut1_neigh_ip_addr, data.routemap, vrf_flag) + create_bgp_neighbor_route_map_config(dut2, data.new_as_num, v6_dut1_neigh_ip_addr, data.routemap, vrf_flag) + bgpfeature.create_bgp_router(dut, data.as_num, '') + create_bgp_neighbor_config(dut, data.as_num, v6_dut1_neigh_ip_addr, data.remote_as_num, data.routemap) + #link_bgp_neighbor_to_routemap(dut, data.as_num, v6_dut1_neigh_ip_addr, data.routemap, vrf_flag) + bgpfeature.create_bgp_router(dut2, data.new_as_num, '') + base_vlan = 3 + max_vlan = max_paths + 1 + for index in range(base_vlan, max_vlan): + v6_tok = str(hex(index)[2:]) + v6_dut1_neigh_ip_addr = "2100:0:" + v6_tok + "::2" + v6_dut2_neigh_ip_addr2 = "2100:0:" + v6_tok + "::1" + create_bgp_neighbor_config(dut, data.as_num, v6_dut1_neigh_ip_addr, data.new_as_num, data.routemap) + create_bgp_neighbor_config(dut2, data.new_as_num, v6_dut2_neigh_ip_addr2, data.as_num, data.routemap) + #bgpfeature.create_bgp_neighbor(dut, data.as_num, v6_dut1_neigh_ip_addr, data.new_as_num, family="ipv6") + #bgpfeature.create_bgp_neighbor(dut2, data.new_as_num, v6_dut2_neigh_ip_addr2, data.as_num, family="ipv6") + + bgp_conf=tg2.tg_emulation_bgp_config(handle=h1['handle'], mode='enable', ip_version='6', + active_connect_enable='1', local_as='200', remote_as='100', remote_ipv6_addr='2100:0:2::1') + max_route_str = str(max_routes) + + bgp_route=tg2.tg_emulation_bgp_route_config(handle=bgp_conf['handle'], mode='add', ip_version='6', + num_routes=max_route_str, prefix='3300:0:0:2::1', as_path='as_seq:1') + bgp_ctrl=tg2.tg_emulation_bgp_control(handle=bgp_conf['handle'], mode='start') + command = "show run bgp" + st.config(dut, command) + command = "show ndp" + st.config(dut, command) + command = "show bgp ipv6 summary" + st.config(dut, command) + st.config(dut2, command) + command = "show ipv6 route | head -1000" + st.config(dut, command) + st.config(dut2, command) + + tr1=tg2.tg_traffic_config(port_handle=tg_ph_2, emulation_src_handle=h2['handle'], + emulation_dst_handle=bgp_route['handle'], circuit_endpoint_type='ipv6', mode='create', + transmit_mode='continuous', length_mode='fixed', rate_pps=512000, enable_stream_only_gen='0') + #tr1=tg1.tg_traffic_config(port_handle=tg_ph_2, mac_src='00:11:01:00:00:01', mac_dst='80:a2:35:97:eb:c1', ipv6_dst_mode='increment', ipv6_dst_count=200, ipv6_dst_step='::1',ipv6_src_addr='2200:0:2::5', ipv6_dst_addr='3300:0:0:2::1', l3_protocol='ipv6', l2_encap='ethernet_ii_vlan', vlan_id='2', vlan='enable', mode='create', transmit_mode='continuous', length_mode='fixed', rate_pps=512000, enable_stream_only_gen='1') + + + #res=tg_traffic_control(action='run', handle=tr1['stream_id']) + + + + res = tg2.tg_traffic_control(action='run', handle=tr1['stream_id']) + ret1 = check_inter_dut_intf_traffic_counters() + ret2 = check_end_to_end_intf_traffic_counters() + res = tg2.tg_traffic_control(action='stop', handle=tr1['stream_id']) + tg1.tg_traffic_control(action='reset',port_handle=tg_ph_2) + if family == "ipv4": + bgp_rtr2 = tgapi.tg_bgp_config(tg = tg1, handle = bgp_rtr1['conf']['handle'], ctrl_var=ctrl_stop) + else: + bgp_ctrl=tg1.tg_emulation_bgp_control(handle=bgp_conf['handle'], mode='stop') + tg1.tg_interface_config(port_handle=tg_ph_1, handle=h1['handle'], mode='destroy') + tg1.tg_interface_config(port_handle=tg_ph_2, handle=h2['handle'], mode='destroy') + #import pdb;pdb.set_trace() + if apply_file == False: + ip_addr = data.dut1_start_ip_addr + ip_addr2 = data.dut2_start_ip_addr + + base_range = 2 + max_range = max_paths + 1 + for index in range(base_range, max_range): + if family == "ipv4": + command1 = "config interface ip remove "+ "Vlan" + str(index) + " " + ip_addr + command2 = "config interface ip remove "+ "Vlan" + str(index) + " " + ip_addr2 + (is_valid, ip_addr) = ipfeature.increment_ip_addr(ip_addr, "network") + (is_valid, ip_addr2) = ipfeature.increment_ip_addr(ip_addr2, "network") + else: + v6_tok = str(hex(index)[2:]) + if index == 2: + v6_ip_addr2 = "2200:0:" + v6_tok + "::2/64" + else: + v6_ip_addr2 = "2100:0:" + v6_tok + "::2/64" + v6_ip_addr = "2100:0:" + v6_tok + "::1/64" + command1 = "config interface ip remove "+ "Vlan" + str(index) + " " + v6_ip_addr + command2 = "config interface ip remove "+ "Vlan" + str(index) + " " + v6_ip_addr2 + + rv = st.config(dut, command1) + rv = st.config(dut2, command2) + max_vlan = max_paths/4 + base_vlan = 3 + max_vlan = max_vlan - base_vlan + + v_range_t = str(base_vlan) + " " + str(base_vlan + max_vlan ) + vapi.config_vlan_range_members(dut1, v_range_t, data.dut1_ports[0], config='del') + vapi.config_vlan_range_members(dut2, v_range_t, data.dut2_ports[0], config='del') + base_range = 1 + max_range = 4 + max_vlan = max_paths/4 + incr_vlan = max_paths/4 + for index in range(base_range, max_range): + base_vlan = max_vlan + 1 + #max_vlan = max_vlan + 32 + max_vlan = max_vlan + incr_vlan + v_range_t = str(base_vlan) + " " + str(max_vlan) + vapi.config_vlan_range_members(dut1, v_range_t, data.dut1_ports[index], config='del') + vapi.config_vlan_range_members(dut2, v_range_t, data.dut2_ports[index], config='del') + cmd = "config vlan range del 2 129" + st.config(dut, cmd) + my_cmd = "no router bgp {}".format(data.as_num) + st.vtysh_config(dut, my_cmd) + my_cmd = "no router bgp {}".format(data.new_as_num) + st.vtysh_config(dut2, my_cmd) + + + if ret1 == True and ret2 == True: + ret = True + st.log("Test Case PASSED") + else: + ret = False + st.log("Test Case FAILED") + st.log("operation_successful") + return ret + + + +def l3_ecmp_scaling_tc(max_ecmp, use_config_file): + (dut) = (data.dut) + count = 0 + intf_ip_addr = data.start_ip_addr + intf_ip_addr2 = data.start_ip_addr2 + nexthop = data.nexthop_start_ip_addr + member3 = vars.D1T1P3 + member4 = vars.D1T1P4 + # L3 INTF SCALING TEST CASE 1.1 START + json_path = os.getcwd() + apply_file = False + if use_config_file == True: + apply_file = True + + json_apply_path = json_path+"/routing/128_ecmp_config_db.json" + #frr_apply_path = json_path+"/routing/64_ecmp_sr_config.frr" + if apply_file == True: + st.apply_files(dut, [json_apply_path]) + #st.apply_files(dut, [json_apply_path, frr_apply_path]) + max_range = data.base_val+max_ecmp + base_range = data.base_val + + if apply_file == False: + ipfeature.clear_ip_configuration(st.get_dut_names()) + command = "config vlan add {}".format(data.vlan_val) + rv = st.config(dut, command) + command = "config vlan member add {} {}".format(data.vlan_val, member3) + rv = st.config(dut, command) + ip_addr = data.start_ip_addr + #command = "config interface ip add "+ "Vlan" + str(data.vlan_val) + " " + ip_addr+'/24' + command = "config interface ip add "+ "Vlan" + str(data.vlan_val) + " " + ip_addr + rv = st.config(dut, command) + for index in range(base_range, max_range): + command = "config vlan add {}".format(index) + rv = st.config(dut, command) + command = "config vlan member add {} {}".format(index, member3) + rv = st.config(dut, command) + (is_valid, ip_addr) = ipfeature.increment_ip_addr(ip_addr, "network") + command = "config interface ip add "+ "Vlan" + str(index) + " " + ip_addr + rv = st.config(dut, command) + tg_vlan = 101 + command = "config vlan member del {} {}".format(tg_vlan, member3) + rv = st.config(dut, command) + command = "config vlan member add {} {}".format(tg_vlan, member4) + rv = st.config(dut, command) + + + for index in range(base_range, max_range): + #vapi.add_member(dut, data.vlans[index], member, True) + (is_valid, nexthop) = ipfeature.increment_ip_addr(nexthop, "network") + nexthop1 = nexthop + formatted_next_hop = nexthop1.replace("/32","") + ipfeature.create_static_route(dut, formatted_next_hop, data.static_route) + # L3 INTF SCALING TEST CASE 1.1 END + + + data.my_dut_list = st.get_dut_names() + dut1 = data.my_dut_list[0] + + # L3 traffic streams + #For now Spirent link with 100G is not working , so the below code from START to END just books spirent port, it will be rectified + # once infra team provides support for RS-FEC + #START + (tg1, tg_ph_1, tg2, tg_ph_2) = get_handles() + #import pdb;pdb.set_trace() + + tg1.tg_traffic_control(action='reset',port_handle=tg_ph_1) + tg2.tg_traffic_control(action='reset',port_handle=tg_ph_2) + #h1=tg1.tg_interface_config(port_handle=tg_ph_1, mode='config', intf_ip_addr='10.2.101.10', gateway='10.2.101.1', count='8', gateway_step='0.0.1.0', netmask='255.255.255.0', vlan='1', vlan_id='101', vlan_id_count='8', intf_ip_addr_step='0.0.1.0', arp_send_req='1') + #h2=tg2.tg_interface_config(port_handle=tg_ph_2, mode='config', intf_ip_addr='10.2.109.10', gateway='10.2.109.1', count='8', gateway_step='0.0.1.0', netmask='255.255.255.0', vlan='1', vlan_id='109', vlan_id_count='7', intf_ip_addr_step='0.0.1.0', arp_send_req='1') + vid_count = max_ecmp-1 + h1=tg1.tg_interface_config(port_handle=tg_ph_1, mode='config', intf_ip_addr='10.2.102.10', gateway='10.2.102.1', src_mac_addr='00:0c:01:00:00:01', vlan='1', vlan_id='102', count=vid_count, arp_send_req='1', gateway_step='0.0.1.0', intf_ip_addr_step='0.0.1.0') + + h2=tg1.tg_interface_config(port_handle=tg_ph_2, mode='config', intf_ip_addr='10.2.101.10', gateway='10.2.101.1', src_mac_addr='00:0d:01:00:00:01', vlan='1', vlan_id='101', arp_send_req='1', gateway_step='0.0.1.0', intf_ip_addr_step='0.0.1.0') + #h2=tg1.tg_interface_config(port_handle=tg_ph_2, mode='config', intf_ip_addr='10.2.109.10', gateway='10.2.109.1', src_mac_addr='00:0c:01:00:00:01', vlan='1', vlan_id='109', vlan_id_count='8', arp_send_req='1', gateway_step='0.0.1.0', intf_ip_addr_step='0.0.1.0') + h3=tg1.tg_interface_config(port_handle=tg_ph_1, mode='config', intf_ip_addr='10.2.100.10', gateway='10.2.100.1', netmask='255.255.255.0', vlan='1', vlan_id='100', arp_send_req='1') + + + #tr1=tg1.tg_traffic_config(port_handle=tg_ph_1, emulation_src_handle=h3['handle'], ip_dst_addr='200.1.0.1', ip_dst_mode='increment', ip_dst_count='200', ip_dst_step='0.0.0.1', l3_protocol='ipv4', circuit_endpoint_type='ipv4', mode='create', transmit_mode='continuous', length_mode='fixed', rate_pps=512000, enable_stream_only_gen='1') + command_hw = "bcmcmd 'l3 ecmp egress show'" + rv = st.config(dut, command_hw) + tr1=tg1.tg_traffic_config(port_handle=tg_ph_1, mac_src='00:11:01:00:00:01', mac_dst='b8:6a:97:fd:b6:06', ip_dst_mode='increment', ip_dst_count=200, ip_dst_step='0.0.0.1',ip_src_addr='10.2.100.10', ip_dst_addr='200.1.0.1', l3_protocol='ipv4', l2_encap='ethernet_ii_vlan', vlan_id='100', vlan='enable', mode='create', transmit_mode='continuous', length_mode='fixed', rate_pps=512000, enable_stream_only_gen='1') + + res=tg2.tg_traffic_control(action='run', handle=tr1['stream_id']) + count = 0 + #Port Counters + st.wait(20) + output = st.show(dut, "show arp") + #Port Counters + ret = check_intf_traffic_counters() + if ret == True: + count = count+1 + st.log("Test Case 1.14 PASSED") + + tg1.tg_interface_config(port_handle=tg_ph_2, handle=h2['handle'], mode='destroy') + h4=tg1.tg_interface_config(port_handle=tg_ph_1, mode='config', intf_ip_addr='10.2.101.10', gateway='10.2.101.1', src_mac_addr='00:0e:01:00:00:01', vlan='1', vlan_id='101', arp_send_req='1', gateway_step='0.0.1.0', intf_ip_addr_step='0.0.1.0') + + tg_vlan = 101 + command = "config vlan member del {} {}".format(tg_vlan, member4) + rv = st.config(dut, command) + command = "config vlan member add {} {}".format(tg_vlan, member3) + rv = st.config(dut, command) + + tg_intf_ip_addr = data.tg_start_ip_addr + max_range = data.base_val+max_ecmp + base_range = data.base_val+1 + for index in range(base_range, max_range): + data.thresh = 4 + command = "config vlan member del {} {}".format(index, member3) + rv = st.config(dut, command) + command = "config vlan member add {} {}".format(index, member4) + rv = st.config(dut, command) + (is_valid, tg_intf_ip_addr) = ipfeature.increment_ip_addr(tg_intf_ip_addr, "network") + tg_intf_ip_addr_x = tg_intf_ip_addr; + tg_formatted_intf_addr = tg_intf_ip_addr_x.replace("/24","") + tg_formatted_gw_addr = tg_intf_ip_addr_x.replace("10/24","1") + ping_formatted_gw_addr = tg_intf_ip_addr_x.replace("10/24","1") + tg_vlan=index + st.log("tg_vlan: "+str(tg_vlan)) + st.log("tg_formatted_gw_addr: "+str(tg_formatted_gw_addr)) + st.log("tg_formatted_intf_addr: "+str(tg_formatted_intf_addr)) + h2=tg1.tg_interface_config(port_handle=tg_ph_2, mode='config', intf_ip_addr=tg_formatted_intf_addr, gateway=tg_formatted_gw_addr, src_mac_addr='00:0a:01:00:00:01', vlan='1', vlan_id=tg_vlan, vlan_id_count='1', arp_send_req='1', gateway_step='0.0.1.0', intf_ip_addr_step='0.0.1.0') + st.wait(3) + #output = st.show(dut, "show arp") + #Port Counters + ret = check_intf_traffic_counters() + if ret == True: + count = count+1 + st.log("Test Case 1.14 PASSED") + else: + st.log('Traffic test Failed') + + tg1.tg_interface_config(port_handle=tg_ph_2, handle=h2['handle'], mode='destroy') + command = "config vlan member del {} {}".format(index, member4) + rv = st.config(dut, command) + command = "config vlan member add {} {}".format(index, member3) + rv = st.config(dut, command) + #res1=verify_ping(src_obj=tg1, port_handle=tg_ph_1, dev_handle=h1['handle'], dst_ip=ping_formatted_gw_addr,\ + # ping_count='6', exp_count='6') + + + #tg1.tg_traffic_config(mode='create', transmit_mode='continuous', length_mode='fixed', rate_pps=30, + # mac_src='00:00:00:00:00:01', mac_dst='00:00:00:00:00:02', ip_src_addr ='11.11.11.2', + # ip_dst_addr = '11.11.12.2') + + res=tg2.tg_traffic_control(action='stop', handle=tr1['stream_id']) + rv = st.config(dut, command_hw) + tg1.tg_interface_config(port_handle=tg_ph_1, handle=h1['handle'], mode='destroy') + tg1.tg_interface_config(port_handle=tg_ph_1, handle=h3['handle'], mode='destroy') + tg1.tg_interface_config(port_handle=tg_ph_1, handle=h4['handle'], mode='destroy') + #import pdb;pdb.set_trace() + if use_config_file == True: + st.clear_config(dut1) + # This code will not be needed if apply_file is True as config gets cleared already + if apply_file == False: + ip_addr = data.start_ip_addr + + base_range = data.base_val - 1 + #max_range = data.base_val+max_ecmp-1 + max_range = data.base_val+max_ecmp + for index in range(base_range, max_range): + command = "config interface ip remove "+ "Vlan" + str(index) + " " + ip_addr + rv = st.config(dut, command) + command = "config vlan member del {} {}".format(index, member3) + rv = st.config(dut, command) + command = "config vlan del {}".format(index) + rv = st.config(dut, command) + (is_valid, ip_addr) = ipfeature.increment_ip_addr(ip_addr, "network") + #del_vlan = max_range + #command = "config interface ip remove "+ "Vlan" + str(del_vlan) + " " + ip_addr + #rv = st.config(dut, command) + #command = "config vlan member del {} {}".format(del_vlan, member4) + #rv = st.config(dut, command) + #command = "config vlan del {}".format(del_vlan) + #rv = st.config(dut, command) + + + ret = False + st.log("count: "+str(count)) + if count >= data.thresh: + ret = True + st.log("Test Case PASSED") + else: + ret = False + st.log("Test Case FAILED") + st.log("operation_successful") + return ret + +@pytest.mark.l3_scale_ut_ft +def test_ft_l3_Xecmp_scaling_tc(): + (dut) = (data.dut) + max_ecmp_4 = data.max_ecmp/32 + ipfeature.clear_ip_configuration([dut]) + #use_config_file = False + use_config_file = False + ret = l3_ecmp_scaling_tc(max_ecmp_4, use_config_file) + if ret==True: + st.log("Test Case PASSED") + st.report_pass("operation_successful") + else: + st.log("Test Case FAILED") + st.report_fail("operation_failed") + st.report_pass("operation_successful") + + + +@pytest.mark.l3_scale_ut_nr +def nest_l3_32ecmp_scaling_tc(): + (dut) = (data.dut) + max_ecmp_32 = data.max_ecmp/4 + use_config_file = True + ipfeature.clear_ip_configuration([dut]) + ret = l3_ecmp_scaling_tc(max_ecmp_32, use_config_file) + if ret==True: + st.log("Test Case 1.13 PASSED") + st.report_pass("operation_successful") + else: + st.log("Test Case 1.13 FAILED") + st.report_fail("operation_failed") + st.report_pass("operation_successful") + +@pytest.mark.l3_scale_ut_long_run +def test_l3_64ecmp_scaling_tc(): + max_ecmp_64 = data.max_ecmp/2 + use_config_file = True + ret = l3_ecmp_scaling_tc(max_ecmp_64, use_config_file) + if ret==True: + st.log("Test Case 1.14 PASSED") + st.report_pass("operation_successful") + else: + st.log("Test Case 1.14 FAILED") + st.report_fail("operation_failed") + st.report_pass("operation_successful") + +@pytest.mark.l3_scale_ut_long_run +def test_l3_128ecmp_scaling_tc(): + max_ecmp_128 = data.max_ecmp + use_config_file = True + ret = l3_ecmp_scaling_tc(max_ecmp_128, use_config_file) + + if ret==True: + st.log("Test Case 1.15 PASSED") + st.report_pass("operation_successful") + else: + st.log("Test Case 1.15 FAILED") + st.report_fail("operation_failed") + st.report_pass("operation_successful") + +@pytest.mark.l3_scale_ut_ft +def test_max_v4_route_with_max_paths(): + (dut) = (data.dut) + max_ecmp_16 = data.max_ecmp/8 + ipfeature.clear_ip_configuration([dut]) + use_config_file = False + max_routes = 100 + ret = l3_max_route_max_path_scaling_tc(max_ecmp_16, max_routes, use_config_file) + if ret==True: + st.log("Test Case PASSED") + st.report_pass("operation_successful") + else: + st.log("Test Case FAILED") + st.report_fail("operation_failed") + st.report_pass("operation_successful") + +@pytest.mark.l3_scale_ut_ft +def test_max_v6_route_with_max_paths(): + (dut) = (data.dut) + max_ecmp_16 = data.max_ecmp/8 + ipfeature.clear_ip_configuration([dut]) + #use_config_file = False + use_config_file = False + family = "ipv6" + max_routes = 100 + ret = l3_max_route_max_path_scaling_tc(max_ecmp_16, max_routes, use_config_file, family) + if ret==True: + st.log("Test Case PASSED") + st.report_pass("operation_successful") + else: + st.log("Test Case FAILED") + st.report_fail("operation_failed") + st.report_pass("operation_successful") + +@pytest.mark.l3_scale_ut_ft_variant +def test_max_v4_route_with_max_paths_variant(): + (dut) = (data.dut) + max_ecmp = data.max_ecmp + ipfeature.clear_ip_configuration([dut]) + use_config_file = False + max_routes = 65000 + ret = l3_max_route_max_path_scaling_tc(max_ecmp, max_routes, use_config_file) + if ret==True: + st.log("Test Case PASSED") + st.report_pass("operation_successful") + else: + st.log("Test Case FAILED") + st.report_fail("operation_failed") + st.report_pass("operation_successful") + +@pytest.mark.l3_scale_ut_ft_variant +def test_max_v6_route_with_max_paths_variant(): + (dut) = (data.dut) + max_ecmp = data.max_ecmp + ipfeature.clear_ip_configuration([dut]) + #use_config_file = False + use_config_file = False + family = "ipv6" + max_routes = 32000 + ret = l3_max_route_max_path_scaling_tc(max_ecmp, max_routes, use_config_file, family) + if ret==True: + st.log("Test Case PASSED") + st.report_pass("operation_successful") + else: + st.log("Test Case FAILED") + st.report_fail("operation_failed") + st.report_pass("operation_successful") + + + + +@pytest.mark.l3_scale_ut +def test_l3_ecmp_4paths_on_bo_tc(): + (dut) = (data.dut) + count = 0 + intf_ip_addr = data.start_ip_addr + intf_ip_addr2 = data.start_ip_addr2 + #nexthop = data.nexthop_start_ip_addr + nexthop = "10.2.101.10/32" + member1 = vars.D1T1P1 + member2 = vars.D1T1P2 + member3 = vars.D1T1P3 + member4 = vars.D1T1P4 + apply_file = False + + ipfeature.clear_ip_configuration([dut]) + max_range = data.base_val+4 + base_range = data.base_val-1 + if apply_file == False: + command = "config vlan range add 100 105" + rv = st.config(dut, command) + command = "config vlan member add 100 {}".format(member4) + rv = st.config(dut, command) + command = "config vlan member add 101 {}".format(member1) + rv = st.config(dut, command) + command = "config vlan member add 102 {}".format(member2) + rv = st.config(dut, command) + command = "config vlan member add 103 {}".format(member3) + rv = st.config(dut, command) + command = "config vlan member add 104 {}".format(member4) + rv = st.config(dut, command) + ip_addr = data.start_ip_addr + ip_addr = "10.2.100.1/24" + for index in range(base_range, max_range): + command = "config interface ip add "+ "Vlan" + str(index) + " " + ip_addr + rv = st.config(dut, command) + (is_valid, ip_addr) = ipfeature.increment_ip_addr(ip_addr, "network") + + + base_range = data.base_val + max_range = data.base_val+3 + for index in range(base_range, max_range): + (is_valid, nexthop) = ipfeature.increment_ip_addr(nexthop, "network") + nexthop1 = nexthop + formatted_next_hop = nexthop1.replace("/32","") + ipfeature.create_static_route(dut, formatted_next_hop, data.static_route) + + + data.my_dut_list = st.get_dut_names() + dut1 = data.my_dut_list[0] + + (tg1, tg_ph_1, tg2, tg_ph_2, tg_ph_3, tg_ph_4) = get_handles_1() + #import pdb;pdb.set_trace() + + tg1.tg_traffic_control(action='reset',port_handle=tg_ph_1) + tg2.tg_traffic_control(action='reset',port_handle=tg_ph_2) + tg2.tg_traffic_control(action='reset',port_handle=tg_ph_3) + tg2.tg_traffic_control(action='reset',port_handle=tg_ph_4) + + h0=tg1.tg_interface_config(port_handle=tg_ph_4, mode='config', intf_ip_addr='10.2.100.10', gateway='10.2.100.1', src_mac_addr='00:0d:01:00:00:01', vlan='1', vlan_id='100', arp_send_req='1', gateway_step='0.0.1.0', intf_ip_addr_step='0.0.1.0') + + h1=tg1.tg_interface_config(port_handle=tg_ph_1, mode='config', intf_ip_addr='10.2.101.10', gateway='10.2.101.1', src_mac_addr='00:0d:02:00:00:01', vlan='1', vlan_id='101', arp_send_req='1', gateway_step='0.0.1.0', intf_ip_addr_step='0.0.1.0') + + h2=tg1.tg_interface_config(port_handle=tg_ph_2, mode='config', intf_ip_addr='10.2.102.10', gateway='10.2.102.1', src_mac_addr='00:0c:01:00:00:01', vlan='1', vlan_id='102', arp_send_req='1', gateway_step='0.0.1.0', intf_ip_addr_step='0.0.1.0') + + h3=tg1.tg_interface_config(port_handle=tg_ph_3, mode='config', intf_ip_addr='10.2.103.10', gateway='10.2.103.1', src_mac_addr='00:0c:02:00:00:01', vlan='1', vlan_id='103', arp_send_req='1', gateway_step='0.0.1.0', intf_ip_addr_step='0.0.1.0') + + h4=tg1.tg_interface_config(port_handle=tg_ph_4, mode='config', intf_ip_addr='10.2.104.10', gateway='10.2.104.1', src_mac_addr='00:0a:02:00:00:01', vlan='1', vlan_id='104', arp_send_req='1', gateway_step='0.0.1.0', intf_ip_addr_step='0.0.1.0') + + mac_eth = macapi.get_sbin_intf_mac(vars.D1,'eth0') + tr1=tg1.tg_traffic_config(port_handle=tg_ph_4, mac_src='00:11:01:00:00:01', mac_dst=mac_eth, ip_dst_mode='increment', ip_dst_count=200, ip_dst_step='0.0.0.1',ip_src_addr='10.2.100.10', ip_dst_addr='200.1.0.1', l3_protocol='ipv4', l2_encap='ethernet_ii_vlan', vlan_id='100', vlan='enable', mode='create', transmit_mode='continuous', length_mode='fixed', rate_pps=512000, enable_stream_only_gen='1') + + res=tg2.tg_traffic_control(action='run', handle=tr1['stream_id']) + count = 0 + #Port Counters + st.wait(20) + output = st.show(dut, "show arp") + #Port Counters + ret = check_intf_traffic_bo_counters() + if ret == True: + st.log("Test Case PASSED") + res=tg2.tg_traffic_control(action='stop', handle=tr1['stream_id']) + tg1.tg_interface_config(port_handle=tg_ph_1, handle=h0['handle'], mode='destroy') + tg1.tg_interface_config(port_handle=tg_ph_1, handle=h1['handle'], mode='destroy') + tg1.tg_interface_config(port_handle=tg_ph_2, handle=h2['handle'], mode='destroy') + tg1.tg_interface_config(port_handle=tg_ph_3, handle=h3['handle'], mode='destroy') + tg1.tg_interface_config(port_handle=tg_ph_4, handle=h4['handle'], mode='destroy') + + if apply_file == False: + base_range = data.base_val-1 + ip_addr = data.start_ip_addr + max_range = data.base_val+4 + for index in range(base_range, max_range): + command = "config interface ip remove "+ "Vlan" + str(index) + " " + ip_addr + rv = st.config(dut, command) + (is_valid, ip_addr) = ipfeature.increment_ip_addr(ip_addr, "network") + command = "config vlan member del 100 {}".format(member4) + rv = st.config(dut, command) + command = "config vlan member del 101 {}".format(member1) + rv = st.config(dut, command) + command = "config vlan member del 102 {}".format(member2) + rv = st.config(dut, command) + command = "config vlan member del 103 {}".format(member3) + rv = st.config(dut, command) + command = "config vlan member del 104 {}".format(member4) + rv = st.config(dut, command) + command = "config vlan range del 100 105" + rv = st.config(dut, command) + + if ret == True: + st.log("Test Case PASSED") + st.report_pass("operation_successful") + else: + st.log("Test Case FAILED") + st.report_fail("operation_failed") + + diff --git a/spytest/tests/routing/test_ndp.py b/spytest/tests/routing/test_ndp.py new file mode 100644 index 00000000000..62f972f34a2 --- /dev/null +++ b/spytest/tests/routing/test_ndp.py @@ -0,0 +1,100 @@ +import pytest + +from spytest import st, tgapi, SpyTestDict + +import apis.switching.vlan as vlan_obj +import apis.routing.ip as ip_obj +import apis.routing.arp as arp_obj + +data = SpyTestDict() +data.vlan_1 = 64 +data.count = 5 +data.vlan_int_1 = "Vlan{}".format(data.vlan_1) +data.clear_parallel = False +data.local_ip6_addr = ["2001::1", "3001::1"] +data.local_ip6_addr_rt = ["2001::", "3001::", "4001::"] +data.neigh_ip6_addr_gw = ["2001::100", "3001::100", "4001::100"] +data.af_ipv6 = "ipv6" +data.tg_mac1 = '00:0a:01:01:23:01' +data.tg_mac2 = '00:0b:01:01:23:01' +data.tg_mac3 = '00:0c:01:01:23:01' + +@pytest.fixture(scope="module", autouse=True) +def ndp_module_hooks(request): + vars = st.ensure_min_topology("D1T1:2") + + # Initialize TG and TG port handlers + tg_handler = tgapi.get_handles_byname("T1D1P1", "T1D1P2") + tg = tg_handler["tg"] + + # Test setup details + data.my_dut_list = st.get_dut_names() + dut1 = data.my_dut_list[0] + + # NDP module configuration + st.log("NDP module configuration.") + ip_obj.config_ip_addr_interface(dut1, vars.D1T1P1, data.local_ip6_addr[0], 64, family=data.af_ipv6) + vlan_obj.create_vlan(dut1, data.vlan_1) + vlan_obj.add_vlan_member(dut1, data.vlan_1, vars.D1T1P2, True) + ip_obj.config_ip_addr_interface(dut1, data.vlan_int_1, data.local_ip6_addr[1], 64, family=data.af_ipv6) + + # TG ports reset + st.log("Resetting the TG ports") + tg.tg_traffic_control(action="reset", port_handle=tg_handler["tg_ph_list"]) + + # TG protocol interface creation + st.log("TG protocol interface creation") + h1 = tg.tg_interface_config(port_handle=tg_handler["tg_ph_1"], mode='config', + ipv6_intf_addr=data.neigh_ip6_addr_gw[0],ipv6_prefix_length='64', + ipv6_gateway=data.local_ip6_addr[0],src_mac_addr=data.tg_mac1, + arp_send_req='1', count=data.count) + st.log("INTFCONF: " + str(h1)) + h2 = tg.tg_interface_config(port_handle=tg_handler["tg_ph_2"], mode='config', + ipv6_intf_addr=data.neigh_ip6_addr_gw[1],ipv6_prefix_length='64', + ipv6_gateway=data.local_ip6_addr[1],src_mac_addr=data.tg_mac2 , + arp_send_req='1', vlan_id=data.vlan_1, vlan=1, count=data.count) + st.log("INTFCONF: " + str(h2)) + + yield + # NDP module cleanup + st.log("NDP module cleanup.") + ip_obj.clear_ip_configuration(dut1,family="ipv6",thread=data.clear_parallel) + vlan_obj.clear_vlan_configuration(dut1,thread= data.clear_parallel) + +@pytest.fixture(scope="function", autouse=True) +def ndp_func_hooks(request): + # NDP function configuration + yield + # NDP function cleanup + +@pytest.mark.regression +@pytest.mark.community +@pytest.mark.community_fail +def test_ft_ipv6_neighbor_entry(): + ################# Author Details ################ + # Name: Raja Sekhar Uppara + # Email: raja-sekhar.uppara@broadcom.com + ################################################# + # Objective - 1.Verify that IPv6 neighbor entries are created successfully. + # 2.Verify that Ipv6 Static neighbor entries are created successfully. + # 3.'sudo sonic-clear ndp' flushes the existing dymanic entries + ############### Test bed details ################ + # TG1-----DUT-----TG2 + ################################################# + vars = st.get_testbed_vars() + arp_obj.show_ndp(vars.D1) + ndp_dut_count_initial = arp_obj.get_ndp_count(vars.D1) + if ndp_dut_count_initial < 2*data.count: + st.report_fail("ndp_dynamic_entry_fail") + arp_obj.clear_ndp_table(vars.D1) + ndp_dut_count_post_clear = int(arp_obj.get_ndp_count(vars.D1)) + if ndp_dut_count_post_clear > 2: + arp_obj.show_ndp(vars.D1) + st.report_fail("ndp_entries_clearing_failed") + arp_obj.config_static_ndp(vars.D1, data.neigh_ip6_addr_gw[2],data.tg_mac3, vars.D1T1P1) + ndp_dut_count_static = int(arp_obj.get_ndp_count(vars.D1)) + if not ndp_dut_count_static: + st.report_fail("static_ndp_create_fail") + arp_obj.config_static_ndp(vars.D1, data.neigh_ip6_addr_gw[2], data.tg_mac3 , vars.D1T1P1, 'del') + st.report_pass("test_case_passed") + diff --git a/spytest/tests/sanity/test_sanity_l2.py b/spytest/tests/sanity/test_sanity_l2.py new file mode 100644 index 00000000000..6f94b053a1a --- /dev/null +++ b/spytest/tests/sanity/test_sanity_l2.py @@ -0,0 +1,1041 @@ +import pytest + +from spytest import st, tgapi, SpyTestDict + +import apis.switching.portchannel as portchannel_obj +import apis.switching.vlan as vlan_obj +import apis.switching.mac as mac_obj +import utilities.utils as utils_obj +import apis.system.interface as intf_obj +import apis.common.asic as asicapi +import apis.routing.ip as ip_obj +import apis.system.basic as basic_obj +from apis.system.sflow import enable_disable_config, verify_config +from utilities.common import filter_and_select, sprint_vtable, random_vlan_list + +@pytest.fixture(scope="module", autouse=True) +def sanity_l2_module_hooks(request): + st.ensure_min_topology("D1D2:4", "D1T1:3", "D2T1:1") + yield + +@pytest.fixture(scope="function", autouse=True) +def sanity_l2_func_hooks(request): + yield + +global base_line_final_result +base_line_final_result = { + 'test_base_line_portchannel_create_delete': 'NA', + 'test_base_line_random_link_flap_portchannel': 'NA', + 'test_base_line_l2_taggged_forwarding_with_portchannel': 'NA', + 'test_base_line_vlan_port_association': 'NA', + 'test_base_line_port_move_from_vlan_a_to_vlan_b': 'NA', + 'test_base_line_vlan_create_delete_and_mac_learning_with_bum': 'NA', + 'test_base_line_mac_move_single_vlan': 'NA', + 'test_base_line_mac_move_across_vlans': 'NA' +} + +data = SpyTestDict() +data.vlan_id = str(random_vlan_list()[0]) +data.portChannelName = "PortChannel5" +data.mac_addr_cnt = 100 +data.source_mac = "00:00:02:00:00:01" +data.destination_mac = "00:00:01:00:00:01" +data.rate_pps = "100" +data.wait_post_port_channel_up = 10 +data.clear_mac_table_wait_time = 3 +data.post_wait_time_run = 5 +data.post_wait_time_stop = 5 +data.post_wait_time_clear = 1 +data.post_wait_time_create = 2 +data.clear_parallel = True + + +@pytest.mark.base_test_sanity +@pytest.mark.base_test_sanity_optimize +@pytest.mark.community +@pytest.mark.community_pass +def test_base_line_portchannel_create_delete(): + + vars = st.get_testbed_vars() + # Sub test selection + sub_test_1 = 1 + sub_test_2 = 1 + sub_test_3 = 1 + sub_test_4 = 1 + sub_test_5 = 1 + # Global and Topology variable + data.mac_addr_cnt = 2 + data.no_of_port_channel_create_and_delete = 1 + data.post_link_shutdown_wait_time = 1 + data.dut2_lag_members = [vars.D2D1P1, vars.D2D1P2, vars.D2D1P3, vars.D2D1P4] + data.dut1_lag_members = [vars.D1D2P1, vars.D1D2P2, vars.D1D2P3, vars.D1D2P4] + + tg1, tg_ph_1 = tgapi.get_handle_byname("T1D1P1") + tg2, tg_ph_2 = tgapi.get_handle_byname("T1D2P1") + + data.port_hand_list = [tg_ph_1, tg_ph_2] + + intf_obj.clear_interface_counters(vars.D1) + intf_obj.clear_interface_counters(vars.D2) + topology = {vars.D1: {"ports": data.dut1_lag_members, "TGports": vars.D1T1P1}, + vars.D2: {"ports": data.dut2_lag_members, "TGports": vars.D2T1P1}} + + st.log("PRE TEST : Cleanup call are started.") + ip_obj.clear_ip_configuration(st.get_dut_names(), thread=data.clear_parallel) + ip_obj.clear_ip_configuration(st.get_dut_names(), 'ipv6', thread=data.clear_parallel) + vlan_obj.clear_vlan_configuration(st.get_dut_names(), thread=data.clear_parallel) + portchannel_obj.clear_portchannel_configuration(st.get_dut_names(), thread=data.clear_parallel) + + st.log("Sending VLAN traffic and verifying the stats") + tg1.tg_traffic_control(action='reset', port_handle=data.port_hand_list) + tg1.tg_traffic_control(action='clear_stats', port_handle=data.port_hand_list) + tg1.tg_traffic_config(port_handle=tg_ph_1, mode='create', rate_pps=data.rate_pps, mac_src=data.source_mac, + mac_src_mode="increment", + mac_src_count=data.mac_addr_cnt, transmit_mode="continuous", + mac_src_step="00:00:00:00:00:01", mac_dst=data.destination_mac, mac_dst_mode="increment", + mac_dst_count=data.mac_addr_cnt, mac_dst_step="00:00:00:00:00:01", + l2_encap='ethernet_ii_vlan', + vlan_id=data.vlan_id, vlan="enable") + tg2.tg_traffic_config(port_handle=tg_ph_2, mode='create', rate_pps=data.rate_pps, mac_src=data.destination_mac, + mac_src_mode="increment", + mac_src_count=data.mac_addr_cnt, transmit_mode="continuous", + mac_src_step="00:00:00:00:00:01", mac_dst=data.source_mac, mac_dst_mode="increment", + mac_dst_count=data.mac_addr_cnt, mac_dst_step="00:00:00:00:00:01", + l2_encap='ethernet_ii_vlan', + vlan_id=data.vlan_id, vlan="enable") + if sub_test_1: + st.log("#"*90) + st.log("############# Sub test 1: test_base_line_portchannel_create_delete - START ################") + st.log("#"*90) + try: + st.log("Verifying the oper status of ports and do shut and no shut - Initially") + intf_obj.interface_noshutdown(vars.D1, topology[vars.D1]["ports"] + [topology[vars.D1]["TGports"]]) + intf_obj.interface_noshutdown(vars.D2, topology[vars.D2]["ports"] + [topology[vars.D2]["TGports"]]) + st.wait(5) + for dut in topology: + all_ports = topology[dut]["ports"] + [topology[dut]["TGports"]] + for each_port in all_ports: + if not intf_obj.verify_interface_status(dut, each_port, 'oper', 'up'): + intf_obj.interface_shutdown(dut, each_port) + st.wait(data.post_link_shutdown_wait_time) + intf_obj.interface_noshutdown(dut, each_port) + if not intf_obj.verify_interface_status(dut, each_port, 'oper', 'up'): + st.error('{} interface is down on dut'.format(each_port)) + assert False + + st.log("Config Vlan tagged and Port channel on both DUTs") + for dut in topology: + portchannel_obj.create_portchannel(dut, data.portChannelName) + portchannel_obj.add_portchannel_member(dut, data.portChannelName, topology[dut]["ports"]) + vlan_obj.create_vlan(dut, data.vlan_id) + vlan_obj.add_vlan_member(dut, data.vlan_id, [topology[dut]["TGports"], data.portChannelName], True) + + assert vlan_obj.verify_vlan_config(dut, data.vlan_id, tagged=[topology[dut]["TGports"], + data.portChannelName]), \ + st.log("vlan tagged member fail on port {},vlan {}".format([topology[dut]["TGports"], + data.portChannelName], data.vlan_id)) + + st.log("Verifying the Port channel status - Initially") + for dut in topology: + if not portchannel_obj.verify_portchannel_and_member_status(dut, data.portChannelName, + topology[dut]["ports"], + iter_count=6, iter_delay=1, state='up'): + st.error("port channel {} on DUT {} state fail with {}".format(data.portChannelName, dut, "up")) + assert False + + for dut in topology: + if not portchannel_obj.verify_portchannel_state(dut, data.portChannelName, 'up'): + st.log("Port channel is not {}".format('up')) + st.error("port channel {} on dut {} state fail with {}".format(data.portChannelName, dut, "up")) + assert False + st.wait(data.wait_post_port_channel_up) + + #Workaround added for intermittent issue seen on vSonic - when load on server is more + if basic_obj.is_vsonic_device(vars.D1): + st.log("Priming BCMSIM w/ traffic to enable mac learning") + tg1.tg_traffic_control(action='run', port_handle=data.port_hand_list) + st.wait(data.post_wait_time_run) + tg1.tg_traffic_control(action='stop', port_handle=data.port_hand_list) + st.wait(data.post_wait_time_stop) + tg1.tg_traffic_control(action='clear_stats', port_handle=data.port_hand_list) + #workaround end + + st.log("Sending VLAN traffic and verifying the stats") + tg1.tg_traffic_control(action='run', port_handle=data.port_hand_list) + st.wait(data.post_wait_time_run) + tg1.tg_traffic_control(action='stop', port_handle=data.port_hand_list) + st.wait(data.post_wait_time_stop) + + stats_tg1 = tg1.tg_traffic_stats(port_handle=tg_ph_1, mode='aggregate') + total_tx_tg1 = int(stats_tg1[tg_ph_1]['aggregate']['tx']['total_pkts']) + total_rx_tg1 = int(stats_tg1[tg_ph_1]['aggregate']['rx']['total_pkts']) + tx_tg1_95_precentage = (95 * int(total_tx_tg1)) / 100 + stats_tg2 = tg2.tg_traffic_stats(port_handle=tg_ph_2, mode='aggregate') + total_rx_tg2 = int(stats_tg2[tg_ph_2]['aggregate']['rx']['total_pkts']) + total_tx_tg2 = int(stats_tg2[tg_ph_2]['aggregate']['tx']['total_pkts']) + tx_tg2_95_precentage = (95 * int(total_tx_tg2)) / 100 + st.log('total_tx_tg1 = {}'.format(total_tx_tg1)) + st.log('tx_tg1_95_precentage = {}'.format(tx_tg1_95_precentage)) + st.log('total_rx_tg1 = {}'.format(total_rx_tg1)) + st.log('total_tx_tg2 = {}'.format(total_tx_tg2)) + st.log('tx_tg2_95_precentage = {}'.format(tx_tg2_95_precentage)) + st.log('total_rx_tg2 = {}'.format(total_rx_tg2)) + assert tx_tg1_95_precentage <= total_rx_tg2, st.log("traffic_verification_failed") + assert tx_tg2_95_precentage <= total_rx_tg1, st.log("traffic_verification_failed") + st.log("Traffic test passed..") + + st.log("Debug prints.....") + for dut in topology: + asicapi.dump_vlan(dut) + asicapi.dump_l2(dut) + asicapi.dump_trunk(dut) + + st.log("Validating MAC table..") + dut1_mac_address_list = utils_obj.get_mac_address(data.source_mac, start=0, end=data.mac_addr_cnt) + dut2_mac_address_list = utils_obj.get_mac_address(data.destination_mac, start=0, end=data.mac_addr_cnt) + complete_mac_address_list = dut1_mac_address_list + dut2_mac_address_list + + assert mac_obj.verify_mac_address(vars.D1, data.vlan_id, complete_mac_address_list), \ + st.log("mac_address_verification_fail") + assert mac_obj.verify_mac_address(vars.D2, data.vlan_id, complete_mac_address_list), \ + st.log("mac_address_verification_fail") + st.log("MAC table validation passed..") + + st.log("Start Creating and delete the Port channel") + portchannel_list = [] + for i in range(6, data.no_of_port_channel_create_and_delete+6): + portchannel_obj.create_portchannel(vars.D1, "PortChannel00{}".format(i)) + portchannel_obj.create_portchannel(vars.D2, "PortChannel00{}".format(i)) + portchannel_list.append("PortChannel00{}".format(i)) + dut1_portchannel_list = len(portchannel_obj.get_portchannel_list(vars.D1)) + assert int(dut1_portchannel_list) == int(data.no_of_port_channel_create_and_delete+1), \ + st.log("port channel count {} verification fail on port channel {}".format + (data.no_of_port_channel_create_and_delete+1, dut1_portchannel_list)) + dut2_portchannel_list = len(portchannel_obj.get_portchannel_list(vars.D2)) + assert int(dut2_portchannel_list ) == int(data.no_of_port_channel_create_and_delete+1), \ + st.log("port channel count {} verification fail on port channel {}".format + (data.no_of_port_channel_create_and_delete+1, dut2_portchannel_list)) + portchannel_obj.delete_portchannel(vars.D1, portchannel_list) + portchannel_obj.delete_portchannel(vars.D2, portchannel_list) + dut1_portchannel_list = len(portchannel_obj.get_portchannel_list(vars.D1)) + assert int(dut1_portchannel_list) == 1, \ + st.log("port channel count {} verification fail on port channel {}".format(1, dut1_portchannel_list)) + dut2_portchannel_list = len(portchannel_obj.get_portchannel_list(vars.D2)) + assert int(dut2_portchannel_list) == 1, \ + st.log("port channel count {} verification fail on port channel {}".format(1, dut2_portchannel_list)) + + st.log("Traffic checking post port channel create and delete") + tg1.tg_traffic_control(action='clear_stats', port_handle=data.port_hand_list) + st.wait(data.post_wait_time_clear) + tg1.tg_traffic_control(action='run', port_handle=data.port_hand_list) + st.wait(data.post_wait_time_run) + tg1.tg_traffic_control(action='stop', port_handle=data.port_hand_list) + st.wait(data.post_wait_time_stop) + + stats_tg1 = tg1.tg_traffic_stats(port_handle=tg_ph_1, mode='aggregate') + total_tx_tg1 = int(stats_tg1[tg_ph_1]['aggregate']['tx']['total_pkts']) + total_rx_tg1 = int(stats_tg1[tg_ph_1]['aggregate']['rx']['total_pkts']) + tx_tg1_95_precentage = (95 * int(total_tx_tg1)) / 100 + stats_tg2 = tg2.tg_traffic_stats(port_handle=tg_ph_2, mode='aggregate') + total_rx_tg2 = int(stats_tg2[tg_ph_2]['aggregate']['rx']['total_pkts']) + total_tx_tg2 = int(stats_tg2[tg_ph_2]['aggregate']['tx']['total_pkts']) + tx_tg2_95_precentage = (95 * int(total_tx_tg2)) / 100 + st.log('total_tx_tg1 = {}'.format(total_tx_tg1)) + st.log('tx_tg1_95_precentage = {}'.format(tx_tg1_95_precentage)) + st.log('total_rx_tg1 = {}'.format(total_rx_tg1)) + st.log('total_tx_tg2 = {}'.format(total_tx_tg2)) + st.log('tx_tg2_95_precentage = {}'.format(tx_tg2_95_precentage)) + st.log('total_rx_tg2 = {}'.format(total_rx_tg2)) + assert tx_tg1_95_precentage <= total_rx_tg2, st.log("traffic_verification_failed") + assert tx_tg2_95_precentage <= total_rx_tg1, st.log("traffic_verification_failed") + + st.log("Traffic test passed..") + st.log("Sub test 1 : PASSED") + base_line_final_result['test_base_line_portchannel_create_delete'] = 'Pass' + except Exception as e: + st.log(e) + base_line_final_result['test_base_line_portchannel_create_delete'] = 'Fail' + + st.log("#"*90) + st.log("############# Sub test 1: test_base_line_portchannel_create_delete - END ################") + st.log("#"*90) + + if sub_test_2 and base_line_final_result['test_base_line_portchannel_create_delete'] == 'Pass': + st.log("#"*90) + st.log("############# Sub test 2: test_base_line_random_link_flap_portchannel - START ################") + st.log("#"*90) + try: + st.log("Start performing the port channel member link flap") + for i in range(1, 2): + for interface in data.dut2_lag_members: + intf_obj.interface_shutdown(vars.D2, interface) + st.wait(3) + intf_obj.interface_noshutdown(vars.D2, interface) + st.wait(2) + st.log("Verifying the Port channel status - post flap members") + for dut in topology: + if not portchannel_obj.verify_portchannel_and_member_status(dut, data.portChannelName, + topology[dut]["ports"], + iter_count=6, iter_delay=1, state='up'): + st.error("port channel {} on DUT {} state fail with {}".format(data.portChannelName, dut, "up")) + assert False + for dut in topology: + if not portchannel_obj.verify_portchannel_state(dut, data.portChannelName, 'up'): + st.log("Port channel is not {}".format('up')) + st.error("port channel {} on DUT {} state fail with {}".format(data.portChannelName, dut, "up")) + assert False + + st.log("Sub test 2 : PASSED") + base_line_final_result['test_base_line_random_link_flap_portchannel'] = 'Pass' + except Exception as e: + st.log(e) + base_line_final_result['test_base_line_random_link_flap_portchannel'] = 'Fail' + + st.log("#"*90) + st.log("############# Sub test 2: test_base_line_random_link_flap_portchannel - END ################") + st.log("#"*90) + + if sub_test_3 and base_line_final_result['test_base_line_portchannel_create_delete'] == 'Pass': + st.log("#"*90) + st.log("############# Sub test 3: test_base_line_l2_taggged_forwarding_with_portchannel - START " + "################") + st.log("#"*90) + try: + st.log("Verifying L2 tagged traffic forwarding on Port channel") + tg1.tg_traffic_control(action='clear_stats', port_handle=data.port_hand_list) + st.wait(data.post_wait_time_clear) + tg1.tg_traffic_control(action='run', port_handle=data.port_hand_list) + st.wait(data.post_wait_time_run) + tg1.tg_traffic_control(action='stop', port_handle=data.port_hand_list) + st.wait(data.post_wait_time_stop) + + stats_tg1 = tg1.tg_traffic_stats(port_handle=tg_ph_1, mode='aggregate') + total_tx_tg1 = int(stats_tg1[tg_ph_1]['aggregate']['tx']['total_pkts']) + total_rx_tg1 = int(stats_tg1[tg_ph_1]['aggregate']['rx']['total_pkts']) + tx_tg1_95_precentage = (95 * int(total_tx_tg1)) / 100 + stats_tg2 = tg2.tg_traffic_stats(port_handle=tg_ph_2, mode='aggregate') + total_rx_tg2 = int(stats_tg2[tg_ph_2]['aggregate']['rx']['total_pkts']) + total_tx_tg2 = int(stats_tg2[tg_ph_2]['aggregate']['tx']['total_pkts']) + tx_tg2_95_precentage = (95 * int(total_tx_tg2)) / 100 + st.log('total_tx_tg1 = {}'.format(total_tx_tg1)) + st.log('tx_tg1_95_precentage = {}'.format(tx_tg1_95_precentage)) + st.log('total_rx_tg1 = {}'.format(total_rx_tg1)) + st.log('total_tx_tg2 = {}'.format(total_tx_tg2)) + st.log('tx_tg2_95_precentage = {}'.format(tx_tg2_95_precentage)) + st.log('total_rx_tg2 = {}'.format(total_rx_tg2)) + assert tx_tg1_95_precentage <= total_rx_tg2, st.log("traffic_verification_failed") + assert tx_tg2_95_precentage <= total_rx_tg1, st.log("traffic_verification_failed") + st.log("Traffic test passed..") + + st.log("Sub test 3 : PASSED") + base_line_final_result['test_base_line_l2_taggged_forwarding_with_portchannel'] = 'Pass' + except Exception as e: + st.log(e) + base_line_final_result['test_base_line_l2_taggged_forwarding_with_portchannel'] = 'Fail' + + st.log("#"*90) + st.log("############# Sub test 3: test_base_line_l2_taggged_forwarding_with_portchannel - END " + " ################") + st.log("#"*90) + + if sub_test_4 and base_line_final_result['test_base_line_portchannel_create_delete'] == 'Pass': + st.log("#"*90) + st.log("############# Sub test 4: test_base_line_vlan_port_association - START ################") + st.log("#"*90) + try: + st.log("Test clears the vlan config ..") + vlan_obj.clear_vlan_configuration(st.get_dut_names(), thread=data.clear_parallel) + + st.log("Config Vlan and Port channel on both DUTs") + for dut in topology: + vlan_obj.create_vlan(dut, data.vlan_id) + vlan_obj.add_vlan_member(dut, data.vlan_id, [topology[dut]["TGports"], data.portChannelName], True) + assert vlan_obj.verify_vlan_config(dut, data.vlan_id, tagged=[topology[dut]["TGports"], + data.portChannelName]), \ + st.log("vlan tagged member fail on port {} ,vlan {}".format([topology[dut]["TGports"], + data.portChannelName], data.vlan_id)) + + st.log("Verifying the Port channel status") + for dut in topology: + if not portchannel_obj.verify_portchannel_and_member_status(dut, data.portChannelName, + topology[dut]["ports"], + iter_count=6, iter_delay=1, state='up'): + st.error("port channel {} on DUT {} state fail with {}".format(data.portChannelName, dut, "up")) + assert False + for dut in topology: + if not portchannel_obj.verify_portchannel_state(dut, data.portChannelName, 'up'): + st.log("Port channel is not {}".format('up')) + st.error("port channel {} on DUT {} state fail with {}".format(data.portChannelName, dut, "up")) + assert False + + st.wait(data.wait_post_port_channel_up) + st.log("Sending VLAN traffic and verifying the stats") + tg1.tg_traffic_control(action='clear_stats', port_handle=data.port_hand_list) + st.wait(data.post_wait_time_clear) + tg1.tg_traffic_control(action='run', port_handle=data.port_hand_list) + st.wait(data.post_wait_time_run) + tg1.tg_traffic_control(action='stop', port_handle=data.port_hand_list) + st.wait(data.post_wait_time_stop) + + stats_tg1 = tg1.tg_traffic_stats(port_handle=tg_ph_1, mode='aggregate') + total_tx_tg1 = int(stats_tg1[tg_ph_1]['aggregate']['tx']['total_pkts']) + total_rx_tg1 = int(stats_tg1[tg_ph_1]['aggregate']['rx']['total_pkts']) + tx_tg1_95_precentage = (95 * int(total_tx_tg1)) / 100 + stats_tg2 = tg2.tg_traffic_stats(port_handle=tg_ph_2, mode='aggregate') + total_rx_tg2 = int(stats_tg2[tg_ph_2]['aggregate']['rx']['total_pkts']) + total_tx_tg2 = int(stats_tg2[tg_ph_2]['aggregate']['tx']['total_pkts']) + tx_tg2_95_precentage = (95 * int(total_tx_tg2)) / 100 + st.log('total_tx_tg1 = {}'.format(total_tx_tg1)) + st.log('tx_tg1_95_precentage = {}'.format(tx_tg1_95_precentage)) + st.log('total_rx_tg1 = {}'.format(total_rx_tg1)) + st.log('total_tx_tg2 = {}'.format(total_tx_tg2)) + st.log('tx_tg2_95_precentage = {}'.format(tx_tg2_95_precentage)) + st.log('total_rx_tg2 = {}'.format(total_rx_tg2)) + assert tx_tg1_95_precentage <= total_rx_tg2, st.log("traffic_verification_failed") + assert tx_tg2_95_precentage <= total_rx_tg1, st.log("traffic_verification_failed") + st.log("Traffic test passed..") + + st.log("Debug prints.....") + for dut in topology: + asicapi.dump_vlan(dut) + asicapi.dump_l2(dut) + asicapi.dump_trunk(dut) + + st.log("Validating MAC table..") + dut1_mac_address_list = utils_obj.get_mac_address(data.source_mac, start=0, end=data.mac_addr_cnt) + dut2_mac_address_list = utils_obj.get_mac_address(data.destination_mac, start=0, end=data.mac_addr_cnt) + complete_mac_address_list = dut1_mac_address_list + dut2_mac_address_list + + assert mac_obj.verify_mac_address(vars.D1, data.vlan_id, complete_mac_address_list), \ + st.log("mac_address_verification_fail") + assert mac_obj.verify_mac_address(vars.D2, data.vlan_id, complete_mac_address_list), \ + st.log("mac_address_verification_fail") + st.log("MAC table validation passed..") + + st.log("Sub test 4 : PASSED") + base_line_final_result['test_base_line_vlan_port_association'] = 'Pass' + except Exception as e: + st.log(e) + base_line_final_result['test_base_line_vlan_port_association'] = 'Fail' + + st.log("#"*90) + st.log("############# Sub test 4: test_base_line_vlan_port_association - END ################") + st.log("#"*90) + + if sub_test_5 and base_line_final_result['test_base_line_portchannel_create_delete'] == 'Pass': + st.log("#"*90) + st.log("############# Sub test 5: test_base_line_port_move_from_vlan_a_to_vlan_b - START ################") + st.log("#"*90) + try: + st.log("Test clears the vlan config ..") + vlan_obj.clear_vlan_configuration(st.get_dut_names(), thread=data.clear_parallel) + + st.log("Config Vlan Untagged and Port-channel on both DUTs") + for dut in topology: + vlan_obj.create_vlan(dut, data.vlan_id) + vlan_obj.add_vlan_member(dut, data.vlan_id, [topology[dut]["TGports"], data.portChannelName], False) + assert vlan_obj.verify_vlan_config(dut, data.vlan_id, untagged=[topology[dut]["TGports"], + data.portChannelName]), \ + st.log("vlan tagged member fail on port {} ,vlan {}".format([topology[dut]["TGports"], + data.portChannelName], data.vlan_id)) + + st.log("Verifying the Port channel status") + for dut in topology: + if not portchannel_obj.verify_portchannel_and_member_status(dut, data.portChannelName, + topology[dut]["ports"], + iter_count=6, iter_delay=1, state='up'): + st.log("port channel {} on DUT {} state fail with {}".format(data.portChannelName, dut, "up")) + for dut in topology: + if not portchannel_obj.verify_portchannel_state(dut, data.portChannelName, 'up'): + st.log("Port channel is not {}".format('up')) + st.log("port channel {} on DUT {} state fail with {}".format(data.portChannelName, dut, "up")) + st.wait(data.wait_post_port_channel_up) + + st.log("Debug prints.....") + for dut in topology: + asicapi.dump_vlan(dut) + asicapi.dump_l2(dut) + asicapi.dump_trunk(dut) + + st.log("Sending VLAN traffic and verifying the stats") + tg1.tg_traffic_control(action='clear_stats', port_handle=data.port_hand_list) + st.wait(data.post_wait_time_clear) + tg1.tg_traffic_control(action='run', port_handle=data.port_hand_list) + st.wait(data.post_wait_time_run) + tg1.tg_traffic_control(action='stop', port_handle=data.port_hand_list) + st.wait(data.post_wait_time_stop) + + stats_tg1 = tg1.tg_traffic_stats(port_handle=tg_ph_1, mode='aggregate') + total_tx_tg1 = int(stats_tg1[tg_ph_1]['aggregate']['tx']['total_pkts']) + total_rx_tg1 = int(stats_tg1[tg_ph_1]['aggregate']['rx']['total_pkts']) + tx_tg1_95_precentage = (95 * int(total_tx_tg1)) / 100 + stats_tg2 = tg2.tg_traffic_stats(port_handle=tg_ph_2, mode='aggregate') + total_rx_tg2 = int(stats_tg2[tg_ph_2]['aggregate']['rx']['total_pkts']) + total_tx_tg2 = int(stats_tg2[tg_ph_2]['aggregate']['tx']['total_pkts']) + tx_tg2_95_precentage = (95 * int(total_tx_tg2)) / 100 + st.log('total_tx_tg1 = {}'.format(total_tx_tg1)) + st.log('tx_tg1_95_precentage = {}'.format(tx_tg1_95_precentage)) + st.log('total_rx_tg1 = {}'.format(total_rx_tg1)) + st.log('total_tx_tg2 = {}'.format(total_tx_tg2)) + st.log('tx_tg2_95_precentage = {}'.format(tx_tg2_95_precentage)) + st.log('total_rx_tg2 = {}'.format(total_rx_tg2)) + assert tx_tg1_95_precentage <= total_rx_tg2, st.log("traffic_verification_failed") + assert tx_tg2_95_precentage <= total_rx_tg1, st.log("traffic_verification_failed") + st.log("Traffic test passed..") + + st.log("Debug prints.....") + for dut in topology: + asicapi.dump_vlan(dut) + asicapi.dump_l2(dut) + asicapi.dump_trunk(dut) + + st.log("Validating MAC table..") + dut1_mac_address_list = utils_obj.get_mac_address(data.source_mac, start=0, end=data.mac_addr_cnt) + dut2_mac_address_list = utils_obj.get_mac_address(data.destination_mac, start=0, end=data.mac_addr_cnt) + complete_mac_address_list = dut1_mac_address_list + dut2_mac_address_list + + assert mac_obj.verify_mac_address(vars.D1, data.vlan_id, complete_mac_address_list), \ + st.log("mac_address_verification_fail") + assert mac_obj.verify_mac_address(vars.D2, data.vlan_id, complete_mac_address_list), \ + st.log("mac_address_verification_fail") + st.log("MAC table validation passed..") + + st.log("Sub test 5 : PASSED") + base_line_final_result['test_base_line_port_move_from_vlan_a_to_vlan_b'] = 'Pass' + except Exception as e: + st.log(e) + base_line_final_result['test_base_line_port_move_from_vlan_a_to_vlan_b'] = 'Fail' + + st.log("#"*90) + st.log("############# Sub test 5: test_base_line_port_move_from_vlan_a_to_vlan_b - END ################") + st.log("#"*90) + + # Result printing + st.log(sprint_vtable(['Test', 'Result'], + [[k, v] for k, v in filter_and_select([base_line_final_result], + ['test_base_line_portchannel_create_delete', + 'test_base_line_random_link_flap_portchannel', + 'test_base_line_l2_taggged_forwarding_with_portchannel', + 'test_base_line_vlan_port_association', + 'test_base_line_port_move_from_vlan_a_to_vlan_b'] + )[0].items()], 0)) + + st.log("POST TEST : Cleanup call are started.") + ip_obj.clear_ip_configuration(st.get_dut_names(), thread=data.clear_parallel) + ip_obj.clear_ip_configuration(st.get_dut_names(), 'ipv6', thread=data.clear_parallel) + vlan_obj.clear_vlan_configuration(st.get_dut_names(), thread=data.clear_parallel) + portchannel_obj.clear_portchannel_configuration(st.get_dut_names(), thread=data.clear_parallel) + + out = base_line_final_result['test_base_line_portchannel_create_delete'] + if out == 'Pass': + st.report_pass("test_case_passed") + elif out == 'Fail': + st.report_fail("test_case_failed") + else: + st.report_fail("test_case_not_executed") + + +@pytest.mark.base_test_sanity +@pytest.mark.base_test_sanity_optimize +@pytest.mark.depends("test_base_line_portchannel_create_delete") +@pytest.mark.community +@pytest.mark.community_pass +def test_base_line_random_link_flap_portchannel(): + out = base_line_final_result['test_base_line_random_link_flap_portchannel'] + if out == 'Pass': + st.report_pass("test_case_passed") + elif out == 'Fail': + st.report_fail("test_case_failed") + else: + st.report_fail("test_case_not_executed") + + +@pytest.mark.base_test_sanity +@pytest.mark.base_test_sanity_optimize +@pytest.mark.depends("test_base_line_portchannel_create_delete") +@pytest.mark.community +@pytest.mark.community_pass +def test_base_line_l2_taggged_forwarding_with_portchannel(): + out = base_line_final_result['test_base_line_l2_taggged_forwarding_with_portchannel'] + if out == 'Pass': + st.report_pass("test_case_passed") + elif out == 'Fail': + st.report_fail("test_case_failed") + else: + st.report_fail("test_case_not_executed") + + +@pytest.mark.base_test_sanity +@pytest.mark.base_test_sanity_optimize +@pytest.mark.depends("test_base_line_portchannel_create_delete") +@pytest.mark.community +@pytest.mark.community_pass +def test_base_line_vlan_port_association(): + out = base_line_final_result['test_base_line_vlan_port_association'] + if out == 'Pass': + st.report_pass("test_case_passed") + elif out == 'Fail': + st.report_fail("test_case_failed") + else: + st.report_fail("test_case_not_executed") + + +@pytest.mark.base_test_sanity +@pytest.mark.base_test_sanity_optimize +@pytest.mark.depends("test_base_line_portchannel_create_delete") +def test_base_line_port_move_from_vlan_a_to_vlan_b(): + out = base_line_final_result['test_base_line_port_move_from_vlan_a_to_vlan_b'] + if out == 'Pass': + st.report_pass("test_case_passed") + elif out == 'Fail': + st.report_fail("test_case_failed") + else: + st.report_fail("test_case_not_executed") + + +@pytest.mark.base_test_sanity +@pytest.mark.base_test_sanity_optimize +def test_base_line_vlan_create_delete_and_mac_learning_with_bum(): + + vars = st.get_testbed_vars() + # Sub test selection + sub_test_8 = 1 + sub_test_10 = 1 + sub_test_11 = 1 + # Global and Topology variable + data.vlan_id_start = 101 + data.vlan_id_end = 101 + data.vlan_range = (data.vlan_id_end-data.vlan_id_start) + 1 + data.multicast_mac = "01:00:5E:00:00:07" + data.broacast_mac = "FF:FF:FF:FF:FF:FF" + data.unknown_mac = "00:01:02:03:04:05" + data.source_mac = "00:56:78:98:09:45" + data.destination_mac = "00:56:78:98:10:55" + data.rate_pps = 100 + data.vlan = random_vlan_list()[0] + data.vlan_1 = random_vlan_list()[0] + data.tagged_members = [vars.D1T1P1, vars.D1T1P3] + data.tagged_members_1 = [vars.D1T1P2, vars.D1T1P3] + data.ageing_time = 20 + data.age_out_mac_addr = "00:00:00:00:00:09" + data.vlan_id = str(random_vlan_list()[0]) + data.mac_addr_cnt = 2 + data.tg_con_interface = [vars.D1T1P1, vars.D1T1P2, vars.D1T1P3] + + tg1, tg_ph_1 = tgapi.get_handle_byname("T1D1P1") + tg2, tg_ph_2 = tgapi.get_handle_byname("T1D1P2") + tg3, tg_ph_3 = tgapi.get_handle_byname("T1D1P3") + + data.port_hand_list = [tg_ph_1, tg_ph_2, tg_ph_3] + + st.log("PRE TEST : Cleanup call are started.") + ip_obj.clear_ip_configuration(st.get_dut_names(), thread=data.clear_parallel) + ip_obj.clear_ip_configuration(st.get_dut_names(), 'ipv6', thread=data.clear_parallel) + vlan_obj.clear_vlan_configuration(st.get_dut_names(), thread=data.clear_parallel) + portchannel_obj.clear_portchannel_configuration(st.get_dut_names(), thread=data.clear_parallel) + + intf_obj.clear_interface_counters(vars.D1) + + if sub_test_8: + st.log("#"*90) + st.log("############# Sub test 8: test_base_line_vlan_create_delete_and_mac_learning_with_bum - START " + "################") + st.log("#"*90) + try: + st.log("Create and configure Vlan and adding member as tagged members.") + vlan_obj.create_vlan(vars.D1, data.vlan) + vlan_obj.add_vlan_member(vars.D1, data.vlan, data.tg_con_interface, True) + assert vlan_obj.verify_vlan_config(vars.D1, data.vlan, tagged=data.tg_con_interface), \ + st.log("vlan tagged member fail on port {} ,vlan {}".format(data.tg_con_interface, data.vlan)) + + st.log("Start testing the bum traffic ...") + mac_addr_list = {"Broadcast": data.broacast_mac, "Multicast": data.multicast_mac, + "Unknown": data.unknown_mac} + mac_incr_cnt = 7 + final_total_tx_tg1 = 0 + st.log("Clearing TG config ") + tg1.tg_traffic_control(action='clear_stats', port_handle=data.port_hand_list) + for mac_addr in mac_addr_list: + st.log("Start '{}' traffic test with destination MAC = {}".format(mac_addr, mac_addr_list[mac_addr])) + new_src_mac = "00:00:00:00:09:4{}".format(mac_incr_cnt) + + st.log("Reseting and creating the traffic stream.") + tg1.tg_traffic_control(port_handle=tg_ph_1, action='reset') + tg1.tg_traffic_config(port_handle=tg_ph_1, mode='create', rate_pps=data.rate_pps, mac_src=new_src_mac, + transmit_mode="continuous", mac_dst=mac_addr_list[mac_addr], + l2_encap='ethernet_ii_vlan', + vlan_id=data.vlan, vlan="enable") + st.log("Sending traffic and verifying the stats") + tg1.tg_traffic_control(port_handle=tg_ph_1, action='run') + st.wait(data.post_wait_time_run) + tg1.tg_traffic_control(port_handle=tg_ph_1, action='stop') + st.wait(data.post_wait_time_stop) + stats_tg1 = tg1.tg_traffic_stats(port_handle=tg_ph_1, mode="aggregate") + total_tx_tg1 = int(stats_tg1[tg_ph_1]['aggregate']['tx']['total_pkts']) + final_total_tx_tg1 += total_tx_tg1 + st.log("total_tx_tg1 = {}".format(total_tx_tg1)) + + st.log('MAC address validation.') + if not mac_obj.verify_mac_address_table(vars.D1, new_src_mac, port=vars.D1T1P1): + st.error("MAC '{}' is failed to learn in port = {}".format(new_src_mac, vars.D1T1P1)) + assert False + st.log('MAC address validation passed.') + mac_incr_cnt += 1 + + tx_tg1_95_precentage = (95 * int(final_total_tx_tg1)) / 100 + stats_tg2 = tg2.tg_traffic_stats(port_handle=tg_ph_2, mode="aggregate") + total_rx_tg2 = int(stats_tg2[tg_ph_2]['aggregate']['rx']['total_pkts']) + stats_tg3 = tg3.tg_traffic_stats(port_handle=tg_ph_3, mode="aggregate") + total_rx_tg3 = int(stats_tg3[tg_ph_3]['aggregate']['rx']['total_pkts']) + st.log("final_total_tx_tg1 = {}".format(final_total_tx_tg1)) + st.log("tx_tg1_95_precentage = {}".format(tx_tg1_95_precentage)) + st.log("total_rx_tg2 = {}".format(total_rx_tg2)) + st.log("total_rx_tg3 = {}".format(total_rx_tg3)) + intf_obj.show_specific_interface_counters(vars.D1, vars.D1T1P1) + intf_obj.show_specific_interface_counters(vars.D1, vars.D1T1P2) + intf_obj.show_specific_interface_counters(vars.D1, vars.D1T1P3) + if not tx_tg1_95_precentage <= total_rx_tg2: + st.error("traffic_verification_failed") + assert False + if not tx_tg1_95_precentage <= total_rx_tg3: + st.error("traffic_verification_failed") + assert False + st.log("Traffic test passed..") + + base_line_final_result['test_base_line_vlan_create_delete_and_mac_learning_with_bum'] = 'Pass' + except Exception as e: + st.log(e) + base_line_final_result['test_base_line_vlan_create_delete_and_mac_learning_with_bum'] = 'Fail' + + st.log("#"*90) + st.log("############# Sub test 8: test_base_line_vlan_create_delete_and_mac_learning_with_bum - END " + " ################") + st.log("#"*90) + + if sub_test_10 and base_line_final_result['test_base_line_vlan_create_delete_and_mac_learning_with_bum'] == 'Pass': + st.log("#"*90) + st.log("############# Sub test 10: test_base_line_mac_move_single_vlan - START ################") + st.log("#"*90) + try: + st.log("Clearing the MAC table entries.") + mac_obj.clear_mac(vars.D1) + st.wait(data.clear_mac_table_wait_time) + st.log("Configuring the mac aging time - {}".format(0)) + mac_obj.config_mac_agetime(vars.D1, 0) + intf_obj.show_interface_counters_all(vars.D1) + # Step 2 + # Test - Start sending traffic from port 1 with mac address 00:00:00:00:11:11. + # Expected - Verify that MAC address 00:00:00:00:11:11 learned on port 1. + st.log("Reseting ,creating and start the traffic stream on TG1.") + tg1.tg_traffic_control(action='reset', port_handle=tg_ph_1) + tg1.tg_traffic_config(port_handle=tg_ph_1, mode='create', rate_pps=1, mac_src='00:00:00:00:11:11', + transmit_mode="continuous", mac_dst='00:00:00:00:00:22', + l2_encap='ethernet_ii_vlan', vlan_id=data.vlan, vlan="enable") + st.wait(data.post_wait_time_create) + tg1.tg_traffic_control(action='run', port_handle=tg_ph_1) + st.wait(data.post_wait_time_run) + intf_obj.show_interface_counters_all(vars.D1) + st.log("Validating MAC table..") + if not mac_obj.verify_mac_address_table(vars.D1, "00:00:00:00:11:11", vlan=data.vlan, port=vars.D1T1P1): + st.log("mac_address_verification_fail") + assert False + st.log("MAC address validation passed.") + tg1.tg_traffic_control(action='stop', port_handle=tg_ph_1) + st.wait(data.post_wait_time_stop) + + # Step 3 + # Test - Now start sending traffic from port 2 with same MAC address 00:00:00:00:11:11. + # Expected - Verify that MAC address 00:00:00:00:11:11 learned on port 1 flushed out and learned on port 2" + st.log("Reseting ,creating and start the traffic stream on TG2.") + tg2.tg_traffic_control(action='reset', port_handle=tg_ph_2) + tg2.tg_traffic_config(port_handle=tg_ph_2, mode='create', rate_pps=1, mac_src='00:00:00:00:11:11', + transmit_mode="continuous", mac_dst='00:00:00:00:00:22', + l2_encap='ethernet_ii_vlan', vlan_id=data.vlan, vlan="enable") + st.wait(data.post_wait_time_create) + tg2.tg_traffic_control(action='run', port_handle=tg_ph_2) + st.wait(data.post_wait_time_run) + st.log("Validating MAC table..") + intf_obj.show_interface_counters_all(vars.D1) + st.log("Checking the stats for tx TG") + stats1 = tg1.tg_traffic_stats(port_handle=tg_ph_1, mode='aggregate') + total_tx1 = int(stats1[tg_ph_1]['aggregate']['tx']['total_pkts']) + stats2 = tg2.tg_traffic_stats(port_handle=tg_ph_2, mode='aggregate') + total_tx2 = int(stats2[tg_ph_2]['aggregate']['tx']['total_pkts']) + st.log("looping to verify traffic is sent or not") + for i in range(1,5,1): + if total_tx1 and total_tx2 == 0: + st.wait(data.post_wait_time_run) + + if mac_obj.verify_mac_address_table(vars.D1, "00:00:00:00:11:11", vlan=data.vlan, port=vars.D1T1P1): + st.error("failed_clear_mac_learned_on first port") + assert False + if not mac_obj.verify_mac_address_table(vars.D1, "00:00:00:00:11:11", vlan=data.vlan, port=vars.D1T1P2): + st.error("failed_to_learn_mac_after_move") + assert False + st.log("MAC address validation passed.") + tg2.tg_traffic_control(action='stop', port_handle=tg_ph_2) + st.wait(data.post_wait_time_stop) + + # Sending traffic to check if mac has moved or not. + st.log("Reseting ,creating and start the traffic stream on TG3.") + tg3.tg_traffic_control(action='reset', port_handle=tg_ph_3) + tg3.tg_traffic_config(port_handle=tg_ph_3, mode='create', rate_pps=100, mac_dst='00:00:00:00:11:11', + transmit_mode="continuous", mac_src='00:00:00:00:00:33', + l2_encap='ethernet_ii_vlan', vlan_id=data.vlan, vlan="enable") + st.wait(data.post_wait_time_create) + tg1.tg_traffic_control(action='clear_stats', port_handle=data.port_hand_list) + st.wait(data.post_wait_time_clear) + st.wait(data.post_wait_time_create) + tg3.tg_traffic_control(action='run', port_handle=tg_ph_3) + st.wait(data.post_wait_time_run) + tg3.tg_traffic_control(action='stop', port_handle=tg_ph_3) + st.wait(data.post_wait_time_stop) + intf_obj.show_interface_counters_all(vars.D1) + # stats fetching + stats1 = tg1.tg_traffic_stats(port_handle=tg_ph_1, mode='aggregate') + total_rx1 = int(stats1[tg_ph_1]['aggregate']['rx']['total_pkts']) + stats2 = tg2.tg_traffic_stats(port_handle=tg_ph_2, mode='aggregate') + total_rx2 = int(stats2[tg_ph_2]['aggregate']['rx']['total_pkts']) + stats3 = tg3.tg_traffic_stats(port_handle=tg_ph_3, mode='aggregate') + total_tx3 = int(stats3[tg_ph_3]['aggregate']['tx']['total_pkts']) + st.log("Sent Packets On Port 3: {} and Received Packets On Port 1: {} and Received Packets " + "On Port 2: {}".format(total_tx3, total_rx1, total_rx2)) + + if total_tx3 == 0: + st.error("Traffic verification failed: Traffic not initiated from TG3.") + assert False + # This is for allowance towards control packets like LLDP etc.. + if total_rx1 > 10: + st.error("Traffic verification failed : Traffic should not be received on TG1, but received.") + assert False + if total_rx2 < total_tx3: + st.error("Traffic verification failed : Traffic from TG3 not fully received on TG2.") + assert False + + st.log("Traffic test passed..") + base_line_final_result['test_base_line_mac_move_single_vlan'] = 'Pass' + except Exception as e: + st.log(e) + base_line_final_result['test_base_line_mac_move_single_vlan'] = 'Fail' + + st.log("#"*90) + st.log("############# Sub test 10: test_base_line_mac_move_single_vlan - END ################") + st.log("#"*90) + + if sub_test_11 and base_line_final_result['test_base_line_vlan_create_delete_and_mac_learning_with_bum'] == 'Pass'\ + and base_line_final_result['test_base_line_mac_move_single_vlan'] == 'Pass': + st.log("#"*90) + st.log("############# Sub test 11: test_base_line_mac_move_across_vlans - START ################") + st.log("#"*90) + try: + st.log("Creating second vlan config ") + vlan_obj.create_vlan(vars.D1, data.vlan_1) + vlan_obj.add_vlan_member(vars.D1, data.vlan_1, data.tagged_members_1, True) + if not vlan_obj.verify_vlan_config(vars.D1, data.vlan_1, tagged=data.tagged_members_1): + st.error("vlan tagged member fail on port {} ,vlan {}".format(data.tagged_members_1, data.vlan_1)) + assert False + + mac_obj.clear_mac(vars.D1) + st.wait(data.clear_mac_table_wait_time) + mac_obj.config_mac_agetime(vars.D1, 0) + intf_obj.show_interface_counters_all(vars.D1) + # Step 2 + # Test - Start sending traffic from port 1 with mac address 00:00:00:00:11:11. + # Expected - Verify that MAC address 00:00:00:00:11:11 learned on port 1. + tg1.tg_traffic_control(action='reset', port_handle=tg_ph_1) + tg1.tg_traffic_config(port_handle=tg_ph_1, mode='create', rate_pps=1, mac_src='00:00:00:00:11:11', + transmit_mode="continuous", mac_dst='00:00:00:00:00:22', + l2_encap='ethernet_ii_vlan', vlan_id=data.vlan, vlan="enable") + st.wait(data.post_wait_time_create) + tg1.tg_traffic_control(action='run', port_handle=tg_ph_1) + st.wait(data.post_wait_time_run) + + st.log("Validating MAC table..") + intf_obj.show_interface_counters_all(vars.D1) + if not mac_obj.verify_mac_address_table(vars.D1, "00:00:00:00:11:11", vlan=data.vlan, port=vars.D1T1P1): + st.error("mac_failed_to_learn_on_firt_port") + assert False + st.log("MAC address validation passed.") + tg1.tg_traffic_control(action='stop', port_handle=tg_ph_1) + + # Step 3 + # Test - Now start sending traffic from port 2 with same MAC address 00:00:00:00:11:11. + # Expected - Verify that MAC address 00:00:00:00:11:11 learned on port 1 flushed out and learned on port 2" + tg2.tg_traffic_control(action='reset', port_handle=tg_ph_2) + tg2.tg_traffic_config(port_handle=tg_ph_2, mode='create', rate_pps=1, mac_src='00:00:00:00:11:11', + transmit_mode="continuous", mac_dst='00:00:00:00:00:22', + l2_encap='ethernet_ii_vlan', vlan_id=data.vlan_1, vlan="enable") + st.wait(data.post_wait_time_create) + tg2.tg_traffic_control(action='run', port_handle=tg_ph_2) + st.wait(data.post_wait_time_run) + + st.log("Validating MAC table..") + intf_obj.show_interface_counters_all(vars.D1) + st.log("Checking the stats for tx TG") + stats1 = tg1.tg_traffic_stats(port_handle=tg_ph_1, mode='aggregate') + total_tx1 = int(stats1[tg_ph_1]['aggregate']['tx']['total_pkts']) + stats2 = tg2.tg_traffic_stats(port_handle=tg_ph_2, mode='aggregate') + total_tx2 = int(stats2[tg_ph_2]['aggregate']['tx']['total_pkts']) + st.log("looping to verify traffic is sent or not") + for i in range(1, 5, 1): + if total_tx1 and total_tx2 == 0: + st.wait(data.post_wait_time_run) + if not mac_obj.verify_mac_address_table(vars.D1, "00:00:00:00:11:11", vlan=data.vlan, port=vars.D1T1P1): + st.error("mac_address_not_clear") + assert False + st.log("MAC address validation passed.") + + st.log("Validating MAC table..") + if not mac_obj.verify_mac_address_table(vars.D1, "00:00:00:00:11:11", vlan=data.vlan_1, port=vars.D1T1P2): + st.error("mac_failed_to_learn_on_second_port") + assert False + st.log("MAC address validation passed.") + tg2.tg_traffic_control(action='stop', port_handle=tg_ph_2) + + # Sending traffic to check if mac has moved or not. + tg3.tg_traffic_control(action='reset', port_handle=tg_ph_3) + tg3.tg_traffic_config(port_handle=tg_ph_3, mode='create', rate_pps=10, mac_dst='00:00:00:00:11:11', + transmit_mode="continuous", mac_src='00:00:00:00:00:33', + l2_encap='ethernet_ii_vlan', vlan_id=data.vlan, vlan="enable") + st.wait(data.post_wait_time_create) + tg1.tg_traffic_control(action='clear_stats', port_handle=data.port_hand_list) + + st.wait(data.post_wait_time_clear) + tg3.tg_traffic_control(action='run', port_handle=tg_ph_3) + st.wait(data.post_wait_time_run) + tg3.tg_traffic_control(action='stop', port_handle=tg_ph_3) + st.wait(data.post_wait_time_stop) + intf_obj.show_interface_counters_all(vars.D1) + # stats fetching + stats1 = tg1.tg_traffic_stats(port_handle=tg_ph_1, mode='aggregate') + total_rx1 = int(stats1[tg_ph_1]['aggregate']['rx']['total_pkts']) + stats2 = tg2.tg_traffic_stats(port_handle=tg_ph_2, mode='aggregate') + total_rx2 = int(stats2[tg_ph_2]['aggregate']['rx']['total_pkts']) + stats3 = tg3.tg_traffic_stats(port_handle=tg_ph_3, mode='aggregate') + total_tx3 = int(stats3[tg_ph_3]['aggregate']['tx']['total_pkts']) + + st.log("Sent Packets On Port 3: {} and Received Packets On Port 1: {} and Received Packets On" + " Port 2: {}".format(total_tx3, total_rx1, total_rx2)) + + if total_tx3 == 0: + st.error("Traffic verification failed: Traffic not initiated from TG3.") + assert False + # This is for allowance towards control packets like LLDP etc.. + if total_rx2 > 10: + st.error("Traffic verification failed : Traffic should not be received on TG2, but received.") + assert False + if total_rx1 < total_tx3: + st.error("Traffic verification failed : Traffic from TG3 not fully received on TG1.") + assert False + st.log("Traffic test passed..") + + # Sending traffic to check if mac has moved or not. + tg1.tg_traffic_control(action='clear_stats', port_handle=data.port_hand_list) + st.wait(data.post_wait_time_clear) + + tg3.tg_traffic_control(action='reset', port_handle=tg_ph_3) + tg3.tg_traffic_config(port_handle=tg_ph_3, mode='create', rate_pps=10, mac_dst='00:00:00:00:11:11', + transmit_mode="continuous", mac_src='00:00:00:00:00:33', + l2_encap='ethernet_ii_vlan', vlan_id=data.vlan_1, vlan="enable") + st.wait(data.post_wait_time_create) + tg3.tg_traffic_control(action='run', port_handle=tg_ph_3) + st.wait(data.post_wait_time_run) + tg3.tg_traffic_control(action='stop', port_handle=tg_ph_3) + st.wait(data.post_wait_time_stop) + intf_obj.show_interface_counters_all(vars.D1) + # stats fetching + stats1 = tg1.tg_traffic_stats(port_handle=tg_ph_1, mode='aggregate') + total_rx1 = int(stats1[tg_ph_1]['aggregate']['rx']['total_pkts']) + stats2 = tg2.tg_traffic_stats(port_handle=tg_ph_2, mode='aggregate') + total_rx2 = int(stats2[tg_ph_2]['aggregate']['rx']['total_pkts']) + stats3 = tg3.tg_traffic_stats(port_handle=tg_ph_3, mode='aggregate') + total_tx3 = int(stats3[tg_ph_3]['aggregate']['tx']['total_pkts']) + + st.log("Sent Packets On Port 3: {} and Received Packets On Port 1: {} and Received Packets " + "On Port 2: {}".format(total_tx3, total_rx1, total_rx2)) + if total_tx3 == 0: + st.error("Traffic verification failed: Traffic not initiated from TG3.") + assert False + # This is for allowance towards control packets like LLDP etc.. + if total_rx1 > 10: + st.error("Traffic verification failed : Traffic should not be received on TG1, but received.") + assert False + if total_rx2 < total_tx3: + st.error("Traffic verification failed : Traffic from TG3 not fully received on TG2.") + assert False + st.log("Traffic test passed..") + + base_line_final_result['test_base_line_mac_move_across_vlans'] = 'Pass' + except Exception as e: + st.log(e) + base_line_final_result['test_base_line_mac_move_across_vlans'] = 'Fail' + + st.log("#"*90) + st.log("############# Sub test 11: test_base_line_mac_move_across_vlans - END ################") + st.log("#"*90) + + st.log( + sprint_vtable(['Test', 'Result'], + [[k, v] for k, v in + filter_and_select([base_line_final_result], + ['test_base_line_vlan_create_delete_and_mac_learning_with_bum', + 'test_base_line_mac_move_single_vlan', + 'test_base_line_mac_move_across_vlans'] + )[0].items()], 0)) + + st.log("POST TEST : Cleanup call are started.") + ip_obj.clear_ip_configuration(st.get_dut_names(), thread=data.clear_parallel) + ip_obj.clear_ip_configuration(st.get_dut_names(), 'ipv6', thread=data.clear_parallel) + vlan_obj.clear_vlan_configuration(st.get_dut_names(), thread=data.clear_parallel) + portchannel_obj.clear_portchannel_configuration(st.get_dut_names(), thread=data.clear_parallel) + + out = base_line_final_result['test_base_line_vlan_create_delete_and_mac_learning_with_bum'] + if out == 'Pass': + st.report_pass("test_case_passed") + elif out == 'Fail': + st.report_fail("test_case_failed") + else: + st.report_fail("test_case_not_executed") + + +@pytest.mark.base_test_sanity +@pytest.mark.base_test_sanity_optimize +@pytest.mark.depends("test_base_line_vlan_create_delete_and_mac_learning_with_bum") +def test_base_line_mac_move_single_vlan(): + out = base_line_final_result['test_base_line_mac_move_single_vlan'] + if out == 'Pass': + st.report_pass("test_case_passed") + elif out == 'Fail': + st.report_fail("test_case_failed") + else: + st.report_fail("test_case_not_executed") + + +@pytest.mark.base_test_sanity +@pytest.mark.base_test_sanity_optimize +@pytest.mark.depends("test_base_line_vlan_create_delete_and_mac_learning_with_bum") +def test_base_line_mac_move_across_vlans(): + out = base_line_final_result['test_base_line_mac_move_across_vlans'] + if out == 'Pass': + st.report_pass("test_case_passed") + elif out == 'Fail': + st.report_fail("test_case_failed") + else: + st.report_fail("test_case_not_executed") + + +@pytest.mark.base_test_sanity +@pytest.mark.base_test_sanity_optimize +def test_base_line_sflow_config_global_enable_disable_klish(): + """ + To verify basic KLISH commands in base sanity. + """ + vars = st.get_testbed_vars() + cli_type = 'klish' + enable_disable_config(vars.D1, action="enable", cli_type=cli_type) + if not verify_config(vars.D1, data=[{'state': 'up'}], cli_type=cli_type): + st.report_fail("test_case_failed") + enable_disable_config(vars.D1, action="disable", cli_type=cli_type) + if not verify_config(vars.D1, data=[{'state': 'down'}], cli_type=cli_type): + st.report_fail("test_case_failed") + st.report_pass("test_case_passed") diff --git a/spytest/tests/sanity/test_sanity_l3.py b/spytest/tests/sanity/test_sanity_l3.py new file mode 100644 index 00000000000..0451e49fd3b --- /dev/null +++ b/spytest/tests/sanity/test_sanity_l3.py @@ -0,0 +1,267 @@ +import pytest + +from spytest import st, tgapi, SpyTestDict + +import apis.routing.ip as ipfeature +import apis.switching.vlan as vapi +import apis.system.port as papi +import apis.system.interface as intapi +import apis.routing.ip as ip_obj +import apis.switching.portchannel as portchannel_obj +import apis.switching.vlan as vlan_obj +import apis.system.basic as basic_obj + +data = SpyTestDict() +data.my_dut_list = None +data.local = None +data.remote = None +data.mask = "24" +data.counters_threshold = 10 +data.tgen_stats_threshold = 20 +data.tgen_rate_pps = '1000' +data.tgen_l3_len = '500' +data.traffic_run_time = 20 +data.clear_parallel = True + +data.d1t1_ip_addr = "192.168.11.1" +data.d1d2_ip_addr = "192.168.12.1" +data.d2d1_ip_addr = "192.168.12.2" +data.d2t1_ip_addr = "192.168.13.1" +data.t1d1_ip_addr = "192.168.11.2" +data.t1d2_ip_addr = "192.168.13.2" +data.static_ip_list = ["192.168.11.0/24","192.168.13.0/24"] + +data.d1t1_ip_addr_v6 = "2011::1" +data.d1d2_ip_addr_v6 = "2012::1" +data.d2d1_ip_addr_v6 = "2012::2" +data.d2t1_ip_addr_v6 = "2013::1" +data.static_ipv6_list = ["2011::0/64","2013::0/64"] +data.mask_v6 = "64" + +def get_handles(): + tg1, tg_ph_1 = tgapi.get_handle_byname("T1D1P1") + tg2, tg_ph_2 = tgapi.get_handle_byname("T1D2P1") + return (tg1, tg2, tg_ph_1, tg_ph_2) + +@pytest.fixture(scope="module", autouse=True) +def sanity_l3_module_hooks(request): + yield + +def verifyPortStatus(): + data.my_dut_list = st.get_dut_names() + intapi.interface_noshutdown(vars.D1, [vars.D1T1P1,vars.D1D2P1]) + intapi.interface_noshutdown(vars.D2, [vars.D2T1P1,vars.D2D1P1]) + st.wait(5) + for dut,portList in zip(data.my_dut_list,[[vars.D1T1P1,vars.D1D2P1] ,[vars.D2T1P1,vars.D2D1P1]]): + for port in portList: + if not intapi.verify_interface_status(dut,port,'oper', 'up'): + return False + return True + + +def pre_test_l3_fwding(): + # override from testbed + data.my_dut_list = st.get_dut_names() + if len(data.my_dut_list) < 2: + st.report_fail("operation_failed") + return + dut1 = data.my_dut_list[0] + dut2 = data.my_dut_list[1] + if not verifyPortStatus(): + st.report_fail("operation_failed") + + ipfeature.config_ip_addr_interface(dut1, vars.D1T1P1, data.d1t1_ip_addr,data.mask) + ipfeature.config_ip_addr_interface(dut1, vars.D1D2P1, data.d1d2_ip_addr,data.mask) + ipfeature.config_ip_addr_interface(dut2, vars.D2D1P1, data.d2d1_ip_addr,data.mask) + ipfeature.config_ip_addr_interface(dut2, vars.D2T1P1, data.d2t1_ip_addr,data.mask) + ipfeature.create_static_route(dut1, data.d2d1_ip_addr, data.static_ip_list[1]) + ipfeature.create_static_route(dut2, data.d1d2_ip_addr, data.static_ip_list[0]) + # ipv6 + ipfeature.config_ip_addr_interface(dut1, vars.D1T1P1, data.d1t1_ip_addr_v6,data.mask_v6,family='ipv6') + ipfeature.config_ip_addr_interface(dut1, vars.D1D2P1, data.d1d2_ip_addr_v6,data.mask_v6,family='ipv6') + ipfeature.config_ip_addr_interface(dut2, vars.D2D1P1, data.d2d1_ip_addr_v6,data.mask_v6,family='ipv6') + ipfeature.config_ip_addr_interface(dut2, vars.D2T1P1, data.d2t1_ip_addr_v6,data.mask_v6,family='ipv6') + ipfeature.create_static_route(dut1, data.d2d1_ip_addr_v6, data.static_ipv6_list[1], family = 'ipv6') + ipfeature.create_static_route(dut2, data.d1d2_ip_addr_v6, data.static_ipv6_list[0], family = 'ipv6') + +def post_test_l3_fwding(): + data.my_dut_list = st.get_dut_names() + dut1 = data.my_dut_list[0] + dut2 = data.my_dut_list[1] + + ipfeature.delete_static_route(dut1, data.d2d1_ip_addr, data.static_ip_list[1]) + ipfeature.delete_static_route(dut2, data.d1d2_ip_addr, data.static_ip_list[0]) + ipfeature.delete_ip_interface(dut1, vars.D1T1P1, data.d1t1_ip_addr,data.mask) + ipfeature.delete_ip_interface(dut1, vars.D1D2P1, data.d1d2_ip_addr,data.mask) + ipfeature.delete_ip_interface(dut2, vars.D2D1P1, data.d2d1_ip_addr,data.mask) + ipfeature.delete_ip_interface(dut2, vars.D2T1P1, data.d2t1_ip_addr,data.mask) + + ipfeature.delete_ip_interface(dut1, vars.D1T1P1, data.d1t1_ip_addr_v6,data.mask_v6,family='ipv6') + ipfeature.delete_ip_interface(dut1, vars.D1D2P1, data.d1d2_ip_addr_v6,data.mask_v6,family='ipv6') + ipfeature.delete_ip_interface(dut2, vars.D2D1P1, data.d2d1_ip_addr_v6,data.mask_v6,family='ipv6') + ipfeature.delete_ip_interface(dut2, vars.D2T1P1, data.d2t1_ip_addr_v6,data.mask_v6,family='ipv6') + st.show(dut1, "show vlan config") + +@pytest.fixture(scope="module", autouse=True) +def sanity_l3_func_hooks(request): + # add things at the start every test case + # use 'request.function.func_name' to compare + # if any thing specific a particular test case + global vars + vars = st.ensure_min_topology("D1D2:1", "D1T1:1", "D2T1:1") + st.log("POST TEST : Cleanup call are started..") + ip_obj.clear_ip_configuration(st.get_dut_names(),thread= data.clear_parallel) + ip_obj.clear_ip_configuration(st.get_dut_names(),'ipv6',thread= data.clear_parallel) + vlan_obj.clear_vlan_configuration(st.get_dut_names(),thread= data.clear_parallel) + portchannel_obj.clear_portchannel_configuration(st.get_dut_names(),thread= data.clear_parallel) + pre_test_l3_fwding() + yield + post_test_l3_fwding() + # add things at the end every test case + # use 'request.function.func_name' to compare + # if any thing specific a particular test case + +@pytest.mark.base_test_sanity +def test_l3_fwding(): + #pre_test_l3_fwding() + data.my_dut_list = st.get_dut_names() + dut1 = data.my_dut_list[0] + dut2 = data.my_dut_list[1] + + dut1 = vars.D1 + dut2 = vars.D2 + st.show(dut1, "show ip interfaces") + st.show(dut1, "show ipv6 interfaces") + st.show(dut1, "show ip route") + + # L3 traffic streams + (tg1, tg2, tg_ph_1, tg_ph_2) = get_handles() + + tg1.tg_traffic_control(action='reset', port_handle=tg_ph_1) + tg2.tg_traffic_control(action='reset', port_handle=tg_ph_2) + + res=tg1.tg_interface_config(port_handle=tg_ph_1, mode='config', intf_ip_addr=data.t1d1_ip_addr, + gateway=data.d1t1_ip_addr, src_mac_addr='00:0a:01:00:11:01', arp_send_req='1') + st.log("INTFCONF: "+str(res)) + handle1 = res['handle'] + + res=tg2.tg_interface_config(port_handle=tg_ph_2, mode='config', intf_ip_addr=data.t1d2_ip_addr, + gateway=data.d2t1_ip_addr, src_mac_addr='00:0a:01:00:12:01', arp_send_req='1') + st.log("INTFCONF: "+str(res)) + handle2 = res['handle'] + + tg1.tg_traffic_config(port_handle=tg_ph_1, mode='create', transmit_mode='continuous', length_mode='fixed', + l3_length=data.tgen_l3_len, rate_pps=data.tgen_rate_pps, emulation_src_handle=handle1, emulation_dst_handle=handle2) + tg2.tg_traffic_config(port_handle=tg_ph_2, mode='create', transmit_mode='continuous', length_mode='fixed', + l3_length=data.tgen_l3_len, rate_pps=data.tgen_rate_pps, emulation_src_handle=handle2, emulation_dst_handle=handle1) + + tg1.tg_packet_control(port_handle=tg_ph_1, action='start') + tg2.tg_packet_control(port_handle=tg_ph_2, action='start') + + tg1.tg_traffic_control(action='clear_stats', port_handle=tg_ph_1) + tg2.tg_traffic_control(action='clear_stats', port_handle=tg_ph_2) + papi.clear_interface_counters(dut1) + tg1.tg_traffic_control(action='run', port_handle=tg_ph_1) + tg2.tg_traffic_control(action='run', port_handle=tg_ph_2) + st.wait(data.traffic_run_time) + tg1.tg_traffic_control(action='stop', port_handle=tg_ph_1) + tg2.tg_traffic_control(action='stop', port_handle=tg_ph_2) + + st.wait(5) + tg1.tg_packet_control(port_handle=tg_ph_1, action='stop') + tg2.tg_packet_control(port_handle=tg_ph_2, action='stop') + + stats_tg1 = tg1.tg_traffic_stats(port_handle=tg_ph_1,mode='aggregate') + total_tg1_tx = stats_tg1[tg_ph_1]['aggregate']['tx']['total_pkts'] + total_tg1_rx = stats_tg1[tg_ph_1]['aggregate']['rx']['total_pkts'] + + stats_tg2 = tg2.tg_traffic_stats(port_handle=tg_ph_2,mode='aggregate') + total_tg2_tx = stats_tg2[tg_ph_2]['aggregate']['tx']['total_pkts'] + total_tg2_rx = stats_tg2[tg_ph_2]['aggregate']['rx']['total_pkts'] + st.log("Tgen Sent Packets on D1T1P1: {} and Received Packets on D2T1P1: {}".format(total_tg1_tx, total_tg2_rx)) + st.log("Tgen Sent Packets on D2T1P1: {} and Received Packets on D1T1P1: {}".format(total_tg2_tx, total_tg1_rx)) + + if (int(total_tg1_tx) == 0) | (int(total_tg2_tx) == 0): + st.log("Traffic Validation Failed") + st.report_fail("operation_failed") + elif (abs(int(total_tg1_tx)-int(total_tg2_rx)) > data.tgen_stats_threshold): + st.log("Traffic Validation Failed") + st.report_fail("operation_failed") + elif (abs(int(total_tg2_tx)-int(total_tg1_rx)) > data.tgen_stats_threshold): + st.log("Traffic Validation Failed") + st.report_fail("operation_failed") + #Getting interfaces counter values on DUT + DUT_rx_value = papi.get_interface_counters(dut1, vars.D1T1P1, "rx_ok") + DUT_tx_value = papi.get_interface_counters(dut1, vars.D1D2P1, "tx_ok") + + for i in DUT_rx_value: + p1_rcvd = i['rx_ok'] + p1_rcvd = p1_rcvd.replace(",","") + + for i in DUT_tx_value: + p2_txmt = i['tx_ok'] + p2_txmt = p2_txmt.replace(",","") + + st.log("rx_ok counter value on DUT Ingress port: {} and tx_ok xounter value on DUT Egress port : {}".format(p1_rcvd, p2_txmt)) + + if basic_obj.is_vsonic_device(vars.D1): + result1 = ipfeature.ping(dut1, data.d2t1_ip_addr, timeout=7) + result2 = ipfeature.ping(dut1, data.d2t1_ip_addr_v6,'ipv6', timeout=7) + else: + result1 = ipfeature.ping(dut1, data.d2t1_ip_addr) + result2 = ipfeature.ping(dut1, data.d2t1_ip_addr_v6,'ipv6') + + if (abs(int(p1_rcvd)-int(p2_txmt)) > data.counters_threshold) | (p1_rcvd == '0') | (p2_txmt == '0') | (not result1) | (not result2): + st.report_fail("operation_failed") + else: + st.report_pass("operation_successful") + +@pytest.mark.base_test_sanity +def test_l2_to_l3_port(): + data.my_dut_list = st.get_dut_names() + dut1 = data.my_dut_list[0] + data.vlan='10' + data.vlan_int='Vlan'+'10' + result_flag = 1 + + # configure from L3 to L2 port + vapi.create_vlan(dut1, data.vlan) + ipfeature.delete_ip_interface(dut1, vars.D1D2P1, data.d1d2_ip_addr,data.mask) + ipfeature.delete_ip_interface(dut1, vars.D1D2P1, data.d1d2_ip_addr_v6,data.mask_v6,family='ipv6') + + ipfeature.config_ip_addr_interface(dut1, data.vlan_int, data.d1d2_ip_addr,data.mask) + ipfeature.config_ip_addr_interface(dut1, data.vlan_int, data.d1d2_ip_addr_v6,data.mask_v6,family='ipv6') + + vapi.add_vlan_member(dut1, data.vlan, vars.D1D2P1, False) + if not vapi.verify_vlan_config(dut1, str(data.vlan), None, vars.D1D2P1): + result_flag = 0 + + # for now using local ping function till qa branch is merged + if basic_obj.is_vsonic_device(vars.D1): + result1 = ipfeature.ping(dut1, data.d2t1_ip_addr, timeout=7) + result2 = ipfeature.ping(dut1, data.d2t1_ip_addr_v6,'ipv6', timeout=7) + else: + result1 = ipfeature.ping(dut1, data.d2t1_ip_addr) + result2 = ipfeature.ping(dut1, data.d2t1_ip_addr_v6,'ipv6') + + if not result1 or not result2: + result_flag = 0 + + # Revert back from L2 to L3 port + vapi.delete_vlan_member(dut1,data.vlan,[vars.D1D2P1]) + ipfeature.delete_ip_interface(dut1, data.vlan_int, data.d1d2_ip_addr,data.mask) + ipfeature.delete_ip_interface(dut1, data.vlan_int, data.d1d2_ip_addr_v6,data.mask_v6,family='ipv6') + vapi.delete_vlan(dut1, [data.vlan]) + + ipfeature.config_ip_addr_interface(dut1, vars.D1D2P1, data.d1d2_ip_addr,data.mask) + ipfeature.config_ip_addr_interface(dut1, vars.D1D2P1, data.d1d2_ip_addr_v6,data.mask_v6,family='ipv6') + if basic_obj.is_vsonic_device(vars.D1): + st.wait(15) + ping_result = ipfeature.ping(dut1, data.d2t1_ip_addr, timeout=7) + else: + ping_result = ipfeature.ping(dut1, data.d2t1_ip_addr) + if ping_result and result_flag: + st.report_pass("operation_successful") + else: + st.report_fail("operation_failed") + diff --git a/spytest/tests/security/test_security_save_reboot.py b/spytest/tests/security/test_security_save_reboot.py new file mode 100644 index 00000000000..45c08ac3f82 --- /dev/null +++ b/spytest/tests/security/test_security_save_reboot.py @@ -0,0 +1,126 @@ +import pytest +from spytest import st +from spytest.dicts import SpyTestDict +import apis.security.radius as radius +import apis.security.tacacs as security +from utilities.utils import ensure_service_params +import apis.system.reboot as reboot +import apis.security.tacacs as tacacs +import apis.system.switch_configuration as switchconf + +security_data = SpyTestDict() + + +@pytest.fixture(scope="module", autouse=True) +def security_module_hooks(request): + global vars + vars = st.ensure_min_topology("D1") + security_variables() + security_module_prolog() + yield + security_module_epilog() + + +@pytest.fixture(scope="function", autouse=True) +def security_func_hooks(request): + yield + + +def security_variables(): + security_data.clear() + security_data.hosts = ensure_service_params(vars.D1, "radius", "hosts") + security_data.radius_host_ip = ensure_service_params(vars.D1, "radius", "hosts", 0, "ip") + security_data.radius_host_passkey = ensure_service_params(vars.D1, "radius", "hosts", 0, "passkey") + security_data.radius_host_priority = ensure_service_params(vars.D1, "radius", "hosts", 0, "priority") + security_data.global_diff_passkey = ensure_service_params(vars.D1, "radius", "globals", 1, "passkey") + security_data.global_auth_type = ensure_service_params(vars.D1, "radius", "globals", 0, "auth_type") + security_data.global_timeout = ensure_service_params(vars.D1, "radius", "globals", 0, "timeout") + security_data.global_retransmit = ensure_service_params(vars.D1, "radius", "globals", 0, "retransmit") + security_data.tacacs_host_ip = ensure_service_params(vars.D1, "tacacs", "hosts", 0, "ip") + security_data.tacacs_tcp_port = ensure_service_params(vars.D1, "tacacs", "hosts", 0, "tcp_port") + security_data.tacacs_passkey = ensure_service_params(vars.D1, "tacacs", "hosts", 0, "passkey") + security_data.tacacs_timeout = ensure_service_params(vars.D1, "tacacs", "hosts", 0, "timeout") + security_data.tacacs_priority = ensure_service_params(vars.D1, "tacacs", "hosts", 0, "priority") + security_data.tacacs_auth_type = ensure_service_params(vars.D1, "tacacs", "hosts", 0, "auth_type") + + +def security_module_prolog(): + tacacs_config() + tacacs_config_verify() + config_global_radius() + radius_config() + st.log("Verifying radius server details before save-reboot") + checking_radius_config(security_data.radius_host_ip) + + +def security_module_epilog(): + radius.config_server(vars.D1, ip_address=security_data.radius_host_ip, action="delete", cli_type="click") + radius.config_global_server_params(vars.D1, skip_error_check=False, + params={"key": {"value": security_data.global_diff_passkey, "no_form": True}, + "timeout": {"value": security_data.global_timeout, "no_form": True}, + "auth_type": {"value": security_data.global_auth_type, "no_form": True}, + "retransmit": {"value": security_data.global_retransmit, + "no_form": True}}, + cli_type="click") + tacacs.set_tacacs_server(vars.D1, 'delete', security_data.tacacs_host_ip) + + +def verify_security_default_config(dut): + if not security.verify_aaa(dut, 'local (default)', 'False (default)'): + st.report_fail("authentication_default_configs_fail") + + +def config_global_radius(): + if not radius.config_global_server_params(vars.D1, skip_error_check=False, + params={"key": {"value": security_data.global_diff_passkey}, + "auth_type": {"value": security_data.global_auth_type}, + "timeout": {"value": security_data.global_timeout}, + "retransmit": {"value": security_data.global_retransmit}}, + cli_type="click"): + st.report_fail("security_global_params_config_failed") + + +def radius_config(): + radius.config_server(vars.D1, ip_address=security_data.radius_host_ip, key=security_data.radius_host_passkey, + priority=security_data.radius_host_priority, action="add", cli_type="click") + + +def checking_radius_config(ip): + st.log("Checking Radius server config after save and reboot") + if not radius.verify_config(vars.D1, params={"globals": {"global_retransmit": security_data.global_retransmit, + "global_timeout": security_data.global_timeout, + "global_passkey": security_data.global_diff_passkey}, + "servers": [{'priority': security_data.radius_host_priority, 'address': ip, + 'passkey': security_data.radius_host_passkey}]}, + cli_type="click"): + st.report_fail("radius_server_config_not_successful") + else: + st.log("Radius configuration successful") + + +def tacacs_config(): + tacacs.set_tacacs_server(vars.D1, 'add', security_data.tacacs_host_ip, security_data.tacacs_tcp_port, + security_data.tacacs_timeout, security_data.tacacs_passkey, + security_data.tacacs_auth_type, security_data.tacacs_priority) + + +def tacacs_config_verify(): + st.log("Checking whether config is loaded to running config from config_db after save-reboot") + if not switchconf.verify_running_config(vars.D1, "TACPLUS_SERVER", security_data.tacacs_host_ip, "priority", "1"): + st.report_fail("running_config_failed", vars.D1, "TACPLUS_SERVER", security_data.tacacs_host_ip, "priority", + "1") + else: + st.log("tacacs server configuration is successful") + + +@pytest.mark.savereboot +def test_ft_security_config_mgmt_verifying_config_with_save_reboot(): + ''' + Author: Sai Durga + FtOpSoScRaFn006: Verify that radius config retained after config save and reboot + ''' + st.log("performing Config save and reloading the device") + reboot.config_save_reload(vars.D1) + tacacs_config_verify() + checking_radius_config(security_data.radius_host_ip) + st.report_pass("security_config_retained_after_save_reboot") \ No newline at end of file diff --git a/spytest/tests/security/test_tacacs.py b/spytest/tests/security/test_tacacs.py new file mode 100644 index 00000000000..c640570b8d6 --- /dev/null +++ b/spytest/tests/security/test_tacacs.py @@ -0,0 +1,277 @@ +import pytest +from spytest import st +from spytest.dicts import SpyTestDict +import apis.security.tacacs as tacacs_obj +import apis.system.connection as ssh_obj +import apis.routing.ip as ping_obj +import apis.system.basic as bc_obj +import apis.system.basic as basic_obj +from apis.security.rbac import ssh_call +from apis.switching.vlan import clear_vlan_configuration +from utilities.utils import ensure_service_params + +vars = dict() +data = SpyTestDict() + + +@pytest.fixture(scope="module", autouse=True) +def tacacs_module_hooks(request): + # add things at the start of this module + global vars + vars = st.ensure_min_topology("D1") + tacacs_params = st.get_service_info(vars.D1, "tacacs") + st.log("Getting IP address of the device") + data.clear() + data.hosts = ensure_service_params(vars.D1, "tacacs", "hosts") + data.tacacs_ser_ip_1 = ensure_service_params(vars.D1, "tacacs", "hosts", 0, "ip") + data.tcp_port = ensure_service_params(vars.D1, "tacacs", "hosts", 0, "tcp_port") + data.passkey = ensure_service_params(vars.D1, "tacacs", "hosts", 0, "passkey") + data.priority = ensure_service_params(vars.D1, "tacacs", "hosts", 0, "priority") + data.timeout = ensure_service_params(vars.D1, "tacacs", "hosts", 0, "timeout") + data.auth_type = ensure_service_params(vars.D1, "tacacs", "hosts", 0, "auth_type") + data.tacacs_ser_ip_2 = ensure_service_params(vars.D1, "tacacs", "hosts", 1, "ip") + data.priority_server2 = ensure_service_params(vars.D1, "tacacs", "hosts", 1, "priority") + data.time_out = '10' + data.username = 'test' + data.password = 'test' + data.protocol = 'ssh' + data.ssh_port = '22' + data.login_type = "tacacs+" + data.failthrough_mode = 'enable' + data.local_username = 'admin' + data.local_password = 'YourPaSsWoRd' + data.local_password2 = 'broadcom' + data.username1 = 'test' + data.password1 = 'test' + data.rw_user = {'username': data.username, 'password': data.password, 'mode': 'rw'} + data.ro_username = ensure_service_params(vars.D1, "radius", "ro_user", "username") + ensure_device_ipaddress() + st.log("Configuring authentication login parameter as tacacs+ and local") + tacacs_obj.set_aaa_authentication_properties(vars.D1, 'login', 'tacacs+ local') + tacacs_obj.set_tacacs_server(vars.D1, 'add', data.tacacs_ser_ip_1, data.tcp_port, data.timeout, data.passkey, + data.auth_type, data.priority) + yield + config_default_tacacs_properties(vars.D1) + st.log("Deleting all TACACS+ servers from the device") + for i in range(0, 8): + tacacs_obj.set_tacacs_server(vars.D1, 'delete', tacacs_params.hosts[i].ip) + st.log("Making AAA parameters to default") + tacacs_obj.set_aaa_authentication_properties(vars.D1, 'login', 'default') + tacacs_obj.set_aaa_authentication_properties(vars.D1, 'failthrough', 'default') + clear_vlan_configuration([vars.D1]) + + +# add things at the end of this module" + +@pytest.fixture(scope="function", autouse=True) +def tacacs_func_hooks(request): + # add things at the start every test case + # use 'request.function.func_name' to compare + # if any thing specific a particular test case + yield + # add things at the end every test case + # use 'request.function.func_name' to compare + # if any thing specific a particular test case + + +def ensure_device_ipaddress(): + data.ip_address_list = bc_obj.get_ifconfig_inet(vars.D1, 'eth0') + if not data.ip_address_list: + st.report_fail("DUT_does_not_have_IP_address") + data.ip_address = data.ip_address_list[0] + + +def config_default_tacacs_properties(dut): + st.log("Making TACACS+ parameters to default") + tacacs_obj.set_tacacs_properties(dut, 'default', 'authtype') + tacacs_obj.set_tacacs_properties(dut, 'default', 'passkey') + tacacs_obj.set_tacacs_properties(dut, 'default', 'timeout') + + +def verify_tacacs_server_reachability(dut, tacacs_ser_ip): + st.log("Verify that tacacs server connectivity from DUT") + if not ping_obj.ping(dut, tacacs_ser_ip): + st.report_fail("Ping_to_tacacs_server_is_not_successful", tacacs_ser_ip) + + +def verifying_tacacs_config(dut, tacacs_ser_ip): + if not tacacs_obj.verify_tacacs_server(dut, tacacs_ser_ip): + st.report_fail("Tacacs_server_configs_are_not_successful") + + +def debug_info(test_case, server_ip): + if test_case == "test_ft_tacacs_enable_disable_failthrough": + st.log("Checking TACACS+ server is reachable or not from the device") + verify_tacacs_server_reachability(vars.D1, server_ip) + st.log("Checking TACACS+ config in the device") + verifying_tacacs_config(vars.D1, server_ip) + + +@pytest.mark.ssh_login +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_tacacs_ssh_login_with_tacacs_operations(): + """ + Author: Karthik Kumar Goud Battula(karthikkumargoud,battula@broadcom.com) + This testcase covers the below scenarios + scenario-1: Verify default login via tacacs is successful. + scenario-2: Verify that by default, no TACACS servers exist and that a single TACACS server can be added and used for authentication + scenario-3: Verify that SSH login using Cisco ACS TACACS+ Server is successful. + Scenario-4: Verify If TACACS+ Authentication is enabled, a new PAM configuration file ?common-auth-sonic? is created. + Scenario-5: Verify that user is able to execute the ?TACACS-server key command successfully in appropriate mode. + Scenario-6: Verify that authentication to a TACACS server will only take place if the NAS and the server are configured with the same shared key + """ + if not basic_obj.verify_file_on_device(vars.D1, "/etc/pam.d", "common-auth"): + st.report_fail("PAM_file_is_not_created") + if not basic_obj.verify_file_on_device(vars.D1, "/etc/pam.d", "common-auth-sonic"): + st.report_fail("PAM_file_is_not_created") + st.report_pass("test_case_passed") + + +@pytest.mark.functionality_failthrough +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_tacacs_enable_disable_failthrough(): + """ + Author: Karthik Kumar Goud Battula(karthikkumargoud,battula@broadcom.com) + This test case covers the below Scenarios + Scenario-1: Verify the functionality of failthrough mechanism by changing the login authentication order + Scenario-2: Verify the functionality of failthrough mechanism when DUT have multiple server with default priority. + """ + tacacs_obj.set_tacacs_server(vars.D1, 'add', data.tacacs_ser_ip_2, data.tcp_port, data.timeout, data.passkey, + data.auth_type, data.priority_server2) + st.log( + "Trying to SSH to the device using local credetails when login method set to TACACS+ and local and fail through mode is not enabled") + if ssh_obj.connect_to_device(data.ip_address, data.local_username, data.local_password, data.protocol, + data.ssh_port, alt_password=data.local_password2): + st.report_fail("Login_to_DUT_via_SSH_is_failed") + st.log( + "Trying to SSH to the device using TACACS+ credetails when login method set to TACACS+ and local and fail through mode is not enabled") + if not ssh_obj.connect_to_device(data.ip_address, data.username, data.password, data.protocol): + debug_info("test_ft_tacacs_enable_disable_failthrough", data.tacacs_ser_ip_1) + st.report_fail("Login_to_DUT_via_SSH_is_failed") + st.log("Setting login authentication to local and tacacs+") + tacacs_obj.set_aaa_authentication_properties(vars.D1, 'login', 'local tacacs+') + st.log( + "Trying to SSH to the device using local credetails when login method set to local and TACACS+ and fail through mode is not enabled") + if not ssh_obj.connect_to_device(data.ip_address, data.local_username, data.local_password, + alt_password=data.local_password2): + st.report_fail("Login_to_DUT_via_SSH_is_failed") + st.log( + "Trying to SSH to the device using TACACS+ credetails when login method set to local and TACACS+ and fail through mode is not enabled") + if ssh_obj.connect_to_device(data.ip_address, data.username, data.password, data.protocol, data.ssh_port): + st.report_fail("Login_to_DUT_via_SSH_is_failed") + st.log("Configuring AAA login to tacacs+ and local and enabling failthrough mode") + tacacs_obj.set_aaa_authentication_properties(vars.D1, 'login', 'tacacs+ local') + tacacs_obj.set_aaa_authentication_properties(vars.D1, 'failthrough', 'enable') + st.log( + "Trying to SSH to the device using local credetails when login method set to TACACS+ and local and fail through mode is enabled") + if not ssh_obj.connect_to_device(data.ip_address, data.local_username, data.local_password, + alt_password=data.local_password2): + st.report_fail("Login_to_DUT_via_SSH_is_failed") + st.log( + "Trying to SSH to the device using TACACS+ credetails when login method set to TACACS+ and local and fail through mode is enabled") + if not ssh_obj.connect_to_device(data.ip_address, data.username, data.password, data.protocol, data.ssh_port): + debug_info("test_ft_tacacs_enable_disable_failthrough", data.tacacs_ser_ip_1) + st.report_fail("Login_to_DUT_via_SSH_is_failed") + st.report_pass("test_case_passed") + + +@pytest.mark.ssh_login_highest_priorityserver +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_tacacs_ssh_login_highest_priorityserver(): + """ + Author:Karthik Kumar Goud Battula(karthikkumargoud.battula@broadcom.com + This test case covers the below Scenarios + Scenario-1: verify if DUT is configured with more than one tacacs server with priority configured client can login do dut via heightest priority tacacs server + Scenario-2: Verify user login with multiple TACACS server when nondefault priority is configured for all server. + Scenario-3: Verify that if the current TACACS server is unresponsive the NAS will failover to the next configured TACACS server according to configured server priorities and so on. + """ + st.log("Login to the device via SSH using the credentials of highest priority server") + if not ssh_obj.connect_to_device(data.ip_address, data.username1, data.password1, data.protocol): + debug_info("test_ft_tacacs_ssh_login_highest_priorityserver", data.tacacs_ser_ip_2) + st.report_fail("Login_to_DUT_via_SSH_is_failed") + tacacs_obj.set_tacacs_server(vars.D1, 'delete', data.tacacs_ser_ip_1) + st.report_pass("test_case_passed") + + +@pytest.mark.rbac +def test_ft_rbac_rw_tacacs_cred_ssh(): + ''' + FtOpSoScRBACFn009: Verify that admin tacacs user will have all permissions when SSH to the system with username/password. + ''' + ssh_call(vars.D1, login_type='cred', **data.rw_user) + + +@pytest.mark.rbac +def test_ft_rbac_ro_tacacs_cred_ssh(): + ''' + Author: Sai Durga (pchvsai.durga@broadcom,com) + FtOpSoScRBACFn008 Verify that non-admin tacacs user doesn?t have all permissions except show (get) commands when SSH to the system with username/password. + ''' + if not st.exec_ssh(vars.D1, data.ro_username, data.password, ['show vlan config']): + st.report_fail("cmd_not_executed") + if not st.exec_ssh(vars.D1, data.ro_username, data.password, ['sudo config vlan add 1000']): + st.report_fail("admin_user_root_privilege", "non", "got") + st.report_pass("admin_user_root_privilege", "non", "doesnot got") + + +@pytest.mark.modify_tacacsserver_parameters +def test_ft_tacacs_modify_server_parameters(): + """ + + Author: Karthik Kumar Goud Battula(karthikkuamrgoud.battula@broadcom.com) + This Testcase covers the below scenarios + Scenario-1: Verify device behavior when TACACS+ application parameters (valid and invalid) are modified while traffic is running. + Scenario-2: Verify that the key and timeout options default to global but may be specified to unique values on a per Server basis. + Scenario-3: Verify that the NAS will stop communicating with the current server is the server is down after the duration of the configured server timeout or the default timeout value + Scenario-4: Verify that Taccacs server key can be configured with more that 4 special characters + """ + invalid_l4_port = '59' + invalid_pass_key = "key123" + invalid_timeout = '10' + invalid_ip_addr = '10.10.10.1' + tacacs_obj.set_tacacs_properties(vars.D1, 'passkey', 'secretstring') + st.log("Configuring global tacacs server key with special characters") + tacacs_obj.set_tacacs_properties(vars.D1, 'passkey', data.passkey) + st.log("Check client authentication by modifing ip address,timeout,passkey") + tacacs_obj.set_tacacs_server(vars.D1, 'add', invalid_ip_addr, invalid_l4_port, invalid_timeout, invalid_pass_key, + data.auth_type, data.priority_server2) + st.log("Trying to SSH to the device when TACACS+ server is configured with invalid parameters") + if ssh_obj.connect_to_device(data.ip_address, data.username, data.password, data.protocol, data.ssh_port): + st.report_fail("Login_to_DUT_via_SSH_is_failed") + st.log("Deleting the TACACS+ server which is invalid") + tacacs_obj.set_tacacs_server(vars.D1, 'delete', invalid_ip_addr) + st.log("Creating valid TACACS+ server") + tacacs_obj.set_tacacs_server(vars.D1, 'add', data.tacacs_ser_ip_1, data.tcp_port, data.timeout, data.passkey, + data.auth_type, data.priority) + st.log("Trying to SSH to the device with TACACS+ server which is configured with the valid parameters") + if not ssh_obj.connect_to_device(data.ip_address, data.username, data.password, data.protocol, data.ssh_port): + debug_info("test_ft_tacacs_modify_server_parameters", data.tacacs_ser_ip_1) + st.report_fail("Login_to_DUT_via_SSH_is_failed") + st.report_pass("test_case_passed") + + +@pytest.mark.maximum_servers +def test_ft_tacacs_maximum_servers(): + """ + Author: Karthik Kumar Goud Battula(karthikkumargoud.battula@broadcom.com) + This testcase covers the below scenarios + Scenario-1: Verify that more than one TACACS server can be configured on the NAS, upto the maximum number of servers that are allowed. + Scenario-2: Verify that TACACS+ servers with IPv4 and IPv6 address can be added at the same time. + Scenario-3: Verify that Maximum number of TACACS IPv4 servers can be configured on DUT. + Scenario-4: Verify that Maximum number of TACACS IPv6 servers can be configured on DUT. + Scenario-5: Verify the Save and Reload Functionality for TACACS IPv6 feature. + """ + tacacs_params = st.get_service_info(vars.D1, "tacacs") + for i in range(2, 8): + ip_addr = ensure_service_params(vars.D1, "tacacs", "hosts", i, "ip") + priority = ensure_service_params(vars.D1, "tacacs", "hosts", i, "priority") + tacacs_obj.set_tacacs_server(vars.D1, 'add', ip_addr, data.tcp_port, + data.timeout, data.passkey, + data.auth_type, priority) + if not tacacs_obj.verify_tacacs_details(vars.D1, tacacs_params.hosts): + st.report_fail("Tacacs_server_configs_are_not_successful", tacacs_params.hosts) + st.report_pass("test_case_passed") + diff --git a/spytest/tests/switching/test_portchannel.py b/spytest/tests/switching/test_portchannel.py new file mode 100644 index 00000000000..d4b06f1bb90 --- /dev/null +++ b/spytest/tests/switching/test_portchannel.py @@ -0,0 +1,991 @@ +import pytest +from re import findall +from random import randrange as randomnumber + +from spytest import st, tgapi, SpyTestDict + +import apis.switching.portchannel as portchannel_obj +import apis.switching.vlan as vlan_obj +import apis.system.interface as intf_obj +import apis.system.logging as slog +from apis.system.reboot import config_save, config_save_reload +import apis.system.lldp as lldp_obj +import apis.system.basic as basic_obj +import apis.routing.ip as ip_obj +import apis.routing.arp as arp_obj +import apis.system.port as port_obj + +from utilities.parallel import exec_all, exec_parallel, ensure_no_exception +from utilities.common import ExecAllFunc, make_list, filter_and_select +from utilities.common import random_vlan_list, poll_wait + +vars = dict() +data = SpyTestDict() +data.portchannel_name = "PortChannel7" +data.portchannel_name2 = "PortChannel8" +data.vlan = (random_vlan_list(count=2)) +data.vid = data.vlan[0] +data.vlan_id = data.vlan[1] + + +@pytest.fixture(scope="module", autouse=True) +def portchannel_module_hooks(request): + # add things at the start of this module + global vars + vars = st.ensure_min_topology("D1D2:4", "D1T1:1", "D2T1:1") + data.cli_type = 'click' + data.lag_up = 'Up' + data.lag_down = 'Dw' + data.ip_src_count = 1000 + data.ip_dst_count = 1000 + data.tcp_src_port_count = 1000 + data.tcp_dst_port_count = 1000 + data.ip41 = '10.1.1.1' + data.ip42 = '30.1.1.1' + data.ip43 = '40.1.1.1' + data.src_ip = '10.1.1.2' + data.dst_ip = '30.1.1.3' + data.src_port = '123' + data.dst_port = '234' + data.graceful_restart_config = False + data.dut1_rt_int_mac = basic_obj.get_ifconfig_ether(vars.D1, vars.D1T1P1) + data.my_dut_list = st.get_dut_names()[0:2] + data.dut1 = st.get_dut_names()[0] + data.dut2 = st.get_dut_names()[1] + data.members_dut1 = [vars.D1D2P1, vars.D1D2P2, vars.D1D2P3, vars.D1D2P4] + data.members_dut2 = [vars.D2D1P1, vars.D2D1P2, vars.D2D1P3, vars.D2D1P4] + exec_all(True, [[tg_config], [dut_config]], first_on_main=True) + yield + st.log('Module config Cleanup') + vlan_obj.clear_vlan_configuration([data.dut1, data.dut2], cli_type=data.cli_type) + portchannel_obj.clear_portchannel_configuration([data.dut1, data.dut2], cli_type=data.cli_type) + data.tg.tg_traffic_control(action='stop', port_handle=[data.tg_ph_1, data.tg_ph_3]) + + +def tg_config(): + st.log("Getting TG handlers") + data.tg1, data.tg_ph_1 = tgapi.get_handle_byname("T1D1P1") + data.tg3, data.tg_ph_3 = tgapi.get_handle_byname("T1D2P1") + data.tg = data.tg1 + st.log("Reset and clear statistics of TG ports") + data.tg.tg_traffic_control(action='reset', port_handle=[data.tg_ph_1, data.tg_ph_3]) + data.tg.tg_traffic_control(action='clear_stats', port_handle=[data.tg_ph_1, data.tg_ph_3]) + data.h1 = data.tg.tg_interface_config(port_handle=data.tg_ph_1, mode='config', intf_ip_addr=data.ip41, + gateway=data.src_ip, arp_send_req='1') + st.log("INTFCONF: " + str(data.h1)) + data.h2 = data.tg.tg_interface_config(port_handle=data.tg_ph_3, mode='config', intf_ip_addr=data.ip42, + gateway=data.dst_ip, arp_send_req='1') + st.log("INTFCONF: " + str(data.h2)) + data.streams = {} + + +def dut_config(): + st.log('Creating port-channel and adding members in both DUTs') + portchannel_obj.config_portchannel(data.dut1, data.dut2, data.portchannel_name, data.members_dut1, + data.members_dut2, "add", cli_type=data.cli_type) + dict1 = {'interfaces':data.portchannel_name, 'operation':"startup", 'skip_verify':True, 'cli_type':data.cli_type} + output = exec_parallel(True, [vars.D1, vars.D2], intf_obj.interface_operation, [dict1, dict1]) + ensure_no_exception(output[1]) + if not (output[0][0] and output[0][1]): + st.report_fail('interface_admin_startup_fail', data.portchannel_name) + st.log('Creating random VLAN in both the DUTs') + if False in create_vlan_using_thread([vars.D1, vars.D2], [[data.vid], [data.vid]]): + st.report_fail('vlan_create_fail', data.vid) + st.log('Adding Port-Channel and IXIA connected ports as tagged members to the random VLAN') + if False in add_vlan_member_using_thread([vars.D1, vars.D2], [data.vid, data.vid], + [[data.portchannel_name, vars.D1T1P1],[data.portchannel_name, vars.D2T1P1]]): + st.report_fail('vlan_tagged_member_fail', data.portchannel_name, data.vid) + + +@pytest.fixture(scope="function", autouse=True) +def portchannel_func_hooks(request): + data.tg.tg_traffic_control(action='reset', port_handle=data.tg_ph_1) + if request.function.func_name == 'test_ft_portchannel_behavior_with_tagged_traffic': + verify_portchannel_status() + elif request.function.func_name == 'test_ft_untagged_traffic_on_portchannel': + verify_portchannel_status() + config_test_ft_portchannel_with_new_member_and_untagged_traffic() + elif request.function.func_name == 'test_ft_lag_l3_hash_sip_dip_l4port': + verify_portchannel_status() + config_test_ft_lag_l3_hash_sip_dip_l4port() + elif request.function.func_name == 'test_ft_portchannel_with_vlan_variations': + dict1 = {"portchannel": data.portchannel_name, "members": [data.members_dut1[2], + data.members_dut1[3]], "flag": 'del', "cli_type": data.cli_type} + dict2 = {"portchannel": data.portchannel_name, "members": [data.members_dut2[2], + data.members_dut2[3]], "flag": 'del', "cli_type": data.cli_type} + output = exec_parallel(True, [vars.D1, vars.D2], portchannel_obj.add_del_portchannel_member, [dict1, dict2]) + ensure_no_exception(output[1]) + else: + pass + yield + if request.function.func_name == 'test_ft_portchannel_behavior_with_tagged_traffic': + portchannel_behavior_with_tagged_traffic_verify() + elif request.function.func_name == 'test_ft_untagged_traffic_on_portchannel': + portchannel_behavior_with_untagged_traffic_verify() + elif request.function.func_name == 'test_ft_lag_l3_hash_sip_dip_l4port': + unconfig_test_ft_lag_l3_hash_sip_dip_l4port() + elif request.function.func_name == 'test_ft_portchannel_with_vlan_variations': + dict1 = {"portchannel": data.portchannel_name, "members": [data.members_dut1[2], + data.members_dut1[3]], "flag": 'add', + "cli_type": data.cli_type} + dict2 = {"portchannel": data.portchannel_name, "members": [data.members_dut2[2], + data.members_dut2[3]], "flag": 'add', + "cli_type": data.cli_type} + output = exec_parallel(True, [vars.D1, vars.D2], portchannel_obj.add_del_portchannel_member, [dict1, dict2]) + ensure_no_exception(output[1]) + else: + pass + +def verify_portchannel_member_status(dut, portchannel_list, portchannel_status_list, active_members_list, down_members_list): + portchannel_details = portchannel_obj.get_portchannel_list(dut, cli_type=data.cli_type) + for index, portchannel in enumerate(portchannel_list, start=0): + portchannel_dict = {'protocol': None, 'ports': None} + portchannel_dict['protocol'] = filter_and_select(portchannel_details, ['protocol'], {'teamdev': portchannel})[0]['protocol'] + portchannel_dict['ports'] = filter_and_select(portchannel_details, ['ports'], {'teamdev': portchannel})[0]['ports'] + if not portchannel_status_list[index] in findall(r'LACP\(A\)\((\S+)\)', portchannel_dict['protocol']): + st.error('Port-channel state is not matching with the provided state {}'.format(portchannel_status_list[index])) + return False + active_members_set = set(findall(r'(Ethernet\d+)\(S\)', portchannel_dict['ports'])) + down_members_set = set(findall(r'(Ethernet\d+)\(D\)', portchannel_dict['ports'])) + if active_members_list[index]: + if not set(make_list(active_members_list[index])) == active_members_set: + st.log('Provided active members list: {}'.format(active_members_list[index])) + st.log('Active members set after processing is: {}'.format(active_members_set)) + st.error('Verification of active LAG members failed') + return False + if down_members_list[index]: + if not set(make_list(down_members_list[index])) == down_members_set: + st.log('Provided down members list: {}'.format(down_members_list[index])) + st.log('Down members set after processing is: {}'.format(down_members_set)) + st.error('Verification of down LAG members failed') + return False + return True + + +def graceful_restart_prolog(): + dict1 = {'portchannel': data.portchannel_name, 'members': [vars.D1D2P3, vars.D1D2P4], 'cli_type': data.cli_type} + dict2 = {'portchannel': data.portchannel_name, 'members': [vars.D2D1P3, vars.D2D1P4], 'cli_type': data.cli_type} + exceptions = exec_parallel(True, [vars.D1, vars.D2], portchannel_obj.delete_portchannel_member, [dict1, dict2])[1] + ensure_no_exception(exceptions) + dict1 = {'portchannel_list': data.portchannel_name2, 'cli_type': data.cli_type} + dict2 = {'portchannel_list': data.portchannel_name2, 'cli_type': data.cli_type} + exceptions = exec_parallel(True, [vars.D1, vars.D2], portchannel_obj.create_portchannel, [dict1, dict2])[1] + ensure_no_exception(exceptions) + dict1 = {'portchannel': data.portchannel_name2, 'members': [vars.D1D2P3, vars.D1D2P4], 'cli_type': data.cli_type} + dict2 = {'portchannel': data.portchannel_name2, 'members': [vars.D2D1P3, vars.D2D1P4], 'cli_type': data.cli_type} + exceptions = exec_parallel(True, [vars.D1, vars.D2], portchannel_obj.add_portchannel_member, [dict1, dict2])[1] + ensure_no_exception(exceptions) + + +def verify_portchannel_status(delay=2): + dict1 = {'portchannel': data.portchannel_name, 'members_list': data.members_dut1, 'iter_delay': delay} + dict2 = {'portchannel': data.portchannel_name, 'members_list': data.members_dut2, 'iter_delay': delay} + output = exec_parallel(True, [vars.D1, vars.D2], verify_portchannel_cum_member_status, [dict1, dict2]) + ensure_no_exception(output[1]) + +def create_vlan_using_thread(dut_list, vlan_list, thread = True): + sub_list = [[vlan_obj.create_vlan, dut, vlan_list[cnt], data.cli_type] for cnt, dut in enumerate(dut_list, start=0)] + [output, exceptions] = exec_all(thread, sub_list) + return output + +def config_test_ft_portchannel_with_new_member_and_untagged_traffic(): + delete_vlan_member_using_thread([vars.D1, vars.D2], [data.vid, data.vid], [[data.portchannel_name, vars.D1T1P1], + [data.portchannel_name, vars.D2T1P1]]) + add_vlan_member_using_thread([vars.D1, vars.D2], [data.vid, data.vid], [[data.portchannel_name, vars.D1T1P1], + [data.portchannel_name, vars.D2T1P1]], tagged=False) + dict1 = {'vlan_list': data.vid, 'untagged': [data.portchannel_name, vars.D1T1P1]} + dict2 = {'vlan_list': data.vid, 'untagged': [data.portchannel_name, vars.D2T1P1]} + output = exec_parallel(True, [vars.D1, vars.D2], vlan_obj.verify_vlan_config, [dict1, dict2]) + ensure_no_exception(output[1]) + if not output[0][0]: + st.report_fail('vlan_untagged_member_fail', [data.portchannel_name, vars.D1T1P1], data.vid) + if not output[0][1]: + st.report_fail('vlan_untagged_member_fail', [data.portchannel_name, vars.D2T1P1], data.vid) + +def config_test_ft_lag_l3_hash_sip_dip_l4port(): + delete_vlan_member_using_thread([vars.D1, vars.D2], [data.vid, data.vid], [[data.portchannel_name, vars.D1T1P1], + [data.portchannel_name, vars.D2T1P1]]) + verify_portchannel_status() +def portchannel_behavior_with_tagged_traffic_verify(): + data.tg.tg_traffic_control(action='stop', port_handle=data.tg_ph_1) + if data.return_value == 2: + portchannel_obj.create_portchannel(vars.D1, data.portchannel_name, cli_type=data.cli_type) + portchannel_obj.add_portchannel_member(vars.D1, data.portchannel_name, data.members_dut1, cli_type=data.cli_type) + elif data.return_value == 3: + if not intf_obj.interface_operation(vars.D1, data.portchannel_name, 'startup', skip_verify=False, + cli_type=data.cli_type): + st.report_fail('interface_admin_startup_fail', data.portchannel_name) + elif data.return_value == 4: + if not vlan_obj.add_vlan_member(vars.D1, data.vid, [data.portchannel_name], True, cli_type=data.cli_type): + st.report_fail('vlan_tagged_member_fail', data.portchannel_name, data.vid) + if not vlan_obj.verify_vlan_config(vars.D1, data.vid, tagged=[data.portchannel_name]): + st.report_fail('vlan_tagged_member_fail', data.portchannel_name, data.vid) + elif data.return_value == 5: + intf_obj.interface_noshutdown(vars.D1, data.members_dut1, skip_verify=False, + cli_type=data.cli_type) + else: + dict1 = {'portchannel': data.portchannel_name, 'cli_type': data.cli_type} + output = exec_parallel(True, [vars.D1, vars.D2], portchannel_obj.get_portchannel_members, [dict1, dict1]) + ensure_no_exception(output[1]) + member_ports1 = [] + member_ports2 = [] + for port in data.members_dut1: + if port not in output[0][0]: + member_ports1.append(port) + for port in data.members_dut2: + if port not in output[0][1]: + member_ports2.append(port) + add_del_member_using_thread([vars.D1, vars.D2], [data.portchannel_name, data.portchannel_name], + [member_ports1,member_ports2], flag='add') + intf_obj.interface_noshutdown(vars.D1, data.members_dut1, cli_type=data.cli_type) + +def portchannel_behavior_with_untagged_traffic_verify(): + data.tg.tg_traffic_control(action='stop', port_handle=data.tg_ph_1) + delete_vlan_member_using_thread([vars.D1, vars.D2], [data.vid, data.vid], [[data.portchannel_name, vars.D1T1P1], + [data.portchannel_name, vars.D2T1P1]]) + add_vlan_member_using_thread([vars.D1, vars.D2], [data.vid, data.vid], [[data.portchannel_name, vars.D1T1P1], + [data.portchannel_name, vars.D2T1P1]], tagged=False) + +def unconfig_test_ft_portchannel_disabled_with_traffic(): + intf_obj.interface_operation(vars.D1, data.portchannel_name, 'startup', cli_type=data.cli_type) + data.tg.tg_traffic_control(action='stop', port_handle=data.tg_ph_1) + +def unconfig_test_ft_lag_l3_hash_sip_dip_l4port(): + ip_obj.clear_ip_configuration([vars.D1, vars.D2], family='ipv4', thread=True) + add_vlan_member_using_thread([vars.D1, vars.D2], [data.vid, data.vid], [[data.portchannel_name, vars.D1T1P1], + [data.portchannel_name, vars.D2T1P1]],tagged=True) + data.tg.tg_traffic_control(action='stop', port_handle=[data.tg_ph_1, data.tg_ph_3]) + +def clear_intf_counters_using_thread(dut_list, thread=True): + sub_list = [[intf_obj.clear_interface_counters, dut] for dut in dut_list] + [output, exceptions] = exec_all(thread, sub_list) + +def add_del_member_using_thread(dut_list, portchannel_list, member_list, flag = 'add', thread=True): + sub_list = [] + if flag == 'add': + sub_list.append([portchannel_obj.add_del_portchannel_member, dut_list[0], portchannel_list[0], member_list[0], + flag, True, data.cli_type]) + sub_list.append([portchannel_obj.add_del_portchannel_member, dut_list[1], portchannel_list[1], member_list[1], + flag, True, data.cli_type]) + [output, expressions] = exec_all(thread, sub_list) + else: + sub_list.append([portchannel_obj.delete_portchannel_member, dut_list[0], portchannel_list[0], member_list[0], + data.cli_type]) + sub_list.append([portchannel_obj.delete_portchannel_member, dut_list[1], portchannel_list[1], member_list[1], + data.cli_type]) + [output, expressions] = exec_all(thread, sub_list) + +def verify_traffic_hashed_or_not(dut, port_list, pkts_per_port, traffic_loss_verify = False, rx_port = '', + tx_port = '', dut2 =''): + if traffic_loss_verify == True: + sub_list = [] + sub_list.append([intf_obj.show_interface_counters_all, dut]) + sub_list.append([intf_obj.show_interface_counters_all, dut2]) + [output, exceptions] = exec_all(True, sub_list) + data.intf_counters_1, data.intf_counters_2 = output + else: + data.intf_counters_1 = intf_obj.show_interface_counters_all(dut) + data.intf_count_dict = {} + for port in port_list: + for counter_dict in data.intf_counters_1: + if counter_dict['iface'] == port: + try: + tx_ok_counter = counter_dict['tx_ok'].replace(',', '') + data.intf_count_dict[port] = int(tx_ok_counter) if tx_ok_counter.isdigit() else 0 + except: + st.report_fail('invalid_traffic_stats') + if not (data.intf_count_dict[port] >= pkts_per_port): + intf_obj.show_interface_counters_detailed(vars.D1, vars.D1T1P1) + st.report_fail("traffic_not_hashed", dut) + if traffic_loss_verify == True: + for counter_dict in data.intf_counters_1: + if counter_dict['iface'] == rx_port: + try: + rx_ok_counter = counter_dict['rx_ok'].replace(',', '') + data.rx_traffic = int(rx_ok_counter) if rx_ok_counter.isdigit() else 0 + except: + st.report_fail('invalid_traffic_stats') + break + for counter_dict in data.intf_counters_2: + if counter_dict['iface'] == tx_port: + try: + tx_ok_counter = counter_dict['tx_ok'].replace(',', '') + data.tx_traffic = int(tx_ok_counter) if tx_ok_counter.isdigit() else 0 + except: + st.report_fail('invalid_traffic_stats') + break + if not (data.tx_traffic >= 0.95* data.rx_traffic): + st.log("data.tx_traffic:{}".format(data.tx_traffic)) + st.log("data.rx_traffic:{}".format(data.rx_traffic)) + intf_obj.show_interface_counters_detailed(vars.D1, vars.D1T1P1) + st.report_fail('traffic_loss_observed') + return data.intf_count_dict + +def delete_vlan_member_using_thread(dut_list, vlan_list, members_list): + sub_list = [] + sub_list.append([vlan_obj.delete_vlan_member, dut_list[0], vlan_list[0], members_list[0], data.cli_type]) + sub_list.append([vlan_obj.delete_vlan_member, dut_list[1], vlan_list[1], members_list[1], data.cli_type]) + [output, expressions] = exec_all(True, sub_list) + +def add_vlan_member_using_thread(dut_list, vlan_list, port_list, tagged = True): + sub_list = [] + sub_list.append([vlan_obj.add_vlan_member, dut_list[0], vlan_list[0], port_list[0], tagged, False, data.cli_type]) + sub_list.append([vlan_obj.add_vlan_member, dut_list[1], vlan_list[1], port_list[1], tagged, False, data.cli_type]) + [output, expressions] = exec_all(True, sub_list) + return output + +def get_intf_counters_using_thread(dut_list, thread=True): + sub_list = [[intf_obj.show_interface_counters_all, dut] for dut in dut_list] + [output, expressions] = exec_all(thread, sub_list) + return output + +def verify_portchannel_cum_member_status(dut, portchannel, members_list, iter_count=10, iter_delay=2, state='up'): + i = 1 + while i <= iter_count: + st.log("Checking iteration {}".format(i)) + st.wait(iter_delay) + if not portchannel_obj.verify_portchannel_member_state(dut, portchannel, members_list, state='up', + cli_type=data.cli_type): + i += 1 + if i == iter_count: + st.report_fail("portchannel_member_verification_failed", portchannel, dut, members_list) + else: + break + + +def check_lldp_neighbors(dut, port, ipaddress, hostname): + lldp_value=[] + try: + lldp_value = lldp_obj.get_lldp_neighbors(dut, interface=port)[0] + except: + st.report_fail("no_lldp_entries_are_available") + lldp_value_dut2 = lldp_value['chassis_mgmt_ip'] + try: + if not ipaddress[0] == lldp_value_dut2 : + st.report_fail("both_are_not_matching") + except: + st.report_fail("both_are_not_matching") + lldp_value_hostname = lldp_value['chassis_name'] + if not hostname == lldp_value_hostname: + st.report_fail("hostname_is_not_matching") + +def get_mgmt_ip_using_thread(dut_list, mgmt_list, thread=True): + sub_list = [[basic_obj.get_ifconfig_inet, dut, mgmt_list[cnt]] for cnt, dut in enumerate(dut_list, start=0)] + [output, expressions] = exec_all(thread, sub_list) + return output + +def get_hostname_using_thread(dut_list, thread=True): + sub_list = [[basic_obj.get_hostname, dut] for dut in dut_list] + [output, expressions] = exec_all(thread, sub_list) + return output + +def test_ft_portchannel_behavior_with_tagged_traffic(): + ''' + Author: Jagadish + This test case covers below test scenarios/tests + Test scenario-1: Verify that deleting port channel with member ports should not be successful. + Test scenario-2: Verify that removal of a port from a LAG does not interrupt traffic. + Test scenario-3: Verify that L2 LAG hashing functionality working fine in Sonic + Test scenario-4: Verify that adding ports to a LAG causes traffic to redistribute to new ports. + Test scenario-5: Verify LLDP interaction with LAG. + Test scenario-6: Verify that a LAG with only 1 port functions properly. + Test scenario-7: Verify that shutdown and "no shutdown" of port channel group port bring the port back to active state. + Test scenario-8: Verify that the LAG in DUT is not UP when LAG is not created at partner DUT + Test scenario-9: Verify that LAG status should be Down when none of LAG members are in Active state. + Test scenario-10: Verify that no traffic is forwarded on a disabled LAG + Test scenario-11: Verify only participating lags that are members of the VLAN forward tagged traffic + Test scenario-12: Verify that the LAG in DUT is not UP when LAG is not created at partner DUT + ''' + stream = data.tg.tg_traffic_config(port_handle=data.tg_ph_1, mode='create', length_mode='fixed', frame_size=72, + mac_src='00:01:00:00:00:01', mac_src_step='00:00:00:00:00:01', mac_src_mode='increment', mac_src_count=200, + mac_dst='00:02:00:00:00:02', mac_dst_step='00:00:00:00:00:01', mac_dst_mode='increment', mac_dst_count=200, + rate_pps=2000, l2_encap='ethernet_ii_vlan', vlan="enable", vlan_id=data.vid, transmit_mode='continuous') + data.streams['D1T1_SD_Mac_Hash1'] = stream['stream_id'] + data.return_value = 1 + st.log("Test scenario-1: Verifying that deleting port channel with member ports should not be successful") + if portchannel_obj.delete_portchannel(vars.D1, data.portchannel_name, cli_type=data.cli_type): + data.return_value = 2 + st.report_fail('portchannel_with_members_deletion_should_not_successful', data.portchannel_name, vars.D1) + st.log("Test scenario-1: Successfully verified that deleting port channel with member ports should not be successful") + + st.log("Test scenario-2: Verifying that removal of a port from a LAG does not interrupt traffic") + clear_intf_counters_using_thread([vars.D1, vars.D2]) + data.tg.tg_traffic_control(action='run', stream_handle=data.streams['D1T1_SD_Mac_Hash1']) + st.wait(2) + exec_parallel(True, [vars.D1, vars.D2], intf_obj.show_interface_counters_all, [None,None]) + random_number = int(randomnumber(4)) + random_member1 = data.members_dut1[random_number] + if not portchannel_obj.delete_portchannel_member(vars.D1, data.portchannel_name, random_member1, + cli_type=data.cli_type): + st.report_fail('portchannel_member_delete_failed', random_member1, data.portchannel_name) + temp_member_list1 = data.members_dut1[:] + temp_member_list1.remove(random_member1) + if portchannel_obj.verify_portchannel_and_member_status(vars.D1, data.portchannel_name, random_member1, + cli_type=data.cli_type): + st.report_fail('portchannel_member_verification_failed', data.portchannel_name, vars.D1, random_member1) + st.wait(2) + st.log('Test scenario-3: Verifying that L2 LAG hashing functionality working fine in Sonic') + portchannel_members_counters1 = verify_traffic_hashed_or_not(vars.D1, data.members_dut1, 100) + st.log('Test scenario-3: Successfully verified that L2 LAG hashing functionality working fine in Sonic') + + verify_portchannel_cum_member_status(vars.D1, data.portchannel_name, temp_member_list1, iter_delay=1) + portchannel_members_counters2 = verify_traffic_hashed_or_not(vars.D1, data.members_dut1, 100) + if not (portchannel_members_counters1[random_member1]+10 > portchannel_members_counters2[random_member1]): + st.report_fail('portchannel_count_verification_fail', vars.D1, random_member1) + if not portchannel_obj.add_portchannel_member(vars.D1, data.portchannel_name, random_member1, + cli_type=data.cli_type): + st.report_fail('add_members_to_portchannel_failed', random_member1, data.portchannel_name, vars.D1) + verify_portchannel_cum_member_status(vars.D1, data.portchannel_name, data.members_dut1, iter_delay=1) + portchannel_members_counters3 = verify_traffic_hashed_or_not(vars.D1, data.members_dut1, 100) + st.log('Test scenario-4: Verifying that adding ports to a LAG causes traffic to redistribute to new ports') + if not (portchannel_members_counters3[random_member1] >= portchannel_members_counters2[random_member1]+10): + st.report_fail('traffic_not_hashed', vars.D1) + st.log('Test scenario-4: Successfully verified that adding ports to a LAG causes traffic to redistribute to new ports') + + st.log("LAG Members: {}".format(",".join(temp_member_list1))) + st.log("LAG Member Counters-0: {} {} {}".format( portchannel_members_counters1[temp_member_list1[0]], + portchannel_members_counters2[temp_member_list1[0]], portchannel_members_counters3[temp_member_list1[0]])) + st.log("LAG Member Counters-1: {} {} {}".format( portchannel_members_counters1[temp_member_list1[1]], + portchannel_members_counters2[temp_member_list1[1]], portchannel_members_counters3[temp_member_list1[1]])) + st.log("LAG Member Counters-2: {} {} {}".format( portchannel_members_counters1[temp_member_list1[2]], + portchannel_members_counters2[temp_member_list1[2]], portchannel_members_counters3[temp_member_list1[2]])) + + if not ((portchannel_members_counters1[temp_member_list1[0]] < portchannel_members_counters2[temp_member_list1[0]] < + portchannel_members_counters3[temp_member_list1[0]]) + and (portchannel_members_counters1[temp_member_list1[1]] < portchannel_members_counters2[temp_member_list1[1]] < + portchannel_members_counters3[temp_member_list1[1]]) + and (portchannel_members_counters1[temp_member_list1[2]] < portchannel_members_counters2[temp_member_list1[2]] < + portchannel_members_counters3[temp_member_list1[2]])): + st.report_fail('traffic_not_hashed', vars.D1) + data.tg.tg_traffic_control(action='stop', stream_handle=data.streams['D1T1_SD_Mac_Hash1']) + st.wait(1) + st.log('Fetching interface counters in both the DUTs') + data.intf_count1, data.intf_count2 = get_intf_counters_using_thread([vars.D1, vars.D2]) + for counter_dict in data.intf_count1: + if counter_dict['iface'] == vars.D1T1P1: + rx_ok_counter = counter_dict['rx_ok'].replace(',', '') + data.data_rx = int(rx_ok_counter) if rx_ok_counter.isdigit() else 0 + break + for counter_dict in data.intf_count2: + if counter_dict['iface'] == vars.D2T1P1: + tx_ok_counter = counter_dict['tx_ok'].replace(',', '') + data.data_tx = int(tx_ok_counter) if tx_ok_counter.isdigit() else 0 + break + st.log('Total frames sent:{}'.format(data.data_rx)) + st.log('Total frames received:{}'.format(data.data_tx)) + data.data101_tx = 1.05 * data.data_tx + if not (data.data101_tx >= data.data_rx): + st.report_fail('traffic_verification_failed') + st.log("Test scenario-2: Successfully verified that removal of a port from a LAG does not interrupt traffic") + + st.log("Test scenario-7: Verifying that shutdown and 'no shutdown' of port channel group port bring the port back to active state") + st.log('To be added once STP supported') + + st.log("Test scenario-5: Verifying LLDP interaction with LAG") + data.mgmt_int = 'eth0' + ipaddress_d1, ipaddress_d2 = get_mgmt_ip_using_thread([vars.D1, vars.D2], [data.mgmt_int, data.mgmt_int]) + hostname_d1, hostname_d2 = get_hostname_using_thread([vars.D1, vars.D2]) + check_lldp_neighbors(vars.D1, random_member1, ipaddress_d2, hostname_d2) + st.log("Test scenario-5: Successfully verified LLDP interaction with LAG") + + st.log("Test scenario-10: Verifying that no traffic is forwarded on a disabled LAG") + clear_intf_counters_using_thread([vars.D1, vars.D2]) + data.tg.tg_traffic_control(action='run', stream_handle=data.streams['D1T1_SD_Mac_Hash1']) + st.wait(2) + exec_parallel(True, [vars.D1, vars.D2], intf_obj.show_interface_counters_all, [None,None]) + st.log('Administratively disable portchannel in DUT1') + if not intf_obj.interface_operation(data.dut1, data.portchannel_name, 'shutdown', skip_verify=False, + cli_type=data.cli_type): + st.report_fail('interface_admin_shut_down_fail', data.portchannel_name) + st.wait(2) + st.log('Verify whether traffic is hashed over portchannel members or not and fetchig counters') + data.int_counter1 = verify_traffic_hashed_or_not(vars.D1, data.members_dut1, 100) + st.log('Verify whether the portchannel is down or not') + try: + data.portchannel_status_output = portchannel_obj.get_portchannel(vars.D1, portchannel_name=data.portchannel_name)[0] + except: + data.return_value = 3 + st.report_fail('portchannel_verification_failed', data.portchannel_name, vars.D1) + if not (data.portchannel_status_output['protocol'] == 'LACP(A)(Dw)'): + data.return_value = 3 + st.report_fail('portchannel_state_fail', data.portchannel_name, vars.D1, 'down') + data.int_counter2 = verify_traffic_hashed_or_not(vars.D1, data.members_dut1, 100) + if not (((data.int_counter1[vars.D1D2P1] + 10) >= data.int_counter2[vars.D1D2P1]) and + ((data.int_counter1[vars.D1D2P2] + 10) >= data.int_counter2[vars.D1D2P2]) and + ((data.int_counter1[vars.D1D2P3] + 10) >= data.int_counter2[vars.D1D2P3]) and + ((data.int_counter1[vars.D1D2P4] + 10) >= data.int_counter2[vars.D1D2P4])): + data.return_value = 3 + st.report_fail('traffic_hashed', vars.D1) + st.log('Administratively Enable portchannel in DUT1') + if not intf_obj.interface_operation(vars.D1, data.portchannel_name, 'startup', skip_verify=False, + cli_type=data.cli_type): + st.report_fail('interface_admin_startup_fail', data.portchannel_name) + st.log('Verify that whether the portchannel is Up or not') + if not portchannel_obj.verify_portchannel_and_member_status(vars.D1, data.portchannel_name, data.members_dut1, + cli_type=data.cli_type): + st.report_fail('portchannel_state_fail', data.portchannel_name, vars.D1, 'up') + st.wait(1) + data.int_counter3 = verify_traffic_hashed_or_not(vars.D1, data.members_dut1, 300) + if not ((data.int_counter3[vars.D1D2P1] > data.int_counter2[vars.D1D2P1]) and + (data.int_counter3[vars.D1D2P2] > data.int_counter2[vars.D1D2P2]) and + (data.int_counter3[vars.D1D2P3] > data.int_counter2[vars.D1D2P3]) and + (data.int_counter3[vars.D1D2P4] > data.int_counter2[vars.D1D2P4])): + st.report_fail('traffic_not_hashed', vars.D1) + st.log("Test scenario-10: Successfully verified that no traffic is forwarded on a disabled LAG") + + st.log("Test scenario-11: Verifying only participating lags that are members of the VLAN forward tagged traffic") + st.log('Exclude Port-channel from VLAN') + if not vlan_obj.delete_vlan_member(vars.D1, data.vid, [data.portchannel_name], data.cli_type): + data.return_value = 4 + st.report_fail('vlan_member_deletion_failed', data.portchannel_name) + st.wait(2) + st.log('Verify whether traffic is hashed over portchannel members or not and fetchig counters') + data.int_counter1 = verify_traffic_hashed_or_not(vars.D1, data.members_dut1, 100) + if not vlan_obj.verify_vlan_config(vars.D1, data.vid): + st.report_fail('vlan_member_delete_failed', data.vid, data.portchannel_name) + data.int_counter1 = verify_traffic_hashed_or_not(vars.D1, data.members_dut1, 100) + if not (((data.int_counter1[vars.D1D2P1] + 10) >= data.int_counter2[vars.D1D2P1]) and + ((data.int_counter1[vars.D1D2P2] + 10) >= data.int_counter2[vars.D1D2P2]) and + ((data.int_counter1[vars.D1D2P3] + 10) >= data.int_counter2[vars.D1D2P3]) and + ((data.int_counter1[vars.D1D2P4] + 10) >= data.int_counter2[vars.D1D2P4])): + data.return_value = 4 + st.report_fail('traffic_hashed', vars.D1) + st.log('Include Port-channel from VLAN') + if not vlan_obj.add_vlan_member(vars.D1, data.vid, [data.portchannel_name], tagging_mode=True, + cli_type=data.cli_type): + data.return_value = 4 + st.report_fail('vlan_tagged_member_fail', data.portchannel_name, data.vid) + if not vlan_obj.verify_vlan_config(vars.D1, data.vid, tagged=[data.portchannel_name]): + data.return_value = 4 + st.report_fail('vlan_tagged_member_fail', data.portchannel_name, data.vid) + data.int_counter3 = verify_traffic_hashed_or_not(vars.D1, data.members_dut1, 300) + data.tg.tg_traffic_control(action='stop', stream_handle=data.streams['D1T1_SD_Mac_Hash1']) + if not ((data.int_counter3[vars.D1D2P1] > data.int_counter2[vars.D1D2P1]) and + (data.int_counter3[vars.D1D2P2] > data.int_counter2[vars.D1D2P2]) and + (data.int_counter3[vars.D1D2P3] > data.int_counter2[vars.D1D2P3]) and + (data.int_counter3[vars.D1D2P4] > data.int_counter2[vars.D1D2P4])): + st.report_fail('traffic_not_hashed', vars.D1) + st.log("Test scenario-11: Successfully verified only participating lags that are members of the VLAN forward tagged traffic") + + st.log("Test scenario-6: Verifying that a LAG with only 1 port functions properly") + random_member2 = data.members_dut2[random_number] + temp_member_list2 = data.members_dut2[:] + temp_member_list2.remove(random_member2) + add_del_member_using_thread([vars.D1, vars.D2], [data.portchannel_name, data.portchannel_name], + [temp_member_list1,temp_member_list2], flag='del') + sub_list = [] + sub_list.append([portchannel_obj.verify_portchannel_and_member_status, vars.D1, data.portchannel_name, + random_member1, data.cli_type]) + sub_list.append([portchannel_obj.verify_portchannel_and_member_status, vars.D2, data.portchannel_name, + random_member2, data.cli_type]) + [output, expressions] = exec_all(True, sub_list) + st.log("Test scenario-6: Successfully verified that a LAG with only 1 port functions properly") + + st.log("Test scenario-12: Verifying that the LAG in DUT is not UP when LAG is not created at partner DUT") + dict1 = {'portchannel': data.portchannel_name, 'members': temp_member_list1, 'flag': "add", + 'cli_type': data.cli_type} + dict2 = {'portchannel': data.portchannel_name, 'members': random_member2, 'flag': "del", 'cli_type': data.cli_type} + output = exec_parallel(True, [vars.D1, vars.D2], portchannel_obj.add_del_portchannel_member, [dict1, dict2]) + ensure_no_exception(output[1]) + if not output[0][0]: + st.report_fail('portchannel_create_failed', data.portchannel_name, vars.D1) + if not output[0][1]: + st.report_fail('portchannel_deletion_failed', data.portchannel_name) + if not portchannel_obj.poll_for_portchannel_status(vars.D1, data.portchannel_name, "down", cli_type=data.cli_type): + st.report_fail('portchannel_state_fail', data.portchannel_name, vars.D1, 'down') + st.log("Test scenario-12: Successfully Verified that the LAG in DUT is not UP when LAG is not created at partner DUT") + + if not portchannel_obj.add_del_portchannel_member(vars.D2, data.portchannel_name, data.members_dut2, + cli_type=data.cli_type): + st.report_fail('portchannel_create_failed', data.portchannel_name, vars.D2) + verify_portchannel_status(delay=1) + st.log("Verifying that LAG status should be Down when none of LAG members are in Active state") + intf_obj.interface_shutdown(vars.D1, data.members_dut1, skip_verify=False, cli_type=data.cli_type) + if not portchannel_obj.poll_for_portchannel_status(vars.D1, data.portchannel_name, "down", cli_type=data.cli_type): + data.return_value = 5 + st.report_fail('portchannel_state_fail', data.portchannel_name, vars.D1, 'down') + intf_obj.interface_noshutdown(vars.D1, data.members_dut1, skip_verify=False, cli_type=data.cli_type) + st.log("Successfully verified that LAG status should be Down when none of LAG members are in Active state") + st.report_pass("test_case_passed") + + +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_untagged_traffic_on_portchannel(): + ''' + This test case covers below test scenarios/tests + scenario-1: Verify that LAGs treat untagged packets identically to regular ports. + ''' + stream = data.tg.tg_traffic_config(port_handle=data.tg_ph_1, mode='create', length_mode='fixed', frame_size=90, + mac_src='00:05:00:00:00:01', mac_src_step='00:00:00:00:00:01', mac_src_mode='increment', mac_src_count=200, + mac_dst='00:06:00:00:00:02', mac_dst_step='00:00:00:00:00:01', mac_dst_mode='increment', mac_dst_count=200, + pkts_per_burst=2000, l2_encap='ethernet_ii_vlan', transmit_mode='single_burst') + data.streams['D1T1_SD_Mac_Hash3'] = stream['stream_id'] + clear_intf_counters_using_thread([vars.D1, vars.D2]) + data.tg.tg_traffic_control(action='run', stream_handle=data.streams['D1T1_SD_Mac_Hash3']) + st.wait(2) + exec_parallel(True, [vars.D1, vars.D2], intf_obj.show_interface_counters_all, [None,None]) + verify_traffic_hashed_or_not(vars.D1, data.members_dut1 , 400) + st.report_pass('test_case_passed') + + +@pytest.mark.l3_lag_hash +def test_ft_lag_l3_hash_sip_dip_l4port(): + """ + Author: Karthik Kumar Goud Battula(karthikkumargoud,battula@broadcom.com) + scenario1-Verify that L3 LAG hashing functionality working fine in Sonic + scenario2 - Verify an ARP table entry learned on Port-Channel based routing interface is removed + from ARP table after Port-Channel is shutdown. + """ + stream = data.tg.tg_traffic_config(port_handle=data.tg_ph_1, mode='create', length_mode='fixed', mac_dst=data.dut1_rt_int_mac, + mac_src='00:05:00:00:00:01', mac_src_mode='increment', mac_src_step='00:00:00:00:00:01', mac_dst_mode='fixed', + ip_src_addr=data.ip41, ip_src_mode='increment', ip_src_count=data.ip_src_count, ip_src_step='0.0.0.1', mac_src_count=1000, + ip_dst_addr=data.ip42, ip_dst_mode='fixed', pkts_per_burst=1000, l3_protocol='ipv4', transmit_mode='single_burst') + data.streams['D1T1_SD_ip_Hash1'] = stream['stream_id'] + result_state = True + data.subnet = '8' + data.ip_addr_pc1 = '20.1.1.2' + data.ip_addr_pc2 = '20.1.1.3' + data.ipv4 = 'ipv4' + data.ip_addr_po1 = '10.1.1.3' + data.ip_addr_po2 = '30.1.1.2' + data.ip_addr_po3 = '30.1.1.3' + data.static_ip1 = '10.0.0.0/8' + data.static_ip2 = '30.0.0.0/8' + data.static_ip3 = '40.0.0.0/8' + data.remote_mac = '00:00:00:00:00:01' + data.remote_mac2 = '00:00:00:00:00:02' + dict1 = {'interface_name': data.portchannel_name, 'ip_address': data.ip_addr_pc1, 'subnet': data.subnet, + 'family': "ipv4"} + dict2 = {'interface_name': data.portchannel_name, 'ip_address': data.ip_addr_pc2, 'subnet': data.subnet, + 'family': "ipv4"} + output = exec_parallel(True, [vars.D1, vars.D2], ip_obj.config_ip_addr_interface, [dict1, dict2]) + ensure_no_exception(output[1]) + dict1 = {'interface_name': vars.D1T1P1, 'ip_address': data.ip_addr_po1, 'subnet': data.subnet, 'family': "ipv4"} + dict2 = {'interface_name': vars.D2T1P1, 'ip_address': data.ip_addr_po2, 'subnet': data.subnet, 'family': "ipv4"} + output = exec_parallel(True, [vars.D1, vars.D2], ip_obj.config_ip_addr_interface, [dict1, dict2]) + ensure_no_exception(output[1]) + dict1 = {'interface_name': vars.D1T1P1, 'ip_address': "{}/8".format(data.ip_addr_po1), 'family': "ipv4"} + dict2 = {'interface_name': vars.D2T1P1, 'ip_address': "{}/8".format(data.ip_addr_po2), 'family': "ipv4"} + output = exec_parallel(True, [vars.D1, vars.D2], ip_obj.verify_interface_ip_address, [dict1, dict2]) + ensure_no_exception(output[1]) + if not output[0][0]: + st.report_fail('ip_routing_int_create_fail', data.ip_addr_po1) + if not output[0][1]: + st.report_fail('ip_routing_int_create_fail', data.ip_addr_po2) + #Scenario 2 + # ping from partner + ip_obj.ping(vars.D2, data.ip_addr_pc1 , family='ipv4', count=3) + # test arp entry on portchannel + if not arp_obj.get_arp_count(vars.D1, data.ip_addr_pc2): + st.error('Dynamic arp entry on prtchannel failed: ARP_entry_dynamic_entry_fail') + result_state = False + port_obj.shutdown(vars.D1, [data.portchannel_name]) + # test arp entry on portchannel after shutdown it + if arp_obj.get_arp_count(vars.D1, data.ip_addr_pc2): + st.error('Dynamic arp entry on prtchannel is not removed after shutdown:ARP_dynamic_entry_removal_fail') + result_state = False + port_obj.noshutdown(vars.D1, [data.portchannel_name]) + + ip_obj.create_static_route(vars.D1, data.ip_addr_pc2, data.static_ip2, shell='vtysh', family=data.ipv4) + dict1 = {'next_hop': data.ip_addr_pc2, 'static_ip': data.static_ip3, 'shell': "vtysh", 'family': 'ipv4'} + dict2 = {'next_hop': data.ip_addr_po3, 'static_ip': data.static_ip3, 'shell': "vtysh", 'family': 'ipv4'} + output = exec_parallel(True, [vars.D1, vars.D2], ip_obj.create_static_route, [dict1, dict2]) + ensure_no_exception(output[1]) + arp_obj.add_static_arp(vars.D2, data.ip_addr_po3, data.remote_mac) + arp_obj.add_static_arp(vars.D2, data.ip42, data.remote_mac2) + if not ip_obj.verify_ip_route(vars.D1, data.ipv4, ip_address=data.static_ip2, type="S"): + st.error('ip_static_route_create_fail on data.static_ip2') + result_state = False + ip_obj.create_static_route(vars.D2, data.ip_addr_pc1, data.static_ip1, shell='vtysh', family=data.ipv4) + if not ip_obj.verify_ip_route(vars.D2, data.ipv4, ip_address=data.static_ip1, type="S"): + st.error('ip_static_route_create_fail on data.static_ip1') + result_state = False + if not ip_obj.ping(vars.D1, data.ip_addr_pc2): + st.report_fail("ping_fail", data.ip_addr_pc2) + dict1 = {'addresses': data.ip_addr_po2} + dict2 = {'addresses': data.ip_addr_po1} + output = exec_parallel(True, [vars.D1, vars.D2], ip_obj.ping, [dict1, dict2]) + ensure_no_exception(output[1]) + if not output[0][0]: + st.report_fail("ping_fail", data.ip_addr_po2) + if not output[0][1]: + st.report_fail("ping_fail", data.ip_addr_po1) + # Ping from tgen to DUT. + res = tgapi.verify_ping(src_obj=data.tg, port_handle=data.tg_ph_1, dev_handle=data.h1['handle'], dst_ip=data.ip42, + ping_count='1', exp_count='1') + st.log("PING_RES: " + str(res)) + if res: + st.log("Ping succeeded.") + else: + st.log("Ping failed.") + clear_intf_counters_using_thread([vars.D1, vars.D2]) + data.tg.tg_traffic_control(action='run', stream_handle=data.streams['D1T1_SD_ip_Hash1'], enable_arp=0) + st.wait(2) + data.tg.tg_traffic_control(action='stop', stream_handle=data.streams['D1T1_SD_ip_Hash1']) + st.log("Verify that traffic is forwarding over portchannel members") + verify_traffic_hashed_or_not(vars.D1, data.members_dut1, 200, + traffic_loss_verify=True, rx_port=vars.D1T1P1, tx_port=vars.D2T1P1, dut2=vars.D2) + data.tg.tg_traffic_control(action='reset', port_handle=data.tg_ph_1) + stream = data.tg.tg_traffic_config(port_handle=data.tg_ph_1, mode='create', length_mode='fixed', frame_size=90, + mac_src='00:05:00:00:00:01', mac_src_mode='fixed', mac_dst=data.dut1_rt_int_mac, ip_src_addr=data.ip41, + ip_src_mode='fixed', ip_dst_addr=data.ip43, ip_dst_mode='increment', ip_dst_step='0.0.0.1', + ip_dst_count=data.ip_dst_count, pkts_per_burst=2000, l3_protocol='ipv4', transmit_mode='single_burst') + data.streams['D1T1_SD_ip_Hash2'] = stream['stream_id'] + clear_intf_counters_using_thread([vars.D1, vars.D2]) + data.tg.tg_traffic_control(action='run', stream_handle=data.streams['D1T1_SD_ip_Hash2'], enable_arp=0) + st.wait(2) + data.tg.tg_traffic_control(action='stop', stream_handle=data.streams['D1T1_SD_ip_Hash2']) + st.log("Verify that traffic is forwarding over portchannel members") + verify_traffic_hashed_or_not(vars.D1, data.members_dut1, 300, + traffic_loss_verify=True, rx_port=vars.D1T1P1, tx_port=vars.D2T1P1, dut2=vars.D2) + data.tg.tg_traffic_control(action='reset', port_handle=data.tg_ph_1) + stream = data.tg.tg_traffic_config(port_handle=data.tg_ph_1, mode='create', length_mode='fixed', frame_size=90, + mac_src='00:05:00:00:00:01', mac_src_mode='fixed', mac_dst=data.dut1_rt_int_mac, tcp_src_port_step=1, + ip_src_addr=data.ip41, tcp_src_port=data.src_port, tcp_src_port_mode='incr', tcp_src_port_count=data.tcp_src_port_count, + tcp_dst_port=data.dst_port, ip_dst_addr=data.ip42, tcp_dst_port_mode='incr', pkts_per_burst=2000, + l4_protocol='tcp', tcp_dst_port_step=1, tcp_dst_port_count=data.tcp_dst_port_count, l3_protocol='ipv4', transmit_mode='single_burst') + data.streams['D1T1_SD_ip_Hash3'] = stream['stream_id'] + clear_intf_counters_using_thread([vars.D1, vars.D2]) + data.tg.tg_traffic_control(action='run', stream_handle=data.streams['D1T1_SD_ip_Hash3'], enable_arp=0) + st.wait(2) + data.tg.tg_traffic_control(action='stop', stream_handle=data.streams['D1T1_SD_ip_Hash3']) + st.log("Verify that traffic is forwarding over portchannel members") + verify_traffic_hashed_or_not(vars.D1, data.members_dut1, 300, + traffic_loss_verify=True, rx_port=vars.D1T1P1, tx_port=vars.D2T1P1, dut2=vars.D2) + clear_intf_counters_using_thread([vars.D1, vars.D2]) + if result_state: + st.report_pass('test_case_passed') + else: + st.report_fail("traffic_not_hashed", data.dut1) + + +@pytest.mark.lag_member_interchanged +def test_ft_member_state_after_interchanged_the_members_across_portchannels(): + """ + Author: vishnuvardhan.talluri@broadcom.com + scenario; Verify that the LAG members in DUT are not UP when LAG members between two different Lags are + interchanged + :return: + """ + verify_portchannel_status() + portchannel_name_second = "PortChannel102" + result_state = True + + # Remove 2 members from portchannel + dict1 = {'portchannel': data.portchannel_name, 'members': data.members_dut1[2:], 'cli_type': data.cli_type} + dict2 = {'portchannel': data.portchannel_name, 'members': data.members_dut2[2:], 'cli_type': data.cli_type} + output = exec_parallel(True, [vars.D1, vars.D2], portchannel_obj.delete_portchannel_member, [dict1, dict2]) + ensure_no_exception(output[1]) + # add second portchannel + portchannel_obj.config_portchannel(data.dut1, data.dut2, portchannel_name_second, data.members_dut1[2:], + data.members_dut2[2:], "add", cli_type=data.cli_type) + dict1 = {'interfaces': portchannel_name_second, 'operation': "startup", 'skip_verify': True, + 'cli_type': data.cli_type} + output = exec_parallel(True, [vars.D1, vars.D2], intf_obj.interface_operation, [dict1, dict1]) + ensure_no_exception(output[1]) + if not (output[0][0] and output[0][1]): + st.report_fail('interface_admin_startup_fail', portchannel_name_second) + #Verify portchannel is up + dict1 = {'portchannel': portchannel_name_second, 'members': data.members_dut1[2:], 'cli_type': data.cli_type} + dict2 = {'portchannel': portchannel_name_second, 'members': data.members_dut2[2:], 'cli_type': data.cli_type} + output = exec_parallel(True, [vars.D1, vars.D2], portchannel_obj.verify_portchannel_and_member_status, [dict1, dict2]) + ensure_no_exception(output[1]) + if not (output[0][0] and output[0][1]): + result_state = False + # Interchange ports from one portchannel to another portchannel + portchannel_obj.delete_portchannel_member(data.dut1, data.portchannel_name, data.members_dut1[0], data.cli_type) + portchannel_obj.delete_portchannel_member(data.dut1, portchannel_name_second, data.members_dut1[2], data.cli_type) + # Wait 3 times the lacp long timeout period to allow dut members to go down + st.wait(90) + output1 = portchannel_obj.verify_portchannel_member_state(data.dut2, data.portchannel_name, data.members_dut2[0], "down", data.cli_type) + if not output1: + output1 = portchannel_obj.verify_portchannel_member_state(data.dut2, data.portchannel_name, + data.members_dut2[0], "down", data.cli_type) + output2 = portchannel_obj.verify_portchannel_member_state(data.dut2, portchannel_name_second, data.members_dut2[2], "down", data.cli_type) + if not (output1 and output2): + result_state = False + # swapping the ports in DUT1 only + output1 = portchannel_obj.add_portchannel_member(data.dut1, data.portchannel_name, data.members_dut1[2], data.cli_type) + output2 = portchannel_obj.add_portchannel_member(data.dut1, portchannel_name_second, data.members_dut1[0], + data.cli_type) + if not (output1 and output2): + result_state = False + # Wait for few seconds after converge and ensure member ports states proper + st.wait(5) + # Verify portchannel member state with provided state + dict1 = {'portchannel': data.portchannel_name, 'members_list': data.members_dut1[2], 'state': "down", + 'cli_type': data.cli_type} + dict2 = {'portchannel': data.portchannel_name, 'members_list': data.members_dut2[0], 'state': "down", + 'cli_type': data.cli_type} + output = exec_parallel(True, [vars.D1, vars.D2], portchannel_obj.verify_portchannel_member_state, [dict1, dict2]) + ensure_no_exception(output[1]) + if not (output[0][0] and output[0][1]): + result_state = False + dict1 = {'portchannel': portchannel_name_second, 'members_list': data.members_dut1[0], 'state': "down", + 'cli_type': data.cli_type} + dict2 = {'portchannel': portchannel_name_second, 'members_list': data.members_dut2[2], 'state': "down", + 'cli_type': data.cli_type} + output = exec_parallel(True, [vars.D1, vars.D2], portchannel_obj.verify_portchannel_member_state, [dict1, dict2]) + ensure_no_exception(output[1]) + if not (output[0][0] and output[0][1]): + result_state = False + dict1 = {'portchannel': data.portchannel_name, 'members_list': data.members_dut1[1], 'cli_type': data.cli_type} + dict2 = {'portchannel': data.portchannel_name, 'members_list': data.members_dut2[1], 'cli_type': data.cli_type} + output = exec_parallel(True, [vars.D1, vars.D2], portchannel_obj.verify_portchannel_member_state, [dict1, dict2]) + ensure_no_exception(output[1]) + if not (output[0][0] and output[0][1]): + result_state = False + dict1 = {'portchannel': portchannel_name_second, 'members_list': data.members_dut1[3], 'cli_type': data.cli_type} + dict2 = {'portchannel': portchannel_name_second, 'members_list': data.members_dut2[3], 'cli_type': data.cli_type} + output = exec_parallel(True, [vars.D1, vars.D2], portchannel_obj.verify_portchannel_member_state, [dict1, dict2]) + ensure_no_exception(output[1]) + if not (output[0][0] and output[0][1]): + result_state = False + # ensuring module config + portchannel_obj.config_portchannel(data.dut1, data.dut2, portchannel_name_second, + [data.members_dut1[0], data.members_dut1[3]], data.members_dut2[2:], 'delete', + cli_type=data.cli_type) + dict1 = {'portchannel': data.portchannel_name, + 'members': [data.members_dut1[0], data.members_dut1[3]], 'cli_type': data.cli_type} + dict2 = {'portchannel': data.portchannel_name, 'members': data.members_dut2[2:], 'cli_type': data.cli_type} + output = exec_parallel(True, [vars.D1, vars.D2], portchannel_obj.add_portchannel_member, [dict1, dict2]) + ensure_no_exception(output[1]) + if not (output[0][0] and output[0][1]): + result_state = False + if result_state: + st.report_pass("operation_successful") + else: + st.report_fail("portchannel_member_state_failed") + + +@pytest.mark.portchannel_with_vlan_variations +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_portchannel_with_vlan_variations(): + ''' + Author: Jagadish + This test case covers below test scenarios/tests + FtOpSoSwLagFn041 : Verify that port-channel is up or not when port-channel created followed by add it to VLAN and + then making the port-channel up. + FtOpSoSwLagFn042 : Verify that port-channel is up when port-channel is created, making the port-channel up and then + adding the port-channel to VLAN + ''' + dict1 = {'portchannel': data.portchannel_name, 'members_list': [data.members_dut1[0], data.members_dut1[1]]} + dict2 = {'portchannel': data.portchannel_name, 'members_list': [data.members_dut2[0], data.members_dut2[1]]} + output = exec_parallel(True, [vars.D1, vars.D2], verify_portchannel_cum_member_status, [dict1, dict2]) + ensure_no_exception(output[1]) + portchannel_obj.config_portchannel(data.dut1, data.dut2, data.portchannel_name2, [data.members_dut1[2], data.members_dut1[3]], + [data.members_dut2[2], data.members_dut2[3]], "add", cli_type=data.cli_type) + dict1 = {'portchannel': data.portchannel_name2, 'members_list': [data.members_dut1[2], data.members_dut1[3]]} + dict2 = {'portchannel': data.portchannel_name2, 'members_list': [data.members_dut2[2], data.members_dut2[3]]} + output = exec_parallel(True, [vars.D1, vars.D2], verify_portchannel_cum_member_status, [dict1, dict2]) + ensure_no_exception(output[1]) + vlan_obj.create_vlan_and_add_members(vlan_data=[{"dut": [vars.D1,vars.D2], "vlan_id":data.vlan_id, "tagged":data.portchannel_name2}]) + dict1 = {'portchannel': data.portchannel_name2, 'members_list': [data.members_dut1[2], data.members_dut1[3]]} + dict2 = {'portchannel': data.portchannel_name2, 'members_list': [data.members_dut2[2], data.members_dut2[3]]} + output = exec_parallel(True, [vars.D1, vars.D2], verify_portchannel_cum_member_status, [dict1, dict2]) + ensure_no_exception(output[1]) + #Clean up + dict1 = {"vlan": data.vlan_id, "port_list": data.portchannel_name2} + dict2 = {"vlan": data.vlan_id, "port_list": data.portchannel_name2} + output = exec_parallel(True, [vars.D1, vars.D2], vlan_obj.delete_vlan_member, [dict1, dict2]) + ensure_no_exception(output[1]) + dict1 = {"vlan_list": data.vlan_id, "cli_type": data.cli_type} + dict2 = {"vlan_list": data.vlan_id, "cli_type": data.cli_type} + output = exec_parallel(True, [vars.D1, vars.D2], vlan_obj.delete_vlan, [dict1, dict2]) + portchannel_obj.config_portchannel(data.dut1, data.dut2, data.portchannel_name2, [data.members_dut1[2], data.members_dut1[3]], + [data.members_dut2[2], data.members_dut2[3]], "del", cli_type=data.cli_type) + ensure_no_exception(output[1]) + st.report_pass('test_case_passed') + + +def test_ft_lacp_graceful_restart_with_cold_boot(): + ''' + This test case covers below test scenarios/tests + scenario-1: Verify the LACP graceful restart functionality with cold reboot. + ''' + if not data.graceful_restart_config: + graceful_restart_prolog() + data.graceful_restart_config = True + [output, exceptions] = exec_all(True, [ExecAllFunc(poll_wait, verify_portchannel_member_status, 7, vars.D1, [data.portchannel_name, data.portchannel_name2], [data.lag_up, data.lag_up], [[vars.D1D2P1, vars.D1D2P2], [vars.D1D2P3, vars.D1D2P4]], [None, None]), ExecAllFunc(poll_wait, verify_portchannel_member_status, 7, vars.D2, [data.portchannel_name, data.portchannel_name2], [data.lag_up, data.lag_up], [[vars.D2D1P1, vars.D2D1P2], [vars.D2D1P3, vars.D2D1P4]], [None, None])]) + ensure_no_exception(exceptions) + if False in output: + st.report_fail('portchannel_member_state_failed') + config_save(vars.D2) + slog.clear_logging(vars.D2) + [output, exceptions] = exec_all(True, [ExecAllFunc(st.reboot, vars.D2), ExecAllFunc(poll_wait, verify_portchannel_member_status, 7, vars.D1, [data.portchannel_name, data.portchannel_name2], [data.lag_down, data.lag_down], [None, None], [[vars.D1D2P1, vars.D1D2P2], [vars.D1D2P3, vars.D1D2P4]])]) + ensure_no_exception(exceptions) + if False in output: + st.report_fail('portchannel_member_state_failed') + count_msg1 = slog.get_logging_count(vars.D2, severity="NOTICE", filter_list=["teamd#teammgrd: :- sig_handler: --- Received SIGTERM. Terminating PortChannels gracefully"]) + count_msg2 = slog.get_logging_count(vars.D2, severity="NOTICE", filter_list=["teamd#teammgrd: :- sig_handler: --- PortChannels terminated gracefully"]) + if not (count_msg1 == 1 and count_msg2 == 1): + st.error('SYSLOG message is not observed for graceful restart') + st.report_fail('failed_to_generate_lacp_graceful_restart_log_in_syslog') + [output, exceptions] = exec_all(True, [ExecAllFunc(poll_wait, verify_portchannel_member_status, 7, vars.D1, [data.portchannel_name, data.portchannel_name2], [data.lag_up, data.lag_up], [[vars.D1D2P1, vars.D1D2P2], [vars.D1D2P3, vars.D1D2P4]], [None, None]), ExecAllFunc(poll_wait, verify_portchannel_member_status, 7, vars.D2, [data.portchannel_name, data.portchannel_name2], [data.lag_up, data.lag_up], [[vars.D2D1P1, vars.D2D1P2], [vars.D2D1P3, vars.D2D1P4]], [None, None])]) + ensure_no_exception(exceptions) + if False in output: + st.report_fail('portchannel_member_state_failed') + st.report_pass('verify_lacp_graceful_restart_success', 'with cold reboot') + + +def test_ft_lacp_graceful_restart_with_save_reload(): + ''' + This test case covers below test scenarios/tests + scenario-1: Verify the LACP graceful restart functionality with config save and reload. + ''' + if not data.graceful_restart_config: + graceful_restart_prolog() + data.graceful_restart_config = True + [output, exceptions] = exec_all(True, [ExecAllFunc(poll_wait, verify_portchannel_member_status, 7, vars.D1, [data.portchannel_name, data.portchannel_name2], [data.lag_up, data.lag_up], [[vars.D1D2P1, vars.D1D2P2], [vars.D1D2P3, vars.D1D2P4]], [None, None]), ExecAllFunc(poll_wait, verify_portchannel_member_status, 7, vars.D2, [data.portchannel_name, data.portchannel_name2], [data.lag_up, data.lag_up], [[vars.D2D1P1, vars.D2D1P2], [vars.D2D1P3, vars.D2D1P4]], [None, None])]) + ensure_no_exception(exceptions) + slog.clear_logging(vars.D2) + if False in output: + st.report_fail('portchannel_member_state_failed') + [output, exceptions] = exec_all(True, [ExecAllFunc(config_save_reload, vars.D2), ExecAllFunc(poll_wait, verify_portchannel_member_status, 120, vars.D1, [data.portchannel_name, data.portchannel_name2], [data.lag_down, data.lag_down], [None, None], [[vars.D1D2P1, vars.D1D2P2], [vars.D1D2P3, vars.D1D2P4]])]) + ensure_no_exception(exceptions) + if False in output: + st.report_fail('portchannel_member_state_failed') + count_msg1 = slog.get_logging_count(vars.D2, severity="NOTICE", filter_list=["teamd#teammgrd: :- sig_handler: --- Received SIGTERM. Terminating PortChannels gracefully"]) + count_msg2 = slog.get_logging_count(vars.D2, severity="NOTICE", filter_list=["teamd#teammgrd: :- sig_handler: --- PortChannels terminated gracefully"]) + if not (count_msg1 == 1 and count_msg2 == 1): + st.error('SYSLOG message is not observed for graceful restart') + st.report_fail('failed_to_generate_lacp_graceful_restart_log_in_syslog') + [output, exceptions] = exec_all(True, [ExecAllFunc(poll_wait, verify_portchannel_member_status, 7, vars.D1, [data.portchannel_name, data.portchannel_name2], [data.lag_up, data.lag_up], [[vars.D1D2P1, vars.D1D2P2], [vars.D1D2P3, vars.D1D2P4]], [None, None]), ExecAllFunc(poll_wait, verify_portchannel_member_status, 7, vars.D2, [data.portchannel_name, data.portchannel_name2], [data.lag_up, data.lag_up], [[vars.D2D1P1, vars.D2D1P2], [vars.D2D1P3, vars.D2D1P4]], [None, None])]) + ensure_no_exception(exceptions) + if False in output: + st.report_fail('portchannel_member_state_failed') + st.report_pass('verify_lacp_graceful_restart_success', 'with config save reload') + + +def test_ft_lacp_graceful_restart_with_docker_restart(): + ''' + This test case covers below test scenarios/tests + scenario-1: Verify the LACP graceful restart functionality with teamd docker restart. + ''' + if not data.graceful_restart_config: + graceful_restart_prolog() + data.graceful_restart_config = True + [output, exceptions] = exec_all(True, [ExecAllFunc(poll_wait, verify_portchannel_member_status, 7, vars.D1, [data.portchannel_name, data.portchannel_name2], [data.lag_up, data.lag_up], [[vars.D1D2P1, vars.D1D2P2], [vars.D1D2P3, vars.D1D2P4]], [None, None]), ExecAllFunc(poll_wait, verify_portchannel_member_status, 7, vars.D2, [data.portchannel_name, data.portchannel_name2], [data.lag_up, data.lag_up], [[vars.D2D1P1, vars.D2D1P2], [vars.D2D1P3, vars.D2D1P4]], [None, None])]) + ensure_no_exception(exceptions) + if False in output: + st.report_fail('portchannel_member_state_failed') + config_save(vars.D2) + slog.clear_logging(vars.D2) + [output, exceptions] = exec_all(True, [ExecAllFunc(basic_obj.service_operations_by_systemctl, vars.D2, 'teamd', 'restart'), ExecAllFunc(poll_wait, verify_portchannel_member_status, 7, vars.D1, [data.portchannel_name, data.portchannel_name2], [data.lag_down, data.lag_down], [None, None], [[vars.D1D2P1, vars.D1D2P2], [vars.D1D2P3, vars.D1D2P4]])]) + ensure_no_exception(exceptions) + if False in output: + st.report_fail('portchannel_member_state_failed') + if not poll_wait(basic_obj.get_system_status, 180, vars.D2): + st.report_fail('show_system_status_not_ready') + count_msg1 = slog.get_logging_count(vars.D2, severity="NOTICE", filter_list=["teamd#teammgrd: :- sig_handler: --- Received SIGTERM. Terminating PortChannels gracefully"]) + count_msg2 = slog.get_logging_count(vars.D2, severity="NOTICE", filter_list=["teamd#teammgrd: :- sig_handler: --- PortChannels terminated gracefully"]) + if not (count_msg1 == 1 and count_msg2 == 1): + st.error('SYSLOG message is not observed for graceful restart') + st.report_fail('failed_to_generate_lacp_graceful_restart_log_in_syslog') + [output, exceptions] = exec_all(True, [ExecAllFunc(poll_wait, verify_portchannel_member_status, 7, vars.D1, [data.portchannel_name, data.portchannel_name2], [data.lag_up, data.lag_up], [[vars.D1D2P1, vars.D1D2P2], [vars.D1D2P3, vars.D1D2P4]], [None, None]), ExecAllFunc(poll_wait, verify_portchannel_member_status, 7, vars.D2, [data.portchannel_name, data.portchannel_name2], [data.lag_up, data.lag_up], [[vars.D2D1P1, vars.D2D1P2], [vars.D2D1P3, vars.D2D1P4]], [None, None])]) + ensure_no_exception(exceptions) + if False in output: + st.report_fail('portchannel_member_state_failed') + st.report_pass('verify_lacp_graceful_restart_success', 'with teamd docker restart') diff --git a/spytest/tests/switching/test_vlan.py b/spytest/tests/switching/test_vlan.py new file mode 100644 index 00000000000..1265ea4b3f0 --- /dev/null +++ b/spytest/tests/switching/test_vlan.py @@ -0,0 +1,801 @@ +import pytest + +from spytest import st, tgapi, SpyTestDict +from spytest.utils import random_vlan_list + +import apis.switching.vlan as vlan +import apis.system.logging as slog +import apis.switching.mac as mac +import apis.system.storm_control as scapi +import apis.system.interface as ifapi +import apis.switching.portchannel as portchannel +import apis.system.reboot as reboot +import apis.common.wait as waitapi +import apis.system.basic as basic_obj +import apis.switching.mac as mac_obj + +import utilities.utils as utils +from utilities.common import poll_wait +from utilities.parallel import exec_all,ensure_no_exception + +sc_data = SpyTestDict() +tg_info = dict() + +@pytest.fixture(scope="module", autouse=True) +def storm_control_module_hooks(request): + global vars + vars = st.ensure_min_topology("D1D2:2", "D1T1:2", "D2T1:2") + sc_data.version_data = basic_obj.show_version(vars.D1) + vlan_variables() + [out, exceptions] = exec_all(True, [[config_tg_stream], [vlan_module_prolog]], first_on_main=True) + ensure_no_exception(exceptions) + yield + vlan.clear_vlan_configuration(st.get_dut_names(), thread=False, cli_type="click") + + +@pytest.fixture(scope="function", autouse=True) +def storm_control_func_hooks(request): + bum_test_functions = ["test_ft_stormcontrol_verification", "test_ft_stormcontrol_portchannel_intf", + "test_ft_stormcontrol_incremental_bps_max_vlan", + "test_ft_stormcontrol_fast_reboot", "test_ft_stormcontrol_warm_reboot"] + if request.function.func_name in bum_test_functions: + platform_check() + yield + if request.function.func_name == "test_ft_vlan_delete_with_member": + if st.is_community_build(vars.D1): + vlan.create_vlan_and_add_members(sc_data.vlan_data, cli_type=sc_data.cli_type) + + +def platform_check(): + if sc_data.version_data['hwsku'].lower() in hw_constants_DUT['TH3_PLATFORMS']: + st.log("--- Detected BUM UnSupported Platform..") + st.report_unsupported("storm_control_unsupported") + + +def vlan_variables(): + global tg + global tg_handler,hw_constants_DUT + sc_data.cli_type = 'click' + sc_data.vlan_list = random_vlan_list(count=2) + sc_data.non_existent_vlan = str(sc_data.vlan_list[0]) + sc_data.vlan = str(sc_data.vlan_list[1]) + sc_data.kbps = 1000 + sc_data.frame_size = 68 + sc_data.rate_pps =5000 + sc_data.packets = (sc_data.kbps*1024)/(sc_data.frame_size*8) + sc_data.bum_deviation = int(0.10 * sc_data.packets) + sc_data.lower_pkt_count = int(sc_data.packets - sc_data.bum_deviation) + sc_data.higher_pkt_count = int(sc_data.packets + sc_data.bum_deviation) + sc_data.max_vlan = 4093 + sc_data.source_mac = "00:0A:01:00:00:01" + sc_data.source_mac1 = "00:0A:02:00:00:01" + sc_data.line_rate = 100 + sc_data.wait_stream_run = 10 + sc_data.wait_for_stats = 10 + tg_handler = tgapi.get_handles_byname("T1D1P1", "T1D1P2", "T1D2P1", "T1D2P2") + tg = tg_handler["tg"] + tg_info['tg_info'] = tg_handler + tg_info['vlan_id'] = sc_data.vlan + sc_data.vlan_id_start = 1 + sc_data.mac_count = 100 + sc_data.dut_platform = basic_obj.get_hwsku(vars.D1) + sc_data.vlan_data = [{"dut": [vars.D1], "vlan_id": sc_data.vlan, "tagged": [vars.D1T1P1, vars.D1T1P2]}] + hw_constants_DUT = st.get_datastore(vars.D1, "constants") + sc_data.warm_reboot_supported_platforms = hw_constants_DUT['WARM_REBOOT_SUPPORTED_PLATFORMS'] + + +def vlan_module_prolog(): + """ + Module prolog for module configuration + :return: + """ + st.log("Creating vlan in device and adding members ...") + vlan.create_vlan_and_add_members(sc_data.vlan_data, cli_type=sc_data.cli_type) + if not st.is_community_build(vars.D1): + st.banner("Configuring BUM Storm control on interfaces") + interface_list = [vars.D1T1P1, vars.D1T1P2] + storm_control_type = ["broadcast", "unknown-multicast", "unknown-unicast"] + for interface in interface_list: + for stc_type in storm_control_type: + scapi.config(vars.D1, type=stc_type, action="add", interface_name=interface, bits_per_sec=sc_data.kbps, + cli_type="click") + if not scapi.verify_config(vars.D1, interface_name=interface, type=stc_type, rate=sc_data.kbps, + cli_type="click"): + st.report_fail("storm_control_config_verify_failed", stc_type, interface) + + +def config_tg_stream(): + st.log("Traffic Config for verifying BUM storm control feature") + tg.tg_traffic_control(action="reset", port_handle=tg_handler["tg_ph_list"]) + tg_1 = tg.tg_traffic_config(port_handle=tg_handler["tg_ph_1"], mode='create', + transmit_mode='continuous', length_mode='fixed', rate_pps=100, + l2_encap='ethernet_ii', vlan_id=sc_data.vlan, mac_src='00:0a:01:00:00:01', + mac_dst='00:0a:02:00:00:01', high_speed_result_analysis=0, vlan="enable", + track_by='trackingenabled0 vlanVlanId0', vlan_id_tracking=1, + port_handle2=tg_handler["tg_ph_2"],frame_size= sc_data.frame_size) + tg_info['tg1_stream_id'] = tg_1['stream_id'] + + tg_2 = tg.tg_traffic_config(port_handle=tg_handler["tg_ph_2"], mode='create', + transmit_mode='continuous', length_mode='fixed', rate_pps=100, + l2_encap='ethernet_ii', vlan_id=sc_data.vlan, mac_src='00:0a:02:00:00:01', + mac_dst='00:0a:01:00:00:01', high_speed_result_analysis=0, vlan="enable", + track_by='trackingenabled0 vlanVlanId0', vlan_id_tracking=1, + port_handle2=tg_handler["tg_ph_1"],frame_size= sc_data.frame_size) + tg_info['tg2_stream_id'] = tg_2['stream_id'] + return tg_info + + +def vlan_module_epilog(): + if not st.is_community_build(vars.D1): + interface_list = [vars.D1T1P1, vars.D1T1P2] + storm_control_type = ["broadcast", "unknown-multicast", "unknown-unicast"] + for interface in interface_list: + for stc_type in storm_control_type: + scapi.config(vars.D1, type=stc_type, action="del", interface_name=interface, bits_per_sec=sc_data.kbps, + cli_type="click") + vlan.clear_vlan_configuration(st.get_dut_names(), thread=False, cli_type="click") + portchannel.clear_portchannel_configuration(st.get_dut_names(),thread=True) + + +def verify_bum_traffic_mode(mode, tg_stream, skip_traffic_verify=False, duration=10): + """ + :param mode: + :param tg_stream: + :param skip_traffic_verify: + :param duration: + :return: + """ + if mode not in ["unknown-unicast", "unknown-multicast", "broadcast"]: + st.log("Unsupported mode provided") + return False + st.banner("verifying {} traffic ".format(mode)) + st.log("Clearing stats before sending traffic ...") + tg.tg_traffic_control(action="clear_stats", port_handle=tg_handler["tg_ph_list"]) + st.wait(2) + if mode == 'broadcast': + st.log("Enabling {} traffic ".format(mode)) + tg.tg_traffic_config(port_handle=tg_handler["tg_ph_1"], mode='modify',duration=10, stream_id=tg_stream, + mac_src="00:00:00:00:00:01", mac_dst="ff:ff:ff:ff:ff:ff", rate_pps=5000) + elif mode == 'unknown-multicast': + st.log("Enabling {} traffic ".format(mode)) + tg.tg_traffic_config(port_handle=tg_handler["tg_ph_1"], mode='modify', duration=10,stream_id=tg_stream, + mac_src="00:00:00:00:00:01",mac_dst="01:00:5e:01:02:03",rate_pps=5000) + elif mode == 'unknown-unicast': + st.log("Enabling {} traffic ".format(mode)) + tg.tg_traffic_config(port_handle=tg_handler["tg_ph_1"],duration=10, mode='modify', stream_id=tg_stream, + mac_src="00:00:00:00:00:01", mac_dst="00:00:00:00:00:02", + rate_pps=5000) + if not skip_traffic_verify: + ifapi.clear_interface_counters(vars.D1) + ifapi.show_interface_counters_all(vars.D1) + st.log("Starting of traffic from TGen") + tg.tg_traffic_control(action='run', stream_handle=tg_stream, duration=10) + st.wait(sc_data.wait_stream_run) + st.log("Stopping of traffic from TGen to get interface counters") + tg.tg_traffic_control(action='stop', stream_handle=tg_stream) + st.wait(sc_data.wait_for_stats) + ifapi.show_interface_counters_all(vars.D1) + tg_1_stats = tgapi.get_traffic_stats(tg, mode='aggregate', port_handle=tg_handler["tg_ph_1"]) + tg_2_stats = tgapi.get_traffic_stats(tg, mode='aggregate', port_handle=tg_handler["tg_ph_2"]) + counter = tg_2_stats.rx.total_packets + counter2 = tg_1_stats.tx.total_packets + if counter2 == 0: + st.report_fail("storm_control_traffic_verification_failed") + counters_avg = counter / duration + st.log("Average of counters are : {}".format(counters_avg)) + st.log("Higher packet count value is : {}".format(sc_data.higher_pkt_count )) + st.log("Lower packet count value is : {}".format(sc_data.lower_pkt_count)) + if counters_avg > sc_data.higher_pkt_count or counters_avg < sc_data.lower_pkt_count: + st.report_fail("storm_control_traffic_verification_failed") + return True + + +def report_result(status, msg_id): + if status: + st.report_pass(msg_id) + else: + st.report_fail(msg_id) + + +@pytest.mark.vlan_qa_add +@pytest.mark.community +@pytest.mark.community_fail +def test_ft_add_unknownvlan_interface(): + """ + Author: Surendra Kumar Vella (surendrakumar.vella@broadcom.com) + + verify that DUT should not assign unknown pvid + + """ + st.log(" Adding TGen connected interface {} to non-existing vlan {} with untagged mode".format(vars.D1D2P1, + sc_data.non_existent_vlan)) + if vlan.add_vlan_member(vars.D1, sc_data.non_existent_vlan, [vars.D1D2P1], tagging_mode=False, skip_error=True, + cli_type=sc_data.cli_type): + st.report_fail("unknown_vlan_untagged_member_add_fail", vars.D1D2P1, sc_data.non_existent_vlan) + if not vlan.add_vlan_member(vars.D1, sc_data.vlan, [vars.D1D2P1], tagging_mode=True, cli_type=sc_data.cli_type): + st.report_fail("vlan_tagged_member_fail", vars.D1D2P1, sc_data.vlan) + if not vlan.delete_vlan_member(vars.D1, sc_data.vlan, vars.D1D2P1, cli_type=sc_data.cli_type): + st.report_fail("vlan_tagged_member_fail", vars.D1D2P1, sc_data.vlan) + st.report_pass("test_case_passed") + + +@pytest.mark.ft_vlan_delete_with_member +@pytest.mark.community +@pytest.mark.community_fail +def test_ft_vlan_delete_with_member(): + """ + Author: Surendra Kumar Vella (surendrakumar.vella@broadcom.com) + + Verify that user is not able to delete a valn till its members are deleted + + """ + vars = st.ensure_min_topology("D1T1:1") + st.log("Adding TGen connected interfaces to newly created vlan in tagging mode.") + if not vlan.add_vlan_member(vars.D1, sc_data.vlan, [vars.D1D2P1], tagging_mode=True, cli_type=sc_data.cli_type): + st.report_fail("vlan_tagged_member_fail", vars.D1D2P1, sc_data.vlan) + st.log("checking whether vlan with member is deleted or not ") + if not st.is_community_build(vars.D1): + if vlan.delete_vlan(vars.D1, sc_data.vlan, cli_type="click"): + st.report_fail("vlan_deletion_successfull_albiet_having_member", sc_data.vlan) + if not vlan.delete_vlan_member(vars.D1, sc_data.vlan, vars.D1D2P1, cli_type=sc_data.cli_type): + st.report_fail("vlan_tagged_member_fail", vars.D1D2P1, sc_data.vlan) + else: + if not vlan.delete_vlan(vars.D1, sc_data.vlan, cli_type="click"): + st.report_fail("vlan_delete_fail", sc_data.vlan) + st.log("deleting the vlan after its member deletion") + st.report_pass("test_case_passed") + + +@pytest.mark.vlan_trunk_tagged +@pytest.mark.community +@pytest.mark.community_fail +def test_ft_vlan_trunk_tagged(): + """ + Author:Parvez Alam (parvez.alam@broadcom.com) + Verify that over vlan trunk tagged packets received and be sent out with tag or without tag determined + by traffic received from TGen. + """ + # Start L2 traffic on tg1 and apply vlan_id analayzer filter + ifapi.clear_interface_counters(vars.D1) + tg.tg_traffic_control(action="clear_stats", port_handle=tg_handler["tg_ph_list"]) + tg.tg_traffic_control(action='run', stream_handle=[tg_info['tg1_stream_id'], tg_info['tg2_stream_id']]) + st.wait(5) + waitapi.mac_learn() + learned_mac_address = mac.get_mac_all(vars.D1, sc_data.vlan, cli_type=sc_data.cli_type) + if sc_data.source_mac and sc_data.source_mac1 not in learned_mac_address: + tg.tg_traffic_control(action='stop', stream_handle=[tg_info['tg1_stream_id'], tg_info['tg2_stream_id']]) + st.report_fail("mac_failed_to_learn_in_Particular_vlan", sc_data.vlan) + # Stop the traffic and analyzers + tg.tg_traffic_control(action='stop', stream_handle=[tg_info['tg1_stream_id'], tg_info['tg2_stream_id']]) + st.wait(sc_data.wait_stream_run) + st.log("Checking the stats and verifying the traffic flow") + traffic_details = { + '1': { + 'tx_ports': [vars.T1D1P1], + 'tx_obj': [tg], + 'exp_ratio': [1], + 'rx_ports': [vars.T1D1P2], + 'rx_obj': [tg], + }, + '2': { + 'tx_ports': [vars.T1D1P2], + 'tx_obj': [tg], + 'exp_ratio': [1], + 'rx_ports': [vars.T1D1P1], + 'rx_obj': [tg], + } + } + if not tgapi.validate_tgen_traffic(traffic_details=traffic_details, mode='aggregate', comp_type='packet_count'): + st.report_fail("traffic_verification_failed") + # get tx-pkt count for each streams on tg1 + traffic_details = { + '1': { + 'tx_ports': [vars.T1D1P1], + 'tx_obj': [tg], + 'exp_ratio': [1], + 'rx_ports': [vars.T1D1P2], + 'rx_obj': [tg], + 'stream_list': [(tg_info['tg1_stream_id'])], + 'filter_param': [('vlan')], + 'filter_val': [sc_data.vlan], + } + } + + # verify analyzer filter statistics + filter_result = tgapi.validate_tgen_traffic(traffic_details=traffic_details, mode='filter', comp_type='packet_count') + if filter_result: + st.log("ALl packets with created vlan tagged received on TG2") + st.report_pass("test_case_passed") + else: + st.log("Drop seen on TG2 for Packets with created vlan") + st.report_fail("test_case_failed") + + +@pytest.mark.vlan_syslog_verify +@pytest.mark.regression +def test_ft_vlan_syslog_verify(): + """ + Author:Anil Kumar Kacharla + Referrence Topology : Test bed ID:4 D1--Mgmt network + verify VLAN syslog functionality. + """ + vars = st.ensure_min_topology("D1") + sc_data.vlan_test = str(random_vlan_list(1, [int(sc_data.vlan)])[0]) + result = 1 + st.log("checking vlan count before vlan addition or deletion") + count_before_add = slog.get_logging_count(vars.D1, severity="NOTICE", filter_list=["addVlan"]) + count_before_delete = slog.get_logging_count(vars.D1, severity="NOTICE", filter_list=["removeVlan"]) + st.log("vlan count before adding vlan:{}".format(count_before_add)) + st.log("vlan count before deleting vlan:{}".format(count_before_delete)) + vlan.create_vlan(vars.D1, sc_data.vlan_test, cli_type=sc_data.cli_type) + vlan.delete_vlan(vars.D1, sc_data.vlan_test, cli_type=sc_data.cli_type) + st.log("checking vlan count after adding vlan") + count_after_add = slog.get_logging_count(vars.D1, severity="NOTICE", filter_list=["addVlan"]) + st.log("vlan count after adding vlan:{}".format(count_after_add)) + count_after_delete = slog.get_logging_count(vars.D1, severity="NOTICE", filter_list=["removeVlan"]) + st.log("vlan count after deleting vlan:{}".format(count_after_delete)) + if not count_after_add > count_before_add: + st.error("vlan log count increamented after adding vlan:{}".format(count_after_add)) + result = 0 + if not count_after_delete > count_before_delete: + st.error("vlan log count increamented after deleting vlan:{}".format(count_after_delete)) + result = 0 + if not result: + st.report_fail("test_case_fail") + st.log(" vlan count after adding or deleting vlan is incremented") + st.report_pass("test_case_passed") + + +@pytest.mark.stormcontrol +def test_ft_stormcontrol_verification(): + status = 1 + msg_id = "storm_control_traffic_verification_successful" + new_kbps_value = 1010 + st.log("Module config got passed") + st.report_tc_pass('ft_stormcontrol_config_all_same_interface', 'test_case_passed') + if not verify_bum_traffic_mode('broadcast', tg_info['tg1_stream_id'], skip_traffic_verify=False): + st.error("Broadcast traffic verification got failed") + status = 0 + if not verify_bum_traffic_mode('unknown-unicast', tg_info['tg1_stream_id'], skip_traffic_verify=False): + st.error("Unknown-unicast traffic verification got failed") + status = 0 + if not verify_bum_traffic_mode('unknown-multicast', tg_info['tg1_stream_id'], skip_traffic_verify=False): + st.error("Unknown-multicast traffic verification got failed") + status = 0 + if status: + st.report_tc_pass('ft_stormcontrol_BUM_traffic_policer_params', 'test_case_passed') + st.report_tc_pass('ft_stormcontrol_traffic_rate_limited_bpsvalue', 'test_case_passed') + else: + st.report_tc_fail('ft_stormcontrol_BUM_traffic_policer_params', 'test_case_failed') + st.report_tc_fail('ft_stormcontrol_traffic_rate_limited_bpsvalue', 'test_case_failed') + status=1 + st.log("Configuring kbps value on interface to verify kpbs value is independent of interface") + scapi.config(vars.D1, type="broadcast", action="add", interface_name=vars.D1T1P1, + bits_per_sec=new_kbps_value, cli_type="click") + if not scapi.verify_config(vars.D1, interface_name=vars.D1T1P1, type="broadcast", rate=new_kbps_value, + cli_type="click"): + st.error("KBPS value configured on interface is dependent to other interface") + status = 0 + if status: + st.report_tc_pass('ft_stormcontrol_bps_intf_indp', 'test_case_passed') + st.report_tc_pass('ft_stormcontrol_bps_overwrite_new_bps_value', 'test_case_passed') + else: + st.report_tc_fail('ft_stormcontrol_bps_intf_indp', 'test_case_failed') + st.report_tc_fail('ft_stormcontrol_bps_overwrite_new_bps_value', 'test_case_failed') + status = 1 + st.log("configuring back to previous config") + scapi.config(vars.D1, type="broadcast", action="add", interface_name=vars.D1T1P1, + bits_per_sec=sc_data.kbps, cli_type="click") + scapi.verify_config(vars.D1, interface_name=vars.D1T1P1, type="broadcast", rate=sc_data.kbps, + cli_type="click") + if not verify_bum_traffic_mode('broadcast', tg_info['tg1_stream_id'], skip_traffic_verify=False): + st.error("Broadcast traffic verification got failed") + status = 0 + if status: + st.report_tc_pass('ft_stormcontrol_config_clear_noaffect_traffic', 'test_case_passed') + else: + st.report_tc_fail('ft_stormcontrol_config_clear_noaffect_traffic', 'test_case_failed') + status = 1 + st.log("clearing bum traffic type to verify othertraffic does not effect bum storm-control") + scapi.config(vars.D1, type="unknown-unicast", action="del", interface_name=vars.D1T1P1, + bits_per_sec=sc_data.kbps, cli_type="click") + st.log("verifying the other traffic is not get effected.") + if not verify_bum_traffic_mode('unknown-unicast', tg_info['tg1_stream_id'], skip_traffic_verify=True): + st.error("Other_traffic traffic verification got failed") + status = 0 + if status: + st.report_tc_pass('ft_stormcontrol_config_unaffect_other_traffic', 'test_case_passed') + else: + st.report_tc_fail('ft_stormcontrol_config_unaffect_other_traffic', 'test_case_failed') + st.log("configuring back to previous config") + scapi.config(vars.D1, type="unknown-unicast", action="add", interface_name=vars.D1T1P1, bits_per_sec=sc_data.kbps, + cli_type="click") + if not status: + msg_id = "storm_control_traffic_verification_failed" + report_result(status, msg_id) + + +@pytest.mark.stormcontrol +def test_ft_stormcontrol_portchannel_intf(): + status = 1 + msg_id = "storm_control_portchannel_verification_successful" + portchannel_name = 'PortChannel13' + vlan_info = [{"dut": [vars.D2], "vlan_id": sc_data.vlan, "tagged": [vars.D2T1P1, vars.D2T1P2, portchannel_name]}] + portchannel_interfaces_dut1 = [vars.D1D2P1, vars.D1D2P2] + portchannel_interfaces_dut2 = [vars.D2D1P1, vars.D2D1P2] + portchannel.config_portchannel(vars.D1, vars.D2, portchannel_name, portchannel_interfaces_dut1, + portchannel_interfaces_dut2, + config="add", thread=True) + vlan.add_vlan_member(vars.D1, sc_data.vlan, portchannel_name, tagging_mode=True, cli_type=sc_data.cli_type) + vlan.create_vlan_and_add_members(vlan_info, cli_type=sc_data.cli_type) + st.log("Verifying whether stormcontrol config can be applied on portchannel {} interfaces".format(portchannel_name)) + if scapi.config(vars.D1, type="broadcast", action="add", interface_name=portchannel_name, rate=sc_data.kbps, + skip_error_check=True, cli_type="click"): + st.error("storm-control config can be applied on portchannel interface") + status = 0 + else: + st.log("storm-control config cannot be applied on portchannel interface.") + if status: + st.report_tc_pass('ft_stormcontrol_neg_config_vlan_portchannel', 'test_case_passed') + else: + st.report_tc_fail('ft_stormcontrol_neg_config_vlan_portchannel', 'test_case_failed') + status = 1 + st.log("configuring bum stormcontrol on portchannel interfaces") + scapi.config(vars.D1, type="broadcast", action="del", interface_name=vars.D1T1P1, + bits_per_sec=sc_data.kbps, cli_type="click") + scapi.config(vars.D1, type="broadcast", action="del", interface_name=vars.D1T1P2, + bits_per_sec=sc_data.kbps, cli_type="click") + scapi.config(vars.D2, type="broadcast", action="add", interface_name=vars.D2D1P1, + bits_per_sec=sc_data.kbps, cli_type="click") + scapi.config(vars.D2, type="broadcast", action="add", interface_name=vars.D2D1P2, + bits_per_sec=sc_data.kbps, cli_type="click") + verify_bum_traffic_mode('broadcast', tg_info['tg1_stream_id'], skip_traffic_verify=True) + st.log("Clearing interface counters") + ifapi.clear_interface_counters(vars.D2) + tg.tg_traffic_control(action='run',stream_handle=tg_info['tg1_stream_id'], duration=10) + st.wait(sc_data.wait_stream_run) + st.log("Stopping of traffic from TGen to get interface counters") + tg.tg_traffic_control(action='stop', stream_handle=tg_info['tg1_stream_id']) + st.wait(sc_data.wait_for_stats) + tg_1_stats = tgapi.get_traffic_stats(tg, mode='aggregate', port_handle=tg_handler["tg_ph_1"]) + tg_3_stats = tgapi.get_traffic_stats(tg, mode='aggregate', port_handle=tg_handler["tg_ph_3"]) + counter = tg_3_stats.rx.total_packets + counter2 = tg_1_stats.tx.total_packets + try: + time = int(counter2 / sc_data.rate_pps) + counters_avg = counter / time + except: + counters_avg = 0 + st.log("Average of counters are : {}".format(counters_avg)) + st.log("Higher packet count value is : {}".format(sc_data.higher_pkt_count)) + st.log("Lower packet count value is : {}".format(sc_data.lower_pkt_count)) + st.log("value of status is : {}".format(status)) + if counters_avg > sc_data.higher_pkt_count or counters_avg < sc_data.lower_pkt_count: + st.error("storm control traffic verification failed") + status = 0 + if status: + st.report_tc_pass('ft_stormcontrol_portchannel_intf', 'test_case_passed') + else: + st.report_tc_fail('ft_stormcontrol_portchannel_intf', 'test_case_failed') + status = 1 + st.log("Configuring stormcontrol without providing bps value") + if scapi.config(vars.D1, type="broadcast", action="add", interface_name=vars.D1T1P1, skip_error_check=True, + cli_type="click"): + st.error("Storm-control config is accepting not throwing any error") + status = 0 + else: + st.log("Config is not accepted and thrown an error") + if status: + st.report_tc_pass('ft_stormcontrol_neg_config_without_bpsvalue', 'test_case_passed') + else: + st.report_tc_fail('ft_stormcontrol_neg_config_without_bpsvalue', 'test_case_failed') + status = 1 + st.log("unconfiguring of bum stormcontrol type by providing bps value") + if scapi.config(vars.D1, type="broadcast", action="del", interface_name=vars.D1T1P1, rate=sc_data.kbps, + skip_error_check=True, cli_type="click"): + st.error("Storm-control config is removed and not throwing any error") + status = 0 + else: + st.log("Config is not accepted and thrown an error") + if status: + st.report_tc_pass('ft_stormcontrol_neg_unconfig_with_bpsvalue', 'test_case_passed') + else: + st.report_tc_fail('ft_stormcontrol_neg_unconfig_with_bpsvalue', 'test_case_failed') + + st.log("Back to module config") + scapi.config(vars.D2, type="broadcast", action="del", interface_name=vars.D2D1P1, + bits_per_sec=sc_data.kbps, cli_type="click") + scapi.config(vars.D2, type="broadcast", action="del", interface_name=vars.D2D1P2, + bits_per_sec=sc_data.kbps, cli_type="click") + scapi.config(vars.D1, type="broadcast", action="add", interface_name=vars.D1T1P1, + bits_per_sec=sc_data.kbps, cli_type="click") + scapi.config(vars.D1, type="broadcast", action="add", interface_name=vars.D1T1P2, + bits_per_sec=sc_data.kbps, cli_type="click") + st.log("Unconfiguring portchannel config in both devices and only vlan configuration in device2") + vlan.clear_vlan_configuration(vars.D2, cli_type=sc_data.cli_type) + vlan.delete_vlan_member(vars.D1, sc_data.vlan, portchannel_name, cli_type=sc_data.cli_type) + portchannel.clear_portchannel_configuration(st.get_dut_names(), thread=True) + if not status: + msg_id = "storm_control_portchannel_verification_failed" + report_result(status, msg_id) + + +@pytest.mark.stormcontrol +def test_ft_stormcontrol_incremental_bps_max_vlan(): + status = 1 + counters_avg =0 + msg_id = "storm_control_traffic_incremental_bps_max_vlan_successful" + initial_kbps_value = 600 + last_kbps_value = 1000 + interface_list = [vars.D1T1P1, vars.D1T1P2] + for kbps_value in range(initial_kbps_value, last_kbps_value, 200): + for interf in interface_list: + scapi.config(vars.D1, type="broadcast", action="add", interface_name=interf, bits_per_sec=kbps_value) + if not scapi.verify_config(vars.D1, interface_name=interf, type="broadcast", rate=kbps_value): + st.error("incremental kbps is not working") + status = 0 + sc_data.packets = (kbps_value * 1024) / (sc_data.frame_size * 8) + sc_data.bum_deviation1 = int(0.10 * sc_data.packets) + sc_data.lower_pkt_cnt = int(sc_data.packets - sc_data.bum_deviation1) + sc_data.higher_pkt_cnt = int(sc_data.packets + sc_data.bum_deviation1) + for iter in range(1,3,1): + verify_bum_traffic_mode('broadcast', tg_info['tg1_stream_id'], skip_traffic_verify=True) + st.log("Clearing interface counters") + ifapi.clear_interface_counters(vars.D1) + st.log("Starting of traffic from TGen") + tg.tg_traffic_control(action='run',stream_handle=tg_info['tg1_stream_id'], duration=10) + st.wait(sc_data.wait_stream_run) + st.log("Stopping of traffic from TGen to get counters") + tg.tg_traffic_control(action='stop', stream_handle=tg_info['tg1_stream_id']) + st.wait(sc_data.wait_for_stats) + tg_1_stats = tgapi.get_traffic_stats(tg, mode='aggregate', port_handle=tg_handler["tg_ph_1"]) + tg_2_stats = tgapi.get_traffic_stats(tg, mode='aggregate', port_handle=tg_handler["tg_ph_2"]) + counter = tg_2_stats.rx.total_packets + counter2 = tg_1_stats.tx.total_packets + try: + time = int(counter2 / sc_data.rate_pps) + counters_avg = counter / time + except: + counters_avg = 0 + st.log("Average of counters are : {}".format(counters_avg)) + st.log("Higher packet count value is : {}".format(sc_data.higher_pkt_cnt)) + st.log("Lower packet count value is : {}".format(sc_data.lower_pkt_cnt)) + st.log("value of status is : {}".format(status)) + if counters_avg <= sc_data.higher_pkt_cnt and counters_avg >= sc_data.lower_pkt_cnt: + break + if counters_avg > sc_data.higher_pkt_cnt or counters_avg < sc_data.lower_pkt_cnt: + st.error("storm control traffic verification failed") + status = 0 + st.log("Unconfiguring back to previous config") + for interf in interface_list: + scapi.config(vars.D1, type="broadcast", action="add", interface_name=interf, bits_per_sec=sc_data.kbps) + if not status: + msg_id = "storm_control_traffic_incremental_bps_max_vlan_failed" + report_result(status, msg_id) + + +@pytest.mark.stormcontrol +def test_ft_stormcontrol_fast_reboot(): + status = 1 + interface_list = [vars.D1T1P1, vars.D1T1P2] + storm_control_type = ["broadcast", "unknown-multicast", "unknown-unicast"] + msg_id = "storm_control_reboot_successful" + utils.banner_log("Verifying BUM storm control before fast reboot") + if not verify_bum_traffic_mode('broadcast', tg_info['tg1_stream_id'], skip_traffic_verify=False): + st.error("Broadcast traffic verification got failed") + status = 0 + st.log("performing Config save") + reboot.config_save(vars.D1) + ############################################################################################# + utils.banner_log("Performing fast-reboot operation --STARTED") + ############################################################################################# + st.log("performing fast-reboot") + st.reboot(vars.D1, 'fast') + ############################################################################################# + utils.banner_log("Performing fast-reboot operation --COMPLETED") + ############################################################################################# + for interface in interface_list: + for stc_type in storm_control_type: + if not scapi.verify_config(vars.D1, interface_name=interface, type=stc_type, rate=sc_data.kbps, + cli_type="click"): + st.report_fail("storm_control_config_verify_failed", stc_type, interface) + status = 0 + st.log("Traffic Config for verifying BUM storm control feature") + tg.tg_traffic_control(action="reset", port_handle=tg_handler["tg_ph_list"]) + tg_1 = tg.tg_traffic_config(port_handle=tg_handler["tg_ph_1"], mode='create', rate_pps=5000, duration=10, + l2_encap = 'ethernet_ii', vlan_id = sc_data.vlan, mac_src = "00:00:00:00:00:01", + mac_dst = "ff:ff:ff:ff:ff:ff", high_speed_result_analysis = 0, vlan = "enable", + port_handle2 = tg_handler["tg_ph_2"], frame_size = sc_data.frame_size) + tg_info['tg1_stream_id'] = tg_1['stream_id'] + utils.banner_log("Verifying BUM storm control after fast reboot") + if not verify_bum_traffic_mode('broadcast', tg_info['tg1_stream_id'], skip_traffic_verify=False): + st.error("Broadcast traffic verification got failed") + status = 0 + if not status: + msg_id = "storm_control_reboot_failed" + report_result(status, msg_id) + + +@pytest.mark.stormcontrol +def test_ft_stormcontrol_warm_reboot(): + status = 1 + interface_list = [vars.D1T1P1, vars.D1T1P2] + storm_control_type = ["broadcast", "unknown-multicast", "unknown-unicast"] + msg_id = "storm_control_reboot_successful" + utils.banner_log("Verifying BUM storm control before warm reboot") + if not verify_bum_traffic_mode('broadcast', tg_info['tg1_stream_id'], skip_traffic_verify=False): + st.error("Broadcast traffic verification got failed") + status = 0 + st.log("performing Config save") + reboot.config_save(vars.D1) + ############################################################################################# + utils.banner_log("Performing warm-reboot operation --STARTED") + ############################################################################################# + st.log("performing warm-reboot") + st.reboot(vars.D1, 'warm') + ############################################################################################# + utils.banner_log("Performing warm-reboot operation --COMPLETED") + ############################################################################################# + for interface in interface_list: + for stc_type in storm_control_type: + if not scapi.verify_config(vars.D1, interface_name=interface, type=stc_type, rate=sc_data.kbps, + cli_type="click"): + st.report_fail("storm_control_config_verify_failed", stc_type, interface) + status = 0 + st.log("Traffic Config for verifying BUM storm control feature") + tg.tg_traffic_control(action="reset", port_handle=tg_handler["tg_ph_list"]) + tg_1 = tg.tg_traffic_config(port_handle=tg_handler["tg_ph_1"], mode='create', rate_pps=5000, duration=10, + l2_encap = 'ethernet_ii', vlan_id = sc_data.vlan, mac_src = "00:00:00:00:00:01", + mac_dst = "ff:ff:ff:ff:ff:ff", high_speed_result_analysis = 0, vlan = "enable", + port_handle2 = tg_handler["tg_ph_2"], frame_size = sc_data.frame_size) + tg_info['tg1_stream_id'] = tg_1['stream_id'] + utils.banner_log("Verifying BUM storm control after warm reboot") + if not verify_bum_traffic_mode('broadcast', tg_info['tg1_stream_id'], skip_traffic_verify=False): + st.error("Broadcast traffic verification got failed") + status = 0 + if not status: + msg_id = "storm_control_reboot_failed" + if status: + st.report_tc_pass('ft_stormcontrol_config_active_intf_warmboot', 'test_case_passed') + st.report_tc_pass('ft_stormcontrol_rate_limit_across_warmboot', 'test_case_passed') + st.report_tc_pass('ft_stormcontrol_config_restore_warmboot', 'test_case_passed') + else: + st.report_tc_fail('ft_stormcontrol_config_active_intf_warmboot', 'test_case_failed') + st.report_tc_fail('ft_stormcontrol_rate_limit_across_warmboot', 'test_case_failed') + st.report_tc_fail('ft_stormcontrol_config_restore_warmboot', 'test_case_failed') + report_result(status, msg_id) + + +def vlan_module_config(config='yes'): + if config == 'yes': + st.log("Creating max number of vlans") + max_vlan_create() + max_vlan_verify() + add_vlan_members() + else: + vlan.clear_vlan_configuration([vars.D1]) + + +def max_vlan_create(): + vlan.config_vlan_range(vars.D1, "1 {}".format(sc_data.max_vlan), config='add') + + +def max_vlan_verify(): + st.log("verifying whether max vlans are created or not") + if not len(vlan.get_vlan_list(vars.D1)) == sc_data.max_vlan: + st.error("max_vlan_creation_failed") + return False + else: + st.log("creation of max vlans is successful") + return True + + +def add_vlan_members(): + st.log("Participating ixia connected interfaces into max vlans") + vlan.config_vlan_range_members(vars.D1, "1 {}".format(sc_data.max_vlan), vars.D1T1P1, config='add') + vlan.config_vlan_range_members(vars.D1, "1 {}".format(sc_data.max_vlan), vars.D1T1P2, config='add') + +def mac_verify(): + mac_count = mac_obj.get_mac_address_count(vars.D1, vlan=sc_data.vlan, port=vars.D1T1P1, type=None, + mac_search=None) + st.log("Total mac address learnt are : {}".format(mac_count)) + if not int(mac_count) == sc_data.mac_count: + st.error("mac_address_verification_fail") + return False + else: + st.log("Mac address verification got passed") + return True + + +@pytest.mark.vlan_reboot_config_fast_reboot +def test_ft_vlan_save_config_warm_and_fast_reboot(): + ''' + Author: Sai Durga + This script covers the below scenarios + + ft_max_vlan_save_reload Verify the save and reload functionality with max vlan configuration. + ft_max_vlan_fast_reload Verify the max vlan configuration is retained after fast-reboot. + FtOpSoSwVlFn026 Verify that VLAN is present and traffic is not disturbed during and after warm reboot + FtOpSoSysFRFn005 Verify the Fast-Reboot must disrupt control plane not more than 90 seconds (from sonic test suite -configuration tests) + ft_reboot_fdb_fast_reboot Verify that the FDB entry is retained after fast reboot. + + ''' + status = True + msg_id = "max_vlan_config_retain_after_save_fast_warm_reboot" + vlan_module_epilog() + vlan_module_config(config='yes') + st.log("Device name is : {}".format(sc_data.dut_platform)) + if sc_data.dut_platform and sc_data.dut_platform.lower() not in sc_data.warm_reboot_supported_platforms: + st.error("Warm-Reboot is not supported for this platform ({})".format(sc_data.dut_platform)) + st.report_unsupported('test_case_unsupported') + + st.log("Saving the MAX VLAN config on the device") + reboot.config_save(vars.D1) + + st.log("Performing reboot and checking the VLAN configuration") + st.reboot(vars.D1) + st.log("Checking VLAN config after reboot") + max_vlan_verify() + + st.log( + "Sending traffic with 100 MAC,Checking FDB table updated with 100 MAC addresses and performing reboot and checking the VLAN configuration") + tg.tg_traffic_control(action="reset", port_handle=tg_handler["tg_ph_list"]) + tg_1 = tg.tg_traffic_config(port_handle=tg_handler["tg_ph_1"], mode='create', length_mode='fixed', + frame_size=72, + mac_src='00:01:00:00:00:01', mac_src_step='00:00:00:00:00:01', + mac_src_mode='increment', mac_src_count=sc_data.mac_count, + mac_dst='00:02:00:00:00:02', + rate_pps=2000, l2_encap='ethernet_ii_vlan', vlan="enable", + vlan_id=sc_data.vlan, + transmit_mode='continuous') + tg_info['tg1_stream_id'] = tg_1['stream_id'] + tg.tg_traffic_control(action='run', stream_handle=tg_info['tg1_stream_id']) + st.wait(2) + tg.tg_traffic_control(action='stop', stream_handle=tg_info['tg1_stream_id']) + + if not poll_wait(mac_verify, 300): + st.error("mac_address_verification_fail") + + st.log("Performing fast-reboot and checking the VLAN configuration") + st.reboot(vars.D1, 'fast') + st.log("Checking VLAN config after fast-reboot") + max_vlan_verify() + st.log("Sending traffic after fast reboot and checking the FDB table") + tg.tg_traffic_control(action='run', stream_handle=tg_info['tg1_stream_id']) + st.wait(2) + tg.tg_traffic_control(action='stop', stream_handle=tg_info['tg1_stream_id']) + + if not poll_wait(mac_verify, 300): + st.error("mac_address_verification_fail") + + st.log("Performing warm reboot and checking the traffic") + ifapi.clear_interface_counters(vars.D1) + st.wait(2) + ifapi.show_interface_counters_all(vars.D1) + st.wait(2) + tg.tg_traffic_control(action='run', stream_handle=tg_info['tg1_stream_id']) + st.wait(2) + st.reboot(vars.D1, 'warm') + st.log("Checking VLAN config after warm-reboot") + max_vlan_verify() + tg.tg_traffic_control(action='stop', stream_handle=tg_info['tg1_stream_id']) + st.log("Checking traffic is forwarded without any loss after warm-reboot") + st.log("Fetching IXIA statistics") + st.wait(2) + ifapi.show_interface_counters_all(vars.D1) + + stats_tg1 = tgapi.get_traffic_stats(tg, mode="aggregate", port_handle=tg_handler["tg_ph_1"]) + total_tx_tg1 = stats_tg1.tx.total_bytes + + stats_tg2 = tgapi.get_traffic_stats(tg, mode="aggregate", port_handle=tg_handler["tg_ph_2"]) + total_rx_tg2 = stats_tg2.rx.total_bytes + + percentage_95_total_tx_tg1 = (95 * int(total_tx_tg1)) / 100 + st.log("###############") + st.log("Sent bytes: {} and Received bytes : {}".format(percentage_95_total_tx_tg1, total_rx_tg2)) + st.log("##############") + if not int(percentage_95_total_tx_tg1) <= int(total_rx_tg2): + st.report_fail("traffic_transmission_failed", vars.T1D1P1) + + report_result(status, msg_id) + diff --git a/spytest/tests/system/__init__.py b/spytest/tests/system/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/spytest/tests/system/test_box_services.py b/spytest/tests/system/test_box_services.py new file mode 100644 index 00000000000..0a2961ddef4 --- /dev/null +++ b/spytest/tests/system/test_box_services.py @@ -0,0 +1,57 @@ +import pytest +from spytest import st +import apis.system.box_services as boxserv_obj +import apis.system.basic as basic_obj + +pytest.fixture(scope="module", autouse=True) +def box_service_module_hooks(request): + global vars + vars = st.ensure_min_topology("D1") + yield + +@pytest.fixture(scope="function", autouse=True) +def box_service_func_hooks(request): + yield + +@pytest.mark.system_box +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_system_uptime(): + """ + Author: Sreenivasula Reddy V + Validate 'show uptime' command + """ + vars = st.get_testbed_vars() + st.log("About to get system uptime in seconds") + intial_uptime=int(boxserv_obj.get_system_uptime_in_seconds(vars.D1)) + st.log("initial_uptime: {}".format(intial_uptime)) + st.log("About to wait for 1 min") + st.wait(60) + uptime_after_1min=intial_uptime+int(60) + st.log("uptime_after_1min: {}".format(uptime_after_1min)) + st.log("About to check system uptime after 60 sec") + sys_uptime=int(boxserv_obj.get_system_uptime_in_seconds(vars.D1)) + st.log("sys_uptime: {}".format(sys_uptime)) + st.log("About to validate system uptime which should be greater than or equal to system uptime after 1 min") + st.log("uptime_after_1min+60: {}".format(uptime_after_1min+60)) + st.log("Verifying {}<={}<={}".format(uptime_after_1min, sys_uptime, uptime_after_1min + 60)) + if uptime_after_1min<=sys_uptime<=uptime_after_1min+60: + st.log("System Uptime is getting updated with correct value") + else: + st.report_fail("sytem_uptime_fail",vars.D1) + st.report_pass("test_case_passed") + +@pytest.mark.dhcp_relay_docker +def test_ft_dhcp_relay_docker_image(): + """" + Author: Ramakrishnudu.Mangala + Verify DHCP Relay docker file is there in docker image. + Test Bed :TB_16 + """ + vars = st.get_testbed_vars() + st.log("verifying the docker images on DUT") + if not basic_obj.verify_docker_ps(vars.D1, "docker-dhcp-relay:latest") : + if not basic_obj.verify_docker_ps(vars.D1, "docker-dhcp-relay-dbg:latest"): + st.report_fail("operation_failed") + st.log("Verify DHCP Relay docker file is there in docker image") + st.report_pass("test_case_passed") diff --git a/spytest/tests/system/test_device_mgmt.py b/spytest/tests/system/test_device_mgmt.py new file mode 100644 index 00000000000..336f4f2225c --- /dev/null +++ b/spytest/tests/system/test_device_mgmt.py @@ -0,0 +1,64 @@ +import pytest +import apis.routing.ip as ping_obj +import apis.system.interface as intf_obj +import apis.system.basic as basic_obj +from spytest import st +from spytest.dicts import SpyTestDict + +data = SpyTestDict() + +@pytest.fixture(scope="module", autouse=True) +def mgmt_module_hooks(request): + # add things at the start of this module + + yield + # add things at the end of this module" + + +@pytest.fixture(scope="function", autouse=True) +def mgmt_func_hooks(request): + # add things at the start every test case + # use 'request.node.name' to compare + # if any thing specific a particular test case + yield + + # add things at the end every test case + # use 'request.node.name' to compare + # if any thing specific a particular test case + +@pytest.mark.static_ip_on_mgmt_intrf +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_ip_static_ip_on_mgmt_intrf(): + """ + Author:Karthik Kumar Goud Battula(karthikkumargoud.battula@broadcom.com) + Scenario: Verify the configuration of static ip address on eth0 interface and check the reachability + """ + result = True + vars = st.ensure_min_topology("D1", "CONSOLE_ONLY") + data.interface = 'eth0' + intf_obj.enable_dhcp_on_interface(vars.D1, data.interface) + st.wait(5) + ip_address_list = basic_obj.get_ifconfig_inet(vars.D1, data.interface) + if not ip_address_list: + st.report_fail("DUT_does_not_have_IP_address".format(vars.D1, data.interface)) + data.ip_address = ip_address_list[0] + data.netmask = basic_obj.get_ifconfig(vars.D1, data.interface)[0]['netmask'][0] + data.gateway = basic_obj.get_ifconfig_gateway(vars.D1, data.interface) + intf_obj.config_static_ip_to_interface(vars.D1, data.interface, data.ip_address, data.netmask, data.gateway) + st.log("Verify connectivity from DUT") + if not ping_obj.ping(vars.D1, data.gateway): + st.error("Ping is not successful for address {}".format(data.gateway)) + result= False + intf_obj.delete_ip_on_interface_linux(vars.D1, data.interface, "{}/{}".format(data.ip_address, data.netmask)) + ip_address_list = basic_obj.get_ifconfig_inet(vars.D1, data.interface) + if ip_address_list: + st.error("DUT_have_IP_address".format(vars.D1, data.interface)) + result= False + intf_obj.enable_dhcp_on_interface(vars.D1, data.interface) + if not ping_obj.ping(vars.D1, data.gateway): + st.error("Ping is not successful for address".format(data.gateway)) + result= False + if not result: + st.report_fail("test_case_failed") + st.report_pass("test_case_passed") diff --git a/spytest/tests/system/test_lldp.py b/spytest/tests/system/test_lldp.py new file mode 100644 index 00000000000..20674dc66c5 --- /dev/null +++ b/spytest/tests/system/test_lldp.py @@ -0,0 +1,415 @@ +import pytest +from spytest import st +import apis.system.lldp as lldp_obj +import apis.system.snmp as snmp_obj +import apis.system.basic as basic_obj +import apis.system.interface as intf_obj +from spytest.dicts import SpyTestDict + +@pytest.fixture(scope="module", autouse=True) +def lldp_snmp_module_hooks(request): + global vars + vars = st.ensure_min_topology("D1D2:1") + global_vars() + lldp_snmp_pre_config() + yield + lldp_snmp_post_config() + + +@pytest.fixture(scope="function", autouse=True) +def lldp_snmp_func_hooks(request): + global_vars() + yield + + +def global_vars(): + global data + data = SpyTestDict() + data.ro_community = 'test_community' + data.mgmt_int = 'eth0' + data.wait_time = 30 + data.location = 'hyderabad' + data.oid_sysName = '1.3.6.1.2.1.1.5.0' + data.oid_lldplocportid = '1.0.8802.1.1.2.1.3.7.1.3' + data.oid_lldplocsysname = '1.0.8802.1.1.2.1.3.3' + data.oid_lldplocsysdesc = '1.0.8802.1.1.2.1.3.4' + data.oid_lldplocportdesc = '1.0.8802.1.1.2.1.4.1' + data.oid_locmanaddrtable = '1.0.8802.1.1.2.1.1.7' + data.oid_locmanaddrsubtype = '1.0.8802.1.1.2.1.3.8.1.1' + data.oid_locmanaddroid = '1.0.8802.1.1.2.1.3.8.1.6' + data.oid_locmanaddrlen = '1.0.8802.1.1.2.1.3.8.1.3' + data.oid_locmanaddrlfld = '1.0.8802.1.1.2.1.3.8.1.5' + data.oid_locmanaddrentry = '1.0.8802.1.1.2.1.3.8.1' + data.oid_configmanaddrtable = '1.0.8802.1.1.2.1.1.7' + data.oid_configmanaddrentry = '1.0.8802.1.1.2.1.1.7.1' + data.filter = '-Oqv' + +def lldp_snmp_pre_config(): + """ + LLDP Pre Config + """ + global lldp_value + global ipaddress + global lldp_value_remote, lldp_value_gran + global lldp_total_value + data.ipaddress_d1 = basic_obj.get_ifconfig_inet(vars.D1, data.mgmt_int) + data.ipaddress_d2 = basic_obj.get_ifconfig_inet(vars.D2, data.mgmt_int) + if not data.ipaddress_d1: + st.error(" Ip address is not a valid one or the ip is not presented on the device") + st.report_fail("operation_failed") + ipaddress = data.ipaddress_d1[0] + if not intf_obj.poll_for_interfaces(vars.D1,iteration_count=60,delay=1): + st.report_fail("interfaces_not_up_after_poll") + if not intf_obj.poll_for_interfaces(vars.D2,iteration_count=60,delay=1): + st.report_fail("interfaces_not_up_after_poll") + if not lldp_obj.poll_lldp_neighbors(vars.D1, iteration_count=30, delay=1, interface=vars.D1D2P1): + st.report_fail("lldp_neighbors_info_not_found_after_poll") + if not lldp_obj.poll_lldp_neighbors(vars.D2, iteration_count=30, delay=1, interface=vars.D2D1P1): + st.report_fail("lldp_neighbors_info_not_found_after_poll") + st.log(" Getting Ip address of the Device") + lldp_value = lldp_obj.get_lldp_neighbors(vars.D1, interface=vars.D1D2P1) + lldp_value_remote = lldp_obj.get_lldp_neighbors(vars.D2, interface=vars.D2D1P1) + st.log(" LLDP Neighbors value is: {} ".format(lldp_value)) + st.log(" Remote LLDP Neighbors value is: {} ".format(lldp_value_remote)) + if not lldp_value: + st.error("No lldp entries are available") + st.report_fail("operation_failed") + if not lldp_value_remote: + st.error(" No lldp entries are available in Remote") + st.report_fail("operation_failed") + lldp_value = lldp_value[0] + lldp_total_value = lldp_value_remote + lldp_value_remote = lldp_value_remote[0] + lldp_value_gran = lldp_value['chassis_mgmt_ip'] + if not data.ipaddress_d2[0] == lldp_value_gran: + st.error("LLDP info IP and device IP are not matching") + st.report_fail("operation_failed") + # TODO : Need to check the below once the infra defect SONIC-5374 is Fixed + ''' + mac_output = basic_obj.get_platform_syseeprom(vars.D1, 'Serial Number', 'Value') + lldp_value_mac = lldp_value['chassis_id_value'] + st.log("lldp_value_gran is :{}".format(lldp_value_gran)) + if not mac_output == lldp_value_mac: + st.report_fail(" MAC Addresses are not matching ") + ''' + snmp_obj.set_snmp_config(vars.D1, snmp_rocommunity=data.ro_community, snmp_location=data.location) + if not snmp_obj.poll_for_snmp(vars.D1, 30 , 1 , ipaddress= data.ipaddress_d1[0], + oid=data.oid_sysName, community_name=data.ro_community): + st.log("Post SNMP config , snmp is not working") + st.report_fail("operation_failed") + + +def lldp_snmp_post_config(): + """ + LLDP Post Config + """ + snmp_obj.restore_snmp_config(vars.D1) + + +@pytest.mark.lldp_LocManAddrTable +@pytest.mark.regression +def test_ft_lldp_LocManAddrTable(): + """ + Author : Karthikeya Kumar CH + Verify that the LocManAddrTable MIB object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + #### ToDo the below steps for validation as we have a open defect for testing this TC(SONIC-5258) + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_locmanaddrtable, + community_name=data.ro_community,filter=data.filter) + st.log(" Getting LLDP port description:{} from the snmp output ".format(get_snmp_output)) + st.report_pass("test_case_passed") + + +@pytest.mark.lldp_LocManAddrSubType +@pytest.mark.regression +def test_ft_lldp_LocManAddrSubType(): + """ + Author : Karthikeya Kumar CH + Verify that the LocManAddrSubType MIB object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + #### ToDo the below steps for validation as we have a open defect for testing this TC(SONIC-5258) + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_locmanaddrsubtype, + community_name=data.ro_community,filter=data.filter) + st.log(" Getting LLDP port description:{} from the snmp output ".format(get_snmp_output)) + st.report_pass("test_case_passed") + + +@pytest.mark.lldp_LocManAddrOID +@pytest.mark.regression +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_lldp_LocManAddrOID(): + """ + Author : Karthikeya Kumar CH + Verify that the LocManAddrOID MIB object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + #### ToDo the below steps for validation as we have a open defect for testing this TC(SONIC-5258) + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_locmanaddroid, + community_name=data.ro_community,filter=data.filter) + st.log(" Getting LLDP port description:{} from the snmp output ".format(get_snmp_output)) + st.report_pass("test_case_passed") + + +@pytest.mark.lldp_LocManAddrLen +@pytest.mark.regression +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_lldp_LocManAddrLen(): + """ + Author : Karthikeya Kumar CH + Verify that the LocManAddrLen MIB object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + #### ToDo the below steps for validation as we have a open defect for testing this TC(SONIC-5258) + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_locmanaddrlen, + community_name=data.ro_community,filter=data.filter) + st.log(" Getting LLDP port description:{} from the snmp output ".format(get_snmp_output)) + st.report_pass("test_case_passed") + + +@pytest.mark.lldp_LocManAddrlfld +@pytest.mark.regression +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_lldp_LocManAddrlfld(): + """ + Author : Karthikeya Kumar CH + Verify that the LocManAddrlfld MIB object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + #### ToDo the below steps for validation as we have a open defect for testing this TC(SONIC-5258) + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_locmanaddrlfld, + community_name=data.ro_community,filter=data.filter) + st.log(" Getting LLDP port description:{} from the snmp output ".format(get_snmp_output)) + st.report_pass("test_case_passed") + + +@pytest.mark.lldp_LocManAddrEntry +@pytest.mark.regression +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_lldp_LocManAddrEntry(): + """ + Author : Karthikeya Kumar CH + Verify that the LocManAddrEntry MIB object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + #### ToDo the below steps for validation as we have a open defect for testing this TC(SONIC-5258) + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_locmanaddrentry, + community_name=data.ro_community,filter=data.filter) + st.log(" Getting LLDP port description:{} from the snmp output ".format(get_snmp_output)) + st.report_pass("test_case_passed") + + +@pytest.mark.lldp_ConfigManAddrTable +@pytest.mark.regression +def test_ft_lldp_ConfigManAddrTable(): + """ + Author : Karthikeya Kumar CH + Verify that the ConfigManAddrTable MIB object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + #### ToDo the below steps for validation as we have a open defect for testing this TC(SONIC-5258) + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_configmanaddrtable, + community_name=data.ro_community,filter=data.filter) + st.log(" Getting LLDP port description:{} from the snmp output ".format(get_snmp_output)) + st.report_pass("test_case_passed") + + +@pytest.mark.lldp_ConfigManAddrEntry +@pytest.mark.regression +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_lldp_ConfigManAddrEntry(): + """ + Author : Karthikeya Kumar CH + Verify that the ConfigManAddrEntry MIB object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + #### ToDo the below steps for validation as we have a open defect for testing this TC(SONIC-5258) + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_locmanaddrentry, + community_name=data.ro_community,filter=data.filter) + st.log(" Getting LLDP port description:{} from the snmp output ".format(get_snmp_output)) + st.report_pass("test_case_passed") + + +@pytest.mark.lldp_lldplocportid +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_lldp_lldplocportid(): + """ + Author : Karthikeya Kumar CH + Verify the syntax check of the object lldplocportid. + Reference Test Bed : D1 <---> D2 + """ + lldp_value_remote_val = lldp_obj.get_lldp_neighbors(vars.D2, interface=vars.D2D1P1) + snmp_output = snmp_obj.walk_snmp_operation(ipaddress= ipaddress, oid= data.oid_lldplocportid, + community_name= data.ro_community,filter=data.filter) + if not snmp_output: + st.report_fail(" No SNMP Entries are available") + st.log(" Getting LLDP port description:{} from the snmp output ".format(snmp_output)) + output = lldp_value_remote_val[-1] + for port_id in output: + if port_id == "portid_type": + if output[port_id].lower() == 'local': + cli_output = output['portid_value'] + break + cli_output = '"{}"'.format(cli_output) + st.log(" lldp value port is : {} ".format(cli_output)) + if not cli_output in snmp_output: + st.report_fail("lldp_snmp_not_matching") + st.log(" LLDP value is passed ") + st.report_pass("test_case_passed") + + +@pytest.mark.lldp_lldplocsysname +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_lldp_lldplocsysname(): + """ + Author : Karthikeya Kumar CH + Verify the syntax check of the object lldplocsysname. + Reference Test Bed : D1 <---> D2 + """ + snmp_output = snmp_obj.get_snmp_operation(ipaddress= ipaddress, oid= data.oid_lldplocsysname, + community_name=data.ro_community) + if not snmp_output: + st.report_fail(" No SNMP Entries are available") + snmp_output = snmp_output[0] + st.log(" Getting LLDP port description:{} from the snmp output ".format(snmp_output)) + cli_output = lldp_value_remote['chassis_name'] + st.log(" lldp value port is : {} ".format(cli_output)) + if not cli_output in snmp_output: + st.report_fail("lldp_snmp_not_matching") + st.log(" LLDP value is passed ") + st.report_pass("test_case_passed") + + +@pytest.mark.lldp_lldplocsysdesc +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_lldp_lldplocsysdesc(): + """ + Author : Karthikeya Kumar CH + Verify the syntax check of the object lldplocsysdesc. + Reference Test Bed : D1 <---> D2 + """ + snmp_output = snmp_obj.get_snmp_operation(ipaddress=ipaddress, oid= data.oid_lldplocsysdesc, + community_name=data.ro_community) + if not snmp_output: + st.report_fail(" No SNMP Entries are available") + snmp_output = snmp_output[0] + st.log(" Getting LLDP port description:{} from the snmp output ".format(snmp_output)) + cli_output = lldp_value_remote['chassis_descr'] + st.log(" lldp value port is : {} ".format(cli_output)) + if not cli_output in snmp_output: + st.report_fail("lldp_snmp_not_matching") + st.log(" LLDP value is passed ") + st.report_pass("test_case_passed") + +@pytest.mark.lldp_lldplocportdesc +def test_ft_lldp_lldplocportdesc(): + """ + Author : Karthikeya Kumar CH + Verify the syntax check of the object lldplocsysdesc. + Reference Test Bed : D1 <---> D2 + """ + snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid= data.oid_lldplocportdesc, + community_name=data.ro_community,filter=data.filter) + if not snmp_output: + st.report_fail(" No SNMP Entries are available") + st.log(" Getting LLDP port description:{} from the snmp output ".format(snmp_output)) + cli_output = lldp_value['portdescr'] + st.log(" lldp value port is : {} ".format(cli_output)) + if not cli_output in str(snmp_output): + st.report_fail("lldp_snmp_not_matching") + st.log(" LLDP value is passed ") + st.report_pass("test_case_passed") + + +@pytest.mark.lldp_non_default_config +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_lldp_non_default_config(): + """ + Author : Prasad Darnasi + Verify non default LLDP neighbor config. + Reference Test Bed : D1 <--2--> D2 + """ + tc_fall = 0 + + lldp_obj.lldp_config(vars.D2, txinterval= 2) + lldp_obj.lldp_config(vars.D2, txhold = 2) + lldp_obj.lldp_config(vars.D2, capability= 'management-addresses-advertisements' , config= 'no') + lldp_obj.lldp_config(vars.D2, capability= 'capabilities-advertisements', config='no') + lldp_obj.lldp_config(vars.D2, interface = vars.D2D1P2, status = 'disabled') + lldp_obj.lldp_config(vars.D2, hostname = 'SonicTest') + + st.log("Waiting for the lldp update timer to expire") + st.wait(4) + lldp_value = lldp_obj.get_lldp_neighbors(vars.D1, interface=vars.D1D2P1) + lldp_value_1 = lldp_obj.get_lldp_neighbors(vars.D1, interface=vars.D1D2P2) + + lldp_value_gran_new = lldp_value[0]['chassis_mgmt_ip'] + lldp_value_capability_new = lldp_value[0]['chassis_capability_router'] + lldp_value_chassis_name_new = lldp_value[0]['chassis_name'] + + if lldp_value_gran_new is lldp_value_gran: + tc_fall = 1 + st.log('Failed: LLDP neighbor management is seen even though disabled ') + if lldp_value_capability_new != '': + tc_fall = 1 + st.log('Failed: LLDP neighbor capabilities are present even though disabled') + if lldp_value_chassis_name_new != 'SonicTest': + tc_fall = 1 + st.log('Failed: LLDP neighbor system name is not changed to non default ') + if len(lldp_value_1) != 0 : + tc_fall = 1 + st.log('Failed: LLDP neighbor interface is still seen even though LLDP disabled on that ') + + st.log("Unconfig section") + lldp_obj.lldp_config(vars.D2, capability='management-addresses-advertisements', config='yes') + lldp_obj.lldp_config(vars.D2, capability='capabilities-advertisements', config='yes') + lldp_obj.lldp_config(vars.D2, interface=vars.D2D1P2, status='rx-and-tx') + lldp_obj.lldp_config(vars.D2, hostname='sonic') + lldp_obj.lldp_config(vars.D2, txinterval=30) + lldp_obj.lldp_config(vars.D2, txhold=6) + if tc_fall: + st.report_fail('LLDP_non_default_config_is_failed') + st.log("LLDP neighbor values are advertised as configured ") + st.report_pass("test_case_passed") + + +@pytest.mark.lldp_docker_restart +def test_ft_lldp_docker_restart(): + """ + Author : Karthikeya Kumar CH + Verify the LLDP functionality after docker restart. + Reference Test Bed : D1 <--2--> D2 + """ + st.log("Checking the LLDP functionality with docker restart") + basic_obj.service_operations_by_systemctl(vars.D1, 'lldp', 'stop') + basic_obj.service_operations_by_systemctl(vars.D1,'lldp','restart') + if not basic_obj.poll_for_system_status(vars.D1,'lldp',30,1): + st.report_fail("service_not_running".format('lldp')) + if not basic_obj.verify_service_status(vars.D1, 'lldp'): + st.report_fail("lldp_service_not_up") + if not intf_obj.poll_for_interfaces(vars.D1,iteration_count=30,delay=1): + st.report_fail("interfaces_not_up_after_poll") + if not lldp_obj.poll_lldp_neighbors(vars.D1, iteration_count=30, delay=1, interface=vars.D1D2P1): + st.report_fail("lldp_neighbors_info_not_found_after_poll") + lldp_info = lldp_obj.get_lldp_neighbors(vars.D1, interface=vars.D1D2P1) + if not lldp_info: + st.error("No lldp entries are available") + st.report_fail("operation_failed") + lldp_value_dut1 = lldp_info[0] + lldp_output_dut1 = lldp_value_dut1['chassis_name'] + hostname_cli_output = basic_obj.get_hostname(vars.D2) + if lldp_output_dut1 != hostname_cli_output: + st.report_fail("lldp_cli_not_matching") + st.log("LLDP and CLI output values are : LLDP:{} , CLI:{} ".format(lldp_output_dut1,hostname_cli_output)) + st.report_pass("test_case_passed") + diff --git a/spytest/tests/system/test_ntp.py b/spytest/tests/system/test_ntp.py new file mode 100644 index 00000000000..e7af9b1735f --- /dev/null +++ b/spytest/tests/system/test_ntp.py @@ -0,0 +1,126 @@ +import pytest +import time +from spytest import st +from spytest.dicts import SpyTestDict +import utilities.utils as common_obj +import apis.system.reboot as reboot_obj +import apis.system.ntp as ntp_obj +import apis.system.logging as syslog_obj +import apis.system.basic as basic_obj +import utilities.utils as utils_obj +import apis.routing.ip as ping_obj + +@pytest.fixture(scope="module", autouse=True) +def ntp_module_hooks(request): + global vars + vars = dict() + vars = st.get_testbed_vars() + global_vars() + yield +@pytest.fixture(scope="function", autouse=True) +def ntp_func_hooks(request): + global_vars() + yield +def global_vars(): + global data + data = SpyTestDict() + data.servers = utils_obj.ensure_service_params(vars.D1, "ntp", "host") + data.verify_no_server = 'None' + data.ntp_service = 'ntp' + + +def config_ntp_server_on_config_db_file(dut, iplist): + """ + Author: Anil Kumar Kacharla + """ + st.log("Configuring NTP servers in Config_db file") + ntp_obj.add_ntp_servers(dut, iplist=iplist) + data.time_date = time.strftime('%a %B %d %H:%M:%S %Z %Y') + ntp_obj.config_date(vars.D1, data.time_date) + reboot_obj.config_save(vars.D1) + st.log("verifying ntp service status") + if ntp_obj.verify_ntp_service_status(vars.D1, 'active (running)'): + st.log("ntpd is running") + else: + st.log("ntpd is exited and restarting ntp service") + basic_obj.service_operations(vars.D1, data.ntp_service, action="restart") + if not ntp_obj.verify_ntp_server_details(dut,iplist,remote=iplist): + st.log("ip not matching") + st.report_fail("operation_failed") + if not ntp_obj.verify_ntp_service_status(dut, 'active (running)', iteration=65, delay=2): + st.log("ntp is exited") + st.report_fail("operation_failed") + st.log("Verify that NTP server connectivity from DUT") + result = 0 + for server_ip in data.servers: + if not ping_obj.ping(vars.D1, server_ip): + st.log("ping to ntp server is not successfull:{}".format(server_ip)) + result += 1 + if len(data.servers) == result: + st.report_fail("None_of_the_configured_ntp_server_reachable") + if not ntp_obj.verify_ntp_status(vars.D1, iteration=65, delay=2, server=data.servers): + st.log("ntp syncronization failed") + st.report_fail("operation_failed") + + +@pytest.mark.ntp_disable_enable_message_log +@pytest.mark.regression +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_ntp_disable_enable_with_message_log(): + """ + Author: Anil Kumar Kacharla + Referrence Topology : Test bed ID:4 D1--Mgmt network + Verify that Ntp synchronization is successful after doing NTP server on and off and the message log display the correct time based upon the system up time. + """ + st.log("Ensuring minimum topology") + vars = st.ensure_min_topology("D1") + data.string_generate = 'Iam Testing NTP' + data.lines = 1 + data.time_date = time.strftime('%a %B %d %H:%M:%S %Z %Y') + ntp_obj.config_date(vars.D1, data.time_date) + st.log("checking time in message log without ntp ") + log_message_1=syslog_obj.show_logging(vars.D1, severity=None, filter_list=[], lines=data.lines) + if not log_message_1: + st.log("log message_1 not created") + st.report_fail("operation_failed") + clock= common_obj.log_parser(log_message_1[0]) + config_ntp_server_on_config_db_file(vars.D1, data.servers) + st.log("Generating log messages") + syslog_obj.clear_logging(vars.D1) + syslog_obj.write_logging(vars.D1, data.string_generate) + log_message = syslog_obj.show_logging(vars.D1, severity=None, filter_list=[data.string_generate]) + if not log_message: + st.log("log message not created") + st.report_fail("operation_failed") + st.log("printing system clock") + ntp_obj.show_clock(vars.D1) + out = common_obj.log_parser(log_message[0]) + if not (clock[0]['month'] == out[0]['month'] and clock[0]['hours'] == out[0]['hours'] and + clock[0]['date'] == out[0]['date'] and clock[0]['minutes'] <= out[0]['minutes'] or clock[0]['seconds'] >= out[0]['seconds']): + st.log("time not updated") + st.report_fail("operation_failed") + st.log("message log displaying correct timed based on system up time") + st.log("disabling ntp") + basic_obj.service_operations(vars.D1, data.ntp_service, action="stop") + if not ntp_obj.verify_ntp_service_status(vars.D1, 'inactive (dead)'): + st.log("ntp disabled failed") + st.report_fail("operation_failed") + st.log("Enabling NTP") + basic_obj.service_operations(vars.D1, data.ntp_service, action="restart") + if not ntp_obj.verify_ntp_service_status(vars.D1, 'active (running)', iteration=65, delay=2): + st.log("ntp is exited after enable and disable ntp") + st.report_fail("operation_failed") + if not ntp_obj.verify_ntp_status(vars.D1, iteration=65, delay=2, server=data.servers): + st.log("ntp syncronization failed after enable and disable ntp") + st.report_fail("operation_failed") + st.report_pass("test_case_passed") + + +@pytest.mark.ntpdef +def test_ntp_exists_config(): + if ntp_obj.ensure_ntp_config(vars.D1): + st.report_pass("test_case_passed") + else: + st.report_fail("test_case_failed") + diff --git a/spytest/tests/system/test_reboot.py b/spytest/tests/system/test_reboot.py new file mode 100644 index 00000000000..62adae237a6 --- /dev/null +++ b/spytest/tests/system/test_reboot.py @@ -0,0 +1,99 @@ +import pytest + +from spytest import st, SpyTestDict + +import apis.switching.vlan as vlan_obj +import apis.system.logging as log +import apis.system.basic as basic_obj +import apis.system.interface as intf_obj +import apis.system.reboot as reboot_obj +import apis.system.logging as slog_obj + +@pytest.fixture(scope="module", autouse=True) +def reboot_module_hooks(request): + global vars + global data + data = SpyTestDict() + data.iter_count = 3 + data.idle_sleep = 300 + data.max_vlan = 4093 + vars = st.get_testbed_vars() + vars = st.ensure_min_topology("D1") + vlan_obj.config_vlan_range(vars.D1, "1 {}".format(data.max_vlan), config='add') + yield + vlan_obj.clear_vlan_configuration(st.get_dut_names(), thread=False) + + +@pytest.fixture(scope="function", autouse=True) +def reboot_func_hooks(request,reboot_module_hooks): + yield + + +@pytest.mark.hard_reboot_mul_iter +@pytest.mark.community +@pytest.mark.community_fail +def test_ft_sys_hard_reboot_multiple_iter(): + """ + Author : Sreenivasula Reddy + """ + vars = st.ensure_min_topology("D1") + st.log("Hard rebooting device for multiple iterations") + for each_iter in range(1, data.iter_count + 1): + st.log("Hard Reboot iteration number {}".format(each_iter)) + st.log("About to power off power to switch") + st.do_rps(vars.D1, "Off") + st.log("About to Power ON switch") + st.do_rps(vars.D1, "On") + intf_obj.poll_for_interfaces(vars.D1, iteration_count=180, delay=1) + st.log("After hard reboot about to check 'show reboot-cause' reason") + if not reboot_obj.get_reboot_cause(vars.D1): + st.report_fail("verify_hard_reboot_show_reboot_cause_fail", vars.D1) + intf_obj.poll_for_interfaces(vars.D1, iteration_count=180, delay=1) + if not slog_obj.get_logging_count(vars.D1): + st.report_fail("logs_are_not_getting_generated_after_reboot") + st.report_pass('test_case_passed') + + +@pytest.mark.reboot_mul_iter +def test_ft_sys_soft_reboot_multiple_iter(): + ''' + Author : Sreenivasula Reddy + ''' + vars = st.ensure_min_topology("D1") + st.log("Performing save and soft-reboot") + reboot_obj.config_save(vars.D1) + st.log("Soft rebooting device for multiple iterations") + for each_iter in range(1, data.iter_count +1): + st.log("Reload iteration number {}".format(each_iter)) + st.log("About to reload the switch") + st.reboot(vars.D1,"fast") + intf_obj.poll_for_interfaces(vars.D1, iteration_count=180, delay=1) + st.log("After reload about to check 'show platform summary'") + if not basic_obj.get_hwsku(vars.D1): + st.report_fail("After_soft_reboot_DUT_access_fail", data.iter_count) + st.log("performing clearconfig operation") + reboot_obj.config_reload(vars.D1) + st.report_pass('test_case_passed') + + +@pytest.mark.bootup_logging_debug +def test_ft_sytem_bootup_logging_debug(): + """ + Author : Praveen Kumar Kota + Testbed : D1 --- Mgmt Network + Verify that no unwanted logs are shown on DUT console with logging level "Debug". + """ + vars = st.ensure_min_topology("D1") + st.log("clearing the logging buffer") + if not log.clear_logging(vars.D1): + st.report_fail("failed_to_clear_logs", vars.D1) + st.log("enabling logging level as debug") + if not log.set_logging_severity(vars.D1, severity="Debug"): + st.report_fail("logging_severity_level_change_failed", vars.D1) + st.log("waiting the DUT to idle for sometime") + st.wait(data.idle_sleep) + log.set_logging_severity(vars.D1, severity="INFO") + if not log.check_unwanted_logs_in_logging(vars.D1, user_filter=[]): + st.report_fail("logs_are_getting_generated_after_reboot", vars.D1) + st.report_pass("test_case_passed") + diff --git a/spytest/tests/system/test_snmp.py b/spytest/tests/system/test_snmp.py new file mode 100644 index 00000000000..55f5f93b575 --- /dev/null +++ b/spytest/tests/system/test_snmp.py @@ -0,0 +1,1156 @@ +import pytest +import re + +from spytest import st, tgapi, SpyTestDict +from spytest.utils import random_vlan_list + +import apis.system.snmp as snmp_obj +import apis.system.basic as basic_obj +import apis.system.box_services as box_obj +import utilities.utils as util_obj +import apis.switching.vlan as vlan_obj +import apis.switching.mac as mac_obj +import apis.routing.ip as ipfeature +import apis.system.interface as intf_obj +from apis.system.connection import execute_command +from apis.system.connection import connect_to_device +import apis.system.reboot as reboot + +from utilities.utils import ensure_service_params + +data = SpyTestDict() + +@pytest.fixture(scope="module", autouse=True) +def snmp_module_hooks(request): + global vars + vars = st.ensure_min_topology("D1T1:2") + initialize_variables() + snmp_pre_config() + vlan_preconfig() + snmp_traffic_config() + snmp_trap_pre_config() + + yield + snmp_post_config() + vlan_postconfig() + snmp_trap_post_config() + + +@pytest.fixture(scope="function", autouse=True) +def snmp_func_hooks(request): + global ipaddress + ipaddress = st.get_mgmt_ip(vars.D1) + yield + + +def initialize_variables(): + data.clear() + data.ro_community = 'test_123' + data.location = 'hyderabad' + data.contact = "Admin" + data.sysname = "Sonic_device" + data.mgmt_int = 'eth0' + data.wait_time = 30 + data.filter_cli = "-One" + data.oid_sysName = '1.3.6.1.2.1.1.5.0' + data.oid_syUpTime = '1.3.6.1.2.1.25.1.1.0' + data.oid_sysLocation = '1.3.6.1.2.1.1.6.0' + data.oid_sysDescr = '1.3.6.1.2.1.1.1.0' + data.oid_sysContact = '1.3.6.1.2.1.1.4.0' + data.oid_sysObjectId = '1.3.6.1.2.1.1.2.0' + data.oid_mib_2 ='1.3.6.1.2.1' + data.oid_if_mib_all = '1.3.6.1.2.1.31' + data.oid_entity_mib_all = '1.3.6.1.2.1.47' + data.oid_entity_sensor_mib = '1.3.6.1.2.1.99' + data.oid_dot1q_mib = '1.3.6.1.2.1.17.7' + data.oid_dot1db_mib = '1.3.6.1.2.1.17' + data.oid_root_node_walk = '.' + data.oid_IP_MIB_ipAddressRowStatus_ipv6='1.3.6.1.2.1.4.34.1.10.2' + data.oid_IP_MIB_ipAddressStorageType_ipv6 = '1.3.6.1.2.1.4.34.1.11' + data.oid_IPV6_MIB_ipv6IfDescr = '1.3.6.1.2.1.55.1.5.1.2' + data.oid_IPV6_MIB_ipv6IfAdminStatus = '1.3.6.1.2.1.55.1.5.1.9' + data.oid_IPV6_MIB_ipv6IpForwarding = '1.3.6.1.2.1.4.25.0' + data.oid_IPV6_MIB_ipv6IpDefaultHopLimit = '1.3.6.1.2.1.4.26' + data.oid_IPV6_MIB_ipv6ScopeZoneIndexTable = '1.3.6.1.2.1.4.36' + data.oid_ipcidr_route_table = '1.3.6.1.2.1.4.24.4' + data.af_ipv4 = "ipv4" + data.loopback_addr = '67.66.66.66' + data.loopback0= 'Loopback0' + data.oid_dot1d_Base = '1.3.6.1.2.1.17.1' + data.oid_dot1d_Base_Bridge_Address = '1.3.6.1.2.1.17.1.1' + data.oid_dot1d_Base_Num_Ports = '1.3.6.1.2.1.17.1.2' + data.oid_dot1d_Base_Type = '1.3.6.1.2.1.17.1.3' + data.oid_dot1d_Base_Port = '1.3.6.1.2.1.17.1.4.1.1' + data.oid_dot1d_Base_PortIf_Index = '1.3.6.1.2.1.17.1.4.1.2' + data.oid_dot1d_Base_Port_Delay_Exceeded_Discards = '1.3.6.1.2.1.17.1.4.1.4' + data.oid_dot1d_Base_Port_Mtu_Exceeded_Discards = '1.3.6.1.2.1.17.1.4.1.5' + data.oid_dot1d_Tp_Aging_Time = '1.3.6.1.2.1.17.4.2' + data.oid_dot1q_Vlan_Version_Number = '1.3.6.1.2.1.17.7.1.1.1' + data.oid_dot1q_Max_VlanId = '1.3.6.1.2.1.17.7.1.1.2' + data.oid_dot1q_Max_Supported_Vlans = '1.3.6.1.2.1.17.7.1.1.3' + data.oid_dot1q_Num_Vlans = '1.3.6.1.2.1.17.7.1.1.4' + data.oid_dot1q_Vlan_Num_Deletes = '1.3.6.1.2.1.17.7.1.4.1' + data.oid_dot1q_Fdb_Dynamic_Count = '1.3.6.1.2.1.17.7.1.2.1.1.2' + data.oid_dot1q_Tp_Fdb_Address = '1.3.6.1.2.1.17.7.1.2.2.1.1' + data.oid_dot1q_Tp_Fdb_Port = '1.3.6.1.2.1.17.7.1.2.2.1.2' + data.oid_dot1q_Tp_Fdb_Status = '1.3.6.1.2.1.17.7.1.2.2.1.3' + data.oid_dot1q_Vlan_Index = '1.3.6.1.2.1.17.7.1.4.2.1.2' + data.oid_dot1q_Vlan_Current_Egress_Ports = '1.3.6.1.2.1.17.7.1.4.2.1.4' + data.oid_dot1q_Vlan_Current_Untagged_Ports = '1.3.6.1.2.1.17.7.1.4.2.1.5' + data.oid_dot1q_Vlan_Static_Name = '1.3.6.1.2.1.17.7.1.4.3.1.1' + data.oid_dot1q_Vlan_Static_Egress_Ports = '1.3.6.1.2.1.17.7.1.4.3.1.2' + data.oid_dot1q_Vlan_Static_Untagged_Ports = '1.3.6.1.2.1.17.7.1.4.3.1.4' + data.oid_dot1q_Vlan_Static_Row_Status = '1.3.6.1.2.1.17.7.1.4.3.1.5' + data.oid_dot1q_Pvid = '1.3.6.1.2.1.17.7.1.4.5.1.1' + data.source_mac = "00:0a:01:00:00:01" + data.source_mac1 = "00:0a:02:00:00:01" + data.vlan = str(random_vlan_list()[0]) + data.dot1q_Vlan_Static_Table = '1.3.6.1.2.1.17.7.1.4.3' + data.dot1q_Vlan_Current_Table = '1.3.6.1.2.1.17.7.1.4.2' + data.dot1q_Tp_Fdb_Table = '1.3.6.1.2.1.17.7.1.2.2' + data.dot1q_Fdb_Table = '1.3.6.1.2.1.17.7.1.2.1' + data.nsNotifyShutdown='8072.4.0.2' + data.filter = '-Oqv' + + +def snmp_pre_config(): + """ + SNMP pre config + """ + global ipaddress + ipaddress_list = basic_obj.get_ifconfig_inet(vars.D1, data.mgmt_int) + st.log("Checking Ip address of the Device ") + if not ipaddress_list: + st.report_env_fail("ip_verification_fail") + ipaddress = ipaddress_list[0] + st.log("Device ip addresse - {}".format(ipaddress)) + snmp_obj.set_snmp_config(vars.D1, snmp_rocommunity= data.ro_community, snmp_location=data.location) + if not st.is_community_build(): + ipfeature.configure_loopback(vars.D1, loopback_name="Loopback0", config="yes") + ipfeature.config_ip_addr_interface(vars.D1, data.loopback0, data.loopback_addr, 32, family=data.af_ipv4) + if not ipfeature.ping(vars.D1, ipaddress, family='ipv4', external=True): + st.error("Ping reachability is failed between SNMP server and Device.") + if not snmp_obj.poll_for_snmp(vars.D1, data.wait_time, 1, ipaddress=ipaddress, + oid=data.oid_sysName, community_name=data.ro_community): + st.log("Post SNMP config , snmp is not working") + st.report_fail("operation_failed") + +def vlan_preconfig(): + if not vlan_obj.create_vlan(vars.D1, data.vlan): + st.report_fail("vlan_create_fail", data.vlan) + if not st.is_community_build(): + mac_obj.config_mac(vars.D1, data.source_mac, data.vlan, vars.D1T1P1) + st.log("Adding TGen-1 connected interface to newly created vlan in un tagging mode.") + if not vlan_obj.add_vlan_member(vars.D1, data.vlan, vars.D1T1P1, tagging_mode=False): + st.report_fail("vlan_untagged_member_fail", vars.D1T1P1, data.vlan) + st.log("Adding TGen-2 connected interface to newly created vlan in tagging mode.") + if not vlan_obj.add_vlan_member(vars.D1, data.vlan, vars.D1T1P2, tagging_mode=True): + st.report_fail("vlan_untagged_member_fail", vars.D1T1P2, data.vlan) + + +def snmp_traffic_config(): + tg_handler = tgapi.get_handles_byname("T1D1P1", "T1D1P2") + tg = tg_handler["tg"] + tg.tg_traffic_control(action="reset", port_handle=tg_handler["tg_ph_list"]) + tg.tg_traffic_control(action="clear_stats", port_handle=tg_handler["tg_ph_list"]) + + data.streams = {} + stream = tg.tg_traffic_config(port_handle=tg_handler["tg_ph_1"], mode='create', + transmit_mode='continuous', length_mode='fixed', rate_pps=100, frame_size=72, + l2_encap='ethernet_ii_vlan', vlan_id=data.vlan, mac_src='00:0a:01:00:00:01', + mac_src_step='00:00:00:00:00:01', mac_src_mode='increment', mac_src_count=10, + mac_dst='00:0a:12:00:00:01', vlan="enable") + data.streams['stream1'] = stream['stream_id'] + stream = tg.tg_traffic_config(port_handle=tg_handler["tg_ph_2"], mode='create', + transmit_mode='continuous', length_mode='fixed', rate_pps=10, + l2_encap='ethernet_ii_vlan', vlan_id=data.vlan, mac_src='00:0a:12:00:00:01', + mac_dst='00:0a:01:00:00:01', vlan="enable") + data.streams['stream2'] = stream['stream_id'] + intf_obj.clear_interface_counters(vars.D1) + tg.tg_traffic_control(action='run', stream_handle=[data.streams['stream1'], data.streams['stream2']]) + st.wait(2) + tg.tg_traffic_control(action='stop', stream_handle=[data.streams['stream1'], data.streams['stream2']]) + + +def snmp_trap_pre_config(): + global capture_file, ssh_conn_obj + ip = ensure_service_params(vars.D1, "snmptrap", "ip") + username = ensure_service_params(vars.D1, "snmptrap", "username") + password = ensure_service_params(vars.D1, "snmptrap", "password") + path = ensure_service_params(vars.D1, "snmptrap", "path") + + # Connect to the linux machine and check + ssh_conn_obj = connect_to_device(ip, username, password) + if not ssh_conn_obj: + st.report_tc_fail("ssh_connection_failed", ip) + + # enable traps on DUT + snmp_obj.config_snmp_trap(vars.D1, version=2, ip_addr=ip, community= data.ro_community) + + # start capture on the linux machine + capture_file = path + + +def snmp_post_config(): + """ + SNMP post config + """ + snmp_obj.restore_snmp_config(vars.D1) + if not st.is_community_build(): + ipfeature.configure_loopback(vars.D1, loopback_name="Loopback0", config="no") + + +def vlan_postconfig(): + if not st.is_community_build(vars.D1): + mac_obj.clear_mac(vars.D1, port=vars.D1T1P1, vlan=data.vlan) + else: + mac_obj.clear_mac(vars.D1) + vlan_obj.delete_vlan_member(vars.D1, data.vlan, [vars.D1T1P1, vars.D1T1P2]) + vlan_obj.delete_vlan(vars.D1, data.vlan) + + +def snmp_trap_post_config(): + snmp_obj.config_snmp_trap(vars.D1, version=2, ip_addr=None, no_form=True) + clear_cmd = "echo > {}".format(capture_file) + st.log("Clearing the snmptrap log with command '{}'".format(clear_cmd)) + execute_command(ssh_conn_obj, clear_cmd) + +def snmptrapd_checking(): + retval = False + + # check and start the snmptrap on the given server. + ps_cmd = "ps -ealf | grep snmptrapd | grep -v grep" + st.log("Checking for snmptrap process existence with command '{}'".format(ps_cmd)) + output = execute_command(ssh_conn_obj,ps_cmd) + ps_lines = "\n".join(output.split("\n")[:-1]) + + if "snmptrapd" in ps_lines: + retval = True + + return retval + + +def device_eth0_ip_addr(): + """ + To get the ip address of device after reboot. + """ + ipaddress = st.get_mgmt_ip(vars.D1) + st.log("Device ip address - {}".format(ipaddress)) + if not ipfeature.ping(vars.D1, ipaddress, family='ipv4', external=True): + st.error("Ping reachability is failed between SNMP server and Device.") + + +@pytest.mark.snmp_sysName +@pytest.mark.regression +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_snmp_sysName(): + """ + Author : Karthikeya Kumar CH + Verify that the sysName MIB object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + st.log("Ensuring minimum topology") + hostname = basic_obj.get_hostname(vars.D1) + get_snmp_output= snmp_obj.get_snmp_operation(ipaddress=ipaddress, oid=data.oid_sysName, + community_name=data.ro_community) + st.log("hostname Device('{}') and SNMP('{}')".format(hostname, get_snmp_output[0])) + if not get_snmp_output[0] == hostname: + st.report_fail("sysName_verification_fail") + st.report_pass("test_case_passed") + + +@pytest.mark.regression +@pytest.mark.snmp_sysUpTime +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_snmp_test_syUpTime(): + """ + Author : Karthikeya Kumar CH + Verify that the sysUpTime MIB object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + st.log("Ensuring minimum topology") + get_snmp_output = snmp_obj.get_snmp_operation(ipaddress=ipaddress, oid=data.oid_syUpTime, + community_name=data.ro_community) + uptime_cli_sec = box_obj.get_system_uptime_in_seconds(vars.D1) + days, hours,minutes, seconds = re.findall(r"(\d+):(\d+):(\d+):(\d+).\d+", get_snmp_output[0])[0] + get_snmp_output = util_obj.convert_time_to_seconds(days, hours,minutes, seconds) + st.log("Up time value from DUT is :{} & get_snmp_output value is :{} &" + " get_snmp_output tolerance value is : {}" + .format(uptime_cli_sec, get_snmp_output, get_snmp_output + 3)) + if not (get_snmp_output >= uptime_cli_sec or get_snmp_output+3 >= uptime_cli_sec): + st.report_fail("sysUptime_verification_fail") + st.report_pass("test_case_passed") + + +@pytest.mark.regression +@pytest.mark.snmp_sysLocation +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_snmp_sysLocation(): + """ + Author : Karthikeya Kumar CH + Verify that the sysLocation MIB object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + location_output = snmp_obj.get_snmp_config(vars.D1)[0]["snmp_location"] + st.log("System Location from the device is : {} ".format(location_output)) + get_snmp_output = snmp_obj.get_snmp_operation(ipaddress=ipaddress, + oid=data.oid_sysLocation, community_name=data.ro_community) + st.log("System Location from the SNMP output: {} ".format(get_snmp_output[0])) + if not get_snmp_output[0] == location_output: + st.log(" Up time is not matching between device sysuptime and snmp uptime ") + st.report_fail("sysLocation_verification_fail") + st.report_pass("test_case_passed") + + +@pytest.mark.regression +@pytest.mark.snmp_sysDescr +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_snmp_sysDescr(): + """ + Author : Karthikeya Kumar CH + Verify that the sysDescr MIB object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + descrip_output= basic_obj.show_version(vars.D1)["hwsku"] + get_snmp_output = snmp_obj.get_snmp_operation(ipaddress=ipaddress, oid=data.oid_sysDescr, + community_name=data.ro_community) + get_snmp_output = get_snmp_output[0] + get_snmp_output_value = get_snmp_output.split(' - ')[1].split(':')[1].strip() + if not descrip_output == get_snmp_output_value: + st.report_fail("sysDescr_verification_fail") + st.report_pass("test_case_passed") + + +@pytest.mark.regression +@pytest.mark.snmp_sysContact +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_snmp_sysContact(): + """ + Author : Karthikeya Kumar CH + Verify that the sysContact MIB object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + contact_output = "" + get_snmp_output = snmp_obj.get_snmp_operation(ipaddress=ipaddress, oid=data.oid_sysContact, + community_name=data.ro_community) + get_snmp_output = get_snmp_output[0] + st.log("System Contact from the SNMP output: {} ".format(get_snmp_output)) + st.log("System Contact from the DUT output: {} ".format(contact_output)) + if not contact_output == get_snmp_output: + st.log(" Contact is not matching between device Contact and snmp Contact ") + st.report_fail("sysContact_verification_fail") + st.report_pass("test_case_passed") + +@pytest.mark.regression +@pytest.mark.snmp_mib_2 +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_snmp_mib_2(): + """ + Author : Karthikeya Kumar CH + Verify that snmpwalk on mib_2 MIB object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_mib_2, + community_name=data.ro_community) + if not get_snmp_output: + st.report_fail("get_snmp_output_fail") + st.report_pass("test_case_passed") + + +@pytest.mark.regression +@pytest.mark.snmp_if_mib_all +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_snmp_if_mib_all(): + """ + Author : Karthikeya Kumar CH + Verify that snmpwalk on if_mib_all MIB object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_if_mib_all, + community_name=data.ro_community) + if not get_snmp_output: + st.report_fail("Fail") + st.report_pass("test_case_passed") + + +@pytest.mark.regression +@pytest.mark.snmp_entity_mib_all +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_snmp_entity_mib_all(): + """ + Author : Karthikeya Kumar CH + Verify that snmpwalk on entity_mib_all MIB object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_entity_mib_all, + community_name=data.ro_community) + if not get_snmp_output: + st.report_fail("get_snmp_output_fail") + st.report_pass("test_case_passed") + + +@pytest.mark.regression +@pytest.mark.snmp_entity_sensor +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_snmp_entity_sensor_mib(): + """ + Author : Karthikeya Kumar CH + Verify that snmpwalk on entity_sensor_mib MIB object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_entity_sensor_mib, + community_name=data.ro_community) + if not get_snmp_output: + st.report_fail("get_snmp_output_fail") + st.report_pass("test_case_passed") + + +@pytest.mark.regression +@pytest.mark.snmp_dot1q_dot1db +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_snmp_dot1q_dot1db_mib(): + """ + Author : Karthikeya Kumar CH + Verify that snmpwalk on dot1q MIB object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + snmp_obj.poll_for_snmp_walk(vars.D1, data.wait_time, 3, ipaddress=ipaddress, + oid=data.oid_dot1q_mib, community_name=data.ro_community) + get_snmp_output_dot1q = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_dot1q_mib, + community_name=data.ro_community) + if not get_snmp_output_dot1q: + st.report_fail("get_snmp_output_fail") + get_snmp_output_dot1db = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_dot1db_mib, + community_name=data.ro_community) + if not get_snmp_output_dot1db: + st.report_fail("get_snmp_output_fail") + + st.report_pass("test_case_passed") + + +@pytest.mark.regression +@pytest.mark.snmp_root_node_walk +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_snmp_root_node_walk(): + """ + Author : Karthikeya Kumar CH + Verify that snmpwalk on entity_sensor_mib MIB object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + snmp_obj.poll_for_snmp_walk(vars.D1, data.wait_time, 3, ipaddress=ipaddress, + oid=data.oid_root_node_walk, community_name=data.ro_community) + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_root_node_walk, + community_name=data.ro_community) + if not get_snmp_output: + st.report_fail("get_snmp_output_fail") + st.report_pass("test_case_passed") + +@pytest.mark.regression +@pytest.mark.snmp_ipAddressRowStatus_ipv6 +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_snmp_ipAddressRowStatus_ipv6(): + """ + Author : Karthikeya Kumar CH + Verify that snmpwalk on IP-MIB::ipAddressRowStatus.ipv6 MIB object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_IP_MIB_ipAddressRowStatus_ipv6, + community_name=data.ro_community) + if not get_snmp_output: + st.report_fail("get_snmp_output_fail") + st.report_pass("test_case_passed") + + +@pytest.mark.regression +@pytest.mark.snmp_ipAddressStorageType_ipv6 +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_snmp_ipAddressStorageType_ipv6(): + """ + Author : Karthikeya Kumar CH + Verify that snmpwalk on IP-MIB::ipAddressStorageType.ipv6 MIB object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_IP_MIB_ipAddressStorageType_ipv6, + community_name=data.ro_community) + if not get_snmp_output: + st.report_fail("get_snmp_output_fail") + st.report_pass("test_case_passed") + + +@pytest.mark.regression +@pytest.mark.snmp_ipv6IfDescr +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_snmp_ipv6_If_Descr(): + """ + Author : Karthikeya Kumar CH + Verify that snmpwalk on IPV6-MIB::ipv6IfDescr MIB object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_IPV6_MIB_ipv6IfDescr, + community_name=data.ro_community) + if not get_snmp_output: + st.report_fail("get_snmp_output_fail") + st.report_pass("test_case_passed") + + +@pytest.mark.regression +@pytest.mark.snmp_ipv6IfAdminStatus +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_snmp_ipv6_If_AdminStatus(): + """ + Author : Karthikeya Kumar CH + Verify that snmpwalk on IPV6-MIB::ipv6IfAdminStatus MIB object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_IPV6_MIB_ipv6IfAdminStatus, + community_name=data.ro_community) + if not get_snmp_output: + st.report_fail("get_snmp_output_fail") + st.report_pass("test_case_passed") + + +@pytest.mark.regression +@pytest.mark.snmp_ipv6IpForwarding_and_DefaultHopLimit +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_snmp_ipv6_If_Forward_default_HopLimit(): + """ + Author : Karthikeya Kumar CH + Verify that snmpwalk on ipv6IpForwarding and ipv6IpDefaultHopLimit MIB object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_IPV6_MIB_ipv6IpForwarding, + community_name=data.ro_community) + get_snmp_output_1 = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_IPV6_MIB_ipv6IpDefaultHopLimit, + community_name=data.ro_community) + if not get_snmp_output: + st.report_fail("get_snmp_output_fail") + if not get_snmp_output_1: + st.report_fail("get_snmp_output_fail") + + st.report_pass("test_case_passed") + + +@pytest.mark.regression +@pytest.mark.snmp_ipv6IpForwarding_and_DefaultHopLimit +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_snmp_ipv6scope_index_table(): + """ + Author : Karthikeya Kumar CH + Verify that snmpwalk on ipv6ScopeZoneIndexTable MIB object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_IPV6_MIB_ipv6ScopeZoneIndexTable, + community_name=data.ro_community) + if not get_snmp_output: + st.report_fail("get_snmp_output_fail") + st.report_pass("test_case_passed") + + +@pytest.mark.regression +@pytest.mark.snmp_ipcidrroutetable +def test_ft_snmp_ipcidr_route_table(): + """ + Author : Karthikeya Kumar CH + Verify that snmpwalk walk on ipCidrroutetable MIB functions properly + Reference Test Bed : D1 --- Mgmt Network + """ + snmp_obj.poll_for_snmp_walk(vars.D1, data.wait_time,3, ipaddress=ipaddress, + oid=data.oid_ipcidr_route_table, community_name=data.ro_community) + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_ipcidr_route_table, + community_name=data.ro_community) + if not get_snmp_output: + st.report_fail("get_snmp_output_fail") + st.report_pass("test_case_passed") + +@pytest.mark.snmp_dot1d_bridge +@pytest.mark.regression +def test_ft_snmp_dot1d_base_bridge_address(): + """ + Author : Prasad Darnasi + Verify that the dot1dBaseBridgeAddress Object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_dot1d_Base_Bridge_Address, + community_name=data.ro_community) + mac_address=basic_obj.get_ifconfig_ether(vars.D1,'eth0') + if not str(mac_address) in get_snmp_output[0]: + st.report_fail("snmp_output_failed", "dot1dBaseBridgeAddress") + st.report_pass("test_case_passed") + +@pytest.mark.snmp_dot1d_bridge +@pytest.mark.regression +def test_ft_snmp_dot1d_base_num_ports(): + """ + Author : Prasad Darnasi + Verify that the dot1dBaseNumPorts Object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_dot1d_Base_Num_Ports, + community_name=data.ro_community,filter=data.filter) + if str(2) not in get_snmp_output: + st.report_fail("snmp_output_failed", "dot1dBaseNumPorts") + st.report_pass("test_case_passed") + +@pytest.mark.snmp_dot1d_bridge +@pytest.mark.regression +def test_ft_snmp_dot1d_base_type(): + """ + Author : Prasad Darnasi + Verify that the dot1dBaseType Object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_dot1d_Base_Type, + community_name=data.ro_community) + if not get_snmp_output: + st.report_fail("snmp_output_failed", "dot1dBaseType") + st.report_pass("test_case_passed") + +@pytest.mark.snmp_dot1d_bridge +@pytest.mark.regression +def test_ft_snmp_dot1d_base_port(): + """ + Author : Prasad Darnasi + Verify that the dot1dBasePort Object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid= data.oid_dot1d_Base_Port, + community_name=data.ro_community,filter=data.filter) + intf_name1=util_obj.get_interface_number_from_name(vars.D1T1P1) + intf_name2 = util_obj.get_interface_number_from_name(vars.D1T1P2) + if (intf_name1.get('number') not in str(get_snmp_output)) and (intf_name2.get('number') not in str(get_snmp_output)): + st.report_fail("snmp_output_failed", "dot1dBasePort") + st.report_pass("test_case_passed") + +@pytest.mark.snmp_dot1d_bridge +@pytest.mark.regression +def test_ft_snmp_dot1d_base_port_ifindex(): + """ + Author : Prasad Darnasi + Verify that the dot1dBasePortIfIndex Object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid= data.oid_dot1d_Base_PortIf_Index, + community_name=data.ro_community) + if not get_snmp_output: + st.report_fail("snmp_output_failed", "dot1dBasePortIfIndex") + st.report_pass("test_case_passed") + +@pytest.mark.snmp_dot1d_bridge +@pytest.mark.regression +def test_ft_snmp_dot1d_base_port_delay_exceeded_discards(): + """ + Author : Prasad Darnasi + Verify that the dot1dBasePortDelayExceededDiscards Object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid= data.oid_dot1d_Base_Port_Delay_Exceeded_Discards, + community_name=data.ro_community) + if not get_snmp_output: + st.report_fail("snmp_output_failed", "dot1dBasePortDelayExceededDiscards") + st.report_pass("test_case_passed") + +@pytest.mark.snmp_dot1d_bridge +@pytest.mark.regression +def test_ft_snmp_dot1d_base_port_mtu_exceeded_discards(): + """ + Author : Prasad Darnasi + Verify that the dot1dBasePortMtuExceededDiscards Object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid= data.oid_dot1d_Base_Port_Mtu_Exceeded_Discards, + community_name=data.ro_community) + if not get_snmp_output: + st.report_fail("snmp_output_failed", "dot1dBasePortMtuExceededDiscards") + st.report_pass("test_case_passed") + +@pytest.mark.snmp_dot1d_bridge +@pytest.mark.regression +def test_ft_snmp_dot1d_tp_aging_time(): + """ + Author : Prasad Darnasi + Verify that the dot1dTpAgingTime Object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid= data.oid_dot1d_Tp_Aging_Time, + community_name=data.ro_community) + if not get_snmp_output: + st.report_fail("snmp_output_failed", "dot1dTpAgingTime") + st.report_pass("test_case_passed") + +@pytest.mark.snmp_dot1q_table_requirement +@pytest.mark.regression +def test_ft_snmp_dot1q_fdb_dynamic_count(): + """ + Author : Prasad Darnasi + Verify that the dot1qFdbDynamicCount Object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid= data.oid_dot1q_Fdb_Dynamic_Count, + community_name=data.ro_community) + count=mac_obj.get_mac_count(vars.D1) + if str(count-1) not in get_snmp_output[0]: + st.report_fail("snmp_output_failed", "dot1qFdbDynamicCount") + st.report_pass("test_case_passed") + +@pytest.mark.snmp_dot1q_table_requirement +@pytest.mark.regression +def test_ft_snmp_dot1q_tp_fdb_port(): + """ + Author : Prasad Darnasi + Verify that the dot1qTpFdbPort Object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid= data.oid_dot1q_Tp_Fdb_Port, + community_name=data.ro_community) + intf_name = util_obj.get_interface_number_from_name(vars.D1T1P2) + if intf_name.get('number') not in get_snmp_output[0]: + st.report_fail("snmp_output_failed", "dot1qTpFdbPort") + st.report_pass("test_case_passed") + +@pytest.mark.snmp_dot1q_table_requirement +@pytest.mark.regression +def test_ft_snmp_dot1q_tp_fdb_status(): + """ + Author : Prasad Darnasi + Verify that the dot1qTpFdbStatus Object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid= data.oid_dot1q_Tp_Fdb_Status, + community_name=data.ro_community) + if not get_snmp_output: + st.report_fail("snmp_output_failed", "dot1qTpFdbStatus") + st.report_pass("test_case_passed") + +@pytest.mark.snmp_dot1q_table_requirement +@pytest.mark.regression +def test_ft_snmp_dot1q_vlan_current_egress_ports(): + """ + Author : Prasad Darnasi + Verify that the dot1qVlanCurrentEgressPorts Object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid= data.oid_dot1q_Vlan_Current_Egress_Ports, + community_name=data.ro_community,filter=data.filter) + if len(get_snmp_output[0].split(" ")) != 2: + st.report_fail("snmp_output_failed", "dot1qVlanCurrentEgressPorts") + st.report_pass("test_case_passed") + +@pytest.mark.snmp_dot1q_table_requirement +@pytest.mark.regression +def test_ft_snmp_dot1q_vlan_current_untagged_ports(): + """ + Author : Prasad Darnasi + Verify that the dot1qVlanCurrentUntaggedPorts Object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid= data.oid_dot1q_Vlan_Current_Untagged_Ports, + community_name=data.ro_community) + if not get_snmp_output: + st.report_fail("snmp_output_failed", "dot1qVlanCurrentUntaggedPorts") + st.report_pass("test_case_passed") + +@pytest.mark.snmp_dot1q_table_requirement +@pytest.mark.regression +def test_ft_snmp_dot1q_vlan_static_untagged_ports(): + """ + Author : Prasad Darnasi + Verify that the dot1qVlanStaticUntaggedPorts Object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid= data.oid_dot1q_Vlan_Static_Untagged_Ports, + community_name=data.ro_community) + if not get_snmp_output: + st.report_fail("snmp_output_failed", "dot1qVlanStaticUntaggedPorts") + st.report_pass("test_case_passed") + +@pytest.mark.snmp_dot1q_table_requirement +@pytest.mark.regression +def test_ft_snmp_dot1q_vlan_static_row_status(): + """ + Author : Prasad Darnasi + Verify that the dot1qVlanStaticRowStatus Object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid= data.oid_dot1q_Vlan_Static_Row_Status, + community_name=data.ro_community) + if not get_snmp_output: + st.report_fail("snmp_output_failed", "dot1qVlanStaticRowStatus") + st.report_pass("test_case_passed") + +@pytest.mark.snmp_dot1q_table_requirement +@pytest.mark.regression +def test_ft_snmp_dot1q_pvid(): + """ + Author : Prasad Darnasi + Verify that the dot1qPvid Object functions properly. + Reference Test Bed : D1 --- Mgmt Network + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid= data.oid_dot1q_Pvid, + community_name=data.ro_community,filter=data.filter) + if str(data.vlan) not in str(get_snmp_output): + st.report_fail("snmp_output_failed", "dot1qPvid") + st.report_pass("test_case_passed") + +@pytest.mark.snmp_dot1q_table_requirement +@pytest.mark.regression +def test_ft_snmp_dot1q_vlan_static_name(): + """ + Author : Prasad Darnasi + Verify that the dot1qVlanStaticName Object functions properly. + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_dot1q_Vlan_Static_Name, + community_name=data.ro_community,filter=data.filter) + if data.vlan not in get_snmp_output[0]: + st.report_fail("snmp_output_failed", "dot1qVlanStaticName") + st.report_pass("test_case_passed") + + +@pytest.mark.snmp_dot1q_table_requirement +@pytest.mark.regression +def test_ft_snmp_dot1q_vlan_static_egress_ports(): + """ + Author : Prasad Darnasi + Verify that the dot1qVlanStaticEgressPorts Object functions properly. + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_dot1q_Vlan_Static_Egress_Ports, + community_name=data.ro_community,filter=data.filter) + if len(get_snmp_output[0].split(" ")) != 2: + st.report_fail("snmp_output_failed", "dot1qVlanStaticEgressPorts") + st.report_pass("test_case_passed") + +@pytest.mark.snmp_dot1q_dot1q_scale_and_performance +@pytest.mark.regression +def test_ft_snmp_dot1q_vlan_version_number(): + """ + Author : Prasad Darnasi + Verify that the dot1qVlanVersionNumber Object functions properly. + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_dot1q_Vlan_Version_Number, + community_name=data.ro_community) + if not get_snmp_output: + st.report_fail("snmp_output_failed", "dot1qVlanVersionNumber") + st.report_pass("test_case_passed") + +@pytest.mark.snmp_dot1q_scale_and_performance +@pytest.mark.regression +def test_ft_snmp_dot1q_max_vlanid(): + """ + Author : Prasad Darnasi + Verify that the dot1qMaxVlanId Object functions properly. + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_dot1q_Max_VlanId, + community_name=data.ro_community,filter=data.filter) + if st.get_datastore(vars.D1, "constants","default")['MAX_VLAN_ID'] not in get_snmp_output[0]: + st.report_fail("snmp_output_failed", "dot1qMaxVlanId") + st.report_pass("test_case_passed") + +@pytest.mark.snmp_dot1q_scale_and_performance +@pytest.mark.regression +def test_ft_snmp_dot1q_max_supported_vlans(): + """ + Author : Prasad Darnasi + Verify that the dot1qMaxSupportedVlans Object functions properly. + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_dot1q_Max_Supported_Vlans, + community_name=data.ro_community,filter=data.filter) + if st.get_datastore(vars.D1, "constants", "default")['MAX_SUPPORTED_VLANS'] not in get_snmp_output[0]: + st.report_fail("snmp_output_failed", "dot1qMaxSupportedVlans") + st.report_pass("test_case_passed") + +@pytest.mark.snmp_dot1q_scale_and_performance +@pytest.mark.regression +def test_ft_snmp_dot1q_num_vlans(): + """ + Author : Prasad Darnasi + Verify that the dot1qNumVlans Object functions properly. + """ + count=vlan_obj.get_vlan_count(vars.D1) + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_dot1q_Num_Vlans, + community_name=data.ro_community,filter=data.filter) + if str(count) not in get_snmp_output: + st.report_fail("snmp_output_failed", "dot1qNumVlans") + st.report_pass("test_case_passed") + +@pytest.mark.snmp_dot1q_scale_and_performance +@pytest.mark.regression +def test_ft_snmp_dot1q_vlan_num_deletes(): + """ + Author : Prasad Darnasi + Verify that the dot1qVlanNumDeletes Object functions properly. + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.oid_dot1q_Vlan_Num_Deletes, + community_name=data.ro_community) + if not get_snmp_output: + st.report_fail("snmp_output_failed", "dot1qVlanNumDeletes") + st.report_pass("test_case_passed") + +@pytest.mark.snmp_dot1q_table_requirement +@pytest.mark.regression +def test_ft_snmp_vlan_static_table(): + """ + Author : Prasad Darnasi + Verify that the dot1qVlanStaticEntry Object functions properly. + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.dot1q_Vlan_Static_Table, + community_name=data.ro_community) + out = snmp_obj.get_oids_from_walk_output(get_snmp_output) + out = [str(x) for x in out] + for x in out: + get_snmp_output = snmp_obj.get_snmp_operation(ipaddress=ipaddress, oid=x, + community_name=data.ro_community) + if not get_snmp_output: + st.report_fail("snmp_output_failed", "dot1qVlanStaticTable") + get_snmp_output = snmp_obj.get_snmp_operation(ipaddress=ipaddress, oid=x, + community_name=data.ro_community,get_next=True) + if not get_snmp_output: + st.report_fail("snmp_output_failed", "dot1qVlanStaticTable") + st.report_pass("test_case_passed") + +@pytest.mark.snmp_dot1q_table_requirement +@pytest.mark.regression +def test_ft_snmp_dot1q_vlan_index(): + """ + Author : Prasad Darnasi + Verify that the dot1qVlanIndex Object functions properly. + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid= data.dot1q_Vlan_Current_Table, + community_name=data.ro_community) + out = snmp_obj.get_oids_from_walk_output(get_snmp_output) + out = [str(x) for x in out] + for x in out: + get_snmp_output = snmp_obj.get_snmp_operation(ipaddress=ipaddress, oid=x, + community_name=data.ro_community) + if not get_snmp_output: + st.report_fail("snmp_output_failed", "dot1qVlanIndex") + get_snmp_output = snmp_obj.get_snmp_operation(ipaddress=ipaddress, oid=x, + community_name=data.ro_community,get_next=True) + if not get_snmp_output: + st.report_fail("snmp_output_failed", "dot1qVlanIndex") + st.report_pass("test_case_passed") + +@pytest.mark.snmp_dot1q_table_requirement +@pytest.mark.regression +def test_ft_snmp_dot1q_tp_fdb_address(): + """ + Author : Prasad Darnasi + Verify that the dot1qTpFdbAddress Object functions properly. + """ + snmp_obj.poll_for_snmp_walk(vars.D1, data.wait_time,1, ipaddress=ipaddress, + oid=data.dot1q_Tp_Fdb_Table, community_name=data.ro_community) + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.dot1q_Tp_Fdb_Table, + community_name=data.ro_community) + out = snmp_obj.get_oids_from_walk_output(get_snmp_output) + out = [str(x) for x in out] + for x in out: + get_snmp_output = snmp_obj.get_snmp_operation(ipaddress=ipaddress, oid=x, + community_name=data.ro_community) + if not get_snmp_output: + st.report_fail("snmp_output_failed", "dot1qTpFdbAddress") + get_snmp_output = snmp_obj.get_snmp_operation(ipaddress=ipaddress, oid=x, + community_name=data.ro_community,get_next=True) + if not get_snmp_output: + st.report_fail("snmp_output_failed", "dot1qTpFdbAddress") + st.report_pass("test_case_passed") + +@pytest.mark.snmp_dot1q_table_requirement +@pytest.mark.regression +def test_ft_snmp_dot1q_fdb_table(): + """ + Author : Prasad Darnasi + Verify that the dot1qFdbEntry Object functions properly. + """ + get_snmp_output = snmp_obj.walk_snmp_operation(ipaddress=ipaddress, oid=data.dot1q_Fdb_Table, + community_name=data.ro_community) + out = snmp_obj.get_oids_from_walk_output(get_snmp_output) + out = [str(x) for x in out] + for x in out: + get_snmp_output = snmp_obj.get_snmp_operation(ipaddress=ipaddress, oid=x, + community_name=data.ro_community) + if not get_snmp_output: + st.report_fail("snmp_output_failed", "dot1qFdbTable") + get_snmp_output = snmp_obj.get_snmp_operation(ipaddress=ipaddress, oid=x, + community_name=data.ro_community,get_next=True) + if not get_snmp_output: + st.report_fail("snmp_output_failed", "dot1qFdbTable") + st.report_pass("test_case_passed") + +@pytest.mark.snmp_trap +def test_ft_snmp_link_down_trap(): + """ + Author : Prasad Darnasi + Verify that trap is sent when a link is down. + """ + check_flag = snmptrapd_checking() + if not check_flag: + st.report_fail("snmptrapd_not_running") + + # trigger trap on DUT + intf_obj.interface_shutdown(vars.D1, vars.D1T1P1) + intf_obj.interface_noshutdown(vars.D1, vars.D1T1P1) + + # get data from capture + read_cmd = "cat {}".format(capture_file) + + output = execute_command(ssh_conn_obj, read_cmd) + trap_lines = output.split("\n")[:-1] + + result=any('linkDown' in x for x in trap_lines) + if result == 0: + st.report_fail("snmp_output_failed", "linkDown") + else: + st.report_pass("test_case_passed") + +@pytest.mark.snmp_trap +def test_ft_snmp_link_up_trap(): + """ + Author : Prasad Darnasi + Verify that trap is sent when a link is UP. + """ + check_flag = snmptrapd_checking() + if not check_flag: + st.report_fail("snmptrapd_not_running") + + # trigger trap on DUT + intf_obj.interface_shutdown(vars.D1, vars.D1T1P1) + intf_obj.interface_noshutdown(vars.D1, vars.D1T1P1) + + # get data from capture + read_cmd = "cat {}".format(capture_file) + + output = execute_command(ssh_conn_obj, read_cmd) + trap_lines = output.split("\n")[:-1] + + result = any('linkUp' in x for x in trap_lines) + if result == 0: + st.report_fail("snmp_output_failed", "linkUp") + else: + st.report_pass("test_case_passed") + +@pytest.mark.snmp_trap +def test_ft_snmp_coldstart_trap(): + """ + Author : Prasad Darnasi + Verify that trap is sent when rps reboot is performed. + """ + check_flag = snmptrapd_checking() + if not check_flag: + st.report_fail("snmptrapd_not_running") + + # trigger trap on DUT + st.reboot(vars.D1) + + # Get the ip address of the switch after reboot + device_eth0_ip_addr() + + # get data from capture + read_cmd = "cat {}".format(capture_file) + + output = execute_command(ssh_conn_obj,read_cmd) + trap_lines = output.split("\n")[:-1] + + result = any('coldStart' in x for x in trap_lines) + if result == 0: + st.report_fail("snmp_output_failed", "coldStart") + else: + st.report_pass("test_case_passed") + +@pytest.mark.snmp_trap +def test_ft_snmp_nsnotifyshutdown_trap(): + """ + Author : Prasad Darnasi + Verify that trap is sent when snmp docker is restarted. + """ + check_flag = snmptrapd_checking() + if not check_flag: + st.report_fail("snmptrapd_not_running") + + # trigger trap on DUT + basic_obj.docker_operation(vars.D1,"snmp","restart") + + # get data from capture + read_cmd = "cat {}".format(capture_file) + output = execute_command(ssh_conn_obj,read_cmd) + trap_lines = output.split("\n")[:-1] + + result = any(data.nsNotifyShutdown in x for x in trap_lines) + if result == 0: + st.report_fail("snmp_output_failed", "nsNotifyShutdown") + else: + st.report_pass("test_case_passed") + +@pytest.mark.snmp_trap +def test_ft_snmp_warmstart_trap(): + """ + Author : Prasad Darnasi + Verify that trap is sent when reboot is performed. + """ + check_flag = snmptrapd_checking() + if not check_flag: + st.report_fail("snmptrapd_not_running") + + # trigger trap on DUT + reboot.config_save(vars.D1) + st.reboot(vars.D1, 'warm') + + # Get the ip address of the switch after reboot + device_eth0_ip_addr() + + # get data from capture + read_cmd = "cat {}".format(capture_file) + output = execute_command(ssh_conn_obj,read_cmd) + trap_lines = output.split("\n")[:-1] + + result = any('warmStart' in x for x in trap_lines) + if result == 0: + for i in range(1, 4): + read_cmd = "cat {}".format(capture_file) + output = execute_command(ssh_conn_obj, read_cmd) + trap_lines = output.split("\n")[:-1] + result = any('warmStart' in x for x in trap_lines) + if result == 1: + break + st.wait(10) + if result == 0: + st.report_fail("snmp_output_failed", "warmStart") + else: + st.report_pass("test_case_passed") + +@pytest.mark.snmp_docker_restart +def test_ft_snmp_docker_restart(): + """ + Author : Karthikeya Kumar CH + Verify that the sysName MIB object functions properly after docker restart + Reference Test Bed : D1--- Mgmt Network + """ + basic_obj.service_operations_by_systemctl(vars.D1, 'snmp', 'restart') + if not basic_obj.poll_for_system_status(vars.D1, 'snmp', 30, 1): + st.report_fail("service_not_running".format('snmp')) + if not basic_obj.verify_service_status(vars.D1, 'snmp'): + st.report_fail("snmp_service_not_up") + hostname =basic_obj.get_hostname(vars.D1) + get_snmp_output= snmp_obj.get_snmp_operation(ipaddress=ipaddress, oid=data.oid_sysName, + community_name=data.ro_community) + st.log("hostname Device('{}') and SNMP('{}')".format(hostname, get_snmp_output[0])) + if not get_snmp_output[0] == hostname: + st.report_fail("sysName_verification_fail_after_docker_restart") + st.report_pass("test_case_passed") + + + diff --git a/spytest/tests/system/test_ssh.py b/spytest/tests/system/test_ssh.py new file mode 100644 index 00000000000..2bc12133afc --- /dev/null +++ b/spytest/tests/system/test_ssh.py @@ -0,0 +1,382 @@ +import pytest,random, json +from spytest import st +from spytest.dicts import SpyTestDict +from apis.system.ssh import enable_ssh, disable_ssh, enable_sshv6, disable_sshv6 +from apis.system.reboot import config_save +from apis.security.user import config_user +from utilities.common import random_username, random_password +import tests.qos.acl.acl_json_config as acl_data +import apis.routing.ip as ip_obj +from utilities import parallel +import apis.qos.acl as acl_obj +from utilities.utils import ensure_service_params +from spytest.utils import poll_wait +import apis.system.snmp as snmp_obj +import apis.system.basic as basic_obj +from apis.system.connection import connect_to_device, ssh_disconnect, execute_command +from apis.system.basic import get_docker_ps, get_and_match_docker_count, verify_docker_status + + +ssh_data = SpyTestDict() + +def initialize_variables(): + ssh_data.clear() + ssh_data.usr_default = 'admin' + ssh_data.pwd_default = ['YourPaSsWoRd', 'broadcom'] + ssh_data.pwd_final = '' + ssh_data.usr_non_default = random_username(random.randint(5, 31)) + ssh_data.pwd_non_default = random_password(random.randint(6, 12)) + ssh_data.commands_to_verify = ['show system status'] + ssh_data.ipv4_address_D1D2P1 = "2.2.2.1" + ssh_data.ipv4_address_D2D1P1 = "2.2.2.2" + ssh_data.ipv4_address_D1D2P2 = "2.2.3.1" + ssh_data.ipv4_address_D2D1P2 = "2.2.3.2" + ssh_data.ipv4_network = "2.2.2.0/24" + ssh_data.ipv6_address_D1D2P1 = "1001::1" + ssh_data.ipv6_address_D2D1P1 = "1001::2" + ssh_data.ipv6_address_D1D2P2 = "2001::1" + ssh_data.ipv6_address_D2D1P2 = "2001::2" + ssh_data.ipv6_network_D1 = "1001::0/64" + ssh_data.ipv4_mask = "24" + ssh_data.ipv6_mask = "64" + ssh_data.ro_community = 'test_123' + ssh_data.location = 'hyderabad' + ssh_data.contact = "Admin" + ssh_data.sysname = "Sonic_device" + ssh_data.oid_sysName = '1.3.6.1.2.1.1.5.0' + + +@pytest.fixture(scope="module", autouse=True) +def ssh_module_hooks(request): + global vars + vars = st.ensure_min_topology("D1D2:2") + initialize_variables() + ssh_module_prolog() + config_ip_address() + enable_sshv6(vars.D1) + + yield + config_ip_address(oper='remove') + acl_obj.delete_acl_table(vars.D1) + disable_sshv6(vars.D1) + snmp_config(config='remove') + + +@pytest.fixture(scope="function", autouse=True) +def ssh_func_hooks(request): + yield + + +def config_nondefault_user(config='add'): + if config == 'add': + st.log("creating non-default username={},password={}".format(ssh_data.usr_non_default, ssh_data.pwd_non_default)) + config_user(vars.D1, ssh_data.usr_non_default, 'add') + if not st.change_passwd(vars.D1, ssh_data.usr_non_default, ssh_data.pwd_non_default): + st.error("Failed to create non-default username={},password={}".format(ssh_data.usr_non_default, + ssh_data.pwd_non_default)) + return False + st.log('Saving the configuration') + config_save(vars.D1) + else: + config_user(vars.D1, ssh_data.usr_non_default, 'del') + return True + + +def config_ip_address(oper ='add'): + st.log("Configuring ipv4 address on D1 and D2 connected port") + dict1 = {'interface_name': vars.D1D2P1, 'ip_address': ssh_data.ipv4_address_D1D2P1, 'subnet': ssh_data.ipv4_mask, + 'family': "ipv4", 'config': oper} + dict2 = {'interface_name': vars.D2D1P1, 'ip_address': ssh_data.ipv4_address_D2D1P1, 'subnet': ssh_data.ipv4_mask, + 'family': "ipv4", 'config': oper} + parallel.exec_parallel(True, [vars.D1, vars.D2], ip_obj.config_ip_addr_interface, [dict1, dict2]) + + dict1 = {'interface_name': vars.D1D2P2, 'ip_address': ssh_data.ipv4_address_D1D2P2, 'subnet': ssh_data.ipv4_mask, + 'family': "ipv4", 'config': oper} + dict2 = {'interface_name': vars.D2D1P2, 'ip_address': ssh_data.ipv4_address_D2D1P2, 'subnet': ssh_data.ipv4_mask, + 'family': "ipv4", 'config': oper} + parallel.exec_parallel(True, [vars.D1, vars.D2], ip_obj.config_ip_addr_interface, [dict1, dict2]) + + st.log("Configuring ipv6 address on D1 and D2 connected port") + dict1 = {'interface_name': vars.D1D2P1, 'ip_address': ssh_data.ipv6_address_D1D2P1, 'subnet': ssh_data.ipv6_mask, + 'family': "ipv6", 'config': oper} + dict2 = {'interface_name': vars.D2D1P1, 'ip_address': ssh_data.ipv6_address_D2D1P1, 'subnet': ssh_data.ipv6_mask, + 'family': "ipv6", 'config': oper} + parallel.exec_parallel(True, [vars.D1, vars.D2], ip_obj.config_ip_addr_interface, [dict1, dict2]) + + dict1 = {'interface_name': vars.D1D2P2, 'ip_address': ssh_data.ipv6_address_D1D2P2, 'subnet': ssh_data.ipv6_mask, + 'family': "ipv6", 'config': oper} + dict2 = {'interface_name': vars.D2D1P2, 'ip_address': ssh_data.ipv6_address_D2D1P2, 'subnet': ssh_data.ipv6_mask, + 'family': "ipv6", 'config': oper} + parallel.exec_parallel(True, [vars.D1, vars.D2], ip_obj.config_ip_addr_interface, [dict1, dict2]) + + +def snmp_config(config='add'): + global ssh_conn_obj + if config == 'add': + ip = ensure_service_params(vars.D1, "snmptrap", "ip") + username = ensure_service_params(vars.D1, "snmptrap", "username") + password = ensure_service_params(vars.D1, "snmptrap", "password") + snmp_obj.set_snmp_config(vars.D1, snmp_rocommunity=ssh_data.ro_community, snmp_location=ssh_data.location) + # Connect to the linux machine and check + ssh_conn_obj = connect_to_device(ip, username, password) + if not ssh_conn_obj: + return False + else: + snmp_obj.restore_snmp_config(vars.D1) + return True + + +def ssh_module_prolog(): + enable_ssh(vars.D1) + st.log('connecting to device with username={},password={}'.format(ssh_data.usr_default, ssh_data.pwd_default[0])) + ssh_data.pwd_final = ssh_data.pwd_default[0] + if not st.exec_ssh(vars.D1, ssh_data.usr_default, ssh_data.pwd_default[0], ssh_data.commands_to_verify): + ssh_data.pwd_final = ssh_data.pwd_default[1] + st.log('Retry - connecting to device with username={},password={}'.format(ssh_data.usr_default, + ssh_data.pwd_default[1])) + if not st.exec_ssh(vars.D1, ssh_data.usr_default, ssh_data.pwd_default[1], ssh_data.commands_to_verify): + st.log("SSH connection failed with default Credentials.") + st.report_fail("ssh_failed") + st.banner("--> Detected device default password = {}".format(ssh_data.pwd_final)) + + +def change_acl_rules(config, rule_name, attribute, value): + config["ACL_RULE"][rule_name][attribute] = value + + +def apply_acl_config(dut, config): + json_config = json.dumps(config) + json.loads(json_config) + st.apply_json2(dut, json_config) + + +def verify_ssh_connection(dut, ip, username, password, cmds="show vlan config"): + output = st.exec_ssh_remote_dut(dut, ip, username, password, cmds) + if "Connection timed out" in output or "option requires an argument" in output or "Connection refused" in output: + st.error("SSH Connection Failed: IP-{}, User-{}, Password-{}".format(ip, username, password)) + return False + st.log("SSH Connection sucess: IP-{}, User-{}, Password-{}".format(ip, username, password)) + return True + + +@pytest.mark.ssh_disable +@pytest.mark.regression +@pytest.mark.community +@pytest.mark.community_pass +def test_ft_ssh_service_disable(): + """ + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + """ + disable_ssh(vars.D1) + st.log('connecting to device with username={},password={}'.format(ssh_data.usr_default, ssh_data.pwd_final)) + output = st.exec_ssh(vars.D1, ssh_data.usr_default, ssh_data.pwd_final, ssh_data.commands_to_verify) + enable_ssh(vars.D1) + if output: + st.error("SSH connection Success even when disabled the SSH service.") + st.report_fail("test_case_failed") + else: + st.report_pass("test_case_passed") + +@pytest.mark.acl_test_controlplane +@pytest.mark.ssh_verify +@pytest.mark.regression +def test_ft_ssh_add_user_verify(): + """ + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + """ + user_ssh = 0 + acl_sshv4 = 0 + acl_sshv6 = 0 + acl_snmp = 0 + + if not snmp_config(config='add'): acl_snmp = + 1 + + ipaddress = st.get_mgmt_ip(vars.D1) + if not ipaddress: + st.report_env_fail("ip_verification_fail") + + snmp_cmd = "snmpget -Oqv -v 2c -c {} {} {}".format(ssh_data.ro_community, ipaddress, ssh_data.oid_sysName) + + out = config_nondefault_user() + if not out: user_ssh = + 1 + + st.log("connecting to device with username={},password={}".format(ssh_data.usr_default, ssh_data.pwd_final)) + if not st.exec_ssh(vars.D1, ssh_data.usr_default, ssh_data.pwd_final, ssh_data.commands_to_verify): + st.error('Cannot SSH into Device with default credentials') + user_ssh = + 1 + + st.log('connecting to device with username={},password={}'.format(ssh_data.usr_non_default, + ssh_data.pwd_non_default)) + if not st.exec_ssh(vars.D1, ssh_data.usr_non_default, ssh_data.pwd_non_default, ssh_data.commands_to_verify): + st.error('Cannot SSH into Device with non-default credentials') + user_ssh = + 1 + + output = verify_ssh_connection(vars.D2, ssh_data.ipv4_address_D1D2P2, ssh_data.usr_default, ssh_data.pwd_final) + if not output: + user_ssh = + 1 + + IPAddr = ensure_service_params(vars.D1, "snmptrap", "ip") + "/32" + change_acl_rules(acl_data.acl_json_config_control_plane, "SNMP_ACL|RULE_1", "SRC_IP", IPAddr) + change_acl_rules(acl_data.acl_json_config_control_plane, "SSH_ONLY|RULE_1", "SRC_IP", IPAddr) + change_acl_rules(acl_data.acl_json_config_control_plane, "SSH_ONLY|RULE_2", "SRC_IP", ssh_data.ipv4_network) + change_acl_rules(acl_data.acl_json_config_control_plane, "V6_SSH_ONLY|RULE_1", "SRC_IPV6", ssh_data.ipv6_network_D1) + acl_config = acl_data.acl_json_config_control_plane + st.log("ACL_DATA: {}".format(acl_config)) + apply_acl_config(vars.D1, acl_config) + acl_obj.show_acl_table(vars.D1) + acl_obj.show_acl_rule(vars.D1) + + if not poll_wait(acl_obj.verify_acl_table_rule, 5, vars.D1, "SNMP_ACL", "RULE_1"): + st.error("Failed to create ACL rule '{}' ".format("SNMP_ACL")) + acl_snmp =+ 1 + + if not poll_wait(acl_obj.verify_acl_table_rule, 5, vars.D1, "SSH_ONLY", "RULE_1"): + st.error("Failed to create ACL rule '{}' ".format("SSH_ONLY")) + acl_sshv4 =+ 1 + + if not poll_wait(acl_obj.verify_acl_table_rule, 5, vars.D1, "V6_SSH_ONLY", "RULE_1"): + st.error("Failed to create ACL rule '{}' ".format("V6_SSH_ONLY")) + acl_sshv6 =+ 1 + + hostname = basic_obj.get_hostname(vars.D1) + st.log("HOSTNAME: {}".format(hostname)) + snmp_out = execute_command(ssh_conn_obj, snmp_cmd) + if hostname not in snmp_out: acl_snmp = + 1 + + st.log("connecting to device with default username={},password={}".format(ssh_data.usr_default, ssh_data.pwd_final)) + output = verify_ssh_connection(vars.D2, ssh_data.ipv4_address_D1D2P2, ssh_data.usr_default, ssh_data.pwd_final) + if output: acl_sshv4 =+ 1 + + output = verify_ssh_connection(vars.D2, ssh_data.ipv6_address_D1D2P2, ssh_data.usr_default, ssh_data.pwd_final) + if output: acl_sshv6 = + 1 + + output = verify_ssh_connection(vars.D2, ssh_data.ipv4_address_D1D2P1, ssh_data.usr_default, ssh_data.pwd_final) + if not output: acl_sshv4 = + 1 + + output = verify_ssh_connection(vars.D2, ssh_data.ipv6_address_D1D2P1, ssh_data.usr_default, ssh_data.pwd_final) + if not output: acl_sshv6 = + 1 + + st.log("connecting to device with non default username={},password={}".format(ssh_data.usr_non_default, ssh_data.pwd_non_default)) + output = verify_ssh_connection(vars.D2, ssh_data.ipv4_address_D1D2P1, ssh_data.usr_non_default, ssh_data.pwd_non_default) + if not output: acl_sshv4 = + 1 + + output = verify_ssh_connection(vars.D2, ssh_data.ipv6_address_D1D2P1, ssh_data.usr_non_default, ssh_data.pwd_non_default) + if not output: acl_sshv6 = + 1 + + config_save(vars.D1) + st.log('rebooting the device.') + st.reboot(vars.D1, 'fast') + + acl_obj.show_acl_table(vars.D1) + acl_obj.show_acl_rule(vars.D1) + + if not poll_wait(acl_obj.verify_acl_table_rule, 5, vars.D1, "SSH_ONLY", "RULE_1"): + st.log("Failed to create ACL rule '{}' ".format("SSH_ONLY")) + acl_sshv4 = + 1 + + if not poll_wait(acl_obj.verify_acl_table_rule, 5, vars.D1, "V6_SSH_ONLY", "RULE_1"): + st.log("Failed to create ACL rule '{}' ".format("V6_SSH_ONLY")) + acl_sshv4 = + 1 + + if not poll_wait(acl_obj.verify_acl_table_rule, 5, vars.D1, "SNMP_ACL", "RULE_1"): + st.error("Failed to create ACL rule '{}' ".format("SNMP_ACL")) + acl_snmp =+ 1 + + hostname = basic_obj.get_hostname(vars.D1) + snmp_out = execute_command(ssh_conn_obj, snmp_cmd) + if hostname not in snmp_out: acl_snmp = + 1 + ''' + change_acl_rules(acl_data.acl_json_config_control_plane, "SNMP_ACL|RULE_1", "SRC_IP", "2.2.2.2/24") + acl_config = acl_data.acl_json_config_control_plane + apply_acl_config(vars.D1, acl_config) + acl_obj.show_acl_rule(vars.D1) + + snmp_out = execute_command(ssh_conn_obj, snmp_cmd) + if "Timeout" not in snmp_out: acl_snmp = + 1 + ''' + st.log("connecting to device with default username={},password={}".format(ssh_data.usr_default, ssh_data.pwd_final)) + output = verify_ssh_connection(vars.D2, ssh_data.ipv4_address_D1D2P2, ssh_data.usr_default, ssh_data.pwd_final) + if output: acl_sshv4 = + 1 + + output = verify_ssh_connection(vars.D2, ssh_data.ipv6_address_D1D2P2, ssh_data.usr_default, ssh_data.pwd_final) + if output: acl_sshv6 = + 1 + + output = verify_ssh_connection(vars.D2, ssh_data.ipv4_address_D1D2P1, ssh_data.usr_default, ssh_data.pwd_final) + if not output: acl_sshv4 = + 1 + + output = verify_ssh_connection(vars.D2, ssh_data.ipv6_address_D1D2P1, ssh_data.usr_default, ssh_data.pwd_final) + if not output: acl_sshv6 = + 1 + + if acl_sshv4: + st.report_tc_fail("test_ft_controlplane_acl_service_sshv4", "ssh_failed", "with control plane ACL service SSHv4 after reboot") + else: + st.report_tc_pass("test_ft_controlplane_acl_service_sshv4", "ssh_failed", "with control plane ACL service SSHv4 after reboot") + + if acl_sshv6: + st.report_tc_fail("test_ft_controlplane_acl_service_sshv6", "ssh_failed", "with control plane ACL service SSHv6 after reboot") + else: + st.report_tc_pass("test_ft_controlplane_acl_service_sshv6", "ssh_failed", "with control plane ACL service SSHv6 after reboot") + + if acl_snmp: + st.report_tc_fail("test_ft_controlplane_acl_service_snmp", "snmp_output_failed", "with control plane ACL service SNMP after reboot") + else: + st.report_tc_pass("test_ft_controlplane_acl_service_snmp", "snmp_output_failed", "with control plane ACL service SNMP after reboot") + + acl_obj.delete_acl_table(vars.D1) + + if acl_sshv4 or acl_sshv6 or acl_snmp: + st.generate_tech_support(vars.D1, "controlplane_acl_services_after_reboot") + + st.log("connecting to device with username={},password={}".format(ssh_data.usr_default, ssh_data.pwd_final)) + if not st.exec_ssh(vars.D1, ssh_data.usr_default, ssh_data.pwd_final, ssh_data.commands_to_verify): + st.error('Cannot SSH into Device with default credentials after reboot') + user_ssh = + 1 + + st.log('connecting to device with username={},password={}'.format(ssh_data.usr_non_default, + ssh_data.pwd_non_default)) + if not st.exec_ssh(vars.D1, ssh_data.usr_non_default, ssh_data.pwd_non_default, ssh_data.commands_to_verify): + st.error('Cannot SSH into Device with non-default credentials after reboot') + user_ssh = + 1 + + config_nondefault_user(config='remove') + + if user_ssh: + st.report_fail("ssh_failed") + st.report_pass("test_case_passed") + + +@pytest.mark.ssh_verify +@pytest.mark.regression +def test_ft_ssh_config_reload_docker(): + """ + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + """ + result = True + get_docker_ps(vars.D1) + count = get_and_match_docker_count(vars.D1) + ssh_d1 = connect_to_device(st.get_mgmt_ip(vars.D1), ssh_data.usr_default, ssh_data.pwd_final) + if ssh_d1: + st.log("Executing command - 'sudo config reload -y &' in to the SSH session.") + st.log(execute_command(ssh_d1, 'sudo config reload -y &')) + st.wait(5, 'After executing "config reload" cmd on SSH session.') + st.log("Forcefully disconnecting the SSH session..") + ssh_disconnect(ssh_d1) + else: + st.error('Cannot SSH into Device with default credentials') + st.report_fail("ssh_failed") + + if not poll_wait(verify_docker_status, 180, vars.D1, 'Exited'): + st.error("Post 'config reload' from SSH, dockers are not auto recovered.") + result = False + + if result: + if not poll_wait(get_and_match_docker_count, 180, vars.D1, count): + st.error("Post 'config reload' from SSH, ALL dockers are not UP.") + result = False + + if not result: + st.log("Test Failed: So recovering the device by reboot.") + st.reboot(vars.D1) + st.report_fail("test_case_failed") + st.report_pass("test_case_passed") + diff --git a/spytest/utilities/__init__.py b/spytest/utilities/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/spytest/utilities/common.py b/spytest/utilities/common.py new file mode 100644 index 00000000000..1e8ec5bef85 --- /dev/null +++ b/spytest/utilities/common.py @@ -0,0 +1,669 @@ +import os +import re +import sys +import csv +import time +import base64 +import random +import socket +import string +import struct +import hashlib +import textwrap +import datetime +import subprocess +from inspect import currentframe +from collections import OrderedDict + +import yaml + +from tabulate import tabulate +from prettytable import PrettyTable +from jinja2 import Environment + +from . import json_helpers as json + +def find_file(filename, paths=[]): + if os.path.isfile(filename): + return filename + for path in paths: + if os.path.isfile(path): + path = os.path.dirname(path) + filename1 = os.path.join(path, filename) + if os.path.isfile(filename1): + return filename1 + return None + +def ensure_parent(filename): + path = os.path.dirname(filename) + path = os.path.abspath(path) + if not os.path.exists(path): + os.makedirs(path) + +def open_file(filename, mode="r"): + + if mode == "w": + ensure_parent(filename) + + if sys.version_info.major < 3: + return open(filename, mode+"b") + + return open(filename, mode, newline='') + +def delete_file(filename): + if os.path.exists(filename): + os.remove(filename) + return True + return False + +def write_file(filename, data, mode="w"): + if filename: + ensure_parent(filename) + fh = open(filename, mode) + fh.write(data) + fh.close() + return data + +def make_list(arg): + """ + todo: Update Documentation + :param arg: + :type arg: + :return: + :rtype: + """ + if arg is None: + return [] + if isinstance(arg, list): + return arg + return [arg] + +def filter_and_select(output, select=None, match=None): + """ + + This method applies the given match in the output and + returns columns as per given select + + :param output: output to which match has to be applied + :param select: select expression + :param match: match expression + :return: columns as per the select + """ + def match_entry(ent, match): + if isinstance(match, list): + # list of matches - select if any one is matched + for m in match: + if not m or match_entry(ent, m): + return True + return False + elif not isinstance(match, dict): + print("expecting the match to be a dict") + # select if all conditions match + for key, value in match.items(): + if key not in ent or str(ent[key]) != str(value): + return False + return True + + def select_entry(ent, select): + newd = dict() + for col in select: + if col not in ent: + return None + newd[col] = ent[col] + return newd + + # collect the matched/all entries + retval = [] + for ent in output: + if not match or match_entry(ent, match): + retval.append(ent) + + # return all columns if select is not specified + if not select: + return retval + + # return only columns specified in select + retval2 = [] + for ent in retval: + tmp = select_entry(ent, select) + if tmp: + retval2.append(tmp) + return retval2 + + +def compare_data(data1, data2, ignore=None, expected=True): + """ + todo: Update Documentation + :param data1: + :type data1: + :param data2: + :type data2: + :param ignore: + :type ignore: + :param expected: + :type expected: + :return: + :rtype: + """ + print(data1) + print(data2) + return expected + + +def sprint_data(d, msg=""): + rv = "========================{}===========================\n".format(msg) + rv = rv + "{}".format(d) + rv = rv + "\n=====================================================\n" + return rv + +def print_data(d, msg=""): + print (sprint_data(d, msg)) + +def sprint_yaml(d, msg="", default_flow_style=False): + rv = "========================{}===========================\n".format(msg) + rv = rv + yaml.dump(d, default_flow_style=default_flow_style) + rv = rv + "\n=====================================================\n" + return rv + +def print_yaml(d, msg="", default_flow_style=False): + print (sprint_yaml(d, msg, default_flow_style)) + +def random_string(slen=10): + include_list = string.ascii_letters + string.digits + return ''.join(random.choice(include_list) for i in range(slen)) + +def random_username(slen=10): + include_list = string.ascii_lowercase + string.digits + '_-' + first_char = random.choice(string.ascii_lowercase+'_') + return first_char+''.join(random.choice(include_list) for _ in range(slen-1)) + +def random_password(slen=10): + include_list = string.ascii_letters + string.digits + '!@#$%^&*()' + return ''.join(random.choice(include_list) for _ in range(slen)) + +def random_vlan_list(count=1, exclude=[]): + """ + todo: Update Documentation + :param count: + :type count: + :param exclude: + :type exclude: + :return: + :rtype: + """ + retval = [] + while count > 0: + val = random.randint(2, 4094) + if exclude and val in exclude: + pass + elif val not in retval: + retval.append(val) + count = count - 1 + return retval + +def get_proc_name(): + """ + todo: Update Documentation + :return: + :rtype: + """ + return sys._getframe(1).f_code.co_name + +def get_line_number(): + cf = currentframe() + return cf.f_back.f_lineno + +def trace(fmt, *args): + sys.stdout.write(fmt % args) + +def trim_dict(d, match=["", None, {}]): + new_dict = {} + if not isinstance(d, dict): + return new_dict + for k, v in d.items(): + if isinstance(v, dict): + v = trim_dict(v, match) + if v not in match: + new_dict[k] = v + return new_dict + +def is_unicode(arg): + if sys.version_info[0] >= 3: + return bool(isinstance(arg, str)) + return bool(isinstance(arg, unicode)) + +def ipcheck(addr): + try: + subprocess.check_output(["ping", "-c", "1", "-w", "2", str(addr)]) + return True + except subprocess.CalledProcessError: + return False + +def sprintf(fmt, *args): + return fmt % args + +def md5(fname): + hash_md5 = hashlib.md5() + with open(fname, "rb") as f: + for chunk in iter(lambda: f.read(4096), b""): + hash_md5.update(chunk) + return hash_md5.hexdigest() + +def b64encode(file_path): + fh = open_file(file_path) + text = fh.read() + encoded_data = base64.b64encode(text) + retval = [] + for i in xrange((len(encoded_data) / 76) + 1): + retval.append(encoded_data[i * 76:(i + 1) * 76]) + return retval + +######################## to be removed after refactoring #################### +######################## to be removed after refactoring #################### +class ExecAllFunc(object): + def __init__(self, func, *args, **kwargs): + self.func = func + self.args = args + self.kwargs = kwargs + +def exec_foreach (use_threads, items, func, *args, **kwargs): + from . import parallel + return parallel.exec_foreach (use_threads, items, func, *args, **kwargs) + +def exec_all(use_threads, entries, first_on_main=False): + from . import parallel + return parallel.exec_all(use_threads, entries, first_on_main) +######################## to be removed after refactoring #################### +######################## to be removed after refactoring #################### + +def sprint_vtable(header, rows, max_width=0): + t = PrettyTable(header) + #t.align = "l" + if max_width: + t.max_width = max_width + t.hrules = True + for row in rows: + t.add_row(row) + return str(t) + +def sprint_htable(header, row): + t = PrettyTable(["Name", "value"]) + t.hrules = True + for index in range(0, len(row)): + t.add_row([header[index], row[index]]) + return str(t) + +def date_parse(datestr): + try: + return datetime.datetime.strptime(datestr, '%Y-%m-%d %H:%M:%S') + except: + pass + try: + return datetime.datetime.strptime(datestr, '%Y-%m-%d %H:%M:%S.%f') + except: + pass + return None + +def time_parse(timestr): + try: + (h,m,s) = timestr.split(':') + secs = int(h) * 3600 + int(m) * 60 + int(s) + except: + secs = 0 + return secs + +def time_format(seconds): + hour = seconds // 3600 + seconds = seconds % 3600 + minutes = seconds // 60 + seconds = seconds % 60 + return "%d:%02d:%02d" % (hour, minutes, seconds) + +def time_diff(start, end, fmt=False, add=0): + if not start or not end: + seconds = 0 + else: + time_taken = end - start + seconds = time_taken.total_seconds() + if time_taken.microseconds >= 500: + seconds = seconds + 1 + seconds = seconds + add + if not fmt: + return int(seconds) + return time_format(seconds) + +def dict_reduce(first, second): + return {k: v for k, v in first.items() if k not in second} + +def get_digits(arg, sortit=False): + """ + Get all the digits in the given list or string + :param arg: + :type arg: string/list + :param sortit: sort the output or not + :type sortit: boolean + :return: list of integers + :rtype: list + """ + retval = [] + for ent in make_list(arg): + dlist = re.findall(r'\d+', str(ent)) + retval.extend([int(x) for x in dlist if x not in retval]) + if sortit: + retval.sort() + return retval + +def iprange(start, count, incr=1, exclude=[]): + start_addr = struct.unpack("!I", socket.inet_aton(start))[0] + retval = [] + while len(retval) < count: + while True: + addr = socket.inet_ntoa(struct.pack('>I', start_addr)) + start_addr = start_addr + incr + if addr not in exclude: + retval.append(addr) + break + return retval + +def string_list(text): + str_list = [] + for arg in make_list(text): + arg = str(arg) if arg else "" + arg = arg.strip() + for ent in arg.split("\n"): + ent = ent.strip() + if ent: + str_list.append(ent) + return str_list + +def split_byall(text, tostr=False, sep=",;"): + text = str(text) if text else "" + text = text.strip() + text = text.replace("\n", " ") + if sep: + for ch in list(sep): + text = text.replace(ch, " ") + retval = [] + for ent in text.split(): + if tostr: + retval.append(str(ent)) + else: + retval.append(ent) + return retval + +def read_lines(filepath): + fh = open(filepath, 'r') + data = fh.readlines() + fh.close() + data = map(str.strip, data) + return data + +def find_duplicate(items, unique=[]): + retval = [] + for item in items: + if item not in unique: + unique.append(item) + else: + retval.append(item) + return retval + +def write_csv_writer(cols, rows, writer, append=False): + if not append: + writer.writeheader() + + for i in range(0, len(rows)): + d = OrderedDict() + for j in range(0, len(cols)): + d[cols[j]] = rows[i][j] + writer.writerow(d) + +def write_csv_file(cols, rows, filepath, append=False): + if sys.version_info.major < 3: + mode = "ab" if append else "wb" + fd = open(filepath, mode) + else: + mode = "a" if append else "w" + fd = open(filepath, mode, newline='') + writer = csv.DictWriter(fd, fieldnames=cols, dialect="excel") + write_csv_writer(cols, rows, writer, append) + fd.flush() + fd.close() + +def write_html_table(cols, rows, filepath=None): + html = """{table}""" + tbl=tabulate(rows, headers=cols, tablefmt="html") + html = html.format(table=tbl) + html = html.replace("", "
") + + return write_file(filepath, html) + +def write_html_table2(cols, rows, filepath=None, links=None): + template = textwrap.dedent("""\ +
+ + {%- for col in cols %} + + {%- endfor %} + + + {%- for row in rows %} + + {%- for cell in row %} + + {%- endfor %} + + {%- endfor %} + +
{{col}}
{{cell}}
+ """) + + if links: + l_rows = [] + for index in range(0, len(rows)): + l_row = list(rows[index]) + if links[index]: + l_row[0]="{}".format(links[index],l_row[0]) + l_rows.append(l_row) + else: + l_rows = rows + html = Environment().from_string(template).render(cols=cols, rows=l_rows) + return write_file(filepath, html) + +# entries should be output of traceback.format_exc() +def stack_trace(entries): + if isinstance(entries, str): + return [entries] + + retval = [] + index = 0 + try: + for item in reversed(entries): + fname, line, func, text = item + msg = "[{}] {}:{} {} {}".format(index, fname, line, func, text) + index = index + 1 + retval.append(msg) + except: + retval.append("Failed to parse stack trace {}".format(str(entries))) + + return retval + +def poll_wait(method, timeout, *args, **kwargs): + t = time.time() + timeout + while True: + time.sleep(1) + if time.time() > t: + break + elif method(*args, **kwargs): + return True + return False + +def time_span_to_sec(time_span): + try: + return sum(x * int(t) for x, t in zip([3600, 60, 1], time_span.split(":"))) + except: + return 0 + +def to_string(data): + if sys.version_info.major < 3: + return str(data) + if isinstance(data, bytes): + return data.decode("utf-8") + return data + +def split_lines_trim(text): + text = str(text) if text else "" + text = text.replace("\n", " ") + retval = [] + for ent in text.split(): + retval.append(to_string(ent)) + return retval + +def dicts_list_values(dict_list, name): + retval = [] + for d in dict_list: + if name in d: + retval.append(d[name]) + return retval + +def invert_dict(d): + retval = {} + for key in d: + retval.setdefault(d[key], []).append(key) + return retval + +def split_list(data, size): + if size == 0: + size = len(data) + return [data[x:x+size] for x in range(0, len(data), size)] + +def filter_list(full_list, excludes): + s = set(excludes) + return list(x for x in full_list if x not in s) + +def banner(msg, width=80, delimiter="#", wrap=True, func=None, tnl=True, lnl=True): + msg_list = [""] if lnl else [] + msg_list.append(delimiter*width) + if msg != None: + if wrap: output = ["{0} {1} {0}".format(delimiter,each.center(width-4)) + for each in textwrap.wrap(msg, width=width-4)] + else: output = ["{0} {1:{2}} {0}".format(delimiter,each,(width-4)) + for each in textwrap.wrap(msg, width=width-4)] + msg_list.extend(['\n'.join(output), delimiter*width]) + if tnl: msg_list.append("") + for each_line in msg_list: + if func: func(each_line) + else: print(each_line) + +def split_with_quoted_strings(s): + def strip_quotes(s): + if s and (s[0] == '"' or s[0] == "'") and s[0] == s[-1]: + return s[1:-1] + return s + return [strip_quotes(p).replace('\\"', '"').replace("\\'", "'") + for p in re.findall(r'"(?:\\.|[^"])*"|\'(?:\\.|[^\'])*\'|[^\s]+', s)] + +def is_valid_ipv4(s): + regex = r""" + ^(25[0-5]|2[0-4][0-9]|[0-1]?[0-9][0-9]?)\. + (25[0-5]|2[0-4][0-9]|[0-1]?[0-9][0-9]?)\. + (25[0-5]|2[0-4][0-9]|[0-1]?[0-9][0-9]?)\. + (25[0-5]|2[0-4][0-9]|[0-1]?[0-9][0-9]?) + """ + regex = "".join(regex.split()) + return bool(re.search(regex, s)) + +def integer_parse(s, default=None): + #return re.match(r"[-+]?\d+$", s) is not None + try: + return int(s) + except: + return default + +def min(n1, n2): + return n1 if n1 < n2 else n2 + +def max(n1, n2): + return n1 if n1 > n2 else n2 + +def j2_apply(text, **kwargs): + return Environment().from_string(text).render(**kwargs) + +def json_parse(text=None, file=None, paths=[], **kwargs): + root = None + if text: + text = Environment().from_string(text).render(**kwargs) + elif file: + if "::" in file: [file, root] = file.split("::", 2) + file = find_file(file, paths) + text = "\n".join(read_lines(file)) + text = Environment().from_string(text).render(**kwargs) + else: + raise Exception("Neither text nor file argument provided") + data = json.fix(text, "Invalid json text/file supplied", True) + if not root: return data + if root in data: return data[root] + return None + +def convert_to_bits(count_dict): + """ + This method will take nested dictionary(inner dictionaries values are strings) as input and return nested + dictionaries(inner dictionaries values as float values) and convert Kilo Bits, Mega Bits, Giga Bits to Bits + :param count_dict: + :return: + """ + for port, counters in count_dict.items(): + for property, value in counters.items(): + if 'K' in value: + multiple = 10**3 + elif 'M' in value: + multiple = 10**6 + elif 'G' in value: + multiple = 10**9 + else: + multiple = 1 + count_dict[port][property] = float(re.findall(r"\d+[.]?[\d+]?", value.replace(',',''))[0])*multiple + return count_dict + +def get_current_datetime(): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Common function to get current date time + :return: + """ + now = datetime.datetime.now() + return now.strftime("%m%d%Y%H%M%S") + + +def write_to_json_file(content, file_path): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Common function to write the json file + :param content: + :param file_path: + :return: + """ + import json + json_dump = json.dumps(content) + parsed = json.loads(json_dump) + json_content = json.dumps(parsed, indent=4, sort_keys=True) + src_fp = open(file_path, "w") + src_fp.write(json_content) + src_fp.close() + return file_path + +def remove_last_line_from_string(data): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Common function to remove the last line of the string + :param data: + :return: + """ + return data[:data.rfind('\n')] + + +if __name__ == "__main__": + # indent the json file + text = "\n".join(read_lines(sys.argv[1])) + data = json.fix(text, load=True) + print(json.dumps(data)) + diff --git a/spytest/utilities/json_helpers.py b/spytest/utilities/json_helpers.py new file mode 100644 index 00000000000..96eb3549f38 --- /dev/null +++ b/spytest/utilities/json_helpers.py @@ -0,0 +1,49 @@ +import re +import json + +from collections import OrderedDict + +class _Exception(Exception): + def __init__(self, data, exp, msg): + self.exp = exp + self.msg = msg + self.data = data + + def __str__(self): + output = [""] + lines=self.data.split("\n") + for index, line in enumerate(lines): + output.append("{}: {}".format(index+1, line)) + if self.exp: + output.append(str(self.exp)) + if self.msg: + output.append(str(self.msg)) + return "\n".join(output) + +def loads(text, object_pairs_hook=OrderedDict): + return json.loads(text, object_pairs_hook=object_pairs_hook) + +def dumps(data): + return (json.dumps(data, indent=2, separators=(',', ': '))) + +def fix(text, msg="invalid json text", load=False, object_pairs_hook=OrderedDict): + + try: + obj = json.loads(text, object_pairs_hook=object_pairs_hook) + return obj if load else text + except: + pass + + # remove trailing object comma + regex = re.compile(r'(,)\s*}(?=([^"\\]*(\\.|"([^"\\]*\\.)*[^"\\]*"))*[^"]*$)') + text = regex.sub("}", text) + # remove trailing array comma + regex = re.compile(r'(,)\s*\](?=([^"\\]*(\\.|"([^"\\]*\\.)*[^"\\]*"))*[^"]*$)') + text = regex.sub("]", text) + + try: + obj = json.loads(text, object_pairs_hook=object_pairs_hook) + return obj if load else text + except Exception as exp: + raise _Exception(text, exp, msg) + diff --git a/spytest/utilities/parallel.py b/spytest/utilities/parallel.py new file mode 100644 index 00000000000..193aae87d6b --- /dev/null +++ b/spytest/utilities/parallel.py @@ -0,0 +1,243 @@ + +import sys +import time +import traceback +import threading +import utilities.common as utils + +# change this to 1 to force single entry thread calls +min_items = 2 + +shutting_down = False +def set_shutting_down(): + global shutting_down + shutting_down = True + +in_parallel = 0 +def set_in_parallel(val): + global in_parallel + if val: + in_parallel = in_parallel + 1 + else: + in_parallel = in_parallel - 1 + +def get_in_parallel(): + return in_parallel + +def wait_for_threads(threads): + while True: + alive = False + for index, thread in enumerate(threads): + thread.join(timeout=1) + if thread.is_alive(): + alive=True + if not alive or shutting_down: + break + +def exec_foreach (use_threads, items, func, *args, **kwargs): + set_in_parallel(True) + retvals = list() + exceptions = list() + def _thread_func(index, *args, **kwargs): + try: + retvals[index] = func(*args, **kwargs) + exceptions[index] = None + except Exception as e1: + retvals[index] = None + exceptions[index] = traceback.format_exc() + except SystemExit as e2: + retvals[index] = None + exceptions[index] = e2 + + threads = list() + args_list = list(args) + args_list.insert(0, "") + args_list.insert(0, retvals) + index = 0 + for item in items: + retvals.append(None) + exceptions.append(None) + args_list[0] = index + index = index + 1 + args_list[1] = item + args = tuple(args_list) + if not use_threads or len(items) < min_items: + _thread_func(*args, **kwargs) + else: + x = threading.Thread(target=_thread_func, args=args, kwargs=kwargs) + threads.append(x) + x.start() + wait_for_threads(threads) + set_in_parallel(False) + for exp in exceptions: + if isinstance(exp, SystemExit): + sys.exit() + return [retvals, exceptions] + +# remove this once refactored +class ExecAllFunc(utils.ExecAllFunc): + pass + +# rename this once refactored +class ExecAllFunc_todo_rename(object): + def __init__(self, func, *args, **kwargs): + self.func = func + self.args = args + self.kwargs = kwargs + +def exec_all(use_threads, entries, first_on_main=False): + set_in_parallel(True) + retvals = list() + exceptions = list() + def _thread_func(func, index, *args, **kwargs): + try: + retvals[index] = func(*args, **kwargs) + exceptions[index] = None + except Exception as e1: + retvals[index] = None + exceptions[index] = traceback.format_exc() + except SystemExit as e2: + retvals[index] = None + exceptions[index] = e2 + + f_args = None + f_kwargs = {} + threads = list() + index = 0 + for entry in entries: + if isinstance(entry, utils.ExecAllFunc): + kwargs = entry.kwargs + entry2 = [entry.func] + entry2.extend(list(entry.args)) + else: + kwargs = {} + entry2 = entry + entry2.insert(1, index) + index = index + 1 + args = tuple(entry2) + retvals.append(None) + exceptions.append(None) + if not f_args and first_on_main: + f_args = args + f_kwargs = kwargs + elif not use_threads or len(entries) < min_items: + _thread_func(*args, **kwargs) + else: + x = threading.Thread(target=_thread_func, args=args, kwargs=kwargs) + threads.append(x) + x.start() + if first_on_main: + _thread_func(*f_args, **f_kwargs) + wait_for_threads(threads) + set_in_parallel(False) + for exp in exceptions: + if isinstance(exp, SystemExit): + sys.exit() + return [retvals, exceptions] + +def exec_parallel(use_threads, items, func, kwarg_list,*args): + """ + Author:sooria.gajendrababu@broadcom.com + Info: parallel execution function for APIs with only kwargs + :param args: + :return: + + Usage: + dict1 ={"local_asn":dut1_as,'neighbor_ip':enable_bfd_list_1,'config':'yes'} + dict2 ={"local_asn":dut3_as,'neighbor_ip':enable_bfd_list_2,'config':'yes'} + exec_parallel(True,[dut1,dut3],bfd.configure_bfd,[dict1,dict2]) + """ + set_in_parallel(True) + retvals = list() + exceptions = list() + def _thread_func(index, *args, **kwargs): + try: + retvals[index] = func(*args, **kwargs) + exceptions[index] = None + except Exception as e1: + retvals[index] = None + exceptions[index] = traceback.format_exc() + except SystemExit as e2: + retvals[index] = None + exceptions[index] = e2 + threads = list() + args_list = list(args) + args_list.insert(0, "") + args_list.insert(0, retvals) + index = 0 + for item,kwargs in zip(items,kwarg_list): + retvals.append(None) + exceptions.append(None) + args_list[0] = index + index = index + 1 + args_list[1] = item + args = tuple(args_list) + if not use_threads or len(items) < min_items: + _thread_func(*args, **kwargs) + else: + x = threading.Thread(target=_thread_func, args=args, kwargs=kwargs) + threads.append(x) + x.start() + wait_for_threads(threads) + set_in_parallel(False) + for exp in exceptions: + if isinstance(exp, SystemExit): + sys.exit() + return [retvals, exceptions] + +class ExecuteBackgroud(object): + def __init__(self): + self.finished = False + self.func = None + self.args = () + self.kwargs = () + self.event = threading.Event() + self.event.clear() + self.t = threading.Thread(target=self._thread_func) + + def start(self, func, *args, **kwargs): + self.finished = False + self.func = func + self.args = args + self.kwargs = kwargs + self.t.start() + + def run(self): + self.event.set() + + def stop(self): + self.finished = True + self.event.set() + time.sleep(1) + + def is_valid(self): + return bool(self.func) + + def _thread_func(self): + try: + while True: + self.event.wait() + if self.finished: + return + if self.func: + self.func(*self.args, **self.kwargs) + self.event.clear() + except Exception as e1: + print(e1) + except SystemExit as e2: + print(e2) + +def ensure_no_exception(values): + """ + Importing st in function because this file has been imported by + framework so we cannot import framework API here + :param values: + :return: + """ + from spytest import st + for exp in values: + if exp is not None: + st.report_fail("exception_observed", exp) + return True + + diff --git a/spytest/utilities/utils.py b/spytest/utilities/utils.py new file mode 100644 index 00000000000..35b26151951 --- /dev/null +++ b/spytest/utilities/utils.py @@ -0,0 +1,616 @@ +# This file contains the list of command utility functions +# Author : Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) / Prudvi Mangadu (prudvi.mangadu@broadcom.com) + +import re +from spytest import st +import datetime +import json +import random +from utilities.common import filter_and_select, dicts_list_values, make_list +from socket import inet_aton, inet_pton, AF_INET6, AF_INET +from binascii import hexlify +from calendar import monthrange + + +def remove_last_line_from_string(data): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Common function to remove the last line of the string + :param data: + :return: + """ + return data[:data.rfind('\n')] + + +def get_last_line_from_string(data): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Common function to get last line of the string + :param data: + :return: + """ + output = data.split("\n") + return output[0] + + +def date_time_delta(datetime_1, datetime_2, timezone=False): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Common function to get the date time delta + :param datetime_1: + :param datetime_2: + :param timezone: + :return: + """ + try: + datetime_1 = datetime.datetime.strptime(str(datetime_1), "%Y-%m-%d %H:%M:%S %Z") \ + if timezone else datetime.datetime.strptime(str(datetime_1), "%Y-%m-%d %H:%M:%S") + datetime_2 = datetime.datetime.strptime(str(datetime_2), "%Y-%m-%d %H:%M:%S %Z") \ + if timezone else datetime.datetime.strptime(str(datetime_2), "%Y-%m-%d %H:%M:%S") + date_diff = datetime_2 - datetime_1 + return divmod(date_diff.days * 86400 + date_diff.seconds, 60) + except Exception as e: + st.log(e) + st.error(e) + return None + + +def check_file_exists(file_path): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Common function to check the file path + :param file_path: + :return: + """ + import os + exists = os.path.isfile(file_path) + return True if exists else False + + +def get_mac_address(base_mac="00:00:00:00:00:00", start=0, end=100, step=1): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Common function to generate MAC addresses + :param base_mac: + :param start: + :param end: + :param step: + :return: + """ + mac_address_list = list() + base_mac = base_mac.replace(":", '').replace(" ", '') + mac_int = int("0x"+base_mac, 16) + for i in range(mac_int+start, (mac_int+end)*step, step): + mac_address = "{0:0{1}x}".format(i, 12) + mac_formated = ":".join([mac_address[i:i+2] for i in range(0, len(mac_address), 2)]) + mac_address_list.append(mac_formated) + return mac_address_list + + +def log_parser(logs): + """ + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + Common function to parse the logs + :param logs: + :return: + """ + log_li = list(logs) if isinstance(logs, list) else [logs] + pattern = r'(\S+)\s*(\d+)\s*(\d+):(\d+):(\d+).(\d+)\s(\S+)\s*(\S+)\s*(.*)' + rv = [] + for each_log in log_li: + temp = {} + out = re.findall(pattern, each_log) + temp['month'], temp['date'], temp['hours'], temp['minutes'], temp['seconds'], temp[ + 'micro_second'], temp['hostname'], temp['severity'], temp['message'] = out[0] + rv.append(temp) + st.debug(rv) + return rv + + +def get_current_datetime(): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Common function to get current date time + :return: + """ + now = datetime.datetime.now() + return now.strftime("%m%d%Y%H%M%S") + + +def write_to_json_file(content, file_path): + """ + Author: Chaitanya Vella (chaitanya-vella.kumar@broadcom.com) + Common function to write the json file + :param content: + :param file_path: + :return: + """ + json_dump = json.dumps(content) + parsed = json.loads(json_dump) + json_content = json.dumps(parsed, indent=4, sort_keys=True) + src_fp = open(file_path, "w") + src_fp.write(json_content) + src_fp.close() + return file_path + + +def convert_time_to_seconds(days=0, hours=0, minutes=0, seconds=0): + """ + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + Common function to converting time in seconds + :param days: + :param hours: + :param minutes: + :param seconds: + :return: + """ + seconds = int(seconds) + if days: + days = int(days) * 3600 * 24 + if hours: + hours = int(hours) * 3600 + if minutes: + minutes = int(minutes) * 60 + retval = days + hours + minutes + seconds + return retval + + +def convert_time_to_milli_seconds(days=0, hours=0, minutes=0, seconds=0, milli_second=0): + """ + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + Common function to converting time in milli seconds + :param days: + :param hours: + :param minutes: + :param seconds: + :param milli_second: + :return: + """ + seconds = int(seconds) + milli_second = int(milli_second) + if days: + days = int(days) * 3600 * 24 + if hours: + hours = int(hours) * 3600 + if minutes: + minutes = int(minutes) * 60 + retval = ((days + hours + minutes + seconds)*1000 * 1000) + milli_second + return retval + + +def ensure_service_params(dut, *argv): + """ + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param argv: Service Name, follower by keys or list index. + :return: + + How to use?: + # Importing module: + import utilities.utils as utils_obj + # Below API call will check and get the tftp ip addresses from "sonic_services.yaml". + # Also it first check weather "tftp" service is present in "sonic_services.yaml" or not , + # if present then checks for "ip" and return the ip addresses. + # If any of "tftp" or "ip" no present in "sonic_services.yaml" file, then test case aborted and + # moved to the "NES" state " saying "Test case Not Executed(s) - Required service parameters + # is not defined " tftp->ip." + tftp_ip = utils_obj.ensure_service_params(dut,"tftp","ip") + tftp_path = utils_obj.ensure_service_params(dut,"tftp","path") + + tacacs_first_server = utils_obj.ensure_service_params(dut, "tacacs", "hosts",0,"ip") + tacacs_first_username = utils_obj.ensure_service_params(dut, "tacacs", "hosts",0,"username") + tacacs_first_password = utils_obj.ensure_service_params(dut, "tacacs", "hosts",0,"password") + tacacs_second_server = utils_obj.ensure_service_params(dut, "tacacs", "hosts",1,"ip") + tacacs_second_username = utils_obj.ensure_service_params(dut, "tacacs", "hosts",1,"username") + tacacs_second_password = utils_obj.ensure_service_params(dut, "tacacs", "hosts",1,"password") + + """ + if not argv: + st.error("Provide atleast one service to ensure") + return None + + service_string = ' -> '.join([str(e) for e in argv]) + st.log("Ensure service parameter(s) - {}".format(service_string)) + output = st.get_service_info(dut, argv[0]) + if not output: + st.error("'{}' is not specified in services/default.".format(argv[0])) + st.report_env_fail("test_case_not_executed_s_service", service_string) + + for each in argv[1:]: + try: + output = output[each] + except KeyError as e1: + st.log(e1) + st.error("Inside key '{}' : parameter {} is not specified in services/default.".format(argv[0], e1)) + st.report_env_fail("test_case_not_executed_s_service", service_string) + except IndexError as e2: + st.log(e2) + st.error("Inside Key '{}' : list index '{}' is not specified in services/default.".format(argv[0], each)) + st.report_env_fail("test_case_not_executed_s_service", service_string) + except Exception as e3: + st.log(e3) + st.error("Service or Parm '{}' not found.".format(each)) + st.report_env_fail("test_case_not_executed_s_service", service_string) + st.log("Return : {}".format(output)) + return output + + +def banner_log(msg, width=80, delimiter="#", wrap=True): + import textwrap + msg = str(msg) + if wrap: + output = ["{0} {1} {0}".format(delimiter, each.center(width-4)) for each in textwrap.wrap(msg, width=width-4)] + else: + output = ["{0} {1:{2}} {0}".format(delimiter, each, (width-4)) for each in textwrap.wrap(msg, width=width-4)] + msg_full = "\n" + "{}".format(delimiter)*width + "\n" + "{}".format('\n'.join(output)) + \ + '\n' + "{}".format(delimiter)*width + "\n" + for each_line in msg_full.split("\n"): + st.log(each_line) + + +def get_dut_name_from_no(dut_no): + return 'D1'+str(dut_no).zfill(2) + + +def get_dut_ports_dict_from_topo(min_req_topo): + """ + :param : None + :return: a dict of all dut-ports + + Returned dict will map all port connections in globals()['vars'] with some additional details. + key : dut + val : list of intf_properties_tuple + intf_properties_tuple : ( + ifname, + destination_dut, + linkno, + speed) + More interface specific details shall be added to the intf_tuple as and when required + + Example: + For the below given topology in testbed.yaml + + topology: + DUT1: + interfaces: + Ethernet0: {EndDevice: DUT2, EndPort: Ethernet0, params: def_link} + Ethernet4: {EndDevice: DUT2, EndPort: Ethernet4, params: def_link} + Ethernet8: {EndDevice: DUT3, EndPort: Ethernet8, params: def_link} + Ethernet12: {EndDevice: DUT4, EndPort: Ethernet12, params: def_link} + DUT2: + interfaces: + Ethernet16: {EndDevice: DUT3, EndPort: Ethernet16, params: def_link} + Ethernet20: {EndDevice: DUT4, EndPort: Ethernet20, params: def_link} + + This api will return the following dictionary. This output was captured on vsonic. + topology = {'D101': [('Ethernet0', 'D102', '1', 'N/A'), + ('Ethernet4', 'D102', '2', 'N/A'), + ('Ethernet8', 'D103', '1', 'N/A'), + ('Ethernet12', 'D104', '1', 'N/A')], + 'D102': [('Ethernet0', 'D101', '1', 'N/A'), + ('Ethernet4', 'D101', '2', 'N/A'), + ('Ethernet16', 'D103', '1', 'N/A'), + ('Ethernet20', 'D104', '1', 'N/A')], + 'D103': [('Ethernet8', 'D101', '1', 'N/A'), + ('Ethernet16', 'D102', '1', 'N/A')], + 'D104': [('Ethernet12', 'D101', '1', 'N/A'), + ('Ethernet20', 'D102', '1', 'N/A')]} + + """ + import apis.system.interface as intf_obj + sys_vars = st.ensure_min_topology(*min_req_topo) + topology = {} + service_string = ' -> '.join("Build topology dictionary") + for key in sys_vars.keys(): + port_info = re.match(r'D([\d+])([DT])([\d+])P([\d+])', key) + if port_info: + ifname = '' + dest_dut = '' + link_no = '' + intf_speed = '' + (src_dut_no, dst_dut_or_tg, dest_dut_no, link_no) = port_info.groups() + src_dut = 'D' + str(src_dut_no) + src_dut = sys_vars[src_dut] + dest_dut = dst_dut_or_tg+str(dest_dut_no) + if dst_dut_or_tg == 'D': + dest_dut = sys_vars[dest_dut] + + ifname = sys_vars[key] + if (not ifname) or (not ifname.startswith("Ethernet")): + st.error("'{}' is not a Valid Interface name.".format(ifname)) + st.report_env_fail("test_case_not_executed_s_service", service_string) + + intf_status = intf_obj.interface_status_show(src_dut, ifname) + if not intf_status: + st.error("'{}' Interface Speed not Available.".format(ifname)) + st.report_env_fail("test_case_not_executed_s_service", service_string) + intf_speed = intf_status[0]['speed'] + + topology.setdefault(src_dut, []).append((ifname, dest_dut, link_no, intf_speed)) + return topology + + +def remove_duplicates_from_list(params_list): + """ + Common function to remove duplicates from a list + Author: Chaitanya-vella.kumar@broadcom.com + :param params_list: + :return: + """ + if params_list: + return list(dict.fromkeys(params_list)) + return list() + + +def list_diff(list1, list2, identical=False): + """ + API to get the differece in 2 lists + :param list1: + :param list2: + :param identical: + :return: + """ + result = list() + for value in list1: + if identical: + if value in list2: + result.append(value) + else: + if value not in list2: + result.append(value) + return result + + +def get_random_vlans_in_sequence(count=1, start=2, end=3000): + while True: + vlan_list = random.sample([range(start, end)[x:x + count] for x in xrange(0, len(range(start, end)), count)], + k=1)[0] + if len(vlan_list) == count: + return vlan_list + + +def check_empty_values_in_dict(data): + """ + Common function to check the empty values in dictionary + :param data: + :return: + """ + if isinstance(data, dict): + count = 0 + for key, value in data.items(): + if not value: + st.debug("Getting empty value for key={} and value={}".format(key, value)) + count += 1 + if count != 0: + return False + else: + return True + return None + + +def remove_duplicate_dicts_from_list(list_data): + result = list() + if isinstance(list_data, list): + for i in range(len(list_data)): + if list_data[i] not in list_data[i+1:]: + result.append(list_data[i]) + return result + + +def get_interface_number_from_name(interface_name): + """ + Common function to get the interface number from name using for KLISH CLI + Author: Chaitanya-vella.kumar@broadcom.com + :param interface_name: + :return: + """ + if interface_name: + data = re.findall(r'\d+', interface_name) + if data: + if "Ethernet" in interface_name: + return {"type": "Ethernet", "number": data[0]} + elif "PortChannel" in interface_name: + return {"type": "PortChannel", "number": data[0].lstrip("0")} + elif "Vlan" in interface_name: + return {"type": "Vlan", "number": data[0]} + return interface_name + + +def get_dict_from_redis_cli(data): + """ + This will convert show_redis_cli_key_search.tmpl output to the dict(key value pair) + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param data: + :return: + + EX: + data = [{'id': '1', 'name': 'sample'}, {'id': '2', 'name': '100'}, {'id': '3', 'name': 'ipaddress-type'}, + {'id': '4', 'name': 'ipv4'}] + return output: {'ipaddress-type': 'ipv4', 'sample': '100'} + """ + id_list = dicts_list_values(data, 'id') + chunks = [id_list[x:x + 2] for x in xrange(0, len(id_list), 2)] + output = {filter_and_select(data, ['name'], {'id': each[0]})[0]['name']: + filter_and_select(data, ['name'], {'id': each[1]})[0]['name'] for each in chunks} + return output + + +def list_filter_and_select(data, filter_list): + """ + This will search all elements in data list w.r.t to filter list and return matched elements of data. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param data: list + :param filter_list: list + :return: + """ + result = [] + filter_li = make_list(filter_list) + st.debug("Filter List : {}".format(filter_li)) + for each in make_list(data): + if len([x for x in filter_li if re.search(x, each)]) == len(filter_li): + st.debug("Match Found : {}".format(each)) + result.append(each) + return result + + +def util_ip_addr_to_hexa_conv(ipaddr): + """ + This will convert IPv4/v6 address to hex format + :param ipaddr: + :return: + """ + return hexlify(inet_aton(ipaddr)).upper() + +def util_ipv6_addr_to_hexa_conv(ip6addr): + """ + This will convert IPv6 address to hex format + :param ipaddr: + :return: + """ + return hexlify(inet_pton(AF_INET6, ip6addr)).upper() + + +def util_int_to_hexa_conv(int_data, z_fill=4): + """ + This will convert data into hex format and append zero's + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param int_data: + :param z_fill: + :return: + """ + return (hex(int(int_data)))[2:].zfill(z_fill).upper() + + +def ensure_cli_type(cli_type, expected=list()): + if cli_type not in make_list(expected): + st.log("UNSUPPORTED CLI TYPE {} -- EXPECTING {}".format(cli_type, expected)) + return False + else: + return True + + +def hex2int(value, w=0): + """ + This will convert data into hex to int + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param value: + :param w: + :return: + """ + return int("0x{}".format(value), w) + + +def random_cli_type_wrt_day(cli_type_list=['click', 'klish']): + """ + Get random CLI type w.r.t the day. + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param cli_type_list: ['click', 'klish'] + :return: + """ + day = int(datetime.datetime.now().strftime("%d")) + num_days = range(1, monthrange(int(datetime.datetime.now().strftime("%Y")), + int(datetime.datetime.now().strftime("%m")))[1] + 1) + chunks = [num_days[x:x + len(cli_type_list)] for x in xrange(0, len(num_days), len(cli_type_list))] + for each in chunks: + if day in each: + return cli_type_list[each.index(day)] + +def fail_on_error(output): + """ + Common function to fail the test case when there is an error in command execution + :return: + """ + if "%Error:" in output or "% Error:" in output: + st.report_fail("test_case_failed") + + +def convert_ip_to_binary(ip): + octet_list_int = ip.split(".") + octet_list_bin = [format(int(i), '08b') for i in octet_list_int] + binary = ("").join(octet_list_bin) + return binary + + +def get_network_from_address(address, net_size): + ip_bin = convert_ip_to_binary(address) + network = ip_bin[0:32 - (32 - net_size)] + return network + + +def verify_ip_in_network(ip_address, prefix): + st.debug("IP ADDRESS : {}".format(ip_address)) + st.debug("NETWORK : {}".format(prefix)) + [prefix_address, net_size] = prefix.split("/") + net_size = int(net_size) + prefix_network = get_network_from_address(prefix_address, net_size) + ip_network = get_network_from_address(ip_address, net_size) + return ip_network == prefix_network + + +def get_word_count(dut, command, **kwargs): + """ + Author : Prudvi Mangadu (prudvi.mangadu@broadcom.com) + :param dut: + :param command: + :param kwargs: + :return: + """ + output = st.config(dut, command, **kwargs) + out = re.findall(r"^(\d+)", output) + return int(out[0]) if out else 0 + + +def verify_ip4_ip6_in_subnetwork(ip_address, subnetwork): + (ip_integer, version1) = ip4_ip6_to_integer(ip_address) + (ip_lower, ip_upper, version2) = subnetwork_to_ip4_ip6_range(subnetwork) + + if version1 != version2: + raise ValueError("incompatible IP versions") + return (ip_lower <= ip_integer <= ip_upper) + + +def ip4_ip6_to_integer(ip_address): + for version in (AF_INET, AF_INET6): + try: + ip_hex = inet_pton(version, ip_address) + ip_integer = int(hexlify(ip_hex), 16) + + return (ip_integer, 4 if version == AF_INET else 6) + except: + pass + raise ValueError("invalid IP address") + + +def subnetwork_to_ip4_ip6_range(subnetwork): + try: + fragments = subnetwork.split('/') + network_prefix = fragments[0] + netmask_len = int(fragments[1]) + for version in (AF_INET, AF_INET6): + + ip_len = 32 if version == AF_INET else 128 + try: + suffix_mask = (1 << (ip_len - netmask_len)) - 1 + netmask = ((1 << ip_len) - 1) - suffix_mask + ip_hex = inet_pton(version, network_prefix) + ip_lower = int(hexlify(ip_hex), 16) & netmask + ip_upper = ip_lower + suffix_mask + + return (ip_lower, + ip_upper, + 4 if version == AF_INET else 6) + except: + pass + except: + pass + raise ValueError("invalid subnetwork") + + +def bitwise_OR_to_char(char, val): + if len(char) != 1: + st.error('Error, char({}) len > 1.'.format(char)) + return char + + return str(int(char) | int(val)) + +