diff --git a/spytest/spytest/tgen/init.py b/spytest/spytest/tgen/init.py index 21a799cc52..93975aa223 100644 --- a/spytest/spytest/tgen/init.py +++ b/spytest/spytest/tgen/init.py @@ -7,6 +7,7 @@ tcl_custom_pkgdir = os.path.abspath(os.path.dirname(__file__)) py_version = platform.python_version() + def tg_stc_load(version, logger, logs_path=None): stc_version_map = {"4.67": "4.67", "4.91": "4.91"} @@ -71,8 +72,9 @@ def tg_stc_load(version, logger, logs_path=None): 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") + user_root = os.getenv("SPYTEST_USER_ROOT", os.path.abspath(".")) + os.environ['HLPYAPI_LOG'] = logs_path or user_root + os.environ['HOME'] = logs_path or user_root sys.path.insert(0, tcl_path) sys.path.insert(0, stc_hl_api_path) @@ -86,12 +88,13 @@ def tg_ixia_load(version, logger, logs_path=None): "8.4": "8.40", "8.42": "8.42", "9.0": "9.00", "9.00": "9.00", "9.1": "9.10", "9.10": "9.10", - "9.20": "9.20", "9.24": "9.24"} + "9.2": "9.20", "9.20": "9.20", + "9.24": "9.24", "9.31": "9.31"} version_string = str(version) version_string = ixia_version_map.get(version_string, version_string) - if (version_string not in ixia_version_map and - not os.path.exists(os.path.join(tgen_path, "ixia")) and - not os.path.exists(os.path.join(tgen_path, version_string)) ): + if (version_string not in ixia_version_map + and not os.path.exists(os.path.join(tgen_path, "ixia")) + and not os.path.exists(os.path.join(tgen_path, version_string))): logger.error("IXIA: unsupported version {}".format(version_string)) return None @@ -103,18 +106,21 @@ def tg_ixia_load(version, logger, logs_path=None): '9.10': 'HLTSET237', '9.20': 'HLTSET249', '9.24': 'HLTSET251', + '9.31': 'HLTSET261', } ix_path = '' if os.path.exists(os.path.join(tgen_path, version_string)) else "ixia" ixnetwork = os.path.join(tgen_path, ix_path, version_string, "lib") - hl_api = os.path.join(ixnetwork, "hltapi" if os.path.exists(os.path.join(ixnetwork,"hltapi")) else "hlapi", "library") + hl_api = os.path.join(ixnetwork, "hltapi" if os.path.exists(os.path.join(ixnetwork, "hltapi")) else "hlapi", "library") + tcl_path = os.getenv("SCID_TCL85_BIN", def_tcl_path) + tcl_lib_path = os.path.join(tcl_path, "..", "lib") if os.path.exists(ixnetwork) and os.path.exists(hl_api): ngpf_api = os.path.join(hl_api, "common", "ixiangpf", "python") ixn_py_api = os.path.join(ixnetwork, "PythonApi") if version_string in ixia_hltapi_map: os.environ["IXIA_VERSION"] = ixia_hltapi_map[version_string] os.environ["IXIA_HOME"] = ixnetwork - os.environ["TCLLIBPATH"] = str(ixnetwork) + os.environ["TCLLIBPATH"] = " ".join([str(ixnetwork), hl_api, os.path.join(ixnetwork, "TclApi", "IxTclNetwork"), tcl_lib_path]) sys.path.append(ngpf_api) sys.path.append(ixn_py_api) @@ -122,21 +128,17 @@ def tg_ixia_load(version, logger, logs_path=None): return version_string # 9.0 onwards for BRCM - 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-" + py_version) - - ixnetwork_version = os.getenv("IXNETWORK_VERSION", version_string) - hltapi_version = os.getenv("HLAPI_VERSION", version_string) - if not os.path.exists(ixia_root): - ixia_root = os.path.join(tgen_path, "ixia") - hlt_api = os.path.join(ixia_root, "hlapi", hltapi_version) + 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", ixnetwork_version,"lib", "PythonApi") - ixn_tcl_api_1 = os.path.join(ixia_root, "ixnetwork", ixnetwork_version,"lib", "IxTclNetwork") - ixn_tcl_api_2 = os.path.join(ixia_root, "ixnetwork", ixnetwork_version,"lib", "TclApi", "IxTclNetwork") - + 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) @@ -144,6 +146,6 @@ def tg_ixia_load(version, logger, logs_path=None): return version_string + def tg_scapy_load(version, logger, logs_path=None): return version - diff --git a/spytest/spytest/tgen/scapy/afpacket.py b/spytest/spytest/tgen/scapy/afpacket.py index dbffd4247b..cc67b0587f 100755 --- a/spytest/spytest/tgen/scapy/afpacket.py +++ b/spytest/spytest/tgen/scapy/afpacket.py @@ -30,12 +30,14 @@ 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), @@ -47,6 +49,7 @@ class struct_msghdr(Structure): ("msg_flags", c_int), ] + class struct_cmsghdr(Structure): _fields_ = [ ("cmsg_len", c_size_t), @@ -54,6 +57,7 @@ class struct_cmsghdr(Structure): ("cmsg_type", c_int), ] + class struct_tpacket_auxdata(Structure): _fields_ = [ ("tp_status", c_uint), @@ -65,11 +69,13 @@ class struct_tpacket_auxdata(Structure): ("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 @@ -78,6 +84,7 @@ def enable_auxdata(sk): """ sk.setsockopt(SOL_PACKET, PACKET_AUXDATA, 1) + def recv(sk, bufsize): """ Receive a packet from an AF_PACKET socket @@ -112,11 +119,11 @@ def recv(sk, bufsize): # only control message. assert msghdr.msg_controllen >= sizeof(struct_cmsghdr) - cmsghdr = struct_cmsghdr.from_buffer(ctrl_buf) # pylint: disable=E1101 + 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 + 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 diff --git a/spytest/spytest/tgen/scapy/bgp.py b/spytest/spytest/tgen/scapy/bgp.py deleted file mode 100755 index 4ae0966616..0000000000 --- a/spytest/spytest/tgen/scapy/bgp.py +++ /dev/null @@ -1,162 +0,0 @@ -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() - 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/bgp_exabgp.py b/spytest/spytest/tgen/scapy/bgp_exabgp.py new file mode 100755 index 0000000000..5df469db40 --- /dev/null +++ b/spytest/spytest/tgen/scapy/bgp_exabgp.py @@ -0,0 +1,245 @@ +import os +import time +import textwrap + +this_dir = os.path.join(os.path.dirname(__file__)) +python = "/usr/bin/python" + + +def read_version(): + try: + import exabgp.version + return exabgp.version.version + except Exception as e: + print(str(e)) + return "3.4.17" + + +class ExaBgp(object): + + def __init__(self, pif): + self.version = read_version() + self.pif = pif + self.logger = pif.logger + self.utils = pif.utils + self.nslist = [] + self.cleanup() + + def __del__(self): + self.cleanup() + + def cleanup(self): + self.stop() + + def _file(self, ns, extn, backup=False): + return self.logger.mkfile("exabgpd", ns, extn, backup) + + def stop(self): + if self.pif.dbg > 3: + self.pif.os_system("ps -ef") + for ns in self.nslist[:]: + self.stop_one(ns) + root = self.logger.get_logs_path() + for pidfile in self.utils.list_files(root, "exabgpd_*.pid"): + self.pif.kill_by_pidfile(pidfile) + + def stop_one(self, ns): + logfile = self._file(ns, "log") + pidfile = self._file(ns, "pid") + self.pif.log_large_file(logfile) + self.logger.info(self.utils.cat_file(pidfile)) + self.pif.kill_by_pidfile(pidfile, ns) + if ns in self.nslist: + self.nslist.remove(ns) + return True + + def config_one(self, enable, intf, index=0): + ns = "{}_{}".format(intf.name, index) + if not enable: + self.stop_one(ns) + return True + self.nslist.append(ns) + + logfile = self._file(ns, "log", True) + pidfile = self._file(ns, "pid", True) + envfile = self._file(ns, "env", True) + cfgfile = self._file(ns, "cfg", True) + + ipv6_intf_addr = intf.kws.get("ipv6_intf_addr", "") + if ipv6_intf_addr: + family = "ipv6" + intf_ip_addr = ipv6_intf_addr + else: + family = "ipv4" + intf_ip_addr = intf.kws.get("intf_ip_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) + # enable_4_byte_as = self.utils.intval(intf.bgp_kws, "enable_4_byte_as", 0) + # ip_version = self.utils.intval(intf.bgp_kws, "ip_version", 4) + + # create route config + cmdfile, ip_cmdfile = self.config_route(enable, intf, index) + + # build router id from ns + router_id = ns.replace("_", ".") + ".0" + + cmds_30 = textwrap.dedent(""" + group exabgp {{ + neighbor {2} {{ + router-id {6}; + local-address {0}; + peer-as {3}; + local-as {1}; + auto-flush false; + group-updates true; + family {{ + {8} unicast; + }} + process announce-routes {{ + run {7} {4}/exabgp_routes.py {5}; + }} + }} + }} + """.format(intf_ip_addr, local_as, remote_ip_addr, remote_as, this_dir, cmdfile, router_id, python, family)) + + cmds_40 = textwrap.dedent(""" + process announce-routes {{ + run {7} {4}/exabgp_routes.py {5}; + encoder json; + }} + neighbor {2} {{ + router-id {6}; + local-address {0}; + peer-as {3}; + local-as {1}; + auto-flush false; + group-updates true; + family {{ + {8} unicast; + }} + api {{ + processes [ announce-routes ]; + }} + }} + """.format(intf_ip_addr, local_as, remote_ip_addr, remote_as, this_dir, cmdfile, router_id, python, family)) + + if self.version.startswith("4"): + self.utils.fwrite(cmds_40, cfgfile) + else: + self.utils.fwrite(cmds_30, cfgfile) + + cmds = textwrap.dedent(""" + [exabgp.api] + pipename = '{0}' + cli = false + + [exabgp.daemon] + pid = '{1}' + daemonize = true + drop = false + user = root + + [exabgp.log] + all = true + message = true + network = true + packets = true + parser = true + rib = true + routes = true + timers = true + level = DEBUG + destination = '{2}' + """.format(ns, pidfile, logfile)) + self.utils.fwrite(cmds, envfile) + + cmds = textwrap.dedent(""" + set -x + #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) + + self.utils.nsexec(ns, "bash {}".format(sh_file)) + + # self.utils.nsexec(ns, "bash {}".format(ip_cmdfile)) + self.utils.unused(ip_cmdfile) + + self.logger.info(self.utils.cat_file(envfile)) + self.logger.info(self.utils.cat_file(cfgfile)) + self.pif.log_large_file(cmdfile) + time.sleep(5) + self.logger.info(self.utils.cat_file(pidfile)) + self.pif.log_large_file(logfile) + + self.logger.register_log(logfile) + + return True + + def config_route(self, enable, intf, index=0): + ns = "{}_{}".format(intf.name, index) + cmdfile = self._file(ns, "cmd") + ip_cmdfile = self._file(ns, "ip_cmd") + cmds, ip_cmds = [], [] + + for br in intf.bgp_routes.values(): + if not br.enable: + continue + self.logger.dump("BGP ROUTE", br) + as_path = br.kws.get("as_path", None) + as_seq = None + if as_path and "as_seq:" in as_path: + try: + as_seq = int(as_path.replace("as_path:", "")) + except Exception: + as_seq = None + + num_routes = self.utils.intval(br.kws, "num_routes", 0) + prefix = br.kws.get("prefix", "") + if not prefix and num_routes > 0: + msg = "Prefix not specified num_routes={}".format(num_routes) + self.pif.error(msg) + else: + for _ in range(num_routes): + remote_ipv6_addr = intf.bgp_kws.get("remote_ipv6_addr", "") + if remote_ipv6_addr: + cmd = "announce route {}/128 next-hop self".format(prefix) + ip_cmd = "ip -6 addr add {}/128 dev veth1".format(prefix) + prefix = self.utils.incrementIPv6(prefix, "0:0:0:1::") + else: + cmd = "announce route {}/24 next-hop self".format(prefix) + ip_cmd = "ip addr add {}/24 dev veth1".format(prefix) + prefix = self.utils.incrementIPv4(prefix, "0.0.1.0") + # append as-path sequence + if as_seq: + cmd = cmd + "as-path [{}]".format(as_seq) + cmds.append(cmd) + ip_cmds.append(ip_cmd) + self.utils.fwrite("\n".join(cmds), cmdfile) + self.utils.fwrite("\n".join(ip_cmds), ip_cmdfile) + ############################# + # TODO: batch routes + # announce attribute next-hop self nlri 100.10.0.0/16 100.20.0.0/16 + ############################# + return cmdfile, ip_cmdfile + + def control(self, op, intf): + retval = self.config_one(False, intf) + if op not in ["disable", "stop"]: + retval = self.config_one(True, intf) + return retval + + def control_route(self, op, route): + route.enable = bool(op != "remove") + return self.control(op, route.intf) + + +if __name__ == "__main__": + print(read_version()) diff --git a/spytest/spytest/tgen/scapy/dhcps.py b/spytest/spytest/tgen/scapy/dhcps.py new file mode 100755 index 0000000000..abfed75a84 --- /dev/null +++ b/spytest/spytest/tgen/scapy/dhcps.py @@ -0,0 +1,62 @@ + +from emulation import Emulation + + +class Dhcps(Emulation): + + def __init__(self, pif): + super(Dhcps, self).__init__(pif, "dhcps") + + def control(self, server, intf, **kws): + mode = kws.get("mode", "reset") + action = kws.get("action", mode) + ip_version = self.utils.intval(kws, "ip_version", 4) + ns = "{}_{}".format(intf.name, 0) + + if server.dhcp_relay_agents: + ent = list(server.dhcp_relay_agents.values())[0] + start = ent.kws.get("relay_agent_ipaddress_pool", "0.0.0.0") + step = ent.kws.get("relay_agent_ipaddress_step", "0.0.0.0") + count = self.utils.intval(ent.kws, "relay_agent_ipaddress_count", 1) + elif ip_version == 6: + start = server.kws.get("addr_pool_start_addr", "2000::1") + start = server.kws.get("ipaddress_pool", start) + step = server.kws.get("step", "::1") + count = self.utils.intval(server.kws, "addr_pool_addresses_per_server", 1) + count = self.utils.intval(server.kws, "ipaddress_count", count) + else: + start = server.kws.get("ipaddress_pool", "0.0.0.0") + step = server.kws.get("ipaddress_step", "0.0.0.1") + count = self.utils.intval(server.kws, "ipaddress_count", 1) + + end = start + for _ in range(count): + if ip_version == 6: + end = self.utils.incrementIPv6(end, step) + else: + end = self.utils.incrementIPv4(end, step) + + # kill existing server if any + # pidfile = self.logger.mkfile("dhcpd", ns, "pid") + # self.pif.kill_by_pidfile(pidfile, ns) + self.stop_one(ns) + + if action in ["delete", "reset"]: + return True + + logfile = self._file(ns, "log", True) + pidfile = self._file(ns, "pid", True) + + # start dhcpd + cmd = "dnsmasq -i veth1 -p0" + cmd = "{} --dhcp-range={},{}".format(cmd, start, end) + cmd = "{} --pid-file={}".format(cmd, pidfile) + cmd = "{} --log-queries".format(cmd) + cmd = "{} --log-dhcp".format(cmd) + cmd = "{} --log-facility={}".format(cmd, logfile) + output = self.utils.nsexec(ns, cmd) + self.logger.debug("{} -- {}".format(cmd, output)) + if "dnsmasq: bad command line options" in output: + return False + self.logger.register_log(logfile) + return True diff --git a/spytest/spytest/tgen/scapy/dicts.py b/spytest/spytest/tgen/scapy/dicts.py index c98e4d3f17..2bcb680498 100644 --- a/spytest/spytest/tgen/scapy/dicts.py +++ b/spytest/spytest/tgen/scapy/dicts.py @@ -1,9 +1,11 @@ from collections import OrderedDict + class SpyTestDict(OrderedDict): """ todo: Update Documentation """ + def __getattr__(self, name): try: return self[name] diff --git a/spytest/spytest/tgen/scapy/dot1x.py b/spytest/spytest/tgen/scapy/dot1x.py new file mode 100755 index 0000000000..85920f0cfb --- /dev/null +++ b/spytest/spytest/tgen/scapy/dot1x.py @@ -0,0 +1,61 @@ +import time +import textwrap + +from emulation import Emulation + + +class Dot1x(Emulation): + + def __init__(self, pif): + super(Dot1x, self).__init__(pif, "dot1x") + + def config_one(self, enable, client, index=0): + intf = client.intf + ns = "{}_{}".format(intf.name, index) + if not enable: + self.stop_one(ns) + return True + self.nslist.append(ns) + + logfile = self._file(ns, "log", True) + pidfile = self._file(ns, "pid", True) + cfgfile = self._file(ns, "cfg", True) + + cmds = textwrap.dedent(""" + ctrl_interface=/var/run/wpa_supplicant-{0} + ctrl_interface_group=0 + eapol_version=2 + ap_scan=0 + network={{ + key_mgmt=IEEE8021X + eap=TTLS MD5 + identity="{1}" + anonymous_identity="{1}" + password="{2}" + phase1="auth=MD5" + phase2="auth=PAP password={2}" + eapol_flags=0 + }} + """.format(ns, client.get("username"), client.get("password"))) + self.utils.fwrite(cmds, cfgfile) + + cmds = textwrap.dedent(""" + set -x + wpa_supplicant -c {0} -B -P {1} -D wired -i {2} -f {3} -dddd + """.format(cfgfile, pidfile, "veth1", logfile)) + + sh_file = self.utils.fwrite(cmds) + self.utils.nsexec(ns, "bash {}".format(sh_file)) + + self.logger.info(self.utils.cat_file(cfgfile)) + time.sleep(5) + self.logger.info(self.utils.cat_file(pidfile)) + self.pif.log_large_file(logfile) + + return True + + def control(self, mode, client): + retval = self.config_one(False, client) + if mode not in ["disable", "stop", "logoff"]: + retval = self.config_one(True, client) + return retval diff --git a/spytest/spytest/tgen/scapy/driver.py b/spytest/spytest/tgen/scapy/driver.py index b645cc9458..8674b0cd34 100755 --- a/spytest/spytest/tgen/scapy/driver.py +++ b/spytest/spytest/tgen/scapy/driver.py @@ -7,17 +7,20 @@ from or_event import OrEvent from utils import Utils from logger import Logger +from lock import Lock -def isLinkUp(intf, dbg = False): + +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 dbg: + os.system("ifconfig %s" % (intf)) + with open(flags_path, 'r') as fp: if fp.read().strip() != 'down': - return True + return True return False + class ScapyDriver(object): def __init__(self, port, dry=False, dbg=0, logger=None): self.port = port @@ -27,6 +30,7 @@ def __init__(self, port, dry=False, dbg=0, logger=None): self.finished = False self.logger = logger or Logger() self.utils = Utils(self.dry, logger=self.logger) + self.lock = Lock() self.iface = port.iface self.iface_status = None self.packet = ScapyPacket(port.iface, dry=self.dry, dbg=self.dbg, @@ -67,9 +71,9 @@ def rxInit(self): self.rxThread = threading.Thread(target=self.rxThreadMain, args=()) self.rxThread.daemon = True self.rxThread.start() - #self.linkThread = threading.Thread(target=self.linkThreadMain, args=()) - #self.linkThread.daemon = True - #self.linkThread.start() + # self.linkThread = threading.Thread(target=self.linkThreadMain, args=()) + # self.linkThread.daemon = True + # self.linkThread.start() self.linkThread = None def captureQueueInit(self): @@ -97,9 +101,7 @@ 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])) + hex_bytes = ScapyPacket.hex_str(pkt).split() retval.append(hex_bytes) return retval @@ -110,7 +112,8 @@ 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())) + 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() @@ -118,9 +121,9 @@ def rxThreadMain(self): # read packets while self.rx_any_enable(): try: - packet = self.packet.readp(iface=self.iface) + packet = self.packet.readp(self.iface, self.port) if packet: - self.handle_recv(None, packet) + self.handle_recv(packet) except Exception as e: if str(e) != "[Errno 100] Network is down": self.logger.debug(e, traceback.format_exc()) @@ -154,12 +157,12 @@ def handle_stats(self, packet): if self.packet.match_stream(stream, packet): stream.incrStat('framesReceived') stream.incrStat('bytesReceived', pktlen) - break # no need to check in other streams + break # no need to check in other streams def handle_capture(self, packet): self.pkts_captured.append(packet) - def handle_recv(self, hdr, packet): + def handle_recv(self, packet): if self.statState.is_set(): self.handle_stats(packet) if self.captureState.is_set(): @@ -179,13 +182,15 @@ def set_stream_enable2(self, handle, value, duration, msg): for stream_id, stream in self.port.streams.items(): if not handle: stream.enable2 = value - if value: self.stream_pkts[stream_id] = 0 + if value: + self.stream_pkts[stream_id] = 0 self.logger.debug("{}-all: {} {} PKTS: {}".format( msg, self.iface, stream_id, self.stream_pkts[stream_id])) elif stream_id == handle: requested_stream = stream stream.enable2 = value - if value: self.stream_pkts[stream_id] = 0 + if value: + self.stream_pkts[stream_id] = 0 self.logger.debug("{}: {} {} PKTS: {}".format( msg, self.iface, stream_id, self.stream_pkts[stream_id])) if duration > 0: @@ -195,21 +200,27 @@ def set_stream_enable2(self, handle, value, duration, msg): return requested_stream def stop_ack_wait(self, handle): + self.lock.acquire() if handle in self.txStateAck: self.txStateAck[handle].set() + self.lock.release() def start_ack_wait(self, handle, wait=0): + clear = bool(wait <= 0) + + self.lock.acquire() if handle not in self.txStateAck: self.txStateAck[handle] = threading.Event() - self.txStateAck[handle].clear() - elif wait <= 0: - self.txStateAck[handle].clear() + clear = True + ev = self.txStateAck[handle] + self.lock.release() - if wait: - self.txStateAck[handle].wait(wait) + if clear: + ev.clear() + return ev.wait(wait) if wait else True def startTransmit(self, **kws): - self.logger.debug("start-tx: {} {}".format(self.iface, kws)) + self.dbg_tx("start-tx", **kws) # enable selected streams handle = kws.get('handle', None) @@ -217,19 +228,68 @@ def startTransmit(self, **kws): self.set_stream_enable2(handle, True, duration, "tx-enable") # signal the start - self.logger.debug("signal-tx: {} {}".format(self.iface, kws)) + self.dbg_tx("signal-tx", **kws) self.start_ack_wait(handle, 0) - threading.Timer(1.0, self.txState.set).start() - #self.txState.set() + self.enable_tx() + + def set_tx_state(self): + self.lock.acquire() + self.txState.set() + self.lock.release() + + def clear_tx_state(self): + self.lock.acquire() + self.txState.clear() + self.lock.release() + + def get_tx_state(self): + self.lock.acquire() + rv = self.txState.is_set() + self.lock.release() + return rv + + def enable_tx(self, packet=None): + threading.Timer(1.0, self.set_tx_state).start() + + def dbg_tx(self, prefix, **kws): + stats = self.port.getStats() + val = stats.get("framesSent", -1) + msg = "{}: {} {} TX: {}".format(prefix, self.iface, kws, val) + if kws.get("err", False): + self.logger.error(msg) + else: + self.logger.debug(msg) + return msg + + def find_stream(self, handle): + for stream_id, stream in self.port.streams.items(): + if stream_id == handle: + return stream + return None def startTransmitComplete(self, **kws): # wait for first packet to be sent handle = kws.get('handle', None) - self.logger.debug("start-tx-ack-0: {} {}".format(self.iface, kws)) - self.start_ack_wait(handle, 10) - self.logger.debug("start-tx-ack-1: {} {}".format(self.iface, kws)) + start_tx_state = self.get_tx_state() + self.dbg_tx("start-tx-ack-0", tx_state=start_tx_state, **kws) + for _ in range(5): + rv = self.start_ack_wait(handle, 10) + if rv: + self.dbg_tx("start-tx-ack-1", **kws) + break + stream = self.find_stream(handle) + if stream: + self.dbg_tx("start-tx-ack-1", err=True, + start_tx_state=start_tx_state, + tx_state=self.get_tx_state(), + enable=stream.enable, + enable2=stream.enable2, **kws) + else: + self.dbg_tx("start-tx-ack-1", err=True, + start_tx_state=start_tx_state, + tx_state=self.get_tx_state(), **kws) # check if all streams are non-contineous duration = self.utils.intval(kws, 'duration', 0) @@ -242,7 +302,7 @@ def startTransmitComplete(self, **kws): # wait for max 30 seconds to finish ???? for _ in range(30): time.sleep(1) - if not self.txState.is_set(): + if not self.get_tx_state(): self.logger.debug("TX Completed waiting 3 sec for RX") time.sleep(3) break @@ -250,12 +310,13 @@ def startTransmitComplete(self, **kws): self.logger.debug("waiting for duration: {}".format(duration)) time.sleep(duration) self.set_stream_enable2(handle, False, duration, "tx-disable") - if not handle: self.txState.clear() + if not handle: + self.clear_tx_state() else: self.logger.debug("waiting 3 sec") time.sleep(3) - self.logger.debug("start-tx-finished: {} {}".format(self.iface, kws)) + self.dbg_tx("start-tx-finished", **kws) def stopTransmit(self, **kws): @@ -267,13 +328,13 @@ def stopTransmit(self, **kws): stream.enable2 = False return - if not self.txState.is_set(): + if not self.get_tx_state(): return - self.logger.debug("stop-tx: {}".format(self.iface)) - self.txState.clear() + self.dbg_tx("stop-tx") + self.clear_tx_state() for _ in range(10): time.sleep(1) - if not self.txState.is_set(): + if not self.get_tx_state(): break def clear_stats(self): @@ -281,14 +342,14 @@ def clear_stats(self): def txThreadMain(self): while not self.finished: - while not self.txState.is_set(): + while not self.get_tx_state(): 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() + self.clear_tx_state() def txThreadMainInnerStart(self, pwa_list, sids): if self.dbg > 2: @@ -301,7 +362,19 @@ def txThreadMainInnerStart(self, pwa_list, sids): 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() + ########################################################## + # TODO CHECK IF WE NEED TO CLEAR STATS AUTOMATICALLY + ########################################################## + # pwa.stream.clearStat('bytesSent') + # old = pwa.stream.clearStat('framesSent') + # self.logger.debug(" clear framesSent {} {}".format(stream.stream_id, old)) + # for track_port in pwa.stream.track_ports: + # for track_stream in track_port.streams.values(): + # track_stream.clearStat('bytesReceived') + # old = track_stream.clearStat('framesReceived') + # self.logger.debug(" clear framesReceived {} {}".format(track_stream.stream_id, old)) + ########################################################## + pwa.tx_time = self.utils.clock() pwa_list.append(pwa) sids[stream.stream_id] = 0 self.stop_ack_wait(stream.stream_id) @@ -314,13 +387,14 @@ def txThreadMainInner(self): sids = {} pwa_list = [] - self.logger.debug("txThreadMainInner {} start {}".format(self.iface, self.port.streams.keys())) + func = "txThreadMainInner" + self.logger.debug("{} {} start {}".format(func, self.iface, self.port.streams.keys())) if not self.txThreadMainInnerStart(pwa_list, sids): - self.logger.debug("txThreadMainInner {} Nothing Todo".format(self.iface)) + self.logger.debug("{} {} nothing else to TX".format(func, self.iface)) return tx_count = 0 - while (self.txState.is_set()): + while (self.get_tx_state()): # call start again to see if new streams are created # while there are transmitting streams if not self.txThreadMainInnerStart(pwa_list, sids): @@ -335,10 +409,10 @@ def txThreadMainInner(self): continue self.pwa_wait(pwa) try: - send_start_time = time.clock() + send_start_time = self.utils.clock() pkt = self.send_packet(pwa, pwa.stream.stream_id) bytesSent = len(pkt) - send_time = time.clock() - send_start_time + send_time = self.utils.clock() - send_start_time # increment port counters framesSent = self.port.incrStat('framesSent') @@ -352,40 +426,41 @@ def txThreadMainInner(self): # increment stream counters stream_tx = self.stream_pkts[pwa.stream.stream_id] + 1 self.stream_pkts[pwa.stream.stream_id] = stream_tx - if self.dbg > 2 or (self.dbg > 1 and stream_tx%100 == 99): + if self.dbg > 2 or (self.dbg > 1 and stream_tx % 100 == 99): self.logger.debug("{}/{} framesSent: {}".format(self.iface, - pwa.stream.stream_id, stream_tx)) + pwa.stream.stream_id, stream_tx)) except Exception as e: self.logger.log_exception(e, traceback.format_exc()) pwa.stream.enable2 = False else: - build_start_time = time.clock() - pwa = self.packet.build_next(pwa) - if not pwa: continue - build_time = time.clock() - build_start_time - ipg = self.packet.build_ipg(pwa) - pwa.tx_time = time.clock() + ipg - build_time - send_time - pwa_next_list.append(pwa) + build_start_time = self.utils.clock() + pwa_next = self.packet.build_next(pwa) + if not pwa_next: + pwa.stream.enable2 = False + self.logger.debug("{} {} Completed Stream {}".format(func, self.iface, pwa.stream.stream_id)) + continue + build_time = self.utils.clock() - build_start_time + ipg = self.packet.build_ipg(pwa_next) + pwa_next.tx_time = self.utils.clock() + ipg - build_time - send_time + pwa_next_list.append(pwa_next) pwa_list = pwa_next_list - self.logger.debug("txThreadMainInner {} Completed {}".format(self.iface, tx_count)) + self.logger.debug("{} {} Completed {}".format(func, self.iface, tx_count)) def pwa_sort(self, pwa): return pwa.tx_time def pwa_wait(self, pwa): - delay = pwa.tx_time - time.clock() + delay = pwa.tx_time - self.utils.clock() if self.dbg > 2 or (self.dbg > 1 and pwa.left != 0): 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: + delay = 0 if delay < 0 else delay + if delay > 1.0 / 10: self.utils.msleep(delay * 1000, 10) - elif delay > 1.0/100: + elif delay > 1.0 / 100: self.utils.msleep(delay * 1000, 1) - elif delay > 1.0/200: + elif delay > 1.0 / 200: self.utils.usleep(delay * 1000 * 1000, 100) - elif delay > 1.0/500: + elif delay > 1.0 / 500: self.utils.usleep(delay * 1000 * 1000, 10) else: self.utils.usleep(delay * 1000 * 1000) @@ -396,8 +471,11 @@ def send_packet(self, pwa, stream_name): def createInterface(self, intf): return self.packet.if_create(intf) - def deleteInterface(self, intf): - return self.packet.if_delete(intf) + def deleteInterface(self, intf, exiting=False): + return self.packet.if_delete(intf, exiting) + + def validate_interface(self, intf): + return self.packet.if_validate(intf) def ping(self, intf, ping_dst, index=0): return self.packet.ping(intf, ping_dst, index) @@ -405,12 +483,26 @@ def ping(self, intf, ping_dst, index=0): def send_arp(self, intf, index=0): return self.packet.send_arp(intf, index) - def apply_bgp(self, op, enable, intf): - return self.packet.apply_bgp(op, enable, intf) + def control_bgp(self, op, intf): + return self.packet.control_bgp(op, intf) - def apply_bgp_route(self, enable, route): - return self.packet.apply_bgp_route(enable, route) + def control_bgp_route(self, op, route): + return self.packet.control_bgp_route(op, route) def config_igmp(self, mode, intf, host): return self.packet.config_igmp(mode, intf, host) + def control_igmp_querier(self, mode, intf, querier): + return self.packet.control_igmp_querier(mode, intf, querier) + + def control_ospf(self, mode, intf, session): + return self.packet.control_ospf(mode, intf, session) + + def control_dhcpc(self, group, port, **kwargs): + return self.packet.control_dhcpc(group, port, **kwargs) + + def control_dhcps(self, server, intf, **kwargs): + return self.packet.control_dhcps(server, intf, **kwargs) + + def control_dot1x(self, mode, client): + return self.packet.control_dot1x(mode, client) diff --git a/spytest/spytest/tgen/scapy/emulation.py b/spytest/spytest/tgen/scapy/emulation.py new file mode 100755 index 0000000000..ee585748c1 --- /dev/null +++ b/spytest/spytest/tgen/scapy/emulation.py @@ -0,0 +1,38 @@ + +class Emulation(object): + + def __init__(self, pif, name): + self.name = name + self.pif = pif + self.logger = pif.logger + self.utils = pif.utils + self.nslist = [] + self.cleanup() + + def __del__(self): + self.cleanup() + + def cleanup(self): + self.stop() + + def _file(self, ns, extn, backup=False): + return self.logger.mkfile(self.name, ns, extn, backup) + + def stop(self): + if self.pif.dbg > 3: + self.pif.os_system("ps -ef") + for ns in self.nslist[:]: + self.stop_one(ns) + root = self.logger.get_logs_path() + for pidfile in self.utils.list_files(root, "{}_*.pid".format(self.name)): + self.pif.kill_by_pidfile(pidfile) + + def stop_one(self, ns): + logfile = self._file(ns, "log") + pidfile = self._file(ns, "pid") + self.pif.log_large_file(logfile) + self.logger.info(self.utils.cat_file(pidfile)) + self.pif.kill_by_pidfile(pidfile, ns) + if ns in self.nslist: + self.nslist.remove(ns) + return True diff --git a/spytest/spytest/tgen/scapy/exabgp_http_api.py b/spytest/spytest/tgen/scapy/exabgp_http_api.py index fd719b28c8..9067e2a4da 100755 --- a/spytest/spytest/tgen/scapy/exabgp_http_api.py +++ b/spytest/spytest/tgen/scapy/exabgp_http_api.py @@ -1,16 +1,18 @@ -from flask import Flask, request +from flask import Flask, request # pylint: disable=import-error import sys app = Flask(__name__) # Setup a command route to listen for prefix advertisements + + @app.route('/', methods=['POST']) def run_command(): - command = request.form['command'] - sys.stdout.write('%s\n' % command) + # nosemgrep-next-line + sys.stdout.write('%s\n' % request.form['command']) sys.stdout.flush() return 'OK\n' + if __name__ == '__main__': app.run(port=sys.argv[1]) - diff --git a/spytest/spytest/tgen/scapy/exabgp_routes.py b/spytest/spytest/tgen/scapy/exabgp_routes.py index 1403daaf5f..e2dd39a215 100755 --- a/spytest/spytest/tgen/scapy/exabgp_routes.py +++ b/spytest/spytest/tgen/scapy/exabgp_routes.py @@ -1,25 +1,52 @@ #!/usr/bin/env python +import os import sys import time -cmdfile=sys.argv[1] +inc_file = sys.argv[1] if len(sys.argv) > 1 else None +exc_file = sys.argv[2] if len(sys.argv) > 2 else None + def read_lines(filepath): + if not filepath or not os.path.exists(filepath): + return [] fh = open(filepath, 'r') data = fh.readlines() fh.close() data = map(str.strip, data) return data -messages = read_lines(cmdfile) -time.sleep(2) -for index, message in enumerate(messages): - sys.stdout.write( message + '\n') - sys.stdout.flush() - if index % 10 == 0: - time.sleep(.1) +def send_msgs(msgs): + for index, msg in enumerate(msgs): + sys.stdout.write(msg + '\n') + sys.stdout.flush() + if index % 10 == 0: + time.sleep(.1) + + +def wait_for_changes(): + import pyinotify + while True: + class ModHandler(pyinotify.ProcessEvent): + # evt has useful properties, including pathname + def process_default(self, event): + pass + + handler = ModHandler() + wm = pyinotify.WatchManager() + notifier = pyinotify.Notifier(wm, handler) + mask = pyinotify.ALL_EVENTS + wm.add_watch(inc_file, mask) + wm.add_watch(exc_file, mask) + notifier.loop() + + +time.sleep(2) +send_msgs(read_lines(inc_file)) +send_msgs(read_lines(exc_file)) while True: + # wait_for_changes() time.sleep(1) diff --git a/spytest/spytest/tgen/scapy/funcs.py b/spytest/spytest/tgen/scapy/funcs.py new file mode 100755 index 0000000000..b054c82bc9 --- /dev/null +++ b/spytest/spytest/tgen/scapy/funcs.py @@ -0,0 +1,9 @@ + +def read_lines(filename, default=None): + try: + fh = open(filename, 'r') + data = fh.readlines() + fh.close() + return map(str.strip, data) + except Exception: + return default diff --git a/spytest/spytest/tgen/scapy/interface.py b/spytest/spytest/tgen/scapy/interface.py new file mode 100755 index 0000000000..f938e7ac5c --- /dev/null +++ b/spytest/spytest/tgen/scapy/interface.py @@ -0,0 +1,402 @@ +import sys +import textwrap +import traceback +import ipaddress + +try: + pass +except Exception as exp: + print(exp) +from scapy.config import Conf +from utils import Utils + +try: + print("SCAPY VERSION = {}".format(Conf().version)) +except Exception: + print("SCAPY VERSION = UNKNOWN") + +if sys.version_info[0] >= 3: + unicode = str + + +class PacketInterface(object): + + def __init__(self, pif): + self.pif = pif + self.iface = pif.iface + self.dbg = pif.dbg + self.logger = pif.logger + self.utils = pif.utils + + def __del__(self): + self.cleanup() + + def cleanup(self): + print("ScapyPacket {} cleanup...".format(self.iface)) + self.logger.info("ScapyPacket {} cleanup...".format(self.iface)) + + def if_delete_cmds(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)) + + return ns, cmds + + def if_create_cmds(self, index, intf, ip4addr, ip4gw, ip6addr, + ip6gw, smac, vlan_id2, **kws): + ns = "{}_{}".format(intf.name, index) + + vlan_enable = self.utils.intval(kws, "vlan", 0) + veth_name = "veth0" if vlan_enable else "veth1" + + begin, finish, verify = "", "", "" + + # remove existing + _, cmds = self.if_delete_cmds(index, intf) + begin += cmds + + # create the linux interface + begin += textwrap.dedent(""" + ip netns add ns_{0} + ip netns list + 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 + ip link add name {1}-br type bridge + ip link set dev veth_{0} master {1}-br + """.format(ns, intf.iface, veth_name)) + + if vlan_enable: + begin += 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_id2)) + + # set interface mac address + if smac != "00:00:00:00:00:00": + begin += "\nip netns exec ns_{0} ip link set veth1 address {1}".format(ns, smac) + + # assign IPv4 to linux interface + if ip4addr and ip4addr != "0.0.0.0": + begin += "\nip netns exec ns_{0} ip addr add {1}/{2} dev veth1".format(ns, ip4addr, 24) + if ip4gw: + begin += "\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) + begin += "\nip netns exec ns_{0} ip -6 addr add {1}/{2} dev veth1".format(ns, ip6addr, ip6prefix) + if ip6gw: + begin += "\nip netns exec ns_{0} ip -6 route add default via {1}".format(ns, ip6gw) + + _, b, f, v = self.if_arp_cmds(index, intf, ip4addr, ip4gw, ip6addr, ip6gw, **kws) + + return ns, begin + b, finish + f, verify + v + + def if_arp_cmds(self, index, intf, ip4addr, ip4gw, ip6addr, ip6gw, **kws): + ns = "{}_{}".format(intf.name, index) + + begin, finish, verify = "", "", "" + + # send Arp request + arp_send_req = self.utils.intval(kws, "arp_send_req", 1) + if arp_send_req: + if ip4gw: + finish += textwrap.dedent(""" + ip netns exec ns_{0} arping -w 7 -c 1 -I veth1 {1} + """.format(ns, ip4gw)) + verify += textwrap.dedent(""" + ip netns exec ns_{0} ip neigh show + """.format(ns)) + if ip6gw: + finish += textwrap.dedent(""" + ip netns exec ns_{0} ndisc6 -w 2000 {1} veth1 + """.format(ns, ip6gw)) + if ip6addr: + finish += textwrap.dedent(""" + ip netns exec ns_{0} ndisc6 -w 2000 -s {2} {1} veth1 + """.format(ns, ip6gw, ip6addr)) + verify += textwrap.dedent(""" + ip netns exec ns_{0} ip -6 neigh show + """.format(ns)) + + if self.dbg > 1: + verify += textwrap.dedent(""" + ip netns exec ns_{0} ifconfig veth1 + ip netns exec ns_{0} ip addr ls veth1 + """.format(ns)) + + return ns, begin, finish, verify + + def store_cmds(self, cmd_list, b, f, v): + cmd_list[0].append(b) + cmd_list[1].append(f) + cmd_list[2].append(v) + + def change_map(self, ns, is_add): + pass + + def shexec(self, cmds): + output = self.utils.lshexec(cmds) + if "Cannot open network namespace" in output: + self.logger.error(output) + self.utils.ns_debug(None, "Cannot open network namespace") + if self.dbg > 2: + self.logger.debug(output) + return output + + 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) + vlan_id = self.utils.intval(intf.kws, "vlan_id", 1) + vlan_id_count = self.utils.intval(intf.kws, "vlan_id_count", 0) + vlan_id_step = self.utils.intval(intf.kws, "vlan_id_step", 1) + max_count = self.utils.max_value(count, vlan_id_count) + + 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") + + ip6addr = intf.kws.get("ipv6_intf_addr", "") + ip6addr_step = intf.kws.get("ipv6_intf_addr_step", "::1") + ip6gw = intf.kws.get("ipv6_gateway", "") + ip6gw_step = intf.kws.get("ipv6_gateway_step", "::0") + + cmd_list = [[], [], []] + + # set dual stack IPv4 & IPv6 Address + if ip4addr and ip6addr: + for index in range(max_count): + ns, b, f, v = self.if_create_cmds(index, intf, ip4addr, ip4gw, + ip6addr, ip6gw, smac, vlan_id, **intf.kws) + self.store_cmds(cmd_list, b, f, v) + self.change_map(ns, True) + ip4addr = self.utils.incrementIPv4(ip4addr, ip4addr_step) + if ip4gw: + ip4gw = self.utils.incrementIPv4(ip4gw, ip4gw_step) + ip6addr = self.utils.incrementIPv6(ip6addr, ip6addr_step) + if ip6gw: + ip6gw = self.utils.incrementIPv6(ip6gw, ip6gw_step) + if smac != "00:00:00:00:00:00": + smac = self.utils.incrementMac(smac, "00:00:00:00:00:01") + vlan_id = vlan_id + vlan_id_step + # set IPv4 Address + elif ip4addr: + for index in range(max_count): + ns, b, f, v = self.if_create_cmds(index, intf, ip4addr, ip4gw, + None, None, smac, vlan_id, **intf.kws) + self.store_cmds(cmd_list, b, f, v) + self.change_map(ns, True) + ip4addr = self.utils.incrementIPv4(ip4addr, ip4addr_step) + if ip4gw: + 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") + vlan_id = vlan_id + vlan_id_step + # set IPv6 Address + elif ip6addr: + for index in range(max_count): + ns, b, f, v = self.if_create_cmds(index, intf, None, None, + ip6addr, ip6gw, smac, vlan_id, **intf.kws) + self.store_cmds(cmd_list, b, f, v) + self.change_map(ns, True) + ip6addr = self.utils.incrementIPv6(ip6addr, ip6addr_step) + if ip6gw: + ip6gw = self.utils.incrementIPv6(ip6gw, ip6gw_step) + if smac != "00:00:00:00:00:00": + smac = self.utils.incrementMac(smac, "00:00:00:00:00:01") + vlan_id = vlan_id + vlan_id_step + + # execute collected commands + cmds = cmd_list[0] + if cmd_list[1]: + cmds.append("sleep 2") + cmds.extend(cmd_list[1]) + cmds.extend(cmd_list[2]) + + output = self.shexec(cmds) + if "wrong: Device does not exist" in output: + msg = self.logger.error("Failed to create interface") + raise ValueError(msg) + + self.logger.info("read the mac addresses of created interfaces") + for index in range(max_count): + ns = "{}_{}".format(intf.name, index) + if_info, cmd, _ = self.utils.get_ip_addr_dev("veth1", ns) + self.logger.info(cmd, if_info) + mac_addr = if_info.get("link/ether", "00:00:00:00:00:00") + intf.mymac.append(mac_addr) + + def if_delete(self, intf): + count = self.utils.intval(intf.kws, "count", 1) + vlan_id_count = self.utils.intval(intf.kws, "vlan_id_count", 0) + max_count = self.utils.max_value(count, vlan_id_count) + cmd_list = [] + for index in range(max_count): + ns, cmds = self.if_delete_cmds(index, intf) + cmd_list.append(cmds) + self.change_map(ns, False) + self.shexec(cmd_list) + + def if_validate(self, intf): + ns = "{}_{}".format(intf.intf.name, 0) + self.utils.get_ip_addr_dev("veth1", ns) + return True + + def if_send_arp(self, intf): + smac = intf.kws.get("src_mac_addr", "00:00:00:00:00:00").replace(".", ":") + count = self.utils.intval(intf.kws, "count", 1) + vlan_id_count = self.utils.intval(intf.kws, "vlan_id_count", 0) + max_count = self.utils.max_value(count, vlan_id_count) + + # 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") + cmd_list = [[], [], []] + if ip4addr: + for index in range(max_count): + ns, b, f, v = self.if_arp_cmds(index, intf, ip4addr, ip4gw, None, None, **intf.kws) + self.store_cmds(cmd_list, b, f, v) + self.change_map(ns, True) + ip4addr = self.utils.incrementIPv4(ip4addr, ip4addr_step) + if ip4gw: + 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", "") + ip6addr_step = intf.kws.get("ipv6_intf_addr_step", "::1") + ip6gw = intf.kws.get("ipv6_gateway", "") + ip6gw_step = intf.kws.get("ipv6_gateway_step", "::0") + if ip6addr: + for index in range(max_count): + ns, b, f, v = self.if_arp_cmds(index, intf, None, None, ip6addr, ip6gw, **intf.kws) + self.store_cmds(cmd_list, b, f, v) + self.change_map(ns, True) + ip6addr = self.utils.incrementIPv6(ip6addr, ip6addr_step) + if ip6gw: + ip6gw = self.utils.incrementIPv6(ip6gw, ip6gw_step) + if smac != "00:00:00:00:00:00": + smac = self.utils.incrementMac(smac, "00:00:00:00:00:01") + + # execute collected commands + cmds = cmd_list[0] + if cmd_list[1]: + cmds.append("sleep 2") + cmds.extend(cmd_list[1]) + cmds.extend(cmd_list[2]) + self.shexec(cmds) + + def get_my_mac(self, intf, default="00:00:00:00:00:00"): + ns = "{}_{}".format(intf.name, 0) + cmd = "cat /sys/class/net/veth1/address" + output = self.utils.nsexec(ns, cmd).lower() + self.logger.debug("{} = {}".format(cmd, output)) + return output + + def get_arp_mac(self, intf, default, try_cache=True): + + try: + 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 + if ipv4gw != "0.0.0.0": + + # check in interface gateway mac cache + retval = intf.gwmac.get(ipv4gw, None) + if try_cache and retval: + return retval + + for _ in range(3): + retval = self.read_ipv4_gwmac(ns, ipv4gw, 1) + if retval: + return retval + + # try getting from arping output + cmd = "arping -w 7 -c 1 -I veth1 {}".format(ipv4gw) + output = self.utils.nsexec(ns, cmd).lower() + self.logger.debug("{} = \n {}".format(cmd, output)) + retval = Utils.parse_mac(output) + if retval: + return retval + + elif ipv6gw: + + # check in interface gateway mac cache + retval = intf.gwmac.get(ipv6gw, None) + if try_cache and retval: + return retval + + retval = self.read_ipv6_gwmac(ns, ipv6gw) + if retval: + return retval + + except Exception as exp: + self.logger.info(exp) + self.logger.info(traceback.format_exc()) + + return default + + def ping(self, intf, ping_dst, index=0): + ns = "{}_{}".format(intf.name, index) + cmd = "ping -c 1 {}".format(ping_dst) + try: + ip = ipaddress.ip_address(unicode(ping_dst)) + if ip._version == 6: + cmd = "ping6 -c 1 {}".format(ping_dst) + except Exception as exp: + self.pif.error(exp) + + # execute the command + return self.utils.nsexec(ns, cmd) + + def send_arp(self, intf, index=0): + ns = "{}_{}".format(intf.name, index) + ip4gw = intf.kws.get("gateway", "0.0.0.0") + ip6gw = intf.kws.get("ipv6_gateway", "") + if ip6gw: + cmd = "ndisc6 -w 2000 {} veth1".format(ip6gw) + else: + cmd = "arping -w 7 -c 1 -I veth1 {}".format(ip4gw) + rv = self.utils.nsexec(ns, cmd) + if ip6gw: + mac = self.read_ipv6_gwmac(ns, ip6gw) + intf.gwmac[ip6gw] = mac + else: + mac = Utils.parse_mac(rv.lower()) + intf.gwmac[ip4gw] = mac + self.logger.debug("send_arp {} {} {}".format(ip4gw, ip6gw, mac)) + return rv + + def read_ipv6_gwmac(self, ns, ipv6gw, max_try=3): + for i in range(max_try): + cmd = "ip -6 neigh show {}".format(ipv6gw) + output = self.utils.nsexec(ns, cmd).lower() + self.logger.debug("Try-{} {} = \n {}".format(i + 1, cmd, output)) + retval = Utils.parse_mac(output) + if retval: + return retval + return None + + def read_ipv4_gwmac(self, ns, ipv4gw, max_try=3): + for _ in range(max_try): + cmd = "arp -n {}".format(ipv4gw) + output = self.utils.nsexec(ns, cmd).lower() + self.logger.debug("{} = \n {}".format(cmd, output)) + retval = Utils.parse_mac(output) + if retval: + return retval + return None diff --git a/spytest/spytest/tgen/scapy/lock.py b/spytest/spytest/tgen/scapy/lock.py index b37a98ceeb..67fe6a2749 100755 --- a/spytest/spytest/tgen/scapy/lock.py +++ b/spytest/spytest/tgen/scapy/lock.py @@ -1,6 +1,7 @@ import time import threading + class Lock(object): def __init__(self): self.lock = threading.Lock() @@ -20,4 +21,3 @@ def acquire(self, block=True, timeout=None): def release(self): return self.lock.release() - diff --git a/spytest/spytest/tgen/scapy/logger.py b/spytest/spytest/tgen/scapy/logger.py index 085a925af2..729afcd3c5 100755 --- a/spytest/spytest/tgen/scapy/logger.py +++ b/spytest/spytest/tgen/scapy/logger.py @@ -7,36 +7,43 @@ import threading from lock import Lock +from funcs import read_lines + def time_delta(elapsed): seconds = elapsed.total_seconds() - msec = elapsed.microseconds/1000 + 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) + def get_log_lvl_name(lvl): - lvl_map = {"INFO" : "INFO ", "WARNING": "WARN "} + lvl_map = {"INFO": "INFO ", "WARNING": "WARN "} if lvl in lvl_map: return lvl_map[lvl] return lvl + 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 Exception: pass + except Exception: + pass try: num = int(name.replace("Pyro-Worker-", "")) name = "P%04d: " % (num) name = "T%04d: " % (0) - except Exception: pass + except Exception: + pass return name + class LogFormatter(object): def __init__(self): @@ -59,11 +66,13 @@ def format(self, record): prefix = "{} {}{}".format(time_stamp, thid, lvl) return "{} {}".format(prefix, msg) + class Logger(object): - def __init__(self, dry=False, name="scapy-tgen", logs_dir = None): + def __init__(self, dry=False, name="scapy-tgen", logs_dir=None): self.dry = dry self.dbg = 1 self.log_file = None + self.addl_logs = {} self.node_name = "" self.lock = Lock() self.fmt = LogFormatter() @@ -77,11 +86,13 @@ def __init__(self, dry=False, name="scapy-tgen", logs_dir = None): if not self.dry: self.remove_stdout() - #self.create_combined_log() + + self.all_file_handler = None + self.create_combined_log() self.log_file = None self.file_handler = None - self.set_log_file(None) + self.set_log_file("init.log") def remove_stdout(self): stdlog = logging.StreamHandler(sys.stdout) @@ -97,20 +108,33 @@ def ensure_parent(self, log_file): from utils import Utils Utils.ensure_parent(log_file) + def ensure_folder(self, log_file): + from utils import Utils + Utils.ensure_folder(log_file) + def create_combined_log(self): log_file = os.path.join(self.logs_dir2, "all.log") self.ensure_parent(log_file) - self.file_handler = logging.FileHandler(log_file) - self.file_handler.setFormatter(self.fmt) - self.file_handler.setLevel(logging.DEBUG) - self.logger.addHandler(self.file_handler) + self.all_file_handler = logging.FileHandler(log_file) + self.all_file_handler.setFormatter(self.fmt) + self.all_file_handler.setLevel(logging.DEBUG) + self.logger.addHandler(self.all_file_handler) + + def create_link(self, name): + self.logs_dir2 = os.path.join(self.logs_dir, name) + cmd = "cd {0}/..; rm -f {1}; mkdir -p {2}; ln -s {3}/{1} {1}" + cmd = cmd.format(self.logs_dir, name, self.logs_dir2, os.path.basename(self.logs_dir)) + os.system(cmd) + return self.logs_dir2 def set_node_name(self, name, msg=""): self.banner("set_node_name: {} current '{}' new '{}'".format(msg, self.node_name, name)) self.node_name = name if self.fmt: self.fmt.node_name = name - self.logs_dir2 = os.path.join(self.logs_dir, name) + self.create_link("current") + if name: + self.create_link(name) def set_log_file(self, log_file): @@ -133,12 +157,14 @@ def set_log_file(self, log_file): self.log_file = log_file if self.log_file: + pyver = "{}.{}.{}".format(sys.version_info.major, sys.version_info.minor, + sys.version_info.micro) + self.banner("Python: {}".format(pyver)) self.banner("Open file {}".format(self.log_file)) return self.log_file - - def get_log(self, filename=None): + def get_log_path(self, filename=None): if not filename: filename2 = self.log_file elif not os.path.exists(filename) and self.logs_dir2: @@ -149,24 +175,77 @@ def get_log(self, filename=None): filename2 = None if not filename2: - return self.error("Failed to find log file {}".format(filename)) + return self.error("Failed to find file {}".format(filename)) - try: - fh = open(filename2, 'r') - data = fh.readlines() - fh.close() - data = map(str.strip, data) - return "\n".join(data) - except Exception: - return self.error("Failed to read log file {}".format(filename2)) + return filename2 + + def get_log(self, filename=None): + filename2 = self.get_log_path(filename) + if not filename2: + return None + + lines = read_lines(filename2, None) + if lines is None: + lines = [self.error("Failed to read log file {}".format(filename2))] + + for filename2 in self.addl_logs.get(filename, []): + lines.append("=============== {} ============".format(filename2)) + lines = read_lines(filename2, None) + if lines is None: + lines.append(self.error("Failed to read log file {}".format(filename2))) + + return "\n".join(lines) + + def get_pcap(self, filename=None): + filename2 = self.get_log_path(filename) + if not filename2: + return None + + if not filename2.endswith(".pcap"): + filename2 = "{}.pcap".format(filename2) + + lines = read_lines(filename2, None) + if lines is None: + lines = [self.error("Failed to read pcap file {}".format(filename2))] + + return "\n".join(lines) + + def write_pcap(self, pkt, append=True, filename=None): + filename = filename or self.get_log_path() + if not filename: + return False + if not filename.endswith(".pcap"): + filename = "{}.pcap".format(filename) + # self.info("capture packet to {}".format(filename)) + from scapy.all import wrpcap + wrpcap(filename, pkt, append=append) + return True + + def mkfile(self, ftype, suffix, extn, backup=False): + if suffix: + ftype = "{}_{}".format(ftype, suffix) + filename = "{}.{}".format(ftype, extn) + retval = self.get_logs_path(filename) + if backup: + time_spec = datetime.datetime.utcnow().strftime("%Y_%m_%d_%H_%M_%S_%f") + try: + os.rename(retval, "{}.{}".format(retval, time_spec)) + except Exception: + pass + return retval + + def get_logs_path(self, filename=None): + if not filename: + return self.logs_dir2 + return os.path.join(self.logs_dir2, filename) def todo(self, etype, name, value): msg = "{}: {} = {}".format(etype, name, value) - self.error(msg) - raise ValueError(msg) + raise ValueError(self.error(msg)) def _log(self, lvl, msg): - if not msg.strip(): return + if not msg.strip(): + return self.lock.acquire() for line in msg.split("\n"): self.logger.log(lvl, line) @@ -174,30 +253,30 @@ def _log(self, lvl, msg): return msg def log(self, *args, **kwargs): - msg = " ".join(map(str,args)) + msg = " ".join(map(str, args)) return self._log(logging.INFO, msg) def info(self, *args, **kwargs): - msg = " ".join(map(str,args)) + msg = " ".join(map(str, args)) return self._log(logging.INFO, msg) def warning(self, *args, **kwargs): - msg = ["#"*80, " ".join(map(str,args)), "#"*80] + msg = ["#" * 80, " ".join(map(str, args)), "#" * 80] return self._log(logging.WARNING, "\n".join(msg)) def debug(self, *args, **kwargs): - msg = " ".join(map(str,args)) + msg = " ".join(map(str, args)) return self._log(logging.DEBUG, msg) def banner(self, *args, **kwargs): - msg = ["#"*80, " ".join(map(str,args)), "#"*80] + msg = ["#" * 80, " ".join(map(str, args)), "#" * 80] return self.info("\n".join(msg)) def error(self, *args, **kwargs): msg = "" - #msg = msg + "=================================== " - msg = msg + " ".join(map(str,args)) - #msg = msg + "=================================== " + # msg = msg + "=================================== " + msg = msg + " ".join(map(str, args)) + # msg = msg + "=================================== " return self._log(logging.ERROR, msg) def log_exception(self, e, msg): @@ -209,9 +288,22 @@ def log_exception(self, e, msg): self.logger.exception(e) self.lock.release() + def dump(self, msg, obj): + ll = ["=========== {} ==================".format(msg)] + ll.append(repr(obj)) + ll.append("=============================") + self.logger.info("\n".join(ll)) + @staticmethod def setup(): logging.basicConfig() logger = logging.getLogger() logger.setLevel(logging.DEBUG) + def register_log(self, logfile): + if not self.log_file or not logfile: + return + if self.log_file not in self.addl_logs: + self.addl_logs[self.log_file] = [] + if logfile not in self.addl_logs[self.log_file]: + self.addl_logs[self.log_file].append(logfile) diff --git a/spytest/spytest/tgen/scapy/or_event.py b/spytest/spytest/tgen/scapy/or_event.py index 4e5ce6f576..4b066c6885 100755 --- a/spytest/spytest/tgen/scapy/or_event.py +++ b/spytest/spytest/tgen/scapy/or_event.py @@ -1,13 +1,16 @@ 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 @@ -15,8 +18,10 @@ def orify(e, 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): @@ -27,4 +32,3 @@ def changed(): orify(e, changed) changed() return or_event - diff --git a/spytest/spytest/tgen/scapy/packet.py b/spytest/spytest/tgen/scapy/packet.py index 8a9d500740..621b98c11d 100755 --- a/spytest/spytest/tgen/scapy/packet.py +++ b/spytest/spytest/tgen/scapy/packet.py @@ -1,61 +1,67 @@ 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 scapy.all import hexdump, L2Socket, sendp +from scapy.all import hexdump, sendp +try: + from scapy.all import L2Socket +except Exception as exp: + print(exp) + from scapy.arch.linux import 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 scapy.utils import hexstr from dicts import SpyTestDict from utils import Utils +from utils import RunTimeException from logger import Logger - -try: print("SCAPY VERSION = {}".format(Conf().version)) -except Exception: print("SCAPY VERSION = UNKNOWN") - -if sys.version_info[0] >= 3: - unicode = str - -this_dir = os.path.join(os.path.dirname(__file__)) - -#dbg > 1 --- recv/send packet -#dbg > 2 --- recv/send packet summary -#dbg > 3 --- recv/send packet hex +from lock import Lock +from protocol import PacketProtocol +from interface import PacketInterface +from bgp_exabgp import ExaBgp +from dot1x import Dot1x +from dhcps import Dhcps + +try: + print("SCAPY VERSION = {}".format(Conf().version)) +except Exception: + print("SCAPY VERSION = UNKNOWN") + +# dbg > 1 --- recv/send packet +# dbg > 2 --- recv/send packet summary +# dbg > 3 --- recv/send packet hex stale_list_ignore = [ + "debug", "port_handle", "port_handle2", "stream_id", "mode", - "rate_percent", #TODO + "rate_percent", # TODO # - "circuit_endpoint_type", "enable_stream", "enable_stream_only_gen", # # filtered stats - "high_speed_result_analysis", #TODO + "high_speed_result_analysis", # TODO "vlan_id_tracking", "ip_dscp_tracking", "track_by", # filtered stats - "ip_protocol", #TODO + "ip_protocol", # TODO "vlan_id", "vlan_id_mode", @@ -120,50 +126,66 @@ "ipv6_dst_count", ] + class ScapyPacket(object): - def __init__(self, iface, dbg=0, dry=False, hex=False, logger=None): + def __init__(self, iface, dbg=0, dry=False, logger=None): self.dry = dry self.errs = [] + self.use_custom_exp = False self.logger = logger or Logger(dry) - try: self.logger.info("SCAPY VERSION = {}".format(Conf().version)) - except Exception: self.logger.info("SCAPY VERSION = UNKNOWN") + try: + self.logger.info("SCAPY VERSION = {}".format(Conf().version)) + except Exception: + 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.show_summary = bool(self.dbg > 2) - self.hex = hex + self.hex = bool(os.getenv("SPYTEST_SCAPY_HEXDUMP", "0") != "0") self.iface = iface self.is_vde = not dry and iface.startswith("vde") + self.stats_lock = Lock() self.tx_count = 0 self.rx_count = 0 self.rx_sock = None self.tx_sock = None + self.tx_sock_failed = False self.finished = False - self.exabgp_nslist = [] - self.cleanup() self.mtu = 9194 + self.use_bridge = bool(os.getenv("SPYTEST_SCAPY_USE_BRIDGE", "1") != "0") + self.logger.info("use_bridge = {}".format(self.use_bridge)) + self.pp = PacketProtocol(self) + self.pi = PacketInterface(self) + self.bgp = ExaBgp(self) + self.dot1x = Dot1x(self) + self.dhcps = Dhcps(self) + self.cleanup() if iface and not self.dry: - #already init_bridge called in cleanup() - #self.init_bridge(iface) - bridge = "{0}-br".format(iface) - self.os_system("ip link add name {0} type bridge".format(bridge)) - self.os_system("ip link set dev {1} master {0}".format(bridge, iface)) - # let bridge proxy arp packets - #self.os_system("echo 1 > /proc/sys/net/ipv4/conf/{0}-br/proxy_arp".format(iface)) - self.os_system("ip link set dev {0}-br up".format(iface)) + if self.use_bridge: + # already init_bridge called in cleanup() + # self.init_bridge(iface) + self.os_system("ip link add name {0}-br type bridge".format(iface)) + self.os_system("ip link set dev {0} master {0}-br".format(iface)) + # let bridge proxy arp packets + # self.os_system("sysctl -w net.ipv4.conf.{0}-br.proxy_arp=1".format(iface)) + self.os_system("ip link set dev {0}-br up".format(iface)) self.os_system("ip link set dev {0} up".format(iface)) self.os_system("ip link set dev {0} promisc on".format(iface)) self.os_system("ip link set dev {0} mtu {1}".format(iface, self.mtu)) - self.os_system("ip link set dev {0}-br mtu {1}".format(iface, self.mtu)) - self.os_system("echo 0 > /sys/class/net/{0}-br/bridge/multicast_snooping".format(iface)) - #self.os_system("ip link del {0}-rx".format(iface)) + if self.use_bridge: + self.os_system("ip link set dev {0}-br mtu {1}".format(iface, self.mtu)) + # self.os_system("ip link set dev {0}-br mcast_snooping 0".format(iface)) + # self.os_system("sysctl -w net.ipv4.conf.{0}-br.bridge.multicast_snooping=0".format(iface)) + # self.os_system("echo 0 > /sys/devices/virtual/net/{0}-br/bridge/multicast_querier".format(iface)) + # self.os_system("echo 0 > /sys/devices/virtual/net/{0}-br/bridge/multicast_snooping".format(iface)) + # self.os_system("ip link del {0}-rx".format(iface)) self.os_system("ip link add {0}-rx type dummy".format(iface)) self.os_system("ip link set dev {0}-rx mtu {1}".format(iface, self.mtu)) self.configure_ipv6(iface) self.os_system("tc qdisc del dev {0} ingress".format(iface)) self.os_system("tc qdisc add dev {0} ingress".format(iface)) - #self.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)) + # self.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)) self.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)) self.os_system("ip link set {0}-rx up".format(iface)) if self.dbg > 5: @@ -176,10 +198,10 @@ def os_system(self, cmd): def init_bridge(self, iface): if iface and not self.dry: - bridge = "{0}-br".format(iface) - self.os_system("ip link set dev {0} down".format(bridge)) - self.os_system("ip link set dev {0} nomaster".format(iface)) - self.os_system("ip link del {0}".format(bridge)) + if self.use_bridge: + self.os_system("ip link set dev {0}-br down".format(iface)) + self.os_system("ip link set dev {0} nomaster".format(iface)) + self.os_system("ip link del {0}-br".format(iface)) self.os_system("ip link del {0}-rx".format(iface)) time.sleep(1) @@ -188,13 +210,21 @@ def configure_ipv6(self, iface): self.os_system("sysctl -w net.ipv6.conf.{}.forwarding=0".format(iface_rx)) self.os_system("sysctl -w net.ipv6.conf.{}.accept_ra=0".format(iface_rx)) self.os_system("sysctl -w net.ipv6.conf.{}.forwarding=1".format(iface)) - #self.os_system("sysctl -w net.ipv6.conf.{}.accept_ra=1".format(iface)) - self.os_system("sysctl -w net.ipv6.conf.all.forwarding=0") + # self.os_system("sysctl -w net.ipv6.conf.{}.accept_ra=1".format(iface)) + # self.os_system("sysctl -w net.ipv6.conf.all.forwarding=0") def __del__(self): - self.logger.info("packet cleanup todo: ", self.iface) self.cleanup() + @staticmethod + def hex_str(pkt): + return hexstr(pkt, onlyhex=1) + + def warn(self, *args, **kwargs): + msg = self.logger.warning(*args, **kwargs) + self.errs.append(msg) + return msg + def error(self, *args, **kwargs): msg = self.logger.error(*args, **kwargs) self.errs.append(msg) @@ -212,37 +242,52 @@ def get_alerts(self): return errs def clear_stats(self): + self.stats_lock.acquire() self.tx_count = 0 self.rx_count = 0 + self.stats_lock.release() def close_sock(self, sock): - try: sock.close() - except Exception: pass + try: + sock.close() + except Exception: + pass return None def cleanup(self): print("ScapyPacket {} cleanup...".format(self.iface)) self.logger.info("ScapyPacket {} cleanup...".format(self.iface)) - self.exabgpd_stop_all() + self.bgp.cleanup() + self.dot1x.cleanup() + self.dhcps.cleanup() self.finished = True self.rx_sock = self.close_sock(self.rx_sock) self.tx_sock = self.close_sock(self.tx_sock) + self.tx_sock_failed = False self.init_bridge(self.iface) self.finished = False def rx_open(self): - if not self.iface or self.dry: return + 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)) + try: + self.rx_sock.bind((self.iface + "-rx", 3)) + except Exception as exp: + print("Exception in rx_open") + msg = self.os_system("ifconfig") + if not self.use_custom_exp: + raise exp + raise RunTimeException(exp, msg) afpacket.enable_auxdata(self.rx_sock) def set_link(self, status): - msg = "link:{} status:{}".format - self.logger.debug(msg(iface, status)) + msg = "link:{} status:{}".format(self.iface, status) + self.logger.debug(msg) - def readp(self, iface): + def readp(self, iface, port): if self.dry: time.sleep(2) @@ -258,7 +303,9 @@ def readp(self, iface): return None raise exp packet = Ether(data) + self.stats_lock.acquire() self.rx_count = self.rx_count + 1 + self.stats_lock.release() self.trace_stats() if self.dbg > 1: @@ -266,13 +313,18 @@ def readp(self, iface): msg = "readp:{} len:{} count:{} {}".format self.logger.debug(msg(iface, len(data), self.rx_count, cmd)) - if self.dbg > 2: + if self.dbg > 3: self.trace_packet(packet, self.hex) + # handle protocol packets + self.pp.process(port, packet) + return packet def sendp(self, pkt, data, iface, stream_name, left): + self.stats_lock.acquire() self.tx_count = self.tx_count + 1 + self.stats_lock.release() self.trace_stats() if self.dbg > 2 or (self.dbg > 1 and left != 0): @@ -280,62 +332,108 @@ def sendp(self, pkt, data, iface, stream_name, left): msg = "sendp:{}:{} len:{} count:{} {}".format self.logger.debug(msg(iface, stream_name, len(data), self.tx_count, cmd)) - if self.dbg > 2: + if self.dbg > 3: self.trace_packet(pkt, self.hex) - if not self.dry: - if not self.tx_sock: - try: - self.tx_sock = L2Socket(iface) - except Exception as exp: - self.logger.debug("Failed to create L2Socket {} {}".format(iface, exp)) + return self.send(data, iface) + + def mkcmd(self, data): + try: + pkt = Ether(data) + cmd = "" if not self.show_summary else pkt.command() + except Exception: + cmd = "???" + return cmd + + def expmsg(self, data, iface, exp, func): + cmd = self.mkcmd(data) + return "{}:{} len:{} {} {}".format(func, iface, len(data), cmd, str(exp)) + + def send(self, data, iface, trace=False): - if self.tx_sock: - try: return self.tx_sock.send(data) - except Exception: pass + if trace and self.dbg > 2: + cmd = self.mkcmd(data) + msg = "send:{} len:{} count:{} {}".format + self.logger.debug(msg(iface, len(data), self.tx_count, cmd)) + + if self.dry: + return + + if not self.tx_sock: + try: + self.tx_sock = L2Socket(iface) + self.tx_sock_failed = False + except Exception as exp: + func = self.logger.debug if self.tx_sock_failed else self.error + self.tx_sock_failed = True + func("Failed to create L2Socket {} {}".format(iface, exp)) + + err1, err2 = "", "" + + # try sending using sock + if self.tx_sock: try: - sendp(data, iface=iface, verbose=False) + return self.tx_sock.send(data) except Exception as exp: - self.logger.debug("Failed to send legacy {} {}".format(iface, exp)) - if self.is_vde: self.os_system("ip link set dev {0} up".format(iface)) + err1 = self.expmsg(data, iface, exp, "sock-send") + + # try sending using legacy method + try: + return sendp(data, iface=iface, verbose=False) + except Exception as exp: + err2 = self.expmsg(data, iface, exp, "scapy-sendp") + if self.is_vde: + self.os_system("ip link set dev {0} up".format(iface)) + + # trace errors + self.logger.error("Failed to send normal {}".format(err1)) + self.logger.error("Failed to send legacy {}".format(err2)) def trace_stats(self): - #self.logger.debug("Name: {} RX: {} TX: {}".format(self.iface, self.rx_count, self.tx_count)) + # self.logger.debug("Name: {} RX: {} TX: {}".format(self.iface, self.rx_count, self.tx_count)) pass - def show_pkt(self, pkt): + def show_pkt(self, pkt, force=False): + if force: + self.utils.exec_func(None, pkt.show2) + return self.utils.exec_func(None, pkt.summary) if self.dbg > 4: - return self.utils.exec_func(pkt.show2) + return self.utils.exec_func(None, pkt.show2) if self.dbg > 3: - return self.utils.exec_func(pkt.summary) + return self.utils.exec_func(None, pkt.summary) return "" - 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 trace_packet(self, pkt, hex=True, fields=True, force=False): + if not fields and not hex: + return + if isinstance(pkt, str): + pkt = Ether(pkt) + if fields: + self.show_pkt(pkt, force) + if hex: + self.logger.debug(hexdump(pkt, dump=True)) def send_packet(self, pwa, iface, stream_name, left): if pwa.padding: - strpkt = str(pwa.pkt/pwa.padding) + strpkt = self.utils.tobytes(pwa.pkt / pwa.padding) else: - strpkt = str(pwa.pkt) + strpkt = self.utils.tobytes(pwa.pkt) # insert stream id before CRC if pwa.add_signature: sid = pwa.stream.get_sid() - if not sid: sid = "DeadBeef" - if sid: strpkt = strpkt[:-len(sid)] + sid + sid = sid or "DeadBeef" + if sid: + sid = binascii.unhexlify(sid) + strpkt = strpkt[:-len(sid)] + sid - pkt_bytes = self.utils.tobytes(strpkt) try: - crc1 = '{:08x}'.format(socket.htonl(zlib.crc32(pkt_bytes) & 0xFFFFFFFF)) + crc1 = '{:08x}'.format(socket.htonl(zlib.crc32(strpkt) & 0xFFFFFFFF)) crc = binascii.unhexlify(crc1) except Exception: crc = binascii.unhexlify('00' * 4) - bstr = bytes(strpkt+crc) - self.sendp(pwa.pkt, bstr, iface, stream_name, left) + bstr = strpkt + self.utils.tobytes(crc) + self.sendp(Ether(bstr), bstr, iface, stream_name, left) return bstr def check(self, pkt): @@ -352,7 +450,7 @@ def pop_mac(self, d, prop, default): values = val else: values = val.split(" ") - for index,val in enumerate(values): + for index, val in enumerate(values): values[index] = val.replace(".", ":") return values @@ -383,6 +481,13 @@ def get_int(self, d, prop, default): except Exception: self.logger.info(traceback.format_exc()) + def get_hex(self, d, prop, default): + val = d.get(prop, "{}".format(default)) + try: + return int(str(val), 16) + except Exception: + 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 < {}" @@ -393,8 +498,8 @@ 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 + # udp.len + # udp.chksum return udp def build_tcp(self, kws): @@ -404,26 +509,32 @@ def build_tcp(self, kws): tcp.seq = self.pop_int(kws, "tcp_seq_num", 0) tcp.ack = self.pop_int(kws, "tcp_ack_num", 0) flags = [] - if self.pop_int(kws, "tcp_syn_flag", 0): flags.append('S') - if self.pop_int(kws, "tcp_fin_flag", 0): flags.append('F') - if self.pop_int(kws, "tcp_urg_flag", 0): flags.append('U') - if self.pop_int(kws, "tcp_psh_flag", 0): flags.append('P') - if self.pop_int(kws, "tcp_ack_flag", 0): flags.append('A') - if self.pop_int(kws, "tcp_rst_flag", 0): flags.append('R') + if self.pop_int(kws, "tcp_syn_flag", 0): + flags.append('S') + if self.pop_int(kws, "tcp_fin_flag", 0): + flags.append('F') + if self.pop_int(kws, "tcp_urg_flag", 0): + flags.append('U') + if self.pop_int(kws, "tcp_psh_flag", 0): + flags.append('P') + if self.pop_int(kws, "tcp_ack_flag", 0): + flags.append('A') + if self.pop_int(kws, "tcp_rst_flag", 0): + flags.append('R') tcp.flags = "".join(flags) - #tcp.dataofs - #tcp.reserved + # tcp.dataofs + # tcp.reserved tcp.window = self.pop_int(kws, "tcp_window", 0) - #tcp.chksum - #tcp.urgptr - #tcp.options + # 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 + # icmp.code = Driver.getConstValue(icmpCfg.code) + # TODO: icmp_type_count, icmp_type_mode return icmp def build_icmp6(self, kws): @@ -432,7 +543,7 @@ def build_icmp6(self, kws): 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.O = self.pop_int(kws, "icmp_ndp_nam_o_flag", 1) # noqa: ignore=E741 icmp.tgt = self.pop_str(kws, "icmp_target_addr", "::") else: icmp = ICMP() @@ -440,10 +551,10 @@ def build_icmp6(self, kws): def build_igmp(self, kws): igmp = IGMP() - #igmp.mrcode + # 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") + # 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": @@ -455,40 +566,69 @@ def build_igmp(self, kws): igmp = None return igmp + def log_stream_kws(self, stream, msg): + self.logger.debug("{}: {} = {}".format(msg, stream.stream_id, stream.kws)) + + def dbg_stream_kws(self, stream, **kwargs): + stream.kws.setdefault("debug", {}) + for name, value in kwargs.items(): + stream.kws["debug"][name] = value + def fill_emulation_params(self, stream): - circuit_endpoint_type = stream.kws.pop("circuit_endpoint_type", None) - self.logger.debug("stream.kws-0 = {}".format(stream.kws)) + circuit_endpoint_type = stream.kws.pop("circuit_endpoint_type", "ipv4") + self.log_stream_kws(stream, "INITIAL") src_info, dst_info = {}, {} src_intf = stream.kws.pop("emulation_src_handle", None) dst_intf = stream.kws.pop("emulation_dst_handle", None) + if src_intf: - ns = "ns_{}_{}".format(src_intf.name, 0) - src_info = Utils.get_ip_addr_dev("veth1", ns) - self.logger.debug("src_info = {} {}".format(ns, src_info)) - if circuit_endpoint_type == "ipv4": - src_info.pop("inet6", "") - elif circuit_endpoint_type == "ipv6": - src_info.pop("inet", "") - stream.kws["mac_src"] = [src_info.get("link/ether", "00:00:00:00:00:00")] + intf = src_intf.intf + if src_intf != intf: + self.logger.debug("SRC Protocol: {} = {}".format(src_intf.handle, src_intf.kws)) + self.logger.debug("SRC Parent: {} = {}".format(intf.handle, intf.kws)) + + if dst_intf: + intf = dst_intf.intf + if dst_intf != intf: + self.logger.debug("DST Protocol: {} = {}".format(dst_intf.handle, dst_intf.kws)) + self.logger.debug("DST Parent: {} = {}".format(intf.handle, intf.kws)) + if dst_intf: - ns = "ns_{}_{}".format(dst_intf.name, 0) - dst_info = Utils.get_ip_addr_dev("veth1", ns) - self.logger.debug("dst_info = {} {}".format(ns, dst_info)) + intf = dst_intf.intf + ns = "{}_{}".format(intf.name, 0) + dst_info = self.utils.get_ip_addr_dev("veth1", ns)[0] + self.dbg_stream_kws(stream, dst_ns=ns, dst_info=dst_info) if circuit_endpoint_type == "ipv4": dst_info.pop("inet6", "") elif circuit_endpoint_type == "ipv6": dst_info.pop("inet", "") stream.kws["mac_dst"] = [dst_info.get("link/ether", "00:00:00:00:00:00")] + stream.kws["mac_dst"] = "00:00:00:00:00:00" + self.log_stream_kws(stream, "dst_intf") + + if src_intf: + intf = src_intf.intf + ns = "{}_{}".format(intf.name, 0) + src_info = self.utils.get_ip_addr_dev("veth1", ns)[0] + self.dbg_stream_kws(stream, src_ns=ns, dst_info=src_info) + if circuit_endpoint_type == "ipv4": + src_info.pop("inet6", "") + elif circuit_endpoint_type == "ipv6": + src_info.pop("inet", "") + stream.kws["mac_src"] = [src_info.get("link/ether", "00:00:00:00:00:00")] + stream.kws["mac_dst"] = self.get_arp_mac(intf, stream.kws["mac_dst"]) + self.log_stream_kws(stream, "src_intf") # read params from emulation interfaces if src_intf: + intf = src_intf.intf intf_ip_addr = src_info.get("inet", "0.0.0.0").split("/")[0] - intf_ip_addr = src_intf.kws.get("intf_ip_addr", intf_ip_addr) + intf_ip_addr = intf.kws.get("intf_ip_addr", intf_ip_addr) ipv6_intf_addr = src_info.get("inet6", "").split("/")[0] - ipv6_intf_addr = src_intf.kws.get("ipv6_intf_addr", ipv6_intf_addr) - count = self.utils.intval(src_intf.kws, "count", 1) + ipv6_intf_addr = intf.kws.get("ipv6_intf_addr", ipv6_intf_addr) + count = self.utils.intval(intf.kws, "count", 1) if ipv6_intf_addr: stream.kws["l3_protocol"] = "ipv6" stream.kws["ipv6_src_addr"] = ipv6_intf_addr @@ -502,20 +642,18 @@ def fill_emulation_params(self, stream): if count > 1: stream.kws["ip_src_count"] = count stream.kws["ip_src_mode"] = "increment" - #try: - #stream.kws["mac_src"] = [src_info.get("link/ether", "00:00:00:00:00:00")] - #stream.kws["mac_dst"] = [self.get_arp_mac(src_intf)] - #except Exception as exp: - #self.logger.info(exp) - #self.logger.info(traceback.format_exc()) - self.logger.debug("updated stream.kws-1 = {}".format(stream.kws)) + self.log_stream_kws(stream, "src_info") if dst_intf: + intf = dst_intf.intf intf_ip_addr = dst_info.get("inet", "").split("/")[0] - intf_ip_addr = dst_intf.kws.get("intf_ip_addr", intf_ip_addr) + intf_ip_addr = intf.kws.get("intf_ip_addr", intf_ip_addr) ipv6_intf_addr = dst_info.get("inet6", "").split("/")[0] - ipv6_intf_addr = dst_intf.kws.get("ipv6_intf_addr", ipv6_intf_addr) - count = self.utils.intval(dst_intf.kws, "count", 1) + ipv6_intf_addr = intf.kws.get("ipv6_intf_addr", ipv6_intf_addr) + count = self.utils.intval(intf.kws, "count", 1) + if dst_intf.handle.startswith("bgp-route"): + count = self.utils.intval(dst_intf.kws, "num_routes", 1) + intf_ip_addr = dst_intf.kws.get("prefix", intf_ip_addr) if ipv6_intf_addr: stream.kws["l3_protocol"] = "ipv6" stream.kws["ipv6_dst_addr"] = ipv6_intf_addr @@ -528,8 +666,9 @@ def fill_emulation_params(self, stream): if count > 1: stream.kws["ip_dst_count"] = count stream.kws["ip_dst_mode"] = "increment" - #stream.kws["mac_dst"] = [dst_info.get("link/ether", "00:00:00:00:00:00")] - self.logger.debug("updated stream.kws-2 = {}".format(stream.kws)) + self.log_stream_kws(stream, "dst_info") + + self.log_stream_kws(stream, "UPDATED") def build_first(self, stream): @@ -549,7 +688,7 @@ def build_first(self, stream): if rate_percent > 0: rate_pps = self.max_rate_pps self.banner("rate_percent {} not supported using {} pps".format(rate_percent, rate_pps)) - l2_encap = self.pop_str(kws, "l2_encap", "") + l2_encap = self.pop_str(kws, "l2_encap", "ethernet_ii_vlan") l3_protocol = self.pop_str(kws, "l3_protocol", "") l4_protocol = self.pop_str(kws, "l4_protocol", "") vlan_en = self.pop_str(kws, "vlan", "enable") @@ -561,21 +700,22 @@ def build_first(self, stream): 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") - if transmit_mode not in ["continuous", "continuous_burst", "single_burst", "single_pkt"]: + if transmit_mode not in ["continuous", "continuous_burst", "single_burst", "single_pkt", "multi_burst"]: self.logger.todo("unsupported", "transmit_mode", transmit_mode) return None 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) + ethernet_value = self.pop_hex(kws, "ether_type", 0) + ethernet_value = self.pop_hex(kws, "ethernet_value", ethernet_value) 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() + mac_dst_mode = kws.get("mac_dst_mode", "fixed").strip() if mac_dst_mode not in ["fixed", "increment", "decrement", "list"]: self.error("unhandled option mac_dst_mode = {}".format(mac_dst_mode)) - mac_src_mode = kws.get("mac_src_mode", "fixed").strip() + mac_src_mode = kws.get("mac_src_mode", "fixed").strip() if mac_src_mode not in ["fixed", "increment", "decrement", "list"]: self.error("unhandled option mac_src_mode = {}".format(mac_src_mode)) @@ -606,31 +746,32 @@ def build_first(self, stream): 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") + 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 + arp.op = 1 elif arp_oper in ["arpResponse", "arpReply"]: - arp.op = 2 + arp.op = 2 else: self.logger.debug("unknown ARP operation: {}".format(arp_oper)) arp = None if arp: - pkt = self.check(pkt/arp) + pkt = self.check(pkt / arp) elif l3_protocol == "ipv4": ip = IP() - #ip.id - #ip.chksum + # 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 + # ip.frag + # ip.len + # ip.flags + # ip.options proto = self.pop_int(kws, "ip_proto", -1) - if proto >= 0: ip.proto = proto + 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) @@ -641,22 +782,22 @@ def build_first(self, stream): 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) + pkt = self.check(pkt / ip) # add l4_protocol if l4_protocol in ["udp"]: udp = self.build_udp(kws) - pkt = self.check(pkt/udp) + pkt = self.check(pkt / udp) elif l4_protocol in ["tcp"]: tcp = self.build_tcp(kws) - pkt = self.check(pkt/tcp) + pkt = self.check(pkt / tcp) elif l4_protocol in ["icmp"]: icmp = self.build_icmp(kws) - pkt = self.check(pkt/icmp) + pkt = self.check(pkt / icmp) elif l4_protocol in ["igmp"]: igmp = self.build_igmp(kws) if igmp: - pkt = self.check(pkt/igmp) + pkt = self.check(pkt / igmp) elif l4_protocol: self.logger.todo("unsupported-ipv4", "l4_protocol", l4_protocol) elif l3_protocol == "ipv6": @@ -666,22 +807,23 @@ def build_first(self, stream): 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 + if nh: + ip6.nh = nh # add l4_protocol - pkt = self.check(pkt/ip6) + pkt = self.check(pkt / ip6) if l4_protocol in ["udp"]: udp = self.build_udp(kws) - pkt = self.check(pkt/udp) + pkt = self.check(pkt / udp) elif l4_protocol in ["tcp"]: tcp = self.build_tcp(kws) - pkt = self.check(pkt/tcp) + pkt = self.check(pkt / tcp) elif l4_protocol in ["icmp"]: icmp = self.build_icmp6(kws) - pkt = self.check(pkt/icmp) + pkt = self.check(pkt / icmp) elif l4_protocol in ["igmp"]: igmp = self.build_igmp(kws) if igmp: - pkt = self.check(pkt/igmp) + pkt = self.check(pkt / igmp) elif l4_protocol: self.logger.todo("unsupported-ipv6", "l4_protocol", l4_protocol) elif l3_protocol: @@ -690,14 +832,14 @@ def build_first(self, stream): # 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) + 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) + 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 == "single_burst": + if transmit_mode in ["single_burst", "multi_burst"]: left = pkts_per_burst elif transmit_mode == "single_pkt": left = 1 @@ -707,14 +849,14 @@ def build_first(self, stream): # 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 = ''.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) + pkt = self.check(pkt / padding) # update padding length based on frame_size add_signature = False @@ -722,7 +864,7 @@ def build_first(self, stream): padLen = int(frame_size - len(pkt) - 4) if padLen > 0: padding = Padding(binascii.unhexlify('00' * padLen)) - pkt = self.check(pkt/padding) + pkt = self.check(pkt / padding) add_signature = True # verify unhandled options @@ -730,6 +872,10 @@ def build_first(self, stream): if key not in stale_list_ignore: self.error("unhandled option {} = {}".format(key, value)) + # Adjust the Ether Type + if pkt.type == 0x9000: + pkt.type = len(pkt) + pwa = SpyTestDict() pwa.add_signature = add_signature pwa.pkt = pkt @@ -738,7 +884,7 @@ def build_first(self, stream): pwa.pkts_per_burst = pkts_per_burst pwa.transmit_mode = transmit_mode if rate_pps > self.max_rate_pps: - self.error("drop the rate from {} to {}".format(rate_pps, self.max_rate_pps)) + self.warn("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 @@ -757,8 +903,10 @@ def build_first(self, stream): 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) + + if self.dbg > 3: + self.trace_packet(pkt) + self.logger.debug(pwa) pwa.length_mode = length_mode pwa.frame_size = frame_size @@ -774,7 +922,7 @@ 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) + 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)) @@ -797,9 +945,9 @@ def add_padding(self, pwa, first): 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) + 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) @@ -820,9 +968,9 @@ def build_next_dma(self, pwa): 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) + 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) @@ -844,9 +992,9 @@ def build_next_dma(self, pwa): # 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) + 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) @@ -861,9 +1009,9 @@ def build_next_dma(self, pwa): # 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) + 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) @@ -878,9 +1026,9 @@ def build_next_dma(self, pwa): # 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) + 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) @@ -895,9 +1043,9 @@ def build_next_dma(self, pwa): # 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) + 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) @@ -912,9 +1060,9 @@ def build_next_dma(self, pwa): # 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) + 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) @@ -929,9 +1077,9 @@ def build_next_dma(self, pwa): # 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) + 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) @@ -946,9 +1094,9 @@ def build_next_dma(self, pwa): # 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) + 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 @@ -963,9 +1111,9 @@ def build_next_dma(self, pwa): # 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) + 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 @@ -980,9 +1128,9 @@ def build_next_dma(self, pwa): # 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) + 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 @@ -997,9 +1145,9 @@ def build_next_dma(self, pwa): # 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) + 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 @@ -1014,9 +1162,9 @@ def build_next_dma(self, pwa): # 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) + 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 @@ -1039,7 +1187,8 @@ def build_next(self, pwa): self.logger.debug("build_next {}/{} {} left={}".format(self.iface, pwa.stream.stream_id, pwa.transmit_mode, pwa.left)) if pwa.transmit_mode in ["continuous"] and pwa.duration2 > 0: - if pwa.left <= 0: return None + if pwa.left <= 0: + return None pwa.left = pwa.left - 1 pwa = self.build_next_dma(pwa) return pwa @@ -1055,7 +1204,8 @@ def build_next(self, pwa): if pwa.left > 1: pwa = self.build_next_dma(pwa) - if not pwa: return None + if not pwa: + return None pwa.burst_sent = pwa.burst_sent + 1 @@ -1070,227 +1220,52 @@ def build_ipg(self, pwa): return 0 pwa.burst_sent = 0 pps = self.utils.min_value(pwa.rate_pps, self.max_rate_pps) - return (1.0 * pwa.pkts_per_burst)/float(pps) + return (1.0 * pwa.pkts_per_burst) / float(pps) def match_stream(self, stream, pkt): sid = stream.get_sid() - if not sid: return False - strpkt = str(pkt) - if sid == strpkt[-12:-4]: - self.logger.debug("{}: CMP0: {} {}".format(self.iface, sid, strpkt[-12:-4])) + if not sid: + return False + sid = binascii.unhexlify(sid) + strpkt = self.utils.tobytes(pkt) + sig = strpkt[-len(sid) - 4:-4] + msg = "{} {}".format(binascii.hexlify(sid), binascii.hexlify(sig)) + if sid == sig: + self.logger.debug("{}: CMP0: {}".format(self.iface, msg)) return True - #self.logger.debug("{}: CMP1: {} {}".format(self.iface, sid, strpkt[-12:-4])) + if self.dbg > 2: + self.logger.debug("{}: CMP1: {}".format(self.iface, msg)) + self.trace_packet(pkt, hex=True, force=True) return False - def if_delete_cmds(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)) - - return ns, cmds - - def if_create_cmds(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" - - begin, finish, verify = "", "", "" - - # remove existing - _, cmds = self.if_delete_cmds(index, intf) - begin += cmds - - # create the linux interface - begin += 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 - ip link set dev veth_{0} master {1}-br - """.format(ns, intf.iface, veth_name)) - - if vlan_enable: - begin += 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": - begin += "\nip netns exec ns_{0} ip link set veth1 address {1}".format(ns, smac) - - # assign IPv4 to linux interface - if ip4addr: - begin += "\nip netns exec ns_{0} ip addr add {1}/{2} dev veth1".format(ns, ip4addr, 24) - if ip4gw: - begin += "\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) - begin += "\nip netns exec ns_{0} ip -6 addr add {1}/{2} dev veth1".format(ns, ip6addr, ip6prefix) - if ip6gw: - begin += "\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: - finish += textwrap.dedent(""" - ip netns exec ns_{0} arping -c 1 -I veth1 {1} - """.format(ns, ip4gw)) - verify += textwrap.dedent(""" - ip netns exec ns_{0} ip neigh show - """.format(ns)) - if ip6gw: - finish += textwrap.dedent(""" - ip netns exec ns_{0} ndisc6 -w 2000 {1} veth1 - """.format(ns, ip6gw)) - if ip6addr: - finish += textwrap.dedent(""" - ip netns exec ns_{0} ndisc6 -w 2000 -s {2} {1} veth1 - """.format(ns, ip6gw, ip6addr)) - verify += textwrap.dedent(""" - ip netns exec ns_{0} ip -6 neigh show - """.format(ns)) - - if self.dbg > 1: - verify += textwrap.dedent(""" - ip netns exec ns_{0} ifconfig veth1 - ip netns exec ns_{0} ip addr ls veth1 - """.format(ns)) - - return ns, begin, finish, verify - - def store_cmds(self, cmd_list, b, f, v): - cmd_list[0].append(b) - cmd_list[1].append(f) - cmd_list[2].append(v) - - def change_map(self, ns, is_add): - pass - 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") - cmd_list = [[],[],[]] - if ip4addr: - for index in range(count): - ns, b, f, v = self.if_create_cmds(index, intf, ip4addr, ip4gw, None, None, smac, **intf.kws) - self.store_cmds(cmd_list, b, f, v) - self.change_map(ns, True) - ip4addr = self.utils.incrementIPv4(ip4addr, ip4addr_step) - if ip4gw: 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", "") - ip6addr_step = intf.kws.get("ipv6_intf_addr_step", "::1") - ip6gw = intf.kws.get("ipv6_gateway", "") - ip6gw_step = intf.kws.get("ipv6_gateway_step", "::0") - if ip6addr: - for index in range(count): - ns, b, f, v = self.if_create_cmds(index, intf, None, None, ip6addr, ip6gw, smac, **intf.kws) - self.store_cmds(cmd_list, b, f, v) - self.change_map(ns, True) - ip6addr = self.utils.incrementIPv6(ip6addr, ip6addr_step) - if ip6gw: ip6gw = self.utils.incrementIPv6(ip6gw, ip6gw_step) - if smac != "00:00:00:00:00:00": - smac = self.utils.incrementMac(smac, "00:00:00:00:00:01") - - # execute collected commands - cmds = cmd_list[0] - if cmd_list[1]: - cmds.append("sleep 2") - cmds.extend(cmd_list[1]) - cmds.extend(cmd_list[2]) - self.utils.lshexec(cmds) - - def if_delete(self, intf): - count = self.utils.intval(intf.kws, "count", 1) - cmd_list = [] - for index in range(count): - ns, cmds = self.if_delete_cmds(index, intf) - cmd_list.append(cmds) - self.change_map(ns, False) - self.utils.lshexec(cmd_list) - - 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)) + return self.pi.if_create(intf) - # 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)) + def if_delete(self, intf, exiting=False): + if exiting: + self.bgp.stop() + self.dot1x.stop() + self.dhcps.stop() + return self.pi.if_delete(intf) - 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)) + def if_validate(self, intf): + return self.pi.if_validate(intf) - return re.search(r"(([a-f\d]{1,2}\:){5}[a-f\d]{1,2})", output).groups()[0] - except Exception as exp: - self.banner("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.banner("Fail-2: get_arp_mac {} {} {}".format(ipv4gw, ipv6gw, exp)) + def if_send_arp(self, intf): + return self.pi.if_send_arp(intf) - return default + def get_arp_mac(self, intf, default, try_cache=True): + return self.pi.get_arp_mac(intf, default, try_cache) 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.error(exp) - - # execute the command - return self.utils.cmdexec(cmd) + return self.pi.ping(intf, ping_dst, index) 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) + return self.pi.send_arp(intf, index) def log_large_file(self, fname): size = self.utils.wc_l(fname) - if self.dbg > 2: + if self.dbg > 2 or size <= 50: self.logger.info("======= CAT {} LINES {} =====".format(fname, size)) self.logger.info(self.utils.cat_file(fname)) self.logger.info("===================================") @@ -1302,282 +1277,103 @@ def log_large_file(self, fname): self.logger.info(self.utils.ftail(fname, 50)) self.logger.info("===================================") - def exabgpd_file(self, ns, extn): - return "{}/exabgpd_{}.{}".format(self.logger.logs_dir, ns, extn) - - def exabgpd_stop_all(self): - self.os_system("ps -ef") - for pidfile in self.utils.list_files(self.logger.logs_dir, "exabgpd_*.pid"): - cmd = "pkill -F {}".format(pidfile) - print(cmd) - out = self.os_system(cmd) - print(out) - - def exabgpd_stop(self, ns): - logfile = self.exabgpd_file(ns, "log") - pidfile = self.exabgpd_file(ns, "pid") - self.log_large_file(logfile) + def kill_by_pidfile(self, pidfile, ns=None): + if not os.path.exists(pidfile): + return self.logger.info(self.utils.cat_file(pidfile)) cmd = "pkill -F {}".format(pidfile) - self.os_system(cmd) - if ns in self.exabgp_nslist: - self.exabgp_nslist.remove(ns) - return True + if ns: + out = self.utils.nsexec(ns, cmd) + else: + out = self.utils.cmdexec(cmd) + self.logger.info(out) + self.os_system("rm -f {}".format(pidfile)) - def config_exabgp_one(self, enable, intf, index=0): - ns = "{}_{}".format(intf.name, index) - if not enable: - self.exabgpd_stop(ns) - return True - self.exabgp_nslist.append(ns) - - logfile = self.exabgpd_file(ns, "log") - pidfile = self.exabgpd_file(ns, "pid") - envfile = self.exabgpd_file(ns, "env") - cfgfile = self.exabgpd_file(ns, "cfg") - - 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) - #enable_4_byte_as = self.utils.intval(intf.bgp_kws, "enable_4_byte_as", 0) - #ip_version = self.utils.intval(intf.bgp_kws, "ip_version", 4) - - # create route config - cmdfile = self.config_exabgp_route(enable, intf, index) - - # build router id from ns - router_id = ns.replace("_", ".") + ".0" - - cmds = textwrap.dedent(""" - group exabgp {{ - process dump {{ - encoder json; - receive {{ - parsed; - update; - }} - run /usr/bin/python {4}/exabgp_dump.py; - }} - process http-api {{ - run /usr/bin/python {4}/exabgp_http_api.py {5}; - }} - neighbor {2} {{ - router-id {7}; - local-address {0}; - peer-as {3}; - local-as {1}; - auto-flush false; - group-updates true; - process announce-routes {{ - run /usr/bin/python {4}/exabgp_routes.py {6}; - }} - }} - }} - """.format(intf_ip_addr, local_as, remote_ip_addr, remote_as, this_dir, 5000, cmdfile, router_id)) - self.utils.fwrite(cmds, cfgfile) - - cmds = textwrap.dedent(""" - group exabgp {{ - neighbor {2} {{ - router-id {6}; - local-address {0}; - peer-as {3}; - local-as {1}; - auto-flush false; - group-updates true; - process announce-routes {{ - run /usr/bin/python {4}/exabgp_routes.py {5}; - }} - }} - }} - """.format(intf_ip_addr, local_as, remote_ip_addr, remote_as, this_dir, cmdfile, router_id)) - self.utils.fwrite(cmds, cfgfile) - - cmds = textwrap.dedent(""" - #[exabgp.api] - #pipename = '{0}' - - [exabgp.daemon] - pid = '{1}' - daemonize = true - drop = false - user = root - - [exabgp.log] - all = true - destination = '{2}' - """.format(ns, pidfile, logfile)) - self.utils.fwrite(cmds, envfile) - - cmds = textwrap.dedent(""" - set -x - #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) - - self.logger.info(self.utils.cat_file(envfile)) - self.logger.info(self.utils.cat_file(cfgfile)) - self.log_large_file(cmdfile) - time.sleep(5) - self.logger.info(self.utils.cat_file(pidfile)) - self.log_large_file(logfile) + def control_bgp(self, op, intf): + return self.bgp.control(op, intf) - return True + def control_bgp_route(self, op, route): + return self.bgp.control_route(op, route) - def config_exabgp_route(self, enable, intf, index=0): - ns = "{}_{}".format(intf.name, index) - cmdfile = self.exabgpd_file(ns, "cmd") - cmds = [] - - for br in intf.bgp_routes.values(): - if not br.enable: continue - as_path = br.get("as_path", None) - as_seq = None - if as_path and "as_seq:" in as_path: - try: as_seq = int(as_path.replace("as_path:", "")) - except Exception: as_seq = None - - num_routes = self.utils.intval(br, "num_routes", 0) - prefix = br.get("prefix", "") - if not prefix and num_routes > 0: - msg = "Prefix not specified num_routes={}".format(num_routes) - self.error(msg) - else: - for _ in range(num_routes): - remote_ipv6_addr = intf.bgp_kws.get("remote_ipv6_addr", "") - if remote_ipv6_addr: - cmd = "announce route {}/128 next-hop self".format(prefix) - prefix = self.utils.incrementIPv6(prefix, "0:0:0:1::") - else: - cmd = "announce route {}/24 next-hop self".format(prefix) - prefix = self.utils.incrementIPv4(prefix, "0.0.1.0") - # append as-path sequence - if as_seq: cmd = cmd + "as-path [{}]".format(as_seq) - cmds.append(cmd) - self.utils.fwrite("\n".join(cmds), cmdfile) - ############################# - # TODO: batch routes - # announce attribute next-hop self nlri 100.10.0.0/16 100.20.0.0/16 - ############################# - return cmdfile - - 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.error(msg) - #return False - #envfile = self.exabgpd_file(ns, "env") - for _ in range(num_routes): - 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)) - cmd = textwrap.dedent(""" - ip netns exec ns_{0} curl -s --form \ - "command=announce route {1}/128 next-hop self" \ - http://localhost:{2}/' - """.format(ns, prefix, 5000)) - 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)) - cmd = textwrap.dedent(""" - ip netns exec ns_{0} curl -s --form \ - "command=announce route {1}/24 next-hop self" \ - http://localhost:{2}/' - """.format(ns, prefix, 5000)) - prefix = self.utils.incrementIPv4(prefix, "0.0.1.0") - output = self.utils.cmdexec(cmd) - if "could not send command to ExaBGP" in output: - self.error(output) - return False - return True + def config_igmp(self, mode, intf, host): + self.pp.igmp_tx(mode, intf, host) - 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 control_igmp_querier(self, mode, intf, querier): + querier.enable = bool(mode == "start") + self.pp.igmp_tx_query(intf, querier) - def apply_bgp(self, op, enable, intf): - retval = self.config_exabgp(False, intf) - if enable: - retval = self.config_exabgp(True, intf) - return retval + def control_ospf(self, mode, intf, session): + return True - def apply_bgp_route(self, enable, route): - route.enable = enable - return self.apply_bgp("", True, route.intf) + def control_dhcpc(self, group, port, **kws): + params = {"renew": 3, "rebind": 4, "release": 5, "bind": 0} + param = params.get(kws.get("action"), 0) + ip_version = self.utils.intval(kws, "ip_version", 4) + self.pp.dhcp_tx(port, group, param, ip_version=ip_version) + return True - def config_igmp(self, mode, intf, host): + def control_dhcps(self, server, intf, **kws): + if os.getenv("SPYTEST_SCAPY_DHCPS", "1") == "2": + return self.dhcps.control(server, intf, **kws) + mode = kws.get("mode", "reset") + action = kws.get("action", mode) + ip_version = self.utils.intval(kws, "ip_version", 4) 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.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) + if server.dhcp_relay_agents: + ent = list(server.dhcp_relay_agents.values())[0] + start = ent.kws.get("relay_agent_ipaddress_pool", "0.0.0.0") + step = ent.kws.get("relay_agent_ipaddress_step", "0.0.0.0") + count = self.utils.intval(ent.kws, "relay_agent_ipaddress_count", 1) + elif ip_version == 6: + start = server.kws.get("addr_pool_start_addr", "2000::1") + start = server.kws.get("ipaddress_pool", start) + step = server.kws.get("step", "::1") + count = self.utils.intval(server.kws, "addr_pool_addresses_per_server", 1) + count = self.utils.intval(server.kws, "ipaddress_count", count) 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 _ 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) + start = server.kws.get("ipaddress_pool", "0.0.0.0") + step = server.kws.get("ipaddress_step", "0.0.0.1") + count = self.utils.intval(server.kws, "ipaddress_count", 1) + + end = start + for _ in range(count): + if ip_version == 6: + end = self.utils.incrementIPv6(end, step) 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") - kwargs = ut_stream_get(16) - s = ScapyStream(0, 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, "NA", pwa.left) - pwa = packet.build_next(pwa) - if pwa: time.sleep(1) + end = self.utils.incrementIPv4(end, step) + # kill existing server if any + pidfile = self.logger.mkfile("dhcpd", ns, "pid") + self.kill_by_pidfile(pidfile, ns) + + if action in ["delete", "reset"]: + return True + + # start dhcpd + logfile = self.logger.mkfile("dhcpd", ns, "log") + cmd = "dnsmasq -i veth1 -p0" + cmd = "{} --dhcp-range={},{}".format(cmd, start, end) + cmd = "{} --pid-file={}".format(cmd, pidfile) + cmd = "{} --log-queries".format(cmd) + cmd = "{} --log-dhcp".format(cmd) + cmd = "{} --log-facility={}".format(cmd, logfile) + output = self.utils.nsexec(ns, cmd) + self.logger.debug("{} -- {}".format(cmd, output)) + if "dnsmasq: bad command line options" in output: + return False + self.logger.register_log(logfile) + return True + + def control_dot1x(self, mode, client): + if os.getenv("SPYTEST_SCAPY_DOT1X_IMPL", "1") == "2": + return self.dot1x.control(mode, client) + if mode in ["start"]: + client.mode = mode + client.state = "init" + self.pp.dot1x_tx(client.port, client) + elif mode in ["stop", "abort", "logout"]: + client.state = "logoff" + self.pp.dot1x_tx(client.port, client) + client.mode = "stop" + return True diff --git a/spytest/spytest/tgen/scapy/port.py b/spytest/spytest/tgen/scapy/port.py index 25ad410a8f..6d4ce84ee5 100755 --- a/spytest/spytest/tgen/scapy/port.py +++ b/spytest/spytest/tgen/scapy/port.py @@ -1,53 +1,45 @@ +import os import copy -import threading from dicts import SpyTestDict from driver import ScapyDriver from logger import Logger from utils import Utils +from lock import Lock +from stats import port_stats_init -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): + +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, index, stream_id, track_port, *args, **kws): + def __init__(self, port, index, stream_id, track_ports, *args, **kws): self.port = port self.index = index - self.track_port = track_port + self.track_ports = track_ports or [] 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 self.track_port: - self.track_port.track_streams.append(self) - self.stream_lock = threading.Lock() + self.stats = port_stats_init() + # print("ScapyStream: {} {} {}".format(self.port, self.stream_id, kws)) + for track_port in self.track_ports: + track_port.track_streams.append(self) + self.stream_lock = Lock() + self.stats_lock = Lock() def __del__(self): print("ScapyStream {} exiting...".format(self.stream_id)) - if self.track_port: - self.track_port.track_streams.remove(self) + for track_port in self.track_ports: + track_port.track_streams.remove(self) def get_sid(self): - #if not self.track_port: return None - return '{:08x}'.format((int(self.port)<<16) + (int(self.index))) + return '{:08x}'.format((int(self.port) << 16) + (int(self.index))) def lock(self): self.stream_lock.acquire() @@ -55,49 +47,139 @@ def lock(self): def unlock(self): self.stream_lock.release() - def incrStat(self, name, val = 1): + def incrStat(self, name, val=1): + self.stats_lock.acquire() val = incrStat(self.stats, name, val) - #print("incrStat: {} {} {} = {}".format(self.port, self.stream_id, name, val)) + self.stats_lock.release() + # print("incrStat: {} {} {} = {}".format(self.port, self.stream_id, name, val)) return val + def clearStat(self, name): + self.stats_lock.acquire() + old = self.stats[name] + self.stats[name] = 0 + self.stats_lock.release() + return old + def __str__(self): return ''.join([('%s=%s' % x) for x in self.kws.items()]) + class ScapyInterface(object): def __init__(self, port, index, handle, *args, **kws): self.port = port.name self.iface = port.iface self.index = index self.handle = handle + self.intf = self 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() + self.igmp_queriers = SpyTestDict() self.bgp_routes = SpyTestDict() + self.gwmac = SpyTestDict() + self.mymac = [] + self.ospf_sessions = SpyTestDict() + self.dhcp_servers = SpyTestDict() + + def add_dhcp_server(self, *args, **kws): + index = len(self.dhcp_servers) + handle = "dhcp-server-{}-{}-{}".format(self.port, self.index, index) + server = SpyTestDict() + server.index = index + server.kws = copy.copy(kws) + server.intf = self + server.deleted = False + server.connect = False + server.handle = handle + self.dhcp_servers[handle] = server + server.dhcp_relay_agents = SpyTestDict() + return server + + def add_dhcp_relay_agent(self, server, *args, **kws): + index = len(server.dhcp_relay_agents) + handle = "dhcp-relay-{}-{}-{}-{}" + handle = handle.format(self.port, self.index, server.index, index) + agent = SpyTestDict() + agent.index = index + agent.kws = copy.copy(kws) + agent.intf = self + agent.server = server + agent.deleted = False + agent.connect = False + agent.handle = handle + server.dhcp_relay_agents[handle] = agent + return agent def add_igmp_host(self, *args, **kws): index = len(self.igmp_hosts) - host_handle = "igmp-{}-{}-{}".format(self.port, self.index, index) + host_handle = "igmp-host-{}-{}-{}".format(self.port, self.index, index) host = SpyTestDict() + host.index = index + host.kws = copy.copy(kws) host.intf = self host.host_handle = host_handle - host.update(kws) + host.handle = host_handle self.igmp_hosts[host_handle] = host return host + def add_igmp_querier(self, *args, **kws): + index = len(self.igmp_queriers) + querier_handle = "igmp-querier-{}-{}-{}".format(self.port, self.index, index) + querier = SpyTestDict() + querier.index = index + querier.kws = copy.copy(kws) + querier.intf = self + querier.enable = True + querier.querier_handle = querier_handle + querier.handle = querier_handle + self.igmp_queriers[querier_handle] = querier + return querier + def add_bgp_route(self, *args, **kws): index = len(self.bgp_routes) route_handle = "bgp-route-{}-{}-{}".format(self.port, self.index, index) route = SpyTestDict() + route.index = index + route.kws = copy.copy(kws) route.enable = True route.intf = self route.route_handle = route_handle - route.update(kws) + route.handle = route_handle self.bgp_routes[route_handle] = route return route + def add_ospf_session(self, *args, **kws): + index = len(self.ospf_sessions) + handle = "ospf-session-{}-{}-{}".format(self.port, self.index, index) + session = SpyTestDict() + session.index = index + session.kws = copy.copy(kws) + session.intf = self + session.deleted = False + session.active = False + session.handle = handle + self.ospf_sessions[handle] = session + session.routes = SpyTestDict() + return session + + def add_ospf_route(self, session, *args, **kws): + index = len(session.routes) + handle = "ospf-route-{}-{}-{}-{}" + handle = handle.format(self.port, self.index, session.index, index) + route = SpyTestDict() + route.index = index + route.kws = copy.copy(kws) + route.session = session + route.deleted = False + route.handle = handle + session.routes[handle] = route + return route + + class ScapyPort(object): def __init__(self, name, iface, dry=False, dbg=0, logger=None): self.name = name @@ -111,10 +193,12 @@ def __init__(self, name, iface, dry=False, dbg=0, logger=None): self.streams = SpyTestDict() self.track_streams = [] self.interfaces = SpyTestDict() - self.stats = SpyTestDict() - initStatistics(self.stats) + self.stats = port_stats_init() self.driver = ScapyDriver(self, self.dry, self.dbg, self.logger) self.admin_status = True + self.dhcp_clients = SpyTestDict() + self.dot1x_clients = SpyTestDict() + self.stats_lock = Lock() def __del__(self): self.logger.debug("ScapyPort {} exiting...".format(self.name)) @@ -122,16 +206,19 @@ def __del__(self): del self.driver def clean_interfaces(self): - for handle in self.interfaces.keys(): - self.driver.deleteInterface(self.interfaces[handle]) + for handle in list(self.interfaces.keys()): + self.logger.info("Removing interface {}".format(handle)) + self.driver.deleteInterface(self.interfaces[handle], True) del self.interfaces[handle] def clean_streams(self): self.driver.stopTransmit() + for sid in self.streams: + self.logger.info("Removing stream {}".format(sid)) self.streams.clear() for stream in self.track_streams: stream.lock() - stream.track_port = None + stream.track_ports = [] stream.unlock() self.track_streams = [] @@ -141,6 +228,53 @@ def cleanup(self): self.driver.cleanup() self.clean_streams() + def show(self): + self.logger.info(self) + self.logger.info(self.iface) + self.logger.info(self.interfaces) + + def add_dhcp_client(self, *args, **kws): + index = len(self.dhcp_clients) + handle = "dhcp-client-{}-{}".format(self.name, index) + client = SpyTestDict() + client.index = index + client.kws = copy.copy(kws) + client.port = self + client.deleted = False + client.active = False + client.handle = handle + self.dhcp_clients[handle] = client + client.groups = SpyTestDict() + return client + + def add_dhcp_group(self, client, *args, **kws): + index = len(client.groups) + handle = "dhcp-client-group-{}-{}-{}" + handle = handle.format(self.name, client.index, index) + group = SpyTestDict() + group.index = index + group.kws = copy.copy(kws) + group.client = client + group.deleted = False + group.handle = handle + client.groups[handle] = group + return group + + def add_dot1x_client(self, *args, **kws): + index = len(self.dot1x_clients) + handle = "dot1x-client-{}-{}".format(self.name, index) + client = SpyTestDict() + client.index = index + client.kws = copy.copy(kws) + client.port = self + client.deleted = False + client.active = False + client.handle = handle + client.intf = None + client.mode = "" + self.dot1x_clients[handle] = client + return client + def get_alerts(self): errs = [] errs.extend(self.errs) @@ -162,11 +296,17 @@ def set_admin_status(self, val): def get_admin_status(self): return self.admin_status - def incrStat(self, name, val = 1): - return incrStat(self.stats, name, val) + def incrStat(self, name, val=1): + self.stats_lock.acquire() + rv = incrStat(self.stats, name, val) + self.stats_lock.release() + return rv def getStats(self): - return self.stats + self.stats_lock.acquire() + rv = self.stats + self.stats_lock.release() + return rv def getStreamStats(self): res = [] @@ -183,7 +323,8 @@ def traffic_control(self, *args, **kws): 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.send_arp(intf, intf.index) + self.driver.send_arp(intf, 0) self.driver.startTransmit(**kws) return True elif action == "stop": @@ -191,9 +332,11 @@ def traffic_control(self, *args, **kws): elif action == "reset": self.clean_streams() elif action == "clear_stats": - initStatistics(self.stats) + self.stats_lock.acquire() + port_stats_init(self.stats) for stream in self.streams.values(): - initStatistics(stream.stats) + port_stats_init(stream.stats) + self.stats_lock.release() self.driver.clear_stats() else: self.error("unsupported", "traffic_control: action", action) @@ -214,6 +357,9 @@ def packet_control(self, *args, **kws): def packet_stats(self, *args, **kws): return self.driver.getCapture() + def get_all_streams(self): + return list(self.streams.keys()) + def stream_validate(self, handles): for handle in Utils.make_list(handles): if handle not in self.streams: @@ -223,34 +369,47 @@ def stream_validate(self, handles): def stream_encode(self, index): return "stream-{}-{}".format(self.name, index) - def traffic_config(self, track_port, *args, **kws): + def traffic_config(self, track_ports, validate_stream_id, *args, **kws): res = SpyTestDict() mode = kws.get('mode', None) + stream_id_list = Utils.make_list(kws.get('stream_id', [])) if mode == "create": index = len(self.streams) res.stream_id = self.stream_encode(index) - stream = ScapyStream(self.name, index, res.stream_id, track_port, *args, **kws) + stream = ScapyStream(self.name, index, res.stream_id, track_ports, *args, **kws) self.streams[res.stream_id] = stream + track_port_names = [track_port.port_handle for track_port in stream.track_ports] + self.logger.debug("New stream {} tracking {} ...".format(res.stream_id, track_port_names)) elif mode == "remove": - stream_id = kws.get('stream_id', None) - if stream_id not in self.streams: - self.error("invalid", "traffic_config-remove-stream_id", stream_id) - self.driver.stopTransmit(handle = stream_id) + for stream_id in stream_id_list: + if stream_id not in self.streams: + if not validate_stream_id: + break + self.error("invalid", "traffic_config-remove-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", "traffic_config-enable-stream_id", stream_id) - self.streams[stream_id].enable = True + for stream_id in stream_id_list: + if stream_id not in self.streams: + if not validate_stream_id: + break + self.error("invalid", "traffic_config-enable-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", "traffic_config-disable-stream_id", stream_id) - self.streams[stream_id].enable = False + for stream_id in stream_id_list: + if stream_id not in self.streams: + if not validate_stream_id: + break + self.error("invalid", "traffic_config-disable-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", "traffic_config-modify-stream_id", stream_id) - self.streams[stream_id].kws.update(kws) + for stream_id in stream_id_list: + if stream_id not in self.streams: + if not validate_stream_id: + break + self.error("invalid", "traffic_config-modify-stream_id", stream_id) + self.streams[stream_id].kws.update(kws) + elif mode == "reset": + self.streams = SpyTestDict() else: self.error("unsupported", "traffic_config: mode", mode) return res @@ -260,7 +419,15 @@ def find_interface(self, handle): return self.interfaces[handle] route = self.get_bgp_route(handle) if route: - return route.intf + return route + route = self.get_ospf_route(handle) + if route: + return route.session.intf + return None + + def validate_interface(self, intf): + if self.driver.validate_interface(intf): + return intf return None def igmp_host_validate(self, handle): @@ -270,16 +437,16 @@ def igmp_host_validate(self, handle): return False def get_bgp_route(self, handle): - for ih, intf in self.interfaces.items(): - if ih != handle: - for brh, br in intf.bgp_routes.items(): - if brh == handle: - return br - elif intf.bgp_routes: - for brh, br in intf.bgp_routes.items(): - return br - else: - return None + for intf in self.interfaces.values(): + if handle in intf.bgp_routes: + return intf.bgp_routes[handle] + return None + + def get_ospf_route(self, handle): + for intf in self.interfaces.values(): + for session in intf.ospf_sessions.values(): + if handle in session.routes: + return session.routes[handle] return None def interface_validate(self, handle): @@ -296,9 +463,12 @@ def interface_config(self, *args, **kws): ping_dst = kws.get('ping_dst', None) arp_send_req = kws.get('arp_send_req', None) count = self.utils.intval(kws, "count", 1) + vlan_id_count = self.utils.intval(kws, "vlan_id_count", 0) if mode == "config": - if count > 100: - self.error("too large > 100", "count", count) + if count > 256: + self.error("too large > 256", "count", count) + if vlan_id_count > 256: + self.error("too large > 256", "vlan_id_count", vlan_id_count) index = len(self.interfaces) handle = self.interface_encode(index) interface = ScapyInterface(self, index, handle, *args, **kws) @@ -306,166 +476,198 @@ def interface_config(self, *args, **kws): self.driver.createInterface(interface) if count > 1: res.handle = [handle for _ in range(count)] + elif vlan_id_count > 1: + res.handle = [handle for _ in range(vlan_id_count)] else: res.handle = handle + self.logger.debug("New interface {}...".format(res.handle)) elif mode == "destroy": - handle = kws.get('handle', None) - if isinstance(handle, list): - handle = handle[0] + handle = self.get_single("handle", **kws) if not self.interface_validate(handle): - self.error("invalid", "interface_config-destroy-handle", handle) + try: + self.error("invalid", "interface_config-destroy-handle", handle) + except Exception: + return res self.driver.deleteInterface(self.interfaces[handle]) del self.interfaces[handle] + elif mode == "modify": + for intf in self.interfaces.values(): + intf.kws.update(kws) elif mode is None and send_ping and ping_dst: - handle = kws.get('protocol_handle', None) - if isinstance(handle, list): - handle = handle[0] + handle = self.get_single("protocol_handle", **kws) if not self.interface_validate(handle): self.error("invalid", "interface_config-ping-protocol_handle", handle) - #index = self.interfaces[handle].index - #rv = self.driver.ping(self.interfaces[handle], ping_dst, index) + # index = self.interfaces[handle].index + # rv = self.driver.ping(self.interfaces[handle], ping_dst, index) rv = self.driver.ping(self.interfaces[handle], ping_dst, 0) res[self.port_handle] = SpyTestDict() res[self.port_handle].ping_details = rv elif mode is None and arp_send_req: - handle = kws.get('protocol_handle', None) - if isinstance(handle, list): - handle = handle[0] + handle = self.get_single("protocol_handle", **kws) if not self.interface_validate(handle): self.error("invalid", "interface_config-arp-protocol_handle", handle) - index = self.interfaces[handle].index - self.driver.send_arp(self.interfaces[handle], index) + # index = self.interfaces[handle].index + # self.driver.send_arp(self.interfaces[handle], index) + self.driver.send_arp(self.interfaces[handle], 0) 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" + + # verify mode 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", "emulation_bgp_config-handle", handle) - intf = self.interfaces[handle] - intf.bgp_kws = copy.copy(kws) - if mode == "enable": - retval = self.driver.apply_bgp("config", True, intf) - else: - retval = self.driver.apply_bgp("config", False, intf) - if not retval: - self.error("Failed", "emulation_bgp_config: mode", mode) - else: + if mode not in ["enable", "disable"]: self.error("unsupported", "emulation_bgp_config: mode", mode) - return res + return {} + + # verify handle + handle = self.get_single("handle", **kws) + if not self.interface_validate(handle): + self.error("invalid", "emulation_bgp_config-handle", handle) + + # apply config + intf = self.interfaces[handle] + intf.bgp_kws = copy.copy(kws) + # retval = self.driver.control_bgp(mode, intf) + # if not retval: self.error("Failed", "emulation_bgp_config: mode", mode) + + return self.utils.success(handle=handle) def emulation_bgp_route_config(self, *args, **kws): - res = SpyTestDict() - res.status = "1" + + # verify mode mode = kws.pop('mode', None) if mode not in ["add", "remove"]: self.error("unsupported", "emulation_bgp_route_config: mode", mode) - return res - handle = kws.get('handle', None) - res.handle = handle - if isinstance(handle, list): - handle = handle[0] + handle = self.get_single("handle", **kws) + if mode in ["add", "remove"]: + intf = self.find_interface(handle) + if not intf: + self.error("invalid", "emulation_bgp_route_config-handle", handle) + if mode == "add": + route = intf.add_bgp_route(*args, **kws) + retval = self.driver.control_bgp_route(mode, route) + else: + retval = False + for route in intf.intf.bgp_routes.values(): + if route.kws.get("prefix", None) == kws.get("prefix", None): + retval = self.driver.control_bgp_route(mode, route) + if not retval: + self.error("Failed", "emulation_bgp_route_config: mode", mode) + return self.utils.success(handle=route.route_handle) - if mode == "add": - if not self.interface_validate(handle): - self.error("invalid", "emulation_bgp_route_config-add-handle", handle) - return res + # handle withdraw, readvertize + try: + route = self.get_bgp_route(handle) + except Exception as exp: + self.error("failed to parge route", str(exp), handle) + if not route: + self.error("invalid", "emulation_bgp_route_config", handle) + + retval = self.driver.control_bgp_route(mode, route) + + return self.utils.success() + + def emulation_bgp_control(self, *args, **kws): + + # verify mode + mode = kws.get('mode', None) + if mode not in ["start", "stop", "link_flap", "full_route_flap"]: + self.error("unsupported", "emulation_bgp_control: mode", mode) + + # verify handle + handle = self.get_single("handle", **kws) + if self.interface_validate(handle): intf = self.interfaces[handle] - route = intf.add_bgp_route(*args, **kws) - retval = self.driver.apply_bgp_route(True, route) - if retval: - res.handle = route.route_handle else: - try: - route = self.get_bgp_route(handle) - except Exception as exp: - self.error("failed to parge route", str(exp), handle) + route = self.get_bgp_route(handle) if not route: - self.error("invalid", "emulation_bgp_route_config-remove-handle", handle) - return res - retval = self.driver.apply_bgp_route(False, route) - route.enable = False + self.error("invalid", "emulation_bgp_control-handle", handle) + intf = route.intf + # apply + retval = self.driver.control_bgp(mode, intf) if not retval: - self.error("Failed", "emulation_bgp_route_config: mode", mode) - return res + self.error("Failed", "emulation_bgp_control: mode", mode) - def emulation_bgp_control(self, *args, **kws): - res = SpyTestDict() - res.status = "1" - mode = kws.pop('mode', None) - if mode in ["start", "stop"]: - handle = kws.get('handle', None) - if isinstance(handle, list): - handle = handle[0] - if self.interface_validate(handle): - intf = self.interfaces[handle] - else: - route = self.get_bgp_route(handle) - if not route: - self.error("invalid", "emulation_bgp_control-handle", handle) - return res - intf = route.intf - if mode == "start": - retval = self.driver.apply_bgp("control", True, intf) - else: - retval = self.driver.apply_bgp("control", False, intf) - if not retval: - self.error("Failed", "emulation_bgp_control: mode", mode) - else: - self.error("unsupported", "emulation_bgp_control: mode", mode) - return res + return self.utils.success() 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", "emulation_igmp_config-handle", handle) - intf = self.interfaces[handle] - host = intf.add_igmp_host(*args, **kws) - res.host_handle = host.host_handle - else: + if mode not in ["create", "delete", "modify"]: self.error("unsupported", "emulation_igmp_config: mode", mode) - return res + handle = self.get_single("handle", **kws) + if mode == "create": + if handle in self.interfaces: + intf = self.interfaces[handle] + host = intf.add_igmp_host(*args, **kws) + return self.utils.success(host_handle=host.host_handle, + igmp_host_handle=host.host_handle) + + for intf in self.interfaces.values(): + if handle in intf.igmp_hosts: + if mode == "modify": + intf.igmp_hosts[handle].kws.update(kws) + else: + del intf.igmp_hosts[handle] + return self.utils.success() + + return None def emulation_igmp_group_config(self, *args, **kws): + host_handle = kws.get('session_handle', kws.get('handle', None)) + mode = kws.get('mode', None) + for intf in self.interfaces.values(): + if host_handle not in intf.igmp_hosts: + continue + if mode == "clear_all": + intf.igmp_hosts[host_handle].kws["group_pool_data"] = [] + intf.igmp_hosts[host_handle].kws["source_pool_data"] = [] + return self.utils.success() + intf.igmp_hosts[host_handle].kws.update(kws) + self.logger.dump("IGMP HOST", intf.igmp_hosts[host_handle]) + return self.utils.success(group_handle=host_handle) + self.error("Invalid", "emulation_igmp_group_config: session_handle", host_handle) + + def emulation_igmp_querier_config(self, *args, **kws): res = SpyTestDict() - res.status = "1" + mode = kws.get('mode', None) + if mode in ["delete"]: + return res + if mode not in ["create"]: + self.error("unsupported", "emulation_igmp_querier_config: mode", mode) 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: - intf.igmp_hosts[host_handle].update(kws) - res.group_handle = host_handle + if not self.interface_validate(handle): return res - self.error("Invalid", "emulation_igmp_group_config: session_handle", host_handle) + intf = self.interfaces[handle] + querier = intf.add_igmp_querier(*args, **kws) + res.igmp_querier_handle = querier.querier_handle + res.handle = querier.querier_handle + res.status = "1" + return res + return res def emulation_igmp_control(self, *args, **kws): - res = SpyTestDict() - res.status = "1" - host_handle = kws.get('handle', None) mode = kws.get('mode', "start") + host_handle = kws.get('handle', None) 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) + return self.utils.success() + return None + + def emulation_igmp_querier_control(self, *args, **kws): + mode = kws.get('mode', "start") + querier_handle = kws.get('handle', None) + for intf in self.interfaces.values(): + if querier_handle in intf.igmp_queriers: + self.driver.control_igmp_querier(mode, intf, intf.igmp_queriers[querier_handle]) + return self.utils.success() + return None def error(self, etype, name, value): msg = "{}: {} = {}".format(etype, name, value) @@ -473,3 +675,197 @@ def error(self, etype, name, value): self.errs.append(msg) raise ValueError(msg) + def get_single(self, prop, **kwargs): + value = kwargs.get(prop, None) + if isinstance(value, list): + value = value[0] + return value + + def emulation_ospf_config(self, *args, **kws): + mode = kws.get('mode', None) + if mode not in ["create", "delete", "modify"]: + self.error("unsupported", "emulation_ospf_config: mode", mode) + + handle = self.get_single("handle", **kws) + if mode in ["create"]: + if handle in self.interfaces: + intf = self.interfaces[handle] + session = intf.add_ospf_session(*args, **kws) + # TODO: add to driver + return self.utils.success(handle=session.handle) + elif mode in ["delete"]: + for intf in self.interfaces.values(): + if handle in intf.ospf_sessions: + del intf.ospf_sessions[handle] + # TODO: remove from driver + return self.utils.success() + elif mode in ["modify"]: + for intf in self.interfaces.values(): + if handle in intf.ospf_sessions: + intf.ospf_sessions[handle].kws.update(kws) + # TODO: change in driver + return self.utils.success() + + return None + + def emulation_ospf_control(self, *args, **kws): + mode = kws.get('mode', None) + if mode not in ["start", "stop", "age_out_routes"]: + self.error("unsupported", "emulation_ospf_control: mode", mode) + + handle = self.get_single("handle", **kws) + for intf in self.interfaces.values(): + if handle in intf.ospf_sessions: + session = intf.ospf_sessions[handle] + if mode == "start": + session.active = True + elif mode == "stop": + session.active = False + if self.driver.control_ospf(mode, intf, intf.ospf_sessions[handle]): + return self.utils.success() + + return None + + def emulation_ospf_route_config(self, *args, **kws): + mode = kws.get('mode', None) + if mode not in ["create", "delete"]: + self.error("unsupported", "emulation_ospf_route_config: mode", mode) + + handle = self.get_single("handle", **kws) + if mode == "create": + for intf in self.interfaces.values(): + if handle in intf.ospf_sessions: + session = intf.ospf_sessions[handle] + route = intf.add_ospf_route(session, *args, **kws) + return self.utils.success(handle=route.handle, + ipv4_prefix_interface_handle=route.handle) + + if mode == "delete": + for intf in self.interfaces.values(): + for session in intf.ospf_sessions.values(): + if handle in session.routes: + session.routes[handle].deleted = True + del session.routes[handle] + return self.utils.success() + + return None + + def emulation_dhcp_client_config(self, *args, **kws): + mode = kws.get('mode', None) + if mode not in ["create", "reset"]: + self.error("unsupported", "emulation_dhcp_client_config: mode", mode) + handle = self.get_single("handle", **kws) + if mode == "create": + client = self.add_dhcp_client(*args, **kws) + return self.utils.success(handles=client.handle, handle=client.handle) + if mode == "reset": + if handle in self.dhcp_clients: + self.dhcp_clients[handle].deleted = True + # del self.dhcp_clients[handle] + return self.utils.success() + return {} + + def emulation_dhcp_client_group_config(self, *args, **kws): + mode = kws.get('mode', None) + if mode not in ["create"]: + self.error("unsupported", "emulation_dhcp_client_group_config: mode", mode) + + handle = self.get_single("handle", **kws) + if mode == "create": + if handle in self.dhcp_clients: + client = self.dhcp_clients[handle] + group = self.add_dhcp_group(client, *args, **kws) + if kws.get("dhcp6_client_mode", "") != "DHCPV6": + return self.utils.success(handle=group.handle) + return self.utils.success(handle=group.handle, dhcpv6_handle=group.handle) + + return None + + def emulation_dhcp_client_control(self, *args, **kws): + action = kws.get('action', None) + handle = self.get_single("handle", **kws) + if action not in ["renew", "release", "bind", "rebind"]: + self.error("unsupported", "emulation_dhcp_client_control: action", action) + for client in self.dhcp_clients.values(): + if handle in client.groups: + if self.driver.control_dhcpc(client.groups[handle], self, **kws): + return self.utils.success() + return {} + + def emulation_dhcp_server_config(self, *args, **kws): + mode = kws.get('mode', None) + if mode not in ["create", "reset"]: + self.error("unsupported", "emulation_dhcp_server_config: mode", mode) + handle = self.get_single("handle", **kws) + if mode == "create": + if handle in self.interfaces: + intf = self.interfaces[handle] + server = intf.add_dhcp_server(*args, **kws) + return self.utils.success(dhcp_handle=server.handle) + if mode == "reset": + for intf in self.interfaces.values(): + if handle in intf.dhcp_servers: + intf.dhcp_servers[handle].deleted = True + # del intf.dhcp_servers[handle] + self.driver.control_dhcps(intf.dhcp_servers[handle], intf, **kws) + return self.utils.success() + return {} + + def emulation_dhcp_server_relay_agent_config(self, *args, **kws): + mode = kws.get('mode', None) + if mode not in ["create"]: + self.error("unsupported", "emulation_dhcp_server_relay_agent_config: mode", mode) + handle = self.get_single("handle", **kws) + if mode == "create": + for intf in self.interfaces.values(): + if handle in intf.dhcp_servers: + server = intf.dhcp_servers[handle] + agent = intf.add_dhcp_relay_agent(server, *args, **kws) + return self.utils.success(handle=agent.handle) + return {} + + def emulation_dhcp_server_control(self, *args, **kws): + action = kws.get('action', None) + if action not in ["connect"]: + self.error("unsupported", "emulation_dhcp_server_control: action", action) + handle = self.get_single("dhcp_handle", **kws) + for intf in self.interfaces.values(): + if handle in intf.dhcp_servers: + intf.dhcp_servers[handle].connect = True + if self.driver.control_dhcps(intf.dhcp_servers[handle], intf, **kws): + return self.utils.success() + return {} + + def emulation_dot1x_config(self, *args, **kws): + mode = kws.get('mode', None) + if mode not in ["create", "delete"]: + self.error("unsupported", "emulation_dot1x_config: mode", mode) + if mode == "create": + port_handle = kws.pop('port_handle', None) + if self.port_handle == port_handle: + client = self.add_dot1x_client(*args, **kws) + if os.getenv("SPYTEST_SCAPY_DOT1X_IMPL", "1") == "2": + intf_kws = {"mode": "config"} + intf_kws["intf_ip_addr"] = kws.get("local_ip_addr", "0.0.0.0") + intf_kws["src_mac_addr"] = kws.get("mac_addr", "") + intf_kws["gateway"] = kws.get("gateway_ip_addr", "") + res = self.interface_config(**intf_kws) + client.intf = self.interfaces[res.handle] + return self.utils.success(handle=client.handle) + if mode == "delete": + handle = self.get_single("handle", **kws) + if handle in self.dot1x_clients: + self.dot1x_clients[handle].deleted = True + # del self.dot1x_clients[handle] + return self.utils.success() + return {} + + def emulation_dot1x_control(self, *args, **kws): + mode = kws.get('mode', None) + if mode not in ["start", "stop", "abort", "logout"]: + self.error("unsupported", "emulation_dot1x_control: mode", mode) + handle = self.get_single("handle", **kws) + if handle in self.dot1x_clients: + if self.driver.control_dot1x(mode, self.dot1x_clients[handle]): + return self.utils.success() + return {} diff --git a/spytest/spytest/tgen/scapy/protocol.py b/spytest/spytest/tgen/scapy/protocol.py new file mode 100755 index 0000000000..73841c08af --- /dev/null +++ b/spytest/spytest/tgen/scapy/protocol.py @@ -0,0 +1,624 @@ +import copy +import binascii +import traceback + +from scapy.packet import Padding +from scapy.layers.l2 import Dot1Q, Ether +from scapy.layers.inet import IP, UDP +from scapy.layers.inet6 import IPv6 +from scapy.layers.dhcp import BOOTP, DHCP +from scapy.layers.dhcp6 import DUID_LLT +from scapy.layers.dhcp6 import DHCP6_Solicit +from scapy.layers.dhcp6 import DHCP6OptElapsedTime +from scapy.layers.dhcp6 import DHCP6OptClientId +from scapy.layers.dhcp6 import DHCP6OptIA_NA +from scapy.layers.eap import EAPOL, EAP +from scapy.contrib.ospf import OSPF_Hdr, OSPF_Hello, OSPF_DBDesc +from scapy.contrib.ospf import OSPF_LSAck, OSPF_LSA_Hdr, OSPF_LSUpd, OSPF_Link +from scapy.contrib.ospf import OSPF_External_LSA, OSPF_SummaryIP_LSA, OSPF_Router_LSA +from scapy.contrib.igmp import IGMP +from scapy.contrib.igmpv3 import IGMPv3, IGMPv3mr, IGMPv3gr, IGMPv3mq +from scapy.utils import chexdump + + +class PacketProtocol(object): + + def __init__(self, pif): + self.pif = pif + self.logger = pif.logger + self.utils = pif.utils + self.next_xid = 1 + + def __del__(self): + pass + + def process(self, port, pkt): + + if IP in pkt and pkt.proto == 89: + self.ospf_rx(port, pkt) + + if BOOTP in pkt: + self.dhcp_rx(port, pkt) + + if IGMP in pkt or IGMPv3 in pkt: + self.igmp_rx(port, pkt) + + if EAP in pkt: + self.dot1x_rx(port, pkt) + + self.igmp_tx_query_periodic(port) + self.dot1x_tx_periodic(port) + + def pkt_write(self, file_path, pkt, append): + try: + self.logger.write_pcap(pkt, append=True, filename=file_path) + except Exception as exp: + self.logger.error("wrpcap error {}".format(str(exp))) + + def pkt_debug(self, recv, send, name, ifname="", dbg=0): + file_path = self.logger.get_logs_path("{}.pcap".format(name)) + dbg = self.utils.max_value(dbg, self.pif.dbg) + if recv: + if dbg > 1: + self.logger.info("=== RECV {} {}".format(name, recv.command())) + if dbg > 2: + self.logger.debug(chexdump(recv, True)) + msg = "=========== RECV {} ==================".format(name) + self.utils.exec_func(msg, recv.show2) + if dbg > 9: + self.logger.write_pcap(recv) + self.pkt_write(file_path, recv, append=True) + if send: + if dbg > 1: + self.logger.info("=== SEND({}) {} {}".format(ifname, name, send.command())) + if dbg > 2: + self.logger.debug(chexdump(send, True)) + msg = "=========== SEND({}) {} =================".format(ifname, name) + self.utils.exec_func(msg, send.show2) + if dbg > 9: + self.logger.write_pcap(send) + self.pkt_write(file_path, send, append=True) + + def ospf_rx(self, port, pkt): + ifname = port.iface + ospf_pkt_type = pkt[OSPF_Hdr].type + msg = "check OSPF session on {} {} to handle TYPE {}" + self.logger.info(msg.format(port.port_handle, ifname, ospf_pkt_type)) + for iface in port.interfaces.values(): + self.logger.info(msg.format(iface.handle, ifname, ospf_pkt_type)) + for session in iface.ospf_sessions.values(): + self.logger.info(msg.format(session.handle, ifname, session.active)) + if not session.active: + continue + try: + if ospf_pkt_type == 1: + resp = Ether() / IP(ttl=1) / OSPF_Hdr() / OSPF_Hello() + resp.src = "52:54:00:b6:f8:28" + resp.src = iface.mymac[0] + resp[IP].src = iface.kws["intf_ip_addr"] + neighbors = [pkt[OSPF_Hdr].src] + resp[OSPF_Hdr].src = session.kws["router_id"] + resp[OSPF_Hdr].area = session.kws["area_id"] + resp[OSPF_Hello].router = iface.kws["intf_ip_addr"] + resp[OSPF_Hello].router = pkt[OSPF_Hello].router + resp[OSPF_Hello].backup = pkt[OSPF_Hello].backup + resp[OSPF_Hello].neighbors = neighbors + resp[OSPF_Hello].options = pkt[OSPF_Hello].options + self.pkt_debug(pkt, resp, "ospf", ifname) + self.pif.send(resp, ifname, trace=True) + elif ospf_pkt_type == 2: + hdr_list = [] + for route in session.routes.values(): + if route.deleted: + continue + hdr = OSPF_LSA_Hdr() + count, start, step = 0, "", "0.0.1.0" + if route.kws["type"] == "ext_routes": + count = int(route.kws.get("external_number_of_prefix", 1)) + start = route.kws.get("external_prefix_start") + hdr.type = 5 + elif route.kws["type"] == "summary_routes": + count = int(route.kws.get("summary_number_of_prefix", 1)) + start = route.kws.get("summary_prefix_start") + hdr.type = 3 + elif route.kws["ipv4_prefix_route_origin"] in ["same_area", "summary", "external"]: + count = int(route.kws.get("ipv4_prefix_number_of_addresses", 1)) + start = route.kws.get("ipv4_prefix_network_address") + if route.kws["ipv4_prefix_route_origin"] == "summary": + hdr.type = 3 + elif route.kws["ipv4_prefix_route_origin"] == "external": + hdr.type = 5 + else: + hdr.type = 1 + for _ in range(count): + hdr.id = start + start = self.utils.incrementIPv4(start, step) + hdr.adrouter = route.kws.get("router_id", session.kws["router_id"]) + hdr_list.append(copy.copy(hdr)) + try: + self.logger.debug("hdr_list length = {}".format(len(hdr_list))) + except Exception as exp: + self.logger.error(str(exp)) + + mtu = self.pif.get_int(iface.kws, "control_plane_mtu", pkt[OSPF_DBDesc].mtu) + size = len(OSPF_DBDesc(lsaheaders=[OSPF_LSA_Hdr()])) + chunk_size = mtu / size + chunked_list = [hdr_list[i:i + chunk_size] for i in range(0, len(hdr_list), chunk_size)] + chunk_last = len(chunked_list) - 1 + for index, hdr_list in enumerate(chunked_list): + desc = OSPF_DBDesc(lsaheaders=hdr_list) + desc.mtu = mtu + desc.options = pkt[OSPF_DBDesc].options + desc.dbdescr = pkt[OSPF_DBDesc].dbdescr + desc.dbdescr = 0 + if index == chunk_last: + desc.dbdescr = desc.dbdescr | "M" + desc.ddseq = pkt[OSPF_DBDesc].ddseq + resp = Ether() / IP() / OSPF_Hdr() / desc + resp.src = iface.mymac[0] + resp.dst = pkt.src + resp[IP].src = iface.kws["intf_ip_addr"] + resp[IP].dst = pkt[IP].src + resp[OSPF_Hdr].src = session.kws["router_id"] + resp[OSPF_Hdr].area = session.kws["area_id"] + self.pkt_debug(pkt, resp, "ospf", ifname) + self.pif.send(resp, ifname, trace=True) + elif ospf_pkt_type == 3: + lsa_list = [] + # lsa = OSPF_Router_LSA() + # lsa.id = session.kws["router_id"] + # lsa.adrouter = session.kws["router_id"] + # lsa.metric = 20 + # lsa.options = 0 + # lsa_list.append(lsa) + + link = OSPF_Link() + link.id = session.kws["router_id"] + link.data = "100.1.0.2" + link.metric = 10 + lsa = OSPF_Router_LSA(linklist=[link]) + lsa.id = session.kws["router_id"] + lsa.adrouter = session.kws["router_id"] + lsa.flags = 2 + lsa.age = 0 + lsa_list.append(lsa) + + for route in session.routes.values(): + if route.deleted: + continue + if route.kws["type"] == "ext_routes": + start = route.kws.get("external_prefix_start") + for _ in range(int(route.kws.get("external_number_of_prefix", 1))): + lsa = OSPF_External_LSA() + lsa.id = start + start = self.utils.incrementIPv4(start, "0.0.1.0") + lsa.adrouter = route.kws.get("router_id", session.kws["router_id"]) + lsa.metric = int(route.kws.get("external_prefix_metric", 20)) + lsa.options = 0 + lsa_list.append(lsa) + elif route.kws["type"] == "summary_routes": + start = route.kws.get("summary_prefix_start") + for _ in range(int(route.kws.get("summary_number_of_prefix", 1))): + lsa = OSPF_SummaryIP_LSA() + lsa.id = start + start = self.utils.incrementIPv4(start, "0.0.1.0") + lsa.adrouter = route.kws.get("router_id", session.kws["router_id"]) + lsa.metric = int(route.kws.get("summary_prefix_metric", 20)) + lsa.options = 0 + lsa_list.append(lsa) + elif route.kws["ipv4_prefix_route_origin"] in ["same_area", "summary", "external"]: + start = route.kws.get("ipv4_prefix_network_address") + for _ in range(int(route.kws.get("ipv4_prefix_number_of_addresses", 1))): + if route.kws["ipv4_prefix_route_origin"] == "summary": + lsa = OSPF_SummaryIP_LSA() + elif route.kws["ipv4_prefix_route_origin"] == "external": + lsa = OSPF_External_LSA() + else: + lsa = OSPF_Router_LSA() + lsa.id = start + start = self.utils.incrementIPv4(start, "0.0.1.0") + lsa.adrouter = route.kws.get("router_id", session.kws["router_id"]) + lsa.metric = int(route.kws.get("ipv4_prefix_metric", 20)) + lsa.options = 0 + lsa_list.append(lsa) + resp = Ether() / IP() / OSPF_Hdr() / OSPF_LSUpd(lsalist=lsa_list) + resp.src = iface.mymac[0] + resp.dst = pkt.src + resp[IP].src = iface.kws["intf_ip_addr"] + resp[IP].dst = pkt[IP].src + resp[OSPF_Hdr].src = session.kws["router_id"] + resp[OSPF_Hdr].area = session.kws["area_id"] + self.pkt_debug(pkt, resp, "ospf", ifname) + self.pif.send(resp, ifname, trace=True) + elif ospf_pkt_type == 4: + lsalist = pkt[OSPF_LSUpd].lsalist + lsalist = [] + for lsa in pkt[OSPF_LSUpd].lsalist: + # self.utils.exec_func("OSPF_LSUpd-LSA", lsa.show2) + hdr = OSPF_LSA_Hdr(type=lsa.type, id=lsa.id, adrouter=lsa.adrouter) + lsalist.append(hdr) + resp = Ether() / IP() / OSPF_Hdr() / OSPF_LSAck(lsaheaders=lsalist) + resp.src = iface.mymac[0] + resp.dst = pkt.src + resp[IP].src = iface.kws["intf_ip_addr"] + resp[IP].dst = pkt[IP].src + resp[OSPF_Hdr].src = session.kws["router_id"] + resp[OSPF_Hdr].area = session.kws["area_id"] + self.pkt_debug(pkt, resp, "ospf", ifname) + self.pif.send(resp, ifname, trace=True) + elif ospf_pkt_type == 5: + self.pkt_debug(pkt, None, "ospf") + else: + self.pkt_debug(pkt, None, "ospf") + self.logger.error("====== UNHANDLED {} ====".format(ospf_pkt_type)) + except Exception: + self.pkt_debug(pkt, None, "ospf") + self.logger.info(traceback.format_exc()) + return + # self.logger.info("no OSPF session on {}".format(port.port_handle)) + # self.utils.exec_func(None, pkt.show2) + + def dhcp4_tx(self, port, group, action=0, recv=None, server_id="0.0.0.0", + requested_addr="0.0.0.0", server_mac="00:00:00:00:00:00"): + encap = group.kws.get("encap", "ethernet_ii_vlan") + mac_addr = group.kws.get("mac_addr", '00:11:01:00:00:01') + vlan_id_count = self.utils.intval(group.kws, "vlan_id_count", 0) + vlan_id_step = self.utils.intval(group.kws, "vlan_id_step", 1) + vlan_id = self.utils.intval(group.kws, "vlan_id", 0) + num_sessions = self.utils.intval(group.kws, "num_sessions", 0) + max_count = self.utils.max_value(num_sessions, vlan_id_count) + if action in [0, 3, 4, 5]: + # use new xid for action == 0 i.e sending discover + group.kws["xid"] = self.next_xid + self.next_xid = self.next_xid + max_count + for index in range(max_count): + smac, dmac = mac_addr, 'ff:ff:ff:ff:ff:ff' + iid, sip, dip = 0, '0.0.0.0', '255.255.255.255' + res, ch = smac.split(":"), "" + for i in res: + ch = ch + chr(int(i, 16)) + options, param_req_list = [], [1, 3, 58, 59] + xid = group.kws.get("xid", 0) + index + + if action == 0: + options.append(("message-type", "discover")) + options.append(("param_req_list", param_req_list)) + elif action == 1: + options.append(("message-type", "request")) + options.append(("server_id", server_id)) + options.append(("requested_addr", requested_addr)) + options.append(("param_req_list", param_req_list)) + elif action == 2: + options.append(("message-type", "request")) + options.append(("param_req_list", param_req_list)) + dmac = server_mac + iid, sip, dip = 1, requested_addr, server_id + elif action == 3: + options.append(("message-type", "request")) + dmac = group.kws.get("server_mac", server_mac) + options.append(("param_req_list", param_req_list)) + sip = group.kws.get("requested_addr", requested_addr) + dip = group.kws.get("server_id", server_id) + iid = 2 + elif action == 4: + options.append(("message-type", "request")) + options.append(("param_req_list", param_req_list)) + sip = group.kws.get("requested_addr", requested_addr) + iid = 3 + elif action == 5: + options.append(("message-type", "release")) + options.append(("server_id", server_id)) + dmac = group.kws.get("server_mac", server_mac) + sip = group.kws.get("requested_addr", requested_addr) + dip = group.kws.get("server_id", server_id) + iid = 4 + options.append(("end", Padding())) + + pkt = Ether(src=smac, dst=dmac) + if encap in ["ethernet_ii_vlan", "ethernet_ii"] and vlan_id > 0: + # vlan_ether_type = self.pif.get_hex(group.kws, "vlan_ether_type", "0x8100") + # pkt = pkt/Dot1Q(vlan=vlan_id, type=vlan_ether_type) + pkt = pkt / Dot1Q(vlan=vlan_id) + pkt = pkt / IP(id=iid, src=sip, dst=dip, flags="DF") / UDP() + pkt = pkt / BOOTP(chaddr=ch, xid=xid) + pkt = pkt / DHCP(options=options) + # port.driver.enable_tx(pkt) + self.pkt_debug(recv, pkt, "dhcp", port.iface) + self.pif.send(pkt, port.iface, trace=True) + vlan_id = vlan_id + vlan_id_step + mac_addr = self.utils.incrementMac(mac_addr, "00:00:00:00:00:01") + + def mac_to_ipv6_linklocal(self, mac): + # Remove the most common delimiters; dots, dashes, etc. + exclude_chars = [' ', '.', ':', '-'] + mac_value = int("".join([c for c in mac if c not in exclude_chars]), 16) + # Split out the bytes that slot into the IPv6 address + # XOR the most significant byte with 0x02, inverting the + # Universal / Local bit + high2 = mac_value >> 32 & 0xffff ^ 0x0200 + high1 = mac_value >> 24 & 0xff + low1 = mac_value >> 16 & 0xff + low2 = mac_value & 0xffff + return 'fe80::{:04x}:{:02x}ff:fe{:02x}:{:04x}'.format(high2, high1, low1, low2) + + def dhcp6_tx(self, port, group, action=0, recv=None, server_id_in=None, + requested_addr_in=None, server_mac="00:00:00:00:00:00"): + encap = group.kws.get("encap", "ethernet_ii_vlan") + mac_addr = group.kws.get("mac_addr", '00:11:01:00:00:01') + vlan_id_count = self.utils.intval(group.kws, "vlan_id_count", 0) + vlan_id_step = self.utils.intval(group.kws, "vlan_id_step", 1) + vlan_id = self.utils.intval(group.kws, "vlan_id", 0) + num_sessions = self.utils.intval(group.kws, "num_sessions", 0) + max_count = self.utils.max_value(num_sessions, vlan_id_count) + if action in [0, 3, 4, 5]: + # use new xid for action == 0 i.e sending discover + group.kws["xid"] = self.next_xid + self.next_xid = self.next_xid + max_count + for _ in range(max_count): + smac, dmac = mac_addr, 'ff:ff:ff:ff:ff:ff' + ip6_lloc = self.mac_to_ipv6_linklocal(smac) + sip, dip = ip6_lloc, "ff02::1:2" + res, ch = smac.split(":"), "" + for i in res: + ch = ch + chr(int(i, 16)) + options, param_req_list = [], [1, 3, 58, 59] + requested_addr = requested_addr_in or sip + server_id = server_id_in or dip + + if action == 0: + dmac = "33:33:00:01:00:02" + options.append(("message-type", "discover")) + options.append(("param_req_list", param_req_list)) + elif action == 1: + options.append(("message-type", "request")) + options.append(("server_id", server_id)) + options.append(("requested_addr", requested_addr)) + options.append(("param_req_list", param_req_list)) + elif action == 2: + options.append(("message-type", "request")) + options.append(("param_req_list", param_req_list)) + dmac = server_mac + sip, dip = requested_addr, server_id + elif action == 3: + options.append(("message-type", "request")) + dmac = group.kws.get("server_mac", server_mac) + options.append(("param_req_list", param_req_list)) + sip = group.kws.get("requested_addr", requested_addr) + dip = group.kws.get("server_id", server_id) + elif action == 4: + options.append(("message-type", "request")) + options.append(("param_req_list", param_req_list)) + sip = group.kws.get("requested_addr", requested_addr) + elif action == 5: + options.append(("message-type", "release")) + options.append(("server_id", server_id)) + dmac = group.kws.get("server_mac", server_mac) + sip = group.kws.get("requested_addr", requested_addr) + dip = group.kws.get("server_id", server_id) + options.append(("end", Padding())) + + pkt = Ether(src=smac, dst=dmac) + if encap in ["ethernet_ii_vlan", "ethernet_ii"] and vlan_id > 0: + # vlan_ether_type = self.pif.get_hex(group.kws, "vlan_ether_type", "0x8100") + # pkt = pkt/Dot1Q(vlan=vlan_id, type=vlan_ether_type) + pkt = pkt / Dot1Q(vlan=vlan_id) + try: + pkt = pkt / IPv6(src=sip, dst=dip, hlim=1) / UDP() + pkt = pkt / DHCP6_Solicit() + pkt = pkt / DHCP6OptElapsedTime() + pkt = pkt / DHCP6OptClientId(duid=DUID_LLT(hwtype=1, lladdr=smac, type=1)) + pkt = pkt / DHCP6OptIA_NA() + self.pkt_debug(recv, pkt, "dhcpv6", port.iface) + self.pif.send(pkt, port.iface, trace=True) + vlan_id = vlan_id + vlan_id_step + mac_addr = self.utils.incrementMac(mac_addr, "00:00:00:00:00:01") + except Exception as e: + self.logger.info("=== SIP {} DIP {}".format(sip, dip)) + self.logger.error(e) + raise e + # port.driver.enable_tx(pkt) + + def dhcp_tx(self, port, group, action=0, recv=None, server_id=None, + requested_addr=None, server_mac="00:00:00:00:00:00", + ip_version=4): + + if ip_version != 4: + self.dhcp6_tx(port, group, action, recv, server_id, requested_addr, server_mac) + else: + self.dhcp4_tx(port, group, action, recv, server_id or "0.0.0.0", + requested_addr or "0.0.0.0", server_mac) + + def read_dhcp_options(self, pkt): + options = {} + if DHCP in pkt: + for option in pkt[DHCP].options: + options[option[0]] = option[1] + else: + self.pkt_debug(pkt, None, "bootp without dhcp layer", dbg=3) + return options + + def locate_group(self, port, pkt): + xid = pkt[BOOTP].xid + for client in port.dhcp_clients.values(): + for group in client.groups.values(): + vlan_id_count = self.utils.intval(group.kws, "vlan_id_count", 0) + num_sessions = self.utils.intval(group.kws, "num_sessions", 0) + max_count = self.utils.max_value(num_sessions, vlan_id_count) + gxid = group.kws.get("xid", 0) + if xid >= gxid and xid < (gxid + max_count): + return group + return None + + def dhcp_rx(self, port, pkt): + options = self.read_dhcp_options(pkt) + if "message-type" in options: + if options["message-type"] == 2: + # offer received send request + group = self.locate_group(port, pkt) + self.dhcp_tx(port, group, 1, server_id=options["server_id"], + requested_addr=pkt[BOOTP].yiaddr, recv=pkt) + if options["message-type"] == 5 and pkt[IP].id == 0: + # ack received send renew request + group = self.locate_group(port, pkt) + self.dhcp_tx(port, group, 2, server_id=options["server_id"], + requested_addr=pkt[BOOTP].yiaddr, server_mac=pkt.src, recv=pkt) + + def igmp_tx_query(self, intf, querier, recv=None, gaddr="0.0.0.0"): + if not querier.enable: + return + igmp_version = querier.kws.get("igmp_version", "v3") + vlan_enable = self.utils.intval(intf.kws, "vlan", 0) + vlan_id = self.utils.intval(intf.kws, "vlan_id", 0) + sip = intf.kws.get("intf_ip_addr", "0.0.0.0") + smac = intf.mymac[0] + if self.pif.dbg > 2: + self.logger.dump("IGMPQ-TX INTF", intf.kws) + self.logger.dump("IGMPQ-TX HOST", querier) + pkt = Ether(src=smac) + if vlan_enable and vlan_id > 0: + pkt = pkt / Dot1Q(vlan=vlan_id) + if igmp_version in ["v1"]: + pkt = pkt / IP(src=sip) / IGMP(type=0x11, gaddr=gaddr) + pkt[IGMP].igmpize() + elif igmp_version in ["v2"]: + pkt = pkt / IP(src=sip) / IGMP(type=0x11, gaddr=gaddr) + pkt[IGMP].igmpize() + else: + pkt = pkt / IP(src=sip) / IGMPv3(type=0x11) / IGMPv3mq(gaddr=gaddr) + pkt[IGMPv3].igmpize() + self.pkt_debug(recv, pkt, "igmp", intf.iface) + self.pif.send(pkt, intf.iface, trace=True) + + def igmp_tx(self, mode, intf, host, recv=None): + igmp_version = host.kws.get("igmp_version", "v3") + vlan_enable = self.utils.intval(intf.kws, "vlan", 0) + vlan_id = self.utils.intval(intf.kws, "vlan_id", 0) + sip = intf.kws.get("intf_ip_addr", "0.0.0.0") + smac = intf.mymac[0] + if self.pif.dbg > 2: + self.logger.dump("IGMP-TX INTF", intf.kws) + self.logger.dump("IGMP-TX HOST", host) + for grp in host.kws.get("group_pool_data", []): + ip4_addr = grp.kws.get("ip_addr_start", "224.0.0.1") + ip4_step = grp.kws.get("ip_addr_step", "0.0.0.1") + for _ in range(self.utils.intval(grp.kws, "num_groups", 1)): + if mode in ["start", "join"]: + v1_type, v2_type, v3_type = 0x12, 0x16, 0x22 + else: + v1_type, v2_type, v3_type = 0x17, 0x17, 0x22 + pkt = Ether(src=smac) + if vlan_enable > 0 and vlan_id > 0: + pkt = pkt / Dot1Q(vlan=vlan_id) + if igmp_version in ["v1"]: + pkt = pkt / IP(src=sip) / IGMP(type=v1_type, gaddr=ip4_addr) + pkt[IGMP].igmpize() + elif igmp_version in ["v2"]: + pkt = pkt / IP(src=sip) / IGMP(type=v2_type, gaddr=ip4_addr) + pkt[IGMP].igmpize() + else: + pkt = pkt / IP(src=sip) / IGMPv3(type=v3_type) + if mode not in ["start", "join"]: + pkt = pkt / IGMPv3mr(numgrp=1) / IGMPv3gr(rtype=3, maddr=ip4_addr) + else: + srcaddrs = [] + for src in host.kws.get("source_pool_data", []): + ip4_src_addr = src.kws.get("ip_addr_start", "90.0.0.2") + ip4_src_step = src.kws.get("ip_addr_step", "0.0.0.1") + for _ in range(self.utils.intval(src.kws, "num_sources", 1)): + srcaddrs.append(ip4_src_addr) + ip4_src_addr = self.utils.incrementIPv4(ip4_src_addr, ip4_src_step) + rtype = 3 if grp.kws.get("g_filter_mode", "include") else 4 + pkt = pkt / IGMPv3mr(numgrp=1) / IGMPv3gr(rtype=rtype, maddr=ip4_addr, srcaddrs=srcaddrs) + pkt[IGMPv3].igmpize() + self.pkt_debug(recv, pkt, "igmp", intf.iface) + self.pif.send(pkt, intf.iface, trace=True) + ip4_addr = self.utils.incrementIPv4(ip4_addr, ip4_step) + + def igmp_rx(self, port, pkt): + send_query, send_report = False, False + if IGMP in pkt: + send_query = bool(pkt[IGMP].type == 0x17) + send_report = bool(pkt[IGMP].type == 0x11) + elif IGMPv3 in pkt: + send_report = bool(pkt[IGMPv3].type == 0x11) + if IGMPv3gr in pkt and pkt[IGMPv3gr].numsrc <= 0: + send_query = True + + for intf in port.interfaces.values(): + if send_query: + for querier in intf.igmp_queriers.values(): + self.igmp_tx_query(intf, querier, recv=pkt, gaddr=pkt.gaddr) + elif send_report: + for host in intf.igmp_hosts.values(): + self.igmp_tx("join", intf, host, recv=pkt) + + def igmp_tx_query_periodic(self, port): + now = self.utils.clock() + for intf in port.interfaces.values(): + for querier in intf.igmp_queriers.values(): + tx_time = querier.get("tx_time", now) + if tx_time < now: + continue + self.igmp_tx_query(intf, querier) + querier["tx_time"] = now + 60 + + def dot1x_tx_periodic(self, port): + now = self.utils.clock() + for client in port.dot1x_clients.values(): + tx_time = client.get("tx_time", now) + if tx_time < now: + continue + self.dot1x_tx(port, client) + client["tx_time"] = now + 60 + + def dot1x_tx(self, port, client): + if client.mode != "start": + return + smac = client.kws.get("mac_addr", "00:00:00:00:00:00") + if client.state in ["init", "logoff"]: + eapol_type = 1 if client.state == "init" else 2 + for _ in range(self.utils.intval(client.kws, "num_sessions", 1)): + pkt = Ether(src=smac, dst="01:80:c2:00:00:03") + pkt = pkt / EAPOL(version=2, type=eapol_type) + frame_size = 64 + padLen = int(frame_size - len(pkt) - 4) + if padLen > 0: + padding = Padding(binascii.unhexlify('00' * padLen)) + pkt = pkt / padding + self.pkt_debug(None, pkt, "dot1x", port.iface, dbg=3) + self.pif.send(pkt, port.iface, trace=True) + smac = self.utils.incrementMac(smac, "00:00:00:00:00:01") + + def dot1x_rx(self, port, recv): + if recv[EAP].code == 2 and recv[EAP].type == 2: + for client in port.dot1x_clients.values(): + smac = client.kws.get("mac_addr", "00:00:00:00:00:00") + username = client.kws.get("username", "test") + for _ in range(self.utils.intval(client.kws, "num_sessions", 1)): + if recv.dst == smac: + pkt = Ether(src=smac, dst="01:80:c2:00:00:03") + pkt = pkt / EAPOL(version=2, type=0) + pkt = pkt / EAP(code=2, id=recv[EAP].id, type=1, identity=username) + self.pkt_debug(recv, pkt, "dot1x", port.iface, dbg=3) + self.pif.send(pkt, port.iface, trace=True) + client.state = "" + return + smac = self.utils.incrementMac(smac, "00:00:00:00:00:01") + elif recv[EAP].code == 2 and recv[EAP].type == 4: + for client in port.dot1x_clients.values(): + smac = client.kws.get("mac_addr", "00:00:00:00:00:00") + username = client.kws.get("username", "test") + password = client.kws.get("password", "test") + for _ in range(self.utils.intval(client.kws, "num_sessions", 1)): + if recv.dst == smac: + pkt = Ether(src=smac, dst="01:80:c2:00:00:03") + pkt = pkt / EAPOL(version=2, type=0) + pkt = pkt / EAP(code=2, id=recv[EAP].id, type=4) + value = self.utils.md5sum(chr(recv[EAP].id) + self.utils.md5sum(password) + recv[EAP].value) + pkt[EAP].value = value + pkt[EAP].optional_name = username + self.pkt_debug(recv, pkt, "dot1x", port.iface, dbg=3) + self.pif.send(pkt, port.iface, trace=True) + client.state = "" + return + smac = self.utils.incrementMac(smac, "00:00:00:00:00:01") diff --git a/spytest/spytest/tgen/scapy/pyro-service.py b/spytest/spytest/tgen/scapy/pyro-service.py index 8ee7a3c53d..f3a980140a 100755 --- a/spytest/spytest/tgen/scapy/pyro-service.py +++ b/spytest/spytest/tgen/scapy/pyro-service.py @@ -1,39 +1,83 @@ import os +import sys import time import logging +import random +import socket +import fcntl +import struct import threading -import Pyro4 -from Pyro4 import naming -from service import ScapyService +try: + from Pyro5.compatibility import Pyro4 + from Pyro5.nameserver import start_ns as startNS +except Exception: + import Pyro4 + from Pyro4.naming import startNS -Pyro4.config.SERIALIZERS_ACCEPTED = set(["pickle"]) -Pyro4.config.SERIALIZER = 'pickle' +from service import ScapyService -logging.basicConfig() +use_ns = False +default_port = 8009 +logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(name)s:%(levelname)s: %(message)s') logger = logging.getLogger() -logger.setLevel(logging.DEBUG) -#os.environ["PYRO_LOGFILE"] = "pyro.log" -#os.environ["PYRO_LOGLEVEL"] = "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) -import socket -import fcntl -import struct +Pyro4.config.SERIALIZERS_ACCEPTED = set(["pickle", "serpent"]) +Pyro4.config.SERIALIZER = 'pickle' +Pyro4.config.ONEWAY_THREADED = False + +for key, value in Pyro4.config.asDict().items(): + logger.info("%s = %s", key, value) -def get_ip_address(ifname, default="0.0.0.0"): + +def get_ip_address(ifname, default="0.0.0.0", nolog=False): try: + ifname = ifname.encode('utf-8') s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) return socket.inet_ntoa(fcntl.ioctl( s.fileno(), 0x8915, # SIOCGIFADDR struct.pack('256s', ifname[:15]) )[20:24]) - except Exception: + except Exception as exp: + if not nolog: + logger.exception(exp) return default + +def web_server(port=80): + + if sys.version_info[0] >= 3: + import http.server as SimpleHTTPServer + import http.server as BaseHTTPServer + import socketserver as SocketServer + else: + import SimpleHTTPServer + import BaseHTTPServer + import SocketServer + + class ThreadingSimpleServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer): + pass + + class ServerHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): + def guess_type(self, path): + for extn in [".log", ".tgen", ".cmd", ".env", ".cfg"]: + if path.endswith(extn): + return 'text/plain' + return SimpleHTTPServer.SimpleHTTPRequestHandler.guess_type(self, path) + + os.chdir("/") + server = ThreadingSimpleServer(('', port), ServerHandler) + thread = threading.Thread(target=server.serve_forever) + thread.setDaemon(True) + thread.start() + + class NameServer(threading.Thread): def __init__(self, hostname, hmac=None): super(NameServer, self).__init__() @@ -43,8 +87,13 @@ def __init__(self, hostname, hmac=None): self.started = threading.Event() def run(self): - self.uri, self.ns_daemon, self.bc_server = \ - naming.startNS(self.hostname, hmac=self.hmac) + try: + self.uri, self.ns_daemon, self.bc_server = \ + startNS(self.hostname, hmac=self.hmac) + except Exception: + self.uri, self.ns_daemon, self.bc_server = \ + startNS(self.hostname) + self.started.set() if self.bc_server: self.bc_server.runInThread() @@ -57,7 +106,31 @@ def startNameServer(host, hmac=None): ns.started.wait() return ns + +def next_free_port(min_port=5000, max_port=6000, used=[]): + # nosemgrep-next-line + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + while True: + if len(used) >= (max_port - min_port): + break + port = random.randint(min_port, max_port) + if port in used: + continue + used.append(port) + try: + sock.bind(('', port)) + sock.close() + return port + except OSError: + pass + raise IOError('no free ports') + + class CustomDaemon(Pyro4.Daemon): + + def __init__(self, host=None, port=0): + super(CustomDaemon, self).__init__(host=host, port=port) + def clientDisconnect(self, conn): try: logger.info("client disconnects: %s", conn.sock.getpeername()) @@ -65,8 +138,9 @@ def clientDisconnect(self, conn): obj = conn.pyroInstances.get(PyRoScapyService) logger.info("Remove Instance %s", id(obj)) del obj - except Exception: - pass + except Exception as exp: + logger.exception(exp) + @Pyro4.behavior(instance_mode="session", instance_creator=lambda clazz: clazz.create_instance()) class PyRoScapyService(ScapyService): @@ -77,32 +151,47 @@ def create_instance(cls): obj.correlation_id = Pyro4.current_context.correlation_id return obj + def main(): - use_ns = True + pyver = "{}.{}.{}".format(sys.version_info.major, sys.version_info.minor, + sys.version_info.micro) + try: + use_port = int(os.getenv("SPYTEST_SCAPY_INST_PORT", str(default_port))) + except Exception: + use_port = default_port + logger.info("Python: %s", pyver) + logger.info("NS = %s PORT = %d", str(use_ns), use_port) os.environ["PYRO_DETAILED_TRACEBACK"] = "1" for _ in range(10): try: - #custom_daemon = CustomDaemon(port=8009, host="0.0.0.0") - host = get_ip_address('mgmt', None) + port = use_port or next_free_port() + host = get_ip_address('mgmt', None, True) + if not host: + host = get_ip_address('eth0', None, True) if host: - custom_daemon = CustomDaemon(host=host) + logger.info("Creating Deamon %s:%d", str(host), port) + custom_daemon = CustomDaemon(host=host, port=port) else: - custom_daemon = CustomDaemon() + logger.info("Creating Deamon %d", port) + custom_daemon = CustomDaemon(port=port) break except Exception as exp: - logger.info(exp) + logger.exception(exp) custom_daemon = None time.sleep(2) - if use_ns: startNameServer("0.0.0.0") + if use_ns: + startNameServer("0.0.0.0") + + web_server() logger.info("PYRO ScapyService started: %s", custom_daemon) Pyro4.Daemon.serveSimple( { PyRoScapyService: "scapy-tgen" }, - ns=use_ns, daemon = custom_daemon, verbose=True) + ns=use_ns, daemon=custom_daemon, verbose=True) -if __name__=="__main__": - main() +if __name__ == "__main__": + main() diff --git a/spytest/spytest/tgen/scapy/rpyc-service.py b/spytest/spytest/tgen/scapy/rpyc-service.py index d2fcd90696..a096f907f9 100755 --- a/spytest/spytest/tgen/scapy/rpyc-service.py +++ b/spytest/spytest/tgen/scapy/rpyc-service.py @@ -1,16 +1,8 @@ -import os import sys import logging import warnings from signal import signal, SIGINT -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 @@ -21,41 +13,51 @@ logger = logging.getLogger() logger.setLevel(logging.DEBUG) +data = {} + + 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, + 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, + 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') - sys.exit(0) -## install packages needed -#os.system("apt-get install -y iputils-arping") -##os.system("pip install pybrctl") -##os.system("pip install pyroute2") +def main(): + data["scapyServiceObj"] = ScapyService() + + def handler(signal_received, frame): + # Handle any cleanup here + scapyServiceObj = data.pop("scapyServiceObj", None) + if scapyServiceObj: + del data["scapyServiceObj"] + print('SIGINT or CTRL-C detected. Exiting gracefully') + sys.exit(0) + + # install packages needed + # os.system("apt-get install -y iputils-arping") + # os.system("pip install pybrctl") + # os.system("pip install pyroute2") + + signal(SIGINT, handler) + protocol_config = {"allow_pickle": True, "sync_request_timeout": 300, "allow_public_attrs": True, "allow_all_attrs": True, "instantiate_oldstyle_exceptions": True} + t = ThreadedServer(data["scapyServiceObj"], port=8009, logger=logger, protocol_config=protocol_config, backlog=1) + t.start() -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() +if __name__ == "__main__": + main() diff --git a/spytest/spytest/tgen/scapy/server.py b/spytest/spytest/tgen/scapy/server.py index d088c857f2..869f9eecb6 100755 --- a/spytest/spytest/tgen/scapy/server.py +++ b/spytest/spytest/tgen/scapy/server.py @@ -1,5 +1,6 @@ import os import sys +import copy import json import time from datetime import datetime @@ -8,6 +9,10 @@ from port import ScapyPort from logger import Logger from utils import Utils +from stats import dhcpc_stats_aggregate_init +from stats import dhcpc_stats_session_init +from stats import dhcps_stats_aggregate_init + class ScapyServer(object): def __init__(self, dry=False, dbg=0, name="scpy-tgen"): @@ -19,16 +24,17 @@ def __init__(self, dry=False, dbg=0, name="scpy-tgen"): self.mgrps = SpyTestDict() self.msrcs = SpyTestDict() self.portmap = SpyTestDict() - model = os.getenv("SCAPY_TGEN_PORTMAP", "eth1") + self.model = os.getenv("SCAPY_TGEN_PORTMAP", "eth0") + self.count = int(os.getenv("SCAPY_TGEN_PORT_COUNT", "16")) logs_root = os.getenv("SCAPY_TGEN_LOGS_PATH", "/tmp/scapy-tgen") time_spec = datetime.utcnow().strftime("%Y_%m_%d_%H_%M_%S_%f") logs_path = "{}/inst-{}".format(logs_root, time_spec) self.logger = Logger(logs_dir=logs_path, dry=dry, name=name) - self.portmap_init(model) + self.portmap_init(self.model, self.count) self.logger.set_node_name(self.node_name, "init") self.utils = Utils(self.dry, logger=self.logger) + self.utils.exec_cmd("ifconfig -a") if not dry: - self.utils.exec_cmd("ip -all netns del") self.utils.exec_cmd("sysctl -w net.bridge.bridge-nf-call-arptables=0") self.utils.exec_cmd("sysctl -w net.bridge.bridge-nf-call-ip6tables=0") self.utils.exec_cmd("sysctl -w net.bridge.bridge-nf-call-iptables=0") @@ -46,55 +52,67 @@ def trace_api(self, *args, **kws): 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() + self.mgrps.clear() + self.msrcs.clear() - def portmap_init(self, model): + def portmap_init(self, model, count): + count = count or self.count self.portmap.clear() - for i in range(0,16): + for i in range(0, count): if model == "ens6": - self.portmap["1/{}".format(i+1)] = "ens{}".format(i+6) - self.portmap["{}".format(i+1)] = "ens{}".format(i+6) + self.portmap["1/{}".format(i + 1)] = "ens{}".format(i + 6) + self.portmap["{}".format(i + 1)] = "ens{}".format(i + 6) + elif model == "eth0": + self.portmap["1/{}".format(i + 1)] = "eth{}".format(i) + self.portmap["{}".format(i + 1)] = "eth{}".format(i) elif model == "eth1": - self.portmap["1/{}".format(i+1)] = "eth{}".format(i) - self.portmap["{}".format(i+1)] = "eth{}".format(i) + self.portmap["1/{}".format(i + 1)] = "eth{}".format(i + 1) + self.portmap["{}".format(i + 1)] = "eth{}".format(i + 1) elif model == "vde": - self.portmap["1/{}".format(i+1)] = "vde{}".format(i) - self.portmap["{}".format(i+1)] = "vde{}".format(i) + self.portmap["1/{}".format(i + 1)] = "vde{}".format(i) + self.portmap["{}".format(i + 1)] = "vde{}".format(i) + elif model == "TP": + self.portmap["1/{}".format(i + 1)] = "TP{}".format(i + 1) + self.portmap["{}".format(i + 1)] = "TP{}".format(i + 1) + elif model == "TG": + self.portmap["1/{}".format(i + 1)] = "TG{}".format(i + 1) + self.portmap["{}".format(i + 1)] = "TG{}".format(i + 1) else: self.portmap["1/{}".format(i)] = "eth{}".format(i) self.portmap["{}".format(i)] = "eth{}".format(i) - def trace_result(self, res, min_dbg=2): + def trace_result(self, res, min_dbg=0, delay=0): if self.dbg >= min_dbg: - self.logger.debug("RESULT:", json.dumps(res)) + self.logger.debug("RESULT: {}".format(json.dumps(res))) + if delay: + time.sleep(delay) return res - def error(self, etype, name, value): + def error(self, etype, name, value, no_abort=False): msg = "{}: {} = {}".format(etype, name, value) self.logger.error("=================== {} ==================".format(msg)) self.errs.append(msg) - raise ValueError(msg) + if not no_abort: + raise ValueError(msg) def validate_node_name(self, node_name, *args, **kws): func = sys._getframe(1).f_code.co_name - #self.logger.debug("validate_node_name:", self.node_name, node_name, func, args, kws) + # self.logger.debug("validate_node_name:", self.node_name, node_name, func, args, kws) self.logger.debug(node_name, func, args, kws) if node_name != self.node_name: - self.logger.error("node name mismatch need {} got {}".format(self.node_name, node_name)) + self.logger.error("node name mismatch need {} got {} for func {}".format(self.node_name, node_name, func)) return False return True def exposed_server_control(self, node_name, req, data, *args, **kws): func = sys._getframe(0).f_code.co_name - #self.logger.debug(self.node_name, node_name, func, req, data, args, kws) + # self.logger.debug(self.node_name, node_name, func, req, data, args, kws) self.logger.debug(node_name, func, req, data, args, kws) retval = "" if req == "set-name" and not self.node_name: self.node_name = data self.logger.set_node_name(self.node_name, "set-name") + self.logger.info("setting node name {}".format(self.node_name)) return retval if node_name != self.node_name: self.logger.error("node name mismatch need {} got {} for {}".format(self.node_name, node_name, req)) @@ -105,17 +123,24 @@ def exposed_server_control(self, node_name, req, data, *args, **kws): self.dry = data elif req == "set-max-pps": os.environ["SPYTEST_SCAPY_MAX_RATE_PPS"] = str(data) + elif req == "set-env" and args: + os.environ[str(data)] = str(args[0]) elif req == "init-log": retval = self.logger.set_log_file(data) self.logger.banner(node_name, self.node_name, req, data) elif req == "read-log": retval = self.logger.get_log(data) + elif req == "read-pcap": + retval = self.logger.get_pcap(data) elif req == "add-log": self.logger.banner(node_name, self.node_name, req, data) elif req == "clean-all": for port in self.ports.values(): port.clean_streams() - port.clean_interfaces() + try: + port.clean_interfaces() + except Exception: + pass elif req == "get-alerts": errs = [] errs.extend(self.errs) @@ -124,7 +149,7 @@ def exposed_server_control(self, node_name, req, data, *args, **kws): self.errs = [] retval = "\n".join(errs) elif req == "set-model": - self.portmap_init(data) + self.portmap_init(data, None) else: self.error("Invalid", "server request", req) return retval @@ -133,7 +158,8 @@ def fix_port_name(self, pname): return pname.split("/")[-1].strip() def exposed_tg_connect(self, node_name, *args, **kws): - if not self.validate_node_name(node_name, *args, **kws): return "" + if not self.validate_node_name(node_name, *args, **kws): + return "" port_list = kws.get('port_list', []) res = SpyTestDict() res.port_handle = SpyTestDict() @@ -143,97 +169,131 @@ def exposed_tg_connect(self, node_name, *args, **kws): for pobj in self.ports.values(): if pobj.name == pname: delete_ports.append(pobj) + iface = self.portmap.get(pname) + if not iface: + self.error("Invalid", "port name", pname) pobj = ScapyPort(pname, self.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: + for pobj in [x for x in delete_ports]: self.logger.debug("deleting handle {} iface {}".format(pobj.port_handle, pobj.iface)) del pobj res.status = 1 return self.trace_result(res) def exposed_tg_disconnect(self, node_name, *args, **kws): - if not self.validate_node_name(node_name, *args, **kws): return "" + if not self.validate_node_name(node_name, *args, **kws): + return "" res = SpyTestDict() self.cleanup_ports() res.status = 1 return self.trace_result(res) + def all_stream_handles(self): + rv = [] + for port in self.ports.values(): + rv.extend(port.get_all_streams()) + return rv + def get_handle_map(self, stream_handle): - rv = SpyTestDict() - for handle in Utils.make_list(stream_handle): + rev_map, missing, rv = {}, [], SpyTestDict() + stream_handles = Utils.flatten_list(stream_handle, uniq=True) + for handle in stream_handles: for port in self.ports.values(): if not port.stream_validate(handle): continue + rev_map[handle] = port if port not in rv: rv[port] = [handle] else: rv[port].append(handle) - return rv + if handle not in rev_map and handle not in missing: + missing.append(handle) + return rv, missing def port_traffic_control(self, port, complete, *args, **kws): if port.traffic_control(*args, **kws): handle = kws.pop('handle', None) complete.append([port, handle]) + def show_debug_stats(self, prefix=""): + for port in self.ports.values(): + stats = port.getStats() + msg = "{}{}: TX: {} RX: {}".format(prefix, port.name, + stats.framesSent, stats.framesReceived) + self.logger.debug(msg) + for stream, stats in port.getStreamStats(): + msg = " {}: TX: {} RX: {}".format(stream.stream_id, + stats.framesSent, stats.framesReceived) + self.logger.debug(msg) + def exposed_tg_traffic_control(self, node_name, *args, **kws): - if not self.validate_node_name(node_name, *args, **kws): return "" + if not self.validate_node_name(node_name, *args, **kws): + return "" + action = Utils.kwargs_get_strip('action', None, **kws) handle = kws.pop('handle', None) port_handle = kws.pop('port_handle', None) stream_handle = kws.pop('stream_handle', None) res = True complete = [] + wait = 0 + + if action in ["stop", "start", "run"]: + self.show_debug_stats("PRE") + if port_handle: - phandle_list = Utils.make_list(port_handle) + phandle_list = Utils.make_list(port_handle, uniq=True) for phandle in phandle_list: port = self.ports[phandle] + wait = wait + 2 self.port_traffic_control(port, complete, *args, **kws) - elif handle: - handle_map = self.get_handle_map(handle) - for port, handle_list in handle_map.items(): - for handle in handle_list: - kws["handle"] = handle - self.port_traffic_control(port, complete, *args, **kws) - elif stream_handle: - handle_map = self.get_handle_map(stream_handle) + elif handle or stream_handle: + handle_map, missing = self.get_handle_map(handle or stream_handle) + if missing: + msg = "{} \n should be one of {}".format(missing, self.all_stream_handles()) + self.error("invalid", "stream(s)", msg) for port, handle_list in handle_map.items(): for handle in handle_list: kws["handle"] = handle + wait = wait + 2 self.port_traffic_control(port, complete, *args, **kws) else: for port in self.ports.values(): + wait = wait + 2 self.port_traffic_control(port, complete, *args, **kws) # call complete callback for port, handle in complete: - if not handle: kws.pop("handle", "") - else: kws["handle"] = handle + if not handle: + kws.pop("handle", "") + else: + kws["handle"] = handle port.traffic_control_complete(*args, **kws) - action = kws.get('action', None) + # wait for stats + time.sleep(wait) + if action in ["stop", "start", "run"]: - for port in self.ports.values(): - stats = port.getStats() - msg = "{}: TX: {} RX: {}".format(port.name, - stats.framesSent, stats.framesReceived) - self.logger.debug(msg) + self.show_debug_stats("POST") + 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) + raise ValueError(self.logger.error(msg)) if port_handle not in self.ports: msg = "port_handle {} is not valid".format(port_handle) - raise ValueError(msg) + raise ValueError(self.logger.error(msg)) return self.ports[port_handle] def exposed_tg_interface_control(self, node_name, *args, **kws): - if not self.validate_node_name(node_name, *args, **kws): return "" + if not self.validate_node_name(node_name, *args, **kws): + return "" port_handle = kws.get('port_handle', None) port = self.ensure_port_handle(port_handle) - mode = kws.get('mode', "aggregate") + mode = Utils.kwargs_get_strip('mode', "aggregate", **kws) if mode == "break_link": port.set_admin_status(False) return True @@ -245,20 +305,25 @@ def exposed_tg_interface_control(self, node_name, *args, **kws): self.error("Invalid", "mode", mode) def exposed_tg_packet_control(self, node_name, *args, **kws): - if not self.validate_node_name(node_name, *args, **kws): return "" + if not self.validate_node_name(node_name, *args, **kws): + return "" 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) + raise ValueError(self.logger.error(msg)) + + res = True + for phandle in Utils.make_list(port_handle, uniq=True): + port = self.ports[phandle] + res = res and port.packet_control(*args, **kws) return self.trace_result(res) def exposed_tg_packet_stats(self, node_name, *args, **kws): - if not self.validate_node_name(node_name, *args, **kws): return "" + if not self.validate_node_name(node_name, *args, **kws): + return "" res = SpyTestDict() port_handle = kws.get('port_handle', None) - mode = kws.get('mode', "aggregate") + mode = Utils.kwargs_get_strip('mode', "aggregate", **kws) res["status"] = "1" if port_handle and port_handle in self.ports: port = self.ports[port_handle] @@ -276,39 +341,42 @@ def exposed_tg_packet_stats(self, node_name, *args, **kws): return self.trace_result(res, 3) def exposed_tg_traffic_config(self, node_name, *args, **kws): - if not self.validate_node_name(node_name, *args, **kws): return "" + if not self.validate_node_name(node_name, *args, **kws): + return "" port_handle = kws.get('port_handle', None) stream_id = kws.get('stream_id', None) port_handle2 = kws.get('port_handle2', None) + validate_stream_id = True # 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 + track_ports = [] + for phandle in Utils.make_list(port_handle2 or [], uniq=True): + if phandle not in self.ports: + self.error("Invalid", "port_handle2", phandle) + else: + track_ports.append(self.ports[phandle]) # get the port handle from stream if not specified if not port_handle and stream_id: + port_handle = [] + handle_map, missing = self.get_handle_map(stream_id) + if missing: + msg = "{} \n should be one of {}".format(missing, self.all_stream_handles()) + self.error("invalid", "stream(s)", msg) + validate_stream_id = False for phandle, port in self.ports.items(): - if port.stream_validate(stream_id): - port_handle = phandle - break + if port in handle_map and phandle not in port_handle: + port_handle.append(phandle) # 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] + raise ValueError(self.logger.error(msg)) # 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) + intf = self.find_emulation_intf(emulation_src_handle) if not intf: self.error("Invalid", "emulation_src_handle", emulation_src_handle) else: @@ -317,7 +385,7 @@ def exposed_tg_traffic_config(self, node_name, *args, **kws): # 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) + intf = self.find_emulation_intf(emulation_dst_handle) if not intf: self.error("Invalid", "emulation_dst_handle", emulation_dst_handle) else: @@ -325,20 +393,25 @@ def exposed_tg_traffic_config(self, node_name, *args, **kws): # 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) + for phandle in Utils.make_list(port_handle, uniq=True): + if phandle not in self.ports: + self.error("Invalid", "port_handle", phandle) + else: + port = self.ports[phandle] + rv = port.traffic_config(track_ports, validate_stream_id, *args, **kws) + res.update(rv) return self.trace_result(res) - def find_intf(self, handle): + def find_emulation_intf(self, handle): if isinstance(handle, list): handle = handle[0] + if handle in self.mgrps: + return self.mgrps[handle] for port in self.ports.values(): intf = port.find_interface(handle) if intf: - return intf + return port.validate_interface(intf) return None def get_all_handles(self): @@ -348,238 +421,544 @@ def get_all_handles(self): rv[handle] = port return rv - def verify_handle(self, *args, **kws): + def get_port_list(self, port_handle, param_name): + port_list = [] + for phandle in Utils.make_list(port_handle or [], uniq=True): + if phandle not in self.ports: + self.error("Invalid", param_name, phandle) + else: + port_list.append(self.ports[phandle]) + return port_list + + def verify_handle(self, no_abort, *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) + raise ValueError(self.logger.error(msg)) if port_handle: - return self.ports[port_handle] + return self.get_port_list(port_handle, "port_handle") intfs = self.get_all_handles() - if protocol_handle: handle = protocol_handle - if isinstance(handle, list): handle = handle[0] - if handle in intfs: return intfs[handle] + if protocol_handle: + handle = protocol_handle + if isinstance(handle, list): + handle = handle[0] + if handle in intfs: + return [intfs[handle]] self.logger.info("valid interfaces: {}".format(intfs.keys())) if protocol_handle: - self.error("Invalid", "protocol_handle", protocol_handle) + self.error("Invalid", "protocol_handle", protocol_handle, no_abort) else: - self.error("Invalid", "handle", handle) + self.error("Invalid", "handle", handle, no_abort) def exposed_tg_interface_config(self, node_name, *args, **kws): - if not self.validate_node_name(node_name, *args, **kws): return "" - port = self.verify_handle(*args, **kws) - res = port.interface_config(*args, **kws) - time.sleep(2) + if not self.validate_node_name(node_name, *args, **kws): + return "" + res = True + mode = Utils.kwargs_get_strip('mode', None, **kws) + for port in self.verify_handle(bool(mode == "destroy"), *args, **kws): + res = res and port.interface_config(*args, **kws) + if res: + time.sleep(2) return self.trace_result(res) def exposed_tg_emulation_bgp_config(self, node_name, *args, **kws): - if not self.validate_node_name(node_name, *args, **kws): return "" - port = self.verify_handle(*args, **kws) - res = port.emulation_bgp_config(*args, **kws) - time.sleep(2) + if not self.validate_node_name(node_name, *args, **kws): + return "" + res = True + for port in self.verify_handle(False, *args, **kws): + res = res and port.emulation_bgp_config(*args, **kws) + if res: + time.sleep(2) return self.trace_result(res) def exposed_tg_emulation_bgp_route_config(self, node_name, *args, **kws): - if not self.validate_node_name(node_name, *args, **kws): return "" - port = self.verify_handle(*args, **kws) - res = port.emulation_bgp_route_config(*args, **kws) - time.sleep(2) + if not self.validate_node_name(node_name, *args, **kws): + return "" + res = True + for port in self.verify_handle(False, *args, **kws): + res = res and port.emulation_bgp_route_config(*args, **kws) + if res: + time.sleep(2) return self.trace_result(res) def exposed_tg_emulation_bgp_control(self, node_name, *args, **kws): - if not self.validate_node_name(node_name, *args, **kws): return "" - port = self.verify_handle(*args, **kws) - res = port.emulation_bgp_control(*args, **kws) - time.sleep(2) - return self.trace_result(res) - - def exposed_tg_emulation_igmp_config(self, node_name, *args, **kws): - if not self.validate_node_name(node_name, *args, **kws): return "" - port = self.verify_handle(*args, **kws) - res = port.emulation_igmp_config(*args, **kws) - time.sleep(2) + if not self.validate_node_name(node_name, *args, **kws): + return "" + res = True + for port in self.verify_handle(False, *args, **kws): + res = res and port.emulation_bgp_control(*args, **kws) + if res: + time.sleep(2) return self.trace_result(res) def exposed_tg_emulation_multicast_group_config(self, node_name, *args, **kws): - if not self.validate_node_name(node_name, *args, **kws): return "" - mode = kws.get('mode', "create") + if not self.validate_node_name(node_name, *args, **kws): + return "" + mode = Utils.kwargs_get_strip('mode', "create", **kws) index = len(self.mgrps) if mode == "create": group = SpyTestDict() - group.update(kws) + group.kws = copy.copy(kws) group.index = index group.name = "mgrp-{}".format(group.index) self.mgrps[group.name] = group res = SpyTestDict() res["status"] = "1" + res["handle"] = group.name res["mul_group_handle"] = group.name + res["multicast_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, node_name, *args, **kws): - if not self.validate_node_name(node_name, *args, **kws): return "" - mode = kws.get('mode', "create") + if not self.validate_node_name(node_name, *args, **kws): + return "" + mode = Utils.kwargs_get_strip('mode', "create", **kws) 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 + source = SpyTestDict() + source.kws = copy.copy(kws) + source.index = index + source.name = "msrc-{}".format(source.index) + self.msrcs[source.name] = source res = SpyTestDict() res["status"] = "1" - res["mul_source_handle"] = group.name + res["handle"] = source.name + res["mul_source_handle"] = source.name + res["multicast_source_handle"] = source.name return self.trace_result(res) self.error("Invalid", "emulation_multicast_source_config: mode", mode) + def exposed_tg_emulation_igmp_config(self, node_name, *args, **kws): + if not self.validate_node_name(node_name, *args, **kws): + return "" + for port in self.ports.values(): + res = port.emulation_igmp_config(*args, **kws) + if res: + return self.trace_result(res, delay=2) + self.error("Invalid", "exposed_tg_emulation_igmp_config", kws) + return {} + def exposed_tg_emulation_igmp_group_config(self, node_name, *args, **kws): - if not self.validate_node_name(node_name, *args, **kws): return "" - mode = kws.get('mode', "create") + if not self.validate_node_name(node_name, *args, **kws): + return "" + mode = Utils.kwargs_get_strip('mode', "create", **kws) 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) + for ph in Utils.make_list(group_pool_handle): + if ph not in self.mgrps: + self.error("Invalid", "emulation_igmp_group_config: group_pool_handle", ph) 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)) + if source_pool_handle: + for ph in Utils.make_list(source_pool_handle): + if ph not in self.msrcs: + self.error("Invalid", "emulation_igmp_group_config: source_pool_handle", ph) + 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) + if not port.igmp_host_validate(host_handle): + continue + kws["group_pool_data"] = [] + kws["source_pool_data"] = [] + if mode in ["create"]: + for ph in Utils.make_list(group_pool_handle): + grp = copy.copy(self.mgrps[group_pool_handle]) + grp.enable = True + kws["group_pool_data"].append(grp) + if source_pool_handle: + for ph in Utils.make_list(source_pool_handle): + src = copy.copy(self.msrcs[source_pool_handle]) + src.enable = True + kws["source_pool_data"].append(src) + 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_querier_config(self, node_name, *args, **kws): + if not self.validate_node_name(node_name, *args, **kws): + return "" + res = {} + for port in self.ports.values(): + res.update(port.emulation_igmp_querier_config(*args, **kws)) + if res: + time.sleep(2) + else: + self.error("Invalid", "exposed_tg_emulation_igmp_querier_config", kws) + return self.trace_result(res) + def exposed_tg_emulation_igmp_control(self, node_name, *args, **kws): - if not self.validate_node_name(node_name, *args, **kws): return "" - mode = kws.get('mode', "start") + if not self.validate_node_name(node_name, *args, **kws): + return "" + mode = Utils.kwargs_get_strip('mode', "start", **kws) if mode not in ["start", "stop", "join", "leave"]: - self.error("Invalid", "emulation_igmp_control: mode", mode) - host_handle = kws.get('handle', None) + self.error("Invalid", "exposed_tg_emulation_igmp_control: mode", mode) + handle = kws.get('handle', None) + if not handle: + self.error("Invalid", "exposed_tg_emulation_igmp_control: handle", handle) + for port in self.ports.values(): + res = port.emulation_igmp_control(*args, **kws) + if res: + return self.trace_result(res, delay=2) + self.error("Invalid", "exposed_tg_emulation_igmp_control", kws) + return {} + + def exposed_tg_emulation_igmp_querier_control(self, node_name, *args, **kws): + if not self.validate_node_name(node_name, *args, **kws): + return "" + for port in self.ports.values(): + res = port.emulation_igmp_querier_control(*args, **kws) + if res: + return self.trace_result(res, delay=2) + self.error("Invalid", "exposed_tg_emulation_igmp_querier_control", kws) + + def exposed_tg_emulation_ospf_config(self, node_name, *args, **kws): + if not self.validate_node_name(node_name, *args, **kws): + return "" + for port in self.ports.values(): + res = port.emulation_ospf_config(*args, **kws) + if res: + return self.trace_result(res, delay=2) + return {} + + def exposed_tg_emulation_ospf_control(self, node_name, *args, **kws): + if not self.validate_node_name(node_name, *args, **kws): + return "" + for port in self.ports.values(): + res = port.emulation_ospf_control(*args, **kws) + if res: + return self.trace_result(res, delay=2) + return {} + + def exposed_tg_emulation_ospf_route_config(self, node_name, *args, **kws): + if not self.validate_node_name(node_name, *args, **kws): + return "" + for port in self.ports.values(): + res = port.emulation_ospf_route_config(*args, **kws) + if res: + return self.trace_result(res, delay=2) + return {} + + def exposed_tg_emulation_ospf_lsa_config(self, node_name, *args, **kws): + if not self.validate_node_name(node_name, *args, **kws): + return "" + self.error("unsupported", "api", "tg_emulation_ospf_lsa_config") + + def exposed_tg_emulation_ospf_network_group_config(self, node_name, *args, **kws): + if not self.validate_node_name(node_name, *args, **kws): + return "" + self.error("unsupported", "api", "tg_emulation_ospf_network_group_config") + + def exposed_tg_emulation_ospf_topology_route_config(self, node_name, *args, **kws): + if not self.validate_node_name(node_name, *args, **kws): + return "" + self.error("unsupported", "api", "tg_emulation_ospf_topology_route_config") + + def exposed_tg_emulation_dhcp_config(self, node_name, *args, **kws): + if not self.validate_node_name(node_name, *args, **kws): + return "" + port_handle = kws.get('port_handle', None) + if not port_handle or port_handle not in self.ports: + self.error("Invalid", "port_handle", port_handle) + port = self.ports[port_handle] + return port.emulation_dhcp_client_config(*args, **kws) + + def exposed_tg_emulation_dhcp_group_config(self, node_name, *args, **kws): + if not self.validate_node_name(node_name, *args, **kws): + return "" + for port in self.ports.values(): + res = port.emulation_dhcp_client_group_config(*args, **kws) + if res: + return self.trace_result(res, delay=2) + return {} + + def exposed_tg_emulation_dhcp_control(self, node_name, *args, **kws): + if not self.validate_node_name(node_name, *args, **kws): + return "" + port_handle = kws.get('port_handle', None) + if port_handle and port_handle in self.ports: + port = self.ports[port_handle] + return port.emulation_dhcp_client_control(*args, **kws) + for port in self.ports.values(): + res = port.emulation_dhcp_client_control(*args, **kws) + if res: + return self.trace_result(res, delay=2) + return {} + + def exposed_tg_emulation_dhcp_server_config(self, node_name, *args, **kws): + if not self.validate_node_name(node_name, *args, **kws): + return "" + for port in self.ports.values(): + res = port.emulation_dhcp_server_config(*args, **kws) + if res: + return self.trace_result(res, delay=2) + return {} + + def exposed_tg_emulation_dhcp_server_control(self, node_name, *args, **kws): + if not self.validate_node_name(node_name, *args, **kws): + return "" + for port in self.ports.values(): + res = port.emulation_dhcp_server_control(*args, **kws) + if res: + return self.trace_result(res, delay=2) + return {} + + def exposed_tg_emulation_dhcp_server_stats(self, node_name, *args, **kws): + if not self.validate_node_name(node_name, *args, **kws): + return "" + res = SpyTestDict() + mode = Utils.kwargs_get_strip('mode', "aggregate", **kws) + if mode not in ["aggregate_stats", "aggregate"]: + self.error("Invalid", "mode", mode) + action = Utils.kwargs_get_strip('action', "get", **kws) + if action not in ["clear", "collect"]: + self.error("Invalid", "action", action) + port_handle = kws.get('port_handle', None) + if mode in ["aggregate_stats", "aggregate"]: + res["aggregate"] = SpyTestDict() + res["aggregate"][port_handle] = dhcps_stats_aggregate_init(port_handle) + + return self.trace_result(res) + + def exposed_tg_emulation_dhcp_server_relay_agent_config(self, node_name, *args, **kws): + if not self.validate_node_name(node_name, *args, **kws): + return "" 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) + res = port.emulation_dhcp_server_relay_agent_config(*args, **kws) + if res: + return self.trace_result(res, delay=2) + return {} + + def exposed_tg_emulation_dhcp_stats(self, node_name, *args, **kws): + if not self.validate_node_name(node_name, *args, **kws): + return "" + action = Utils.kwargs_get_strip('action', "get", **kws) + mode = Utils.kwargs_get_strip('mode', "aggregate", **kws) + port_handle = kws.get('port_handle', None) + if mode not in ["detailed_session", "session", "aggregate_stats", "aggregate"]: + self.error("Invalid", "mode", mode) + res = SpyTestDict() + + if action == "clear": + # todo + pass + elif mode in ["aggregate_stats", "aggregate"]: + values = dhcpc_stats_aggregate_init(port_handle) + res[port_handle] = SpyTestDict() + res[port_handle]["aggregate"] = values + res["aggregate"] = copy.copy(values) + else: + if port_handle in self.ports: + port = self.ports[port_handle] + res["session"] = SpyTestDict() + for client in port.dhcp_clients.values(): + for group_handle in client.groups: + res["session"][group_handle] = dhcpc_stats_session_init(group_handle) + + return self.trace_result(res) + + def fill_traffic_item_streams(self, port, report_streams, dbg_msgs, rev=False): + for p in self.ports.values(): + for stream, stats in p.getStreamStats(): + for track_port in stream.track_ports: + dbg_msgs.append("{} tracking {}".format(stream.stream_id, track_port.port_handle)) + if track_port == port: + if rev: + if self.dbg > 3: + report_streams.append([stream, stats, stats, port]) + else: + report_streams.append([stream, port.getStats(), stats, port]) + else: + if self.dbg > 3: + report_streams.append([stream, stats, stats, port]) + else: + report_streams.append([stream, stats, port.getStats(), port]) def exposed_tg_traffic_stats(self, node_name, *args, **kws): - if not self.validate_node_name(node_name, *args, **kws): return "" + if not self.validate_node_name(node_name, *args, **kws): + return "" 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: + port_handle = kws.get('port_handle', list(self.ports.keys())) + port_handles = Utils.make_list(port_handle) + for port_handle in port_handles: + if 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(): + continue + port = self.ports[port_handle] + mode = Utils.kwargs_get_strip('mode', "aggregate", **kws) + time.sleep(5) + if mode == "aggregate": + stats = port.getStats() + res[port_handle] = SpyTestDict() + res[port_handle][mode] = SpyTestDict() + res[port_handle]["name"] = port.iface + self.fill_stats(res[port_handle][mode], stats, stats, port=port) + elif mode == "traffic_item_0": + res[mode] = SpyTestDict() 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) + res[mode][stream.stream_id] = SpyTestDict() + self.fill_stats(res[mode][stream.stream_id], stats, stats, stream=stream) + for track_port in stream.track_ports: + for stream, stats in track_port.getStreamStats(): + res[mode][stream.stream_id] = SpyTestDict() + self.fill_stats(res[mode][stream.stream_id], stats, stats, stream=stream) + elif mode == "traffic_item": + res[mode] = SpyTestDict() + + # identify the streams where given port is track port + report_streams, dbg_msgs = [], [] + self.fill_traffic_item_streams(port, report_streams, dbg_msgs) + for stream, _ in port.getStreamStats(): + for track_port in stream.track_ports: + self.fill_traffic_item_streams(track_port, report_streams, dbg_msgs, True) + if not report_streams: + self.logger.error("No traffic item identified for {}".format(port_handle)) + for msg in dbg_msgs: + self.logger.error("CHK: {}".format(msg)) + + # add the stats of the identified streams + for stream, tx_stats, rx_stats, rx_port in report_streams: + if stream.stream_id in res[mode]: + self.logger.warning("Already Filled {}".format(stream.stream_id)) + continue + res[mode][stream.stream_id] = SpyTestDict() + self.fill_stats(res[mode][stream.stream_id], tx_stats, + rx_stats, port=rx_port, stream=stream) + # self.logger.error("RESULT", res) + + elif mode in ["stream", "streams"]: + streams = Utils.make_list(kws.get('streams', [])) + rv, stats_list = self.fill_streams(res, port, streams) + if not rv: + self.error("no-matching-stream", streams, stats_list) + elif mode == "flow": + stats = port.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, port=port) + self.fill_stats(res[mode]["2"], stats, stats, port=port) + 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) + return {"count": val, "max": 0, "min": 0, "sum": 0, "avg": 0} + + def fill_stats(self, res, tx_stats, rx_stats, detailed=False, port=None, stream=None): + if tx_stats: + res["tx"] = SpyTestDict() + # TODO compute the total_pkt_rate + 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["tx"]["name"] = stream.stream_id if stream else "" + if rx_stats: + res["rx"] = SpyTestDict() + # TODO compute the raw_pkt_rate + res["rx"]["total_pkt_rate"] = self.stat_value(1, detailed) + 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) + res["rx"]["name"] = port.port_handle if port else "" + + def fill_streams(self, res, port, stream_id_list=None): + # stats = port.getStats() + # self.logger.error("PORT", port.port_handle, stats) + retval, stats_list = False, port.getStreamStats() + for stream, stats in stats_list: + if stream_id_list and stream.stream_id not in stream_id_list: + continue + retval = True + # self.logger.error("STREAM", stream.stream_id, stats) + res1 = res.setdefault(port.port_handle, SpyTestDict()) + res2 = res1.setdefault("stream", SpyTestDict()) + res3 = res2.setdefault(stream.stream_id, SpyTestDict()) + self.fill_stats(res3, stats, None, stream=stream) + # self.logger.error("RESULT-1", res) + for track_port in stream.track_ports: + res4 = res.setdefault(track_port.port_handle, SpyTestDict()) + res4["name"] = track_port.iface + res5 = res4.setdefault("stream", SpyTestDict()) + res6 = res5.setdefault(stream.stream_id, SpyTestDict()) + stats = track_port.getStats() + # self.logger.error("TRACK-PORT", track_port.port_handle, stats) + self.fill_stats(res6, None, stats, port=track_port) + # self.logger.error("RESULT-2", res) + # for stream2, stats in track_port.getStreamStats(): + # self.logger.error("TRACK-STREAM", stream2.stream_id, stats) + + return retval, stats_list + + def exposed_tg_custom_filter_config(self, node_name, *args, **kws): + self.error("unsupported", "api", "tg_custom_filter_config") + + def exposed_tg_emulation_dot1x_config(self, node_name, *args, **kws): + if not self.validate_node_name(node_name, *args, **kws): + return "" + for port in self.ports.values(): + res = port.emulation_dot1x_config(*args, **kws) + if res: + return self.trace_result(res, delay=2) + self.error("Invalid", "exposed_tg_emulation_dot1x_config", kws) + return {} + + def exposed_tg_emulation_dot1x_control(self, node_name, *args, **kws): + if not self.validate_node_name(node_name, *args, **kws): + return "" + for port in self.ports.values(): + res = port.emulation_dot1x_control(*args, **kws) + if res: + return self.trace_result(res, delay=2) + self.error("Invalid", "exposed_tg_emulation_dot1x_control", kws) + return {} + + def exposed_tg_emulation_mld_config(self, node_name, *args, **kws): + self.error("unsupported", "api", "tg_emulation_mld_config") + + def exposed_tg_emulation_mld_group_config(self, node_name, *args, **kws): + self.error("unsupported", "api", "tg_emulation_mld_group_config") + + def exposed_tg_emulation_mld_querier_config(self, node_name, *args, **kws): + self.error("unsupported", "api", "tg_emulation_mld_querier_config") + + def exposed_tg_emulation_mld_control(self, node_name, *args, **kws): + self.error("unsupported", "api", "tg_emulation_mld_control") + + def exposed_tg_emulation_mld_querier_control(self, node_name, *args, **kws): + self.error("unsupported", "api", "tg_emulation_mld_querier_control") + def exposed_tg_emulation_ipv6_autoconfig(self, node_name, *args, **kws): + self.error("unsupported", "api", "tg_emulation_ipv6_autoconfig") + def exposed_tg_emulation_ipv6_autoconfig_control(self, node_name, *args, **kws): + self.error("unsupported", "api", "tg_emulation_ipv6_autoconfig_control") diff --git a/spytest/spytest/tgen/scapy/service.py b/spytest/spytest/tgen/scapy/service.py index 3c3b837123..0326584f52 100755 --- a/spytest/spytest/tgen/scapy/service.py +++ b/spytest/spytest/tgen/scapy/service.py @@ -1,16 +1,21 @@ import os -import Pyro4 -#import threading + +try: + from Pyro5.compatibility import Pyro4 +except Exception: + import Pyro4 + +# import threading from datetime import datetime from server import ScapyServer + @Pyro4.expose class ScapyService(object): def __init__(self): print("scapy-service-start") dry = bool(os.getenv("SPYTEST_SCAPY_DRYRUN", "0") == "1") - #name = threading.current_thread().name time_spec = datetime.utcnow().strftime("%Y_%m_%d_%H_%M_%S_%f") name = "inst-{}".format(time_spec) self.server = ScapyServer(dry=dry, name=name) @@ -24,55 +29,175 @@ def __del__(self): def server_control(self, *args, **kws): self.server.trace_api(*args, **kws) return self.server.exposed_server_control(*args, **kws) + def tg_connect(self, *args, **kws): self.server.trace_api(*args, **kws) return self.server.exposed_tg_connect(*args, **kws) + def tg_disconnect(self, *args, **kws): self.server.trace_api(*args, **kws) return self.server.exposed_tg_disconnect(*args, **kws) + def tg_traffic_control(self, *args, **kws): self.server.trace_api(*args, **kws) return self.server.exposed_tg_traffic_control(*args, **kws) + def tg_interface_control(self, *args, **kws): self.server.trace_api(*args, **kws) return self.server.exposed_tg_interface_control(*args, **kws) + def tg_packet_control(self, *args, **kws): self.server.trace_api(*args, **kws) return self.server.exposed_tg_packet_control(*args, **kws) + def tg_packet_stats(self, *args, **kws): self.server.trace_api(*args, **kws) return self.server.exposed_tg_packet_stats(*args, **kws) + def tg_traffic_config(self, *args, **kws): self.server.trace_api(*args, **kws) return self.server.exposed_tg_traffic_config(*args, **kws) + def tg_interface_config(self, *args, **kws): self.server.trace_api(*args, **kws) return self.server.exposed_tg_interface_config(*args, **kws) + def tg_traffic_stats(self, *args, **kws): self.server.trace_api(*args, **kws) return self.server.exposed_tg_traffic_stats(*args, **kws) + def tg_emulation_bgp_config(self, *args, **kws): self.server.trace_api(*args, **kws) return self.server.exposed_tg_emulation_bgp_config(*args, **kws) + def tg_emulation_bgp_route_config(self, *args, **kws): self.server.trace_api(*args, **kws) return self.server.exposed_tg_emulation_bgp_route_config(*args, **kws) + def tg_emulation_bgp_control(self, *args, **kws): self.server.trace_api(*args, **kws) return self.server.exposed_tg_emulation_bgp_control(*args, **kws) - def tg_emulation_igmp_config(self, *args, **kws): - self.server.trace_api(*args, **kws) - return self.server.exposed_tg_emulation_igmp_config(*args, **kws) + def tg_emulation_multicast_group_config(self, *args, **kws): self.server.trace_api(*args, **kws) return self.server.exposed_tg_emulation_multicast_group_config(*args, **kws) + def tg_emulation_multicast_source_config(self, *args, **kws): self.server.trace_api(*args, **kws) return self.server.exposed_tg_emulation_multicast_source_config(*args, **kws) + + def tg_emulation_igmp_config(self, *args, **kws): + self.server.trace_api(*args, **kws) + return self.server.exposed_tg_emulation_igmp_config(*args, **kws) + def tg_emulation_igmp_group_config(self, *args, **kws): self.server.trace_api(*args, **kws) return self.server.exposed_tg_emulation_igmp_group_config(*args, **kws) + + def tg_emulation_igmp_querier_config(self, *args, **kws): + self.server.trace_api(*args, **kws) + return self.server.exposed_tg_emulation_igmp_querier_config(*args, **kws) + def tg_emulation_igmp_control(self, *args, **kws): self.server.trace_api(*args, **kws) return self.server.exposed_tg_emulation_igmp_control(*args, **kws) + def tg_emulation_igmp_querier_control(self, *args, **kws): + self.server.trace_api(*args, **kws) + return self.server.exposed_tg_emulation_igmp_querier_control(*args, **kws) + + def tg_emulation_ospf_config(self, *args, **kws): + self.server.trace_api(*args, **kws) + return self.server.exposed_tg_emulation_ospf_config(*args, **kws) + + def tg_emulation_ospf_control(self, *args, **kws): + self.server.trace_api(*args, **kws) + return self.server.exposed_tg_emulation_ospf_control(*args, **kws) + + def tg_emulation_ospf_route_config(self, *args, **kws): + self.server.trace_api(*args, **kws) + return self.server.exposed_tg_emulation_ospf_route_config(*args, **kws) + + def tg_emulation_ospf_lsa_config(self, *args, **kws): + self.server.trace_api(*args, **kws) + return self.server.exposed_tg_emulation_ospf_lsa_config(*args, **kws) + + def tg_emulation_ospf_network_group_config(self, *args, **kws): + self.server.trace_api(*args, **kws) + return self.server.exposed_tg_emulation_ospf_network_group_config(*args, **kws) + + def tg_emulation_ospf_topology_route_config(self, *args, **kws): + self.server.trace_api(*args, **kws) + return self.server.exposed_tg_emulation_ospf_topology_route_config(*args, **kws) + + def tg_emulation_dhcp_config(self, *args, **kws): + self.server.trace_api(*args, **kws) + return self.server.exposed_tg_emulation_dhcp_config(*args, **kws) + + def tg_emulation_dhcp_control(self, *args, **kws): + self.server.trace_api(*args, **kws) + return self.server.exposed_tg_emulation_dhcp_control(*args, **kws) + + def tg_emulation_dhcp_group_config(self, *args, **kws): + self.server.trace_api(*args, **kws) + return self.server.exposed_tg_emulation_dhcp_group_config(*args, **kws) + + def tg_emulation_dhcp_server_config(self, *args, **kws): + self.server.trace_api(*args, **kws) + return self.server.exposed_tg_emulation_dhcp_server_config(*args, **kws) + + def tg_emulation_dhcp_server_control(self, *args, **kws): + self.server.trace_api(*args, **kws) + return self.server.exposed_tg_emulation_dhcp_server_control(*args, **kws) + + def tg_emulation_dhcp_server_stats(self, *args, **kws): + self.server.trace_api(*args, **kws) + return self.server.exposed_tg_emulation_dhcp_server_stats(*args, **kws) + + def tg_emulation_dhcp_server_relay_agent_config(self, *args, **kws): + self.server.trace_api(*args, **kws) + return self.server.exposed_tg_emulation_dhcp_server_relay_agent_config(*args, **kws) + + def tg_emulation_dhcp_stats(self, *args, **kws): + self.server.trace_api(*args, **kws) + return self.server.exposed_tg_emulation_dhcp_stats(*args, **kws) + + def tg_custom_filter_config(self, *args, **kws): + self.server.trace_api(*args, **kws) + return self.server.exposed_tg_custom_filter_config(*args, **kws) + + def tg_emulation_dot1x_config(self, *args, **kws): + self.server.trace_api(*args, **kws) + return self.server.exposed_tg_emulation_dot1x_config(*args, **kws) + + def tg_emulation_dot1x_control(self, *args, **kws): + self.server.trace_api(*args, **kws) + return self.server.exposed_tg_emulation_dot1x_control(*args, **kws) + + def tg_emulation_mld_config(self, *args, **kws): + self.server.trace_api(*args, **kws) + return self.server.exposed_tg_emulation_mld_config(*args, **kws) + + def tg_emulation_mld_group_config(self, *args, **kws): + self.server.trace_api(*args, **kws) + return self.server.exposed_tg_emulation_mld_group_config(*args, **kws) + + def tg_emulation_mld_querier_config(self, *args, **kws): + self.server.trace_api(*args, **kws) + return self.server.exposed_tg_emulation_mld_querier_config(*args, **kws) + + def tg_emulation_mld_control(self, *args, **kws): + self.server.trace_api(*args, **kws) + return self.server.exposed_tg_emulation_mld_control(*args, **kws) + + def tg_emulation_mld_querier_control(self, *args, **kws): + self.server.trace_api(*args, **kws) + return self.server.exposed_tg_emulation_mld_querier_control(*args, **kws) + + def tg_emulation_ipv6_autoconfig(self, *args, **kws): + self.server.trace_api(*args, **kws) + return self.server.exposed_tg_emulation_ipv6_autoconfig(*args, **kws) + + def tg_emulation_ipv6_autoconfig_control(self, *args, **kws): + self.server.trace_api(*args, **kws) + return self.server.exposed_tg_emulation_ipv6_autoconfig_control(*args, **kws) diff --git a/spytest/spytest/tgen/scapy/service.sh b/spytest/spytest/tgen/scapy/service.sh index 540090bb8e..4786d393fd 100755 --- a/spytest/spytest/tgen/scapy/service.sh +++ b/spytest/spytest/tgen/scapy/service.sh @@ -1,6 +1,8 @@ #!/bin/bash -ROOT=/tmp/scapy-tgen +PYTHON=${PYTHON:=python} +ROOT=${ROOT:=/tmp/scapy-tgen} + mkdir -p $ROOT set -x @@ -37,5 +39,5 @@ cleanup() } trap cleanup SIGINT SIGTERM EXIT -python pyro-service.py 2>&1 | tee $HOME/service.log - +#$PYTHON rpyc-service.py 2>&1 | tee $HOME/service.log +$PYTHON pyro-service.py 2>&1 | tee $HOME/service.log diff --git a/spytest/spytest/tgen/scapy/stats.py b/spytest/spytest/tgen/scapy/stats.py new file mode 100755 index 0000000000..201cf7c988 --- /dev/null +++ b/spytest/spytest/tgen/scapy/stats.py @@ -0,0 +1,113 @@ +from dicts import SpyTestDict + + +def port_stats_init(stats=None): + stats = stats or SpyTestDict() + 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 + return stats + + +def dhcpc_stats_aggregate_init(port_name): + res = SpyTestDict() + res.port_name = port_name + res.offer_rx_count = 0 + res.success_percentage = 0 + res.release_tx_count = 0 + res.setup_success = 0 + res.ack_rx_count = 0 + res.rx = SpyTestDict() + res.rx.force_renew = 0 + res.enabled_interfaces = 0 + res.currently_idle = 0 + res.addr_discovered = 0 + res.teardown_initiated = 0 + res.teardown_success = 0 + res.total_failed = 0 + res.request_tx_count = 0 + res.discover_tx_count = 0 + res.currently_attempting = 0 + res.nak_rx_count = 0 + res.sessions_total = 0 + res.sessions_not_started = 0 + res.setup_fail = 0 + res.total_attempted = 0 + res.avgerage_teardown_rate = 0 + res.setup_initiated = 0 + res.currently_bound = 0 + res.teardown_failed = 0 + res.declines_tx_count = 0 + res.average_setup_time = 0 + return res + + +def dhcpc_stats_session_init(handle): + res = SpyTestDict() + res.lease_time = 3600 + res.address = "" + res.device_group = "" + res.port_name = handle + res.protocol = "" + res.offer_rx_count = 1 + res.information = "none" + res.release_tx_count = 0 + res["discover/rapid_commit_tx"] = 0 + res.ack_rx_count = 1 + res.rx = SpyTestDict() + res.rx.force_renew = 0 + res.gateway = "" + res.ip_addr = "0.0.0.0" + res.Address = "" + res.device_id = 0 + res.status = "" + res.request_tx_count = 1 + res.discover_tx_count = 1 + res.lease_establishment_time = 12 + res["ack/rapid_commit_rx"] = 0 + res.nak_rx_count = 0 + res.topology = "" + res["lease/rapid_commit"] = "" + res.Gateway = "" + res.Prefix = 24 + res.declines_tx_count = 0 + return res + + +def dhcps_stats_aggregate_init(port_name): + res = SpyTestDict() + res.port_name = port_name + res.rx = SpyTestDict() + res.rx.solicit = 0 + res.rx.confirm = 0 + res.rx.renew = 0 + res.rx.rebind = 0 + res.rx.request = 0 + res.rx.decline = 0 + res.rx.release = 0 + res.rx.inform = 0 + res.rx.relay_forward = 0 + res.rx.relay_reply = 0 + res.tx = SpyTestDict() + res.tx.advertisement = 0 + res.tx.reply = 0 + res.total_addresses_allocated = 0 + res.total_addresses_renewed = 0 + res.current_addresses_allocated = 0 + res.total_prefixes_allocated = 0 + res.total_prefixes_renewed = 0 + res.current_prefixes_allocated = 0 + res.reconfigure_tx = 0 + res.sessions_up = 0 + res.sessions_down = 0 + res.sessions_not_started = 0 + res.session_total = 0 + res.nak_sent = 0 + res.solicits_ignored = 0 + return res diff --git a/spytest/spytest/tgen/scapy/unit_test.py b/spytest/spytest/tgen/scapy/unit_test.py index d17304595a..6c1c431dde 100755 --- a/spytest/spytest/tgen/scapy/unit_test.py +++ b/spytest/spytest/tgen/scapy/unit_test.py @@ -1,3 +1,9 @@ +#!/bin/sh + +''':' +exec $(dirname $0)/../../../bin/python "$0" "$@" +''' + import os import sys import json @@ -16,12 +22,13 @@ logging.getLogger("Pyro4").setLevel(logging.DEBUG) logging.getLogger("Pyro4.core").setLevel(logging.DEBUG) + class TGScapyTest(ScapyClient): - def __init__(self, tg_ip=None, tg_port=8009, tg_port_list=None): - ScapyClient.__init__(self, tg_ip, tg_port, tg_port_list) - #os.environ["SCAPY_TGEN_PORTMAP"] = "vde" - #self.scapy_connect() - self.scapy_connect(True) + def __init__(self, tg_ip=None, tg_port=8009, tg_port_list=None, dry_run=False): + ScapyClient.__init__(self, None, tg_ip, tg_port, tg_port_list) + # os.environ["SCAPY_TGEN_PORTMAP"] = "vde" + os.environ["SPYTEST_SCAPY_DBG_LVL"] = "100" + self.scapy_connect(dry_run) def __del__(self): pass @@ -32,148 +39,166 @@ def log_call(self, fname, **kwargs): 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) + port_list = 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") + 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") + rx_stats = tg.tg_traffic_stats(port_handle=tg_ph, mode="aggregate") print("RX-STATS", json.dumps(rx_stats)) -def test_single_burst(tg): + +def test_single_burst(tg, rate_pps=2, stats=True): print("============= test_single_burst ==============") - (tg_ph_1, _) = 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) + tg_ph_1 = list(tg.tg_port_handle.values())[0] + # tg.tg_traffic_config(mac_src='00:00:00:00:10:01', mac_dst='52:54:74:68:8b:d2', + tg.tg_traffic_config(mac_src='00:00:00:00:00:01', mac_dst='00:00:00:00:00:02', + l3_protocol='ipv4', ip_src_addr="1.0.0.1", ip_dst_addr="2.0.0.2", + ip_src_mode='increment', ip_dst_mode='increment', + rate_pps=rate_pps, 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) + if stats: + 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) + (tg_ph_1, tg_ph_2) = list(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") + 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") + 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) + (tg_ph_1, tg_ph_2) = list(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") + 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") + 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) + (tg_ph_1, tg_ph_2) = list(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") + 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') + 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') + 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.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_ph_1 = list(tg.tg_port_handle.values())[0] + 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') + gateway='12.12.0.1', netmask='255.255.0.0', vlan=1, + vlan_id=10, vlan_id_step=1, arp_send_req='1', vlan_id_count=10, + 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)) + 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" + 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"]) + 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-0") - tg.tg_traffic_control(action="run",handle="stream-1-2") - tg.tg_traffic_control(action="run",handle="stream-2-2") - tg.tg_traffic_control(action="run",handle="stream-1-1") - tg.tg_traffic_control(action="stop",port_handle="port-1/1") - tg.tg_traffic_control(action="run",handle="stream-2-1") - tg.tg_traffic_control(action="stop",port_handle="port-1/2") + 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-0") + tg.tg_traffic_control(action="run", handle="stream-1-2") + tg.tg_traffic_control(action="run", handle="stream-2-2") + tg.tg_traffic_control(action="run", handle="stream-1-1") + tg.tg_traffic_control(action="stop", port_handle="port-1/1") + tg.tg_traffic_control(action="run", handle="stream-2-1") + tg.tg_traffic_control(action="stop", port_handle="port-1/2") + def test_bgp(tg): res = tg.tg_interface_config(intf_ip_addr='10.10.10.2', gateway='10.10.10.1', arp_send_req='1', port_handle='port-1', mode='config') - tg.tg_emulation_bgp_config(handle=res["handle"],local_as=200,active_connect_enable='1',mode='enable',remote_as=100,remote_ip_addr='10.10.10.1') - tg.tg_emulation_bgp_route_config (handle=res["handle"], prefix='121.1.1.0', num_routes=2, mode='add', as_path='as_seq:1') - tg.tg_emulation_bgp_control (handle=res["handle"], mode="start") + tg.tg_emulation_bgp_config(handle=res["handle"], local_as=200, active_connect_enable='1', mode='enable', remote_as=100, remote_ip_addr='10.10.10.1') + tg.tg_emulation_bgp_route_config(handle=res["handle"], prefix='121.1.1.0', num_routes=2, mode='add', as_path='as_seq:1') + tg.tg_emulation_bgp_control(handle=res["handle"], mode="start") + def test_emulated(tg): - (tg_ph_1, _) = tg.tg_port_handle.values() + tg_ph_1 = list(tg.tg_port_handle.values())[0] 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', '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) + tg.tg_traffic_control(action='run', port_handle=tg_ph_1) + def test_track(tg): tg.tg_traffic_config(port_handle='port-1', port_handle2='port-2', - tcp_src_port=43,mac_dst="3c:2c:99:d3:a7:af", tcp_dst_port_count=10,ip_dst_addr='2.2.2.2', - high_speed_result_analysis=0,ip_dst_mode='fixed',tcp_dst_port=10, tcp_dst_port_step=1,length_mode='fixed', - rate_pps=1,tcp_dst_port_mode='incr', ip_src_addr='1.1.1.2', ip_src_mode='fixed', transmit_mode='continuous', - frame_size='128',l2_encap='ethernet_ii_vlan',l3_protocol='ipv4', mac_src='00:0a:01:00:00:01',l4_protocol='tcp',mode='create') + tcp_src_port=43, mac_dst="3c:2c:99:d3:a7:af", tcp_dst_port_count=10, ip_dst_addr='2.2.2.2', + high_speed_result_analysis=0, ip_dst_mode='fixed', tcp_dst_port=10, tcp_dst_port_step=1, length_mode='fixed', + rate_pps=1, tcp_dst_port_mode='incr', ip_src_addr='1.1.1.2', ip_src_mode='fixed', transmit_mode='continuous', + frame_size='128', l2_encap='ethernet_ii_vlan', l3_protocol='ipv4', mac_src='00:0a:01:00:00:01', l4_protocol='tcp', mode='create') tg.tg_traffic_config(port_handle='port-2', port_handle2='port-1', - mac_src='00:0a:01:00:11:02',tcp_src_port_count=10,tcp_src_port_mode='incr',l4_protocol='tcp',tcp_src_port=100, - length_mode='fixed',mac_dst="a8:2b:b5:ac:45:1f",tcp_src_port_step=1,ipv6_dst_addr='1001::2', - transmit_mode='continuous',high_speed_result_analysis=0,mode='create',frame_size='128', - l2_encap='ethernet_ii_vlan',rate_pps=1,l3_protocol='ipv6',ipv6_src_addr='2001::2') - tg.tg_traffic_control(action='run',duration=10,stream_handle=['stream-1-0', 'stream-2-0']) + mac_src='00:0a:01:00:11:02', tcp_src_port_count=10, tcp_src_port_mode='incr', l4_protocol='tcp', tcp_src_port=100, + length_mode='fixed', mac_dst="a8:2b:b5:ac:45:1f", tcp_src_port_step=1, ipv6_dst_addr='1001::2', + transmit_mode='continuous', high_speed_result_analysis=0, mode='create', frame_size='128', + l2_encap='ethernet_ii_vlan', rate_pps=1, l3_protocol='ipv6', ipv6_src_addr='2001::2') + tg.tg_traffic_control(action='run', duration=10, stream_handle=['stream-1-0', 'stream-2-0']) + def test_main(ipaddr, port=8009): tg = TGScapyTest(ipaddr, port, ["1/1", "1/2"]) @@ -225,20 +250,214 @@ def test_main(ipaddr, port=8009): return tg + def test_main2(ipaddr, port=8009): for i in range(100): print("TRY {}".format(i)) - ports = ["1/{}".format(j+1) for j in range(0,16)] + ports = ["1/{}".format(j + 1) for j in range(0, 16)] tg = TGScapyTest(ipaddr, port, ports) raw_input("press any key") tg.tg_disconnect() raw_input("press any key") + +def test_ospf(ipaddr, port=8009): + tg = TGScapyTest(ipaddr, port, ["1/1", "1/2"], False) + tg_ph_1 = list(tg.tg_port_handle.values())[0] + h1 = tg.tg_interface_config(port_handle=tg_ph_1, mode='config', intf_ip_addr="100.1.0.2", gateway="100.1.0.1", + arp_send_req='1', control_plane_mtu='9100') + print(h1) + ospf_ses = tg.tg_emulation_ospf_config(handle=h1['handle'], mode='create', session_type='ospfv2', router_id='192.0.0.1', + area_id='0.0.0.0', gateway_ip_addr="100.1.0.1", network_type='broadcast', max_mtu='9100') + print(ospf_ses) + # IA = tg.tg_emulation_ospf_route_config(mode='create', type='summary_routes', handle=ospf_ses['handle'], + # summary_number_of_prefix='10', summary_prefix_start='200.1.0.0', + # summary_prefix_length='24', summary_prefix_metric='10', + # router_id='192.0.0.1') + # print("Summary Routes: " + str(IA)) + E1 = tg.tg_emulation_ospf_route_config(mode='create', type='ext_routes', handle=ospf_ses['handle'], + external_number_of_prefix='10', external_prefix_start='202.1.0.0', + external_prefix_length='24', external_prefix_type='1', + router_id='102.1.0.0') + # print("External Type-1 Routes: " + str(E1)) + # E2 = tg.tg_emulation_ospf_route_config(mode='create', type='ext_routes', handle=ospf_ses['handle'], + # external_number_of_prefix='10', external_prefix_start='203.1.0.0', + # external_prefix_length='24', external_prefix_type='2', + # router_id='103.1.0.0') + # print("External Type-2 Routes: " + str(E2)) + + res = tg.tg_emulation_ospf_control(mode='start', handle=ospf_ses['handle']) + print(res) + time.sleep(30) + # import pdb;pdb.set_trace() + res = tg.tg_emulation_ospf_control(mode='stop', handle=ospf_ses['handle']) + print(res) + # res = tg.tg_emulation_ospf_route_config(mode='delete', handle=IA['handle']);print(res) + res = tg.tg_emulation_ospf_route_config(mode='delete', handle=E1['handle']) + print(res) + # res = tg.tg_emulation_ospf_route_config(mode='delete', handle=E2['handle']);print(res) + res = tg.tg_emulation_ospf_config(handle=ospf_ses['handle'], mode='delete') + print(res) + + # import pdb;pdb.set_trace() + sys.exit(0) + + +def test_dhcp(ipaddr, port=8009): + tg = TGScapyTest(ipaddr, port, ["1/1", "1/2"], False) + (tg_ph_1, tg_ph_2) = list(tg.tg_port_handle.values()) + + # DHCP Server Config with switch + h1 = tg.tg_interface_config(port_handle=tg_ph_2, mode='config', intf_ip_addr='1.1.1.1', gateway='1.1.1.2', + arp_send_req='1', control_plane_mtu='9100', vlan='1', vlan_id='10', + resolve_gateway_mac='false') + + dut_mac = '52:54:00:2a:8d:84' + s_conf = tg.tg_emulation_dhcp_server_config(mode='create', ip_version='4', encapsulation='ETHERNET_II', + ipaddress_count='30', ipaddress_pool='1.1.1.3', handle=h1['handle'], + count='1', local_mac='00:10:95:00:00:01', ip_address='1.1.1.1', + ip_gateway='1.1.1.2', remote_mac=dut_mac, pool_count=1) + + s_con = tg.tg_emulation_dhcp_server_control(action='connect', dhcp_handle=s_conf['dhcp_handle']) + print("DHCP Server Control: {}".format(s_con)) + # import pdb;pdb.set_trace() + + # DHCP Client Config(Port Based) + conf = tg.tg_emulation_dhcp_config(mode='create', port_handle=tg_ph_1) + # 'ip_version' is mandatory to configure retry_count + # conf = tg.tg_emulation_dhcp_config(mode='create', port_handle=tg_ph_1, retry_count='11', ip_version='4') + print("DHCP Client: {}".format(conf)) + + group = tg.tg_emulation_dhcp_group_config(handle=conf['handles'], mode='create', encap='ethernet_ii_vlan', vlan_id_count='20', + num_sessions='20', mac_addr='00:10:94:00:00:01', vlan_id=10, vlan_ether_type='0x8100', + dhcp_range_ip_type=4, vlan_id_step=0, gateway_addresses=1) + print("DHCP Group Config: {}".format(group)) + + tg.tg_emulation_dhcp_stats(action='clear', port_handle=tg_ph_1) + cont = tg.tg_emulation_dhcp_control(port_handle=tg_ph_1, action="bind", handle=group['handle']) + print("DHCP Client Control: {}".format(cont)) + tg.tg_emulation_dhcp_stats(port_handle=tg_ph_1, handle=conf['handles'], mode='aggregate', ip_version='4') + # import pdb;pdb.set_trace() + + cont = tg.tg_emulation_dhcp_control(port_handle=tg_ph_1, action="rebind", handle=group['handle']) + print("DHCP Client Control: {}".format(cont)) + # import pdb;pdb.set_trace() + + cont = tg.tg_emulation_dhcp_control(port_handle=tg_ph_1, action="renew", handle=group['handle']) + print("DHCP Client Control: {}".format(cont)) + # import pdb;pdb.set_trace() + + cont = tg.tg_emulation_dhcp_control(port_handle=tg_ph_1, action="release", handle=group['handle']) + print("DHCP Client Control: {}".format(cont)) + # import pdb;pdb.set_trace() + + # tg.tg_emulation_dhcp_stats(port_handle=tg_ph_1, handle=conf['handles'], mode='aggregate', ip_version='4') + tg.tg_emulation_dhcp_config(mode='reset', handle=conf['handles'], port_handle=tg_ph_1) + tg.tg_emulation_dhcp_server_config(mode='reset', handle=s_conf['dhcp_handle'], port_handle=tg_ph_2) + + # import pdb;pdb.set_trace() + sys.exit(0) + + +def test_dhcpv6(ipaddr, port=8009): + tg = TGScapyTest(ipaddr, port, ["1/1", "1/2"], False) + (tg_ph_1, tg_ph_2) = list(tg.tg_port_handle.values()) + + # DHCP Server Config with switch + h1 = tg.tg_interface_config(port_handle=tg_ph_2, ipv6_prefix_length='64', arp_send_req='1', ipv6_intf_addr='2000::2', + src_mac_addr='00:0a:01:00:00:01', ipv6_resolve_gateway_mac='false', + vlan='1', mode='config', ipv6_gateway='2000::1', vlan_id='10') + + s_conf = tg.tg_emulation_dhcp_server_config(count='1', mac_addr='00:10:94:00:00:04', server_emulation_mode='DHCPV6', + handle=h1['handle'], prefix_pool_per_server='20', addr_pool_addresses_per_server='20', + addr_pool_start_addr='2000::3', prefix_pool_prefix_length='64', mode='create', + prefix_pool_step='1', ip_version='6', encapsulation='ethernet_ii', + addr_pool_step_per_server='1', prefix_pool_start_addr='2000::', addr_pool_prefix_length='64') + # import pdb;pdb.set_trace() + + s_con = tg.tg_emulation_dhcp_server_control(action='connect', dhcp_handle=s_conf['dhcp_handle'], ip_version='6') + print("DHCP Server Control: {}".format(s_con)) + # import pdb;pdb.set_trace() + + # DHCP Client Config(Port Based) + conf = tg.tg_emulation_dhcp_config(mode='create', port_handle=tg_ph_1, ip_version='6') + print("DHCP Client: {}".format(conf)) + # import pdb;pdb.set_trace() + + group = tg.tg_emulation_dhcp_group_config(num_sessions='20', dhcp_range_ip_type='6', handle=conf['handles'], + vlan_id_step=0, vlan_cfi=0, client_mac_addr='00:10:01:00:00:01', mode='create', + encap='ethernet_ii_vlan', dhcp6_client_mode='DHCPV6', vlan_id=10, mac_addr='00:10:94:00:00:05') + print("DHCP Group Config: {}".format(group)) + # import pdb;pdb.set_trace() + + tg.tg_emulation_dhcp_stats(action='clear', port_handle=tg_ph_1) + cont = tg.tg_emulation_dhcp_control(port_handle=tg_ph_1, action="bind", handle=group['handle'], ip_version='6') + print("DHCP Client Control: {}".format(cont)) + # import pdb;pdb.set_trace() + + cont = tg.tg_emulation_dhcp_control(port_handle=tg_ph_1, action="rebind", handle=group['handle'], ip_version='6') + print("DHCP Client Control: {}".format(cont)) + + # import pdb;pdb.set_trace() + sys.exit(0) + + +def test_bgp_2(ipaddr, port=8009): + tg = TGScapyTest(ipaddr, port, ["1/1", "1/2"], False) + # (tg_ph_1, tg_ph_2) = list(tg.tg_port_handle.values()) + res = tg.tg_interface_config(intf_ip_addr='10.10.10.2', gateway='10.10.10.1', arp_send_req='1', port_handle='port-1', mode='config') + tg.tg_emulation_bgp_config(handle=res["handle"], local_as=200, active_connect_enable='1', mode='enable', remote_as=100, remote_ip_addr='10.10.10.1') + tg.tg_emulation_bgp_route_config(handle=res["handle"], prefix='121.1.1.0', num_routes=2, mode='add', as_path='as_seq:1') + # import pdb;pdb.set_trace() + tg.tg_emulation_bgp_control(handle=res["handle"], mode="start") + + +def test_dot1x(ipaddr, port=8009): + os.environ["SPYTEST_SCAPY_DOT1X_IMPL"] = "2" + tg = TGScapyTest(ipaddr, port, ["1/1", "1/2"], False) + tg_ph_1 = list(tg.tg_port_handle.values())[0] + res1 = tg.tg_emulation_dot1x_config(username='userp11', eap_auth_method='md5', local_ip_addr='192.168.1.10', port_handle=tg_ph_1, + mode='create', gateway_ip_addr='192.168.1.1', ip_version='ipv4', encapsulation='ethernet_ii', password='userp11', mac_addr='00:00:00:A1:A1:A1') + res2 = tg.tg_emulation_dot1x_config(username='userp12', eap_auth_method='md5', local_ip_addr='192.168.2.10', port_handle=tg_ph_1, + mode='create', gateway_ip_addr='192.168.2.1', ip_version='ipv4', encapsulation='ethernet_ii', password='userp12', mac_addr='00:00:00:A2:A2:A2') + tg.tg_emulation_dot1x_control(handle=res1["handle"], mode="start") + tg.tg_emulation_dot1x_control(handle=res2["handle"], mode="start") + time.sleep(10) + tg.tg_emulation_dot1x_control(handle=res1["handle"], mode="stop") + tg.tg_emulation_dot1x_control(handle=res2["handle"], mode="stop") + + +def test_sample(ipaddr, port=8009): + # tg = TGScapyTest(ipaddr, port, ["1/1", "1/2"], False) + tg = TGScapyTest(ipaddr, port, ["1/1"], False) + + # 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") + for _ in range(100): + # test_clear(tg) + test_single_burst(tg, 20, False) + raw_input("press any key") + # tg.server_control("post-module-epilog", "mod-2") + raw_input("press any key") + + if __name__ == '__main__': ipaddr = sys.argv[1] if len(sys.argv) > 1 else "10.250.0.188" port = sys.argv[2] if len(sys.argv) > 2 else 8009 - #test_main2(ipaddr, port) - tg = test_main(ipaddr, port) + test_sample(ipaddr, port) + sys.exit(0) + test_dot1x(ipaddr, port) + sys.exit(0) + test_dhcpv6(ipaddr, port) + test_bgp_2(ipaddr, port) + test_ospf(ipaddr, port) + test_ospf(ipaddr, port) + # test_main2(ipaddr, port) + # tg = test_main(ipaddr, port) + tg = TGScapyTest(ipaddr, port, ["1/1", "1/2"]) + test_interface(tg) 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 index 7f8014bbf8..b92223709b 100755 --- a/spytest/spytest/tgen/scapy/ut_streams.py +++ b/spytest/spytest/tgen/scapy/ut_streams.py @@ -28,50 +28,71 @@ def _build(**kwargs): 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) - if index == 16: return _build(rate_pps=1, l3_protocol='ipv6', ipv6_src_addr='33f1::1', ipv6_dst_addr='7fe9::1', ipv6_dst_step='::1', ipv6_dst_mode='increment', ipv6_dst_count=10) + 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) + if index == 16: + return _build(rate_pps=1, l3_protocol='ipv6', ipv6_src_addr='33f1::1', ipv6_dst_addr='7fe9::1', ipv6_dst_step='::1', ipv6_dst_mode='increment', ipv6_dst_count=10) return None + def ut_stream_get(index=0, **kws): kwargs = _build2(index) - if kwargs: kwargs.update(kws) + if kwargs: + kwargs.update(kws) return kwargs + if __name__ == '__main__': print(ut_stream_get(0)) for i in range(100): @@ -79,4 +100,3 @@ def ut_stream_get(index=0, **kws): if not d: break print(d) - diff --git a/spytest/spytest/tgen/scapy/utils.py b/spytest/spytest/tgen/scapy/utils.py index a3d342386a..72051de8e8 100755 --- a/spytest/spytest/tgen/scapy/utils.py +++ b/spytest/spytest/tgen/scapy/utils.py @@ -1,4 +1,5 @@ import os +import re import sys import time import glob @@ -13,14 +14,16 @@ from itertools import islice from tempfile import mkstemp import subprocess +from dicts import SpyTestDict try: - from StringIO import StringIO ## for Python 2 + from StringIO import StringIO # for Python 2 except ImportError: - from io import StringIO ## for Python 3 + from io import StringIO # for Python 3 from logger import Logger + class Utils(object): def __init__(self, dry=False, logger=None): self.dry = dry @@ -46,18 +49,30 @@ def fread(self, fname, default=""): pass return default - def fwrite(self, content, fname = "", mode="w"): - if fname == "" or self.dry: + @staticmethod + def kwargs_get_strip(name, default, **kws): + val = kws.get(name, default) or default + return val.strip() if val else val + + @staticmethod + def file_write(content, fname="", mode="w", tmp_files=None): + if fname == "": tmp_dir = os.getenv("TMPDIR", "/tmp/scapy-tgen/tmp/") Utils.ensure_folder(tmp_dir) _, fname = mkstemp(prefix=tmp_dir) - self.tmp_files.append(fname) + if tmp_files is not None: + tmp_files.append(fname) else: Utils.ensure_parent(fname) with open(fname, mode) as fd: fd.write(content) return fname + def fwrite(self, content, fname="", mode="w"): + if self.dry: + fname = "" + return Utils.file_write(content, fname, mode, self.tmp_files) + def fhead(self, fname, count, default=""): try: lines = [] @@ -91,12 +106,20 @@ def process_exec(cmd): p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) (out, err) = p.communicate() p.wait() - return out or err + return Utils.decode(out or err) - def cmdexec(self, cmd, msg=None, dbg=True): + @staticmethod + def decode(data): + try: + return data.decode("utf-8") + except Exception: + return data + + def cmdexec(self, cmd, dbg=True): if dbg: self.logger.debug("cmdexec: " + cmd) - if self.dry: return "skipped-for-dry-run" + if self.dry: + return "skipped-for-dry-run" return self.process_exec(cmd) def shexec(self, *args): @@ -104,6 +127,7 @@ def shexec(self, *args): fname = self.fwrite(cmds) logfile = fname + ".1" self.logger.debug("shexec: " + cmds) + output = "" if not self.dry: cmd = "sh -x %s > %s 2>&1" % (fname, logfile) os.system(cmd) @@ -111,10 +135,11 @@ def shexec(self, *args): self.logger.debug(output) os.unlink(logfile) os.unlink(fname) + return output def lshexec(self, cmdlist): cmds = [cmd for cmd in cmdlist if cmd.strip()] - self.shexec("\n".join(cmds)) + return self.shexec("\n".join(cmds)) def tshexec(self, cmdlist): threads = [] @@ -126,31 +151,37 @@ def tshexec(self, cmdlist): th.join() def cat_file(self, filepath): - marker = "#######################" content = self.fread(filepath).strip() + if not content: + return "" + marker = "#######################" return "\n{0} {1}\n{0}\n".format(marker, content) def line_info(self): stk = inspect.stack() - self.logger.debug(stk[1][1],":",stk[1][2],":", stk[1][3]) + self.logger.debug(stk[1][1], ":", stk[1][2], ":", stk[1][3]) @staticmethod def min_value(v1, v2): return v1 if v1 < v2 else v2 + @staticmethod + def max_value(v1, v2): + return v1 if v1 > v2 else v2 + @staticmethod def incrementMac(mac, step): - step = step.replace(':', '').replace(".",'') - mac = mac.replace(':', '').replace(".",'') + 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)) + 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(".",'') + 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)) + return ':'.join(("%012X" % nextMac)[i:i + 2] for i in range(0, 12, 2)) @staticmethod def randomMac(): @@ -158,15 +189,21 @@ def randomMac(): @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)) + def ip2int(ipstr): + return struct.unpack("!I", socket.inet_aton(ipstr))[0] + + def int2ip(n): + return 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)) + def ip2int(ipstr): + return struct.unpack("!I", socket.inet_aton(ipstr))[0] + + def int2ip(n): + return socket.inet_ntoa(struct.pack("!I", n)) + return int2ip(ip2int(ip) - ip2int(step)) @staticmethod def randomIpv4(): @@ -199,20 +236,20 @@ def ipv4_ip2long(ip): return lngip @staticmethod - def ipv4_long2ip(l): - return '%d.%d.%d.%d' % (l >> 24 & 255, l >> 16 & 255, l >> 8 & 255, l & 255) + def ipv4_long2ip(ll): + return '%d.%d.%d.%d' % (ll >> 24 & 255, ll >> 16 & 255, ll >> 8 & 255, ll & 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: + # 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) + chunks.append('%x' % ((v4_int >> 16) & 0xffff)) + chunks.append('%x' % (v4_int & 0xffff)) + ip = ':'.join(chunks) halves = ip.split('::') hextets = halves[0].split(':') @@ -222,18 +259,19 @@ def ipv6_ip2long(ip): hextets.append('0') for h in h2: hextets.append(h) - # end if + # end if lngip = 0 for h in hextets: - if h == '': h = '0' + if h == '': + h = '0' lngip = (lngip << 16) | int(h, 16) return lngip @staticmethod - def ipv6_long2ip(l): + def ipv6_long2ip(ll): # format as one big hex value - hex_str = '%032x' % l + hex_str = '%032x' % ll # split into double octet chunks without padding zeros hextets = ['%x' % int(hex_str[x:x + 4], 16) for x in range(0, 32, 4)] @@ -249,7 +287,7 @@ def ipv6_long2ip(l): dc_len, dc_start = (run_len, run_start) else: run_len, run_start = (0, -1) - # end for + # end for if dc_len > 1: dc_end = dc_start + dc_len if dc_end == len(hextets): @@ -263,59 +301,82 @@ def ipv6_long2ip(l): @staticmethod def incrementIPv6(ip, step): - return Utils.ipv6_long2ip(Utils.ipv6_ip2long(ip)+Utils.ipv6_ip2long(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)) + 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) + return int(float(val)) @staticmethod def tobytes(s): - if sys.version_info[0] < 3: - return buffer(s) # pylint: disable=undefined-variable - return s.encode() + if isinstance(s, bytes): + return s + try: + return s.encode() + except Exception: + return bytes(s) @staticmethod def get_env_int(name, default): try: - return int(os.getenv(name, default)) + return int(os.getenv(name, str(default))) except Exception: pass return default + @staticmethod + def clock(): + return time.time() + @staticmethod def msleep(delay, block=1): - mdelay = delay /1000.0 - now = time.time() - while now + mdelay > time.time(): - time.sleep(block/1000.0) + unit_time = block / 1000.0 + end_time = time.time() + delay / 1000.0 + while True: + time.sleep(unit_time) + if end_time <= time.time(): + break @staticmethod def usleep(delay, block=1): - mdelay = delay /1000000.0 - now = time.time() - while now + mdelay > time.time(): - time.sleep(block/1000000.0) + unit_time = block / 1000000.0 + end_time = time.time() + delay / 1000000.0 + while True: + time.sleep(unit_time) + if end_time <= time.time(): + break @staticmethod - def make_list(arg): - if isinstance(arg, list): - return arg - return [arg] + def make_list(arg, uniq=False): + rv = arg if isinstance(arg, list) else [arg] + return Utils.make_uniq(rv) if uniq else rv - def exec_func(self, func, *args, **kwargs): + @staticmethod + def make_uniq(arg): + if not isinstance(arg, list): + raise ValueError("input should be list") + rv = [] + for ent in arg: + if ent not in rv: + rv.append(ent) + return rv + + def exec_func(self, msg, func, *args, **kwargs): this_stderr, this_stdout = StringIO(), StringIO() save_stderr, save_stdout = sys.stderr, sys.stdout sys.stderr, sys.stdout = this_stderr, this_stdout func(*args, **kwargs) sys.stderr, sys.stdout = save_stderr, save_stdout msgs = map(str.strip, [this_stdout.getvalue(), this_stderr.getvalue()]) - return self.logger.debug("\n".join([s for s in msgs if s])) + msgs = [s for s in msgs if s] + if msg: + msgs.insert(0, msg) + return self.logger.debug("\n".join(msgs)) def exec_cmd(self, cmd): ret = self.cmdexec(cmd, dbg=False) @@ -349,17 +410,117 @@ def ensure_parent(filename): path = os.path.dirname(filename) Utils.ensure_folder(path) - @staticmethod - def get_ip_addr_dev(intf, ns=None): + def ns_debug(self, ns, msg): + rv = "{}\n{}".format(msg, self.cmdexec("ip netns list")) + raise ValueError(self.logger.error(rv)) + + def nsexec(self, ns, cmd, abort=True): + rv = self.cmdexec("ip netns exec ns_{} {}".format(ns, cmd)) + if abort and "Cannot open network namespace" in rv: + self.ns_debug(ns, rv) + return rv + + def get_ip_addr_dev(self, intf, ns=None): cmd = "ip addr show dev {}".format(intf) - if ns: cmd = "ip netns exec {} {}".format(ns, cmd) - output = Utils.process_exec(cmd).split() + if ns: + output = self.nsexec(ns, cmd).split() + else: + output = self.cmdexec(cmd).split() retval = {} for x in ['inet', 'inet6', 'state', 'link/ether', 'ether']: if x in output: idx = output.index(x) - retval[x] = output[idx+1] - print(cmd, output, retval) - return retval + retval[x] = output[idx + 1] + return retval, cmd, output + + @staticmethod + def parse_mac(output): + match = re.search(r"(([a-f\d]{1,2}\:){5}[a-f\d]{1,2})", output) + if not match: + return None + return match.groups()[0] + + @staticmethod + def unused(*args): + pass + + @staticmethod + def flatten_list(ll, rv=None, uniq=False): + rv = rv or [] + for i in Utils.make_list(ll): + if isinstance(i, list): + rv.extend(Utils.flatten_list(i, rv)) + else: + rv.append(i) + return Utils.make_uniq(rv) if uniq else rv + + @staticmethod + def clone(**kws): + rv = SpyTestDict() + for key, value in kws.items(): + rv[key] = value + return rv + @staticmethod + def success(**kwargs): + res = SpyTestDict() + res.status = "1" + for key, value in kwargs.items(): + res[key] = value + return res + + @staticmethod + def os_fork(pid_file): + try: + pid = os.fork() + if pid > 0: + os._exit(0) + if pid_file: + Utils.file_write(str(os.getpid()), pid_file) + except OSError as exc: + print("Error forking", exc) + + @staticmethod + def redirect_logs(log_file): + fd = os.open('/dev/null', os.O_RDWR) + os.dup2(fd, sys.__stdin__.fileno()) + if log_file is not None: + fake_stdout = open(log_file, 'a', 1) + sys.stdout = fake_stdout + sys.stderr = fake_stdout + fd = fake_stdout.fileno() + os.dup2(fd, sys.__stdout__.fileno()) + os.dup2(fd, sys.__stderr__.fileno()) + if log_file is None: + os.close(fd) + @staticmethod + def deamonize(pid_file, log_file): + Utils.os_fork(None) + os.setsid() + Utils.os_fork(pid_file) + Utils.redirect_logs(log_file) + + @staticmethod + def md5sum(value): + from hashlib import md5 + # nosemgrep-next-line + hasher = md5(value) + return hasher.digest() + + @staticmethod + def prefix_length_to_netmask(prefix_length): + mask = (0xffffffff >> (32 - prefix_length)) << (32 - prefix_length) + return (str((0xff000000 & mask) >> 24) + '.' + + str((0x00ff0000 & mask) >> 16) + '.' + + str((0x0000ff00 & mask) >> 8) + '.' + + str((0x000000ff & mask))) + + +class RunTimeException(RuntimeError): + def __init__(self, *args): + lines = ["Run Time Exception:"] + for arg in args: + lines.append(str(arg)) + message = "\n".join(lines) + super(RunTimeException, self).__init__(message) diff --git a/spytest/spytest/tgen/scapy/yabgp.ini b/spytest/spytest/tgen/scapy/yabgp.ini deleted file mode 100755 index 50f0bf6ae5..0000000000 --- a/spytest/spytest/tgen/scapy/yabgp.ini +++ /dev/null @@ -1,125 +0,0 @@ -[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 index 82e01dc665..199b4f9eb0 100644 --- a/spytest/spytest/tgen/tg.py +++ b/spytest/spytest/tgen/tg.py @@ -5,15 +5,15 @@ import copy import inspect import requests +from netaddr import IPAddress, IPNetwork 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.init import tg_stc_load, tg_scapy_load, tg_ixia_load from spytest.tgen.tg_stubs import TGStubs from spytest.tgen.tg_scapy import ScapyClient from spytest.dicts import SpyTestDict -from netaddr import IPAddress +import utilities.common as utils +import utilities.parallel as putils workarea = None logger = None @@ -24,41 +24,55 @@ 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_start(msg, max_time=300, skip_report=True): + return workarea.profiling_start(msg, max_time, skip_report) + def tgen_profiling_stop(pid): return workarea.profiling_stop(pid) + def tgen_wait(val, msg=None): workarea.tg_wait(val, msg) + 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) + msg = "TG API Fatal Abort: {}".format(dbg_msg) + logger.error(msg) workarea.report_tgen_abort(msgid, *args) + if get_tg_type() in ["scapy"]: + workarea.set_node_dead(None, msg, False) + 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_log_lvl_is_debug(): - lvl_1 = bool(os.getenv('SPYTEST_LOGS_LEVEL') == 'debug') - lvl_2 = bool(os.getenv('SPYTEST_TGEN_LOGS_LEVEL') == 'debug') - return bool(lvl_1 or lvl_2) + lvl_1 = bool(os.getenv('SPYTEST_LOGS_LEVEL') == 'debug') + lvl_2 = bool(os.getenv('SPYTEST_TGEN_LOGS_LEVEL') == 'debug') + return bool(lvl_1 or lvl_2) + 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) @@ -66,23 +80,35 @@ def tgen_get_logs_path_folder(for_file=None): os.makedirs(os.path.abspath(tgen_folder_path)) return tgen_folder_path + +def tgen_fwrite(msg): + 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, "{}\n".format(msg), "a") + return msg + + def tgen_log_call(fname, **kwargs): - args_list=[] + args_list = [] for key, value in kwargs.items(): if isinstance(value, str): - args_list.append("%s='%s'" %(key, value)) + args_list.append("%s='%s'" % (key, value)) elif isinstance(value, int): - args_list.append("%s=%s" %(key, value)) + args_list.append("%s=%s" % (key, value)) elif isinstance(value, list): - args_list.append("%s=%s" %(key, value)) + args_list.append("%s=%s" % (key, value)) else: - args_list.append("%s=%s[%s]" %(key, value, type(value))) - text = "{}({})".format(fname, ",".join(args_list)) - logger.debug('REQ: {}'.format(text)) - 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, "{}\n".format(text), "a") - return text + args_list.append("%s=%s[%s]" % (key, value, type(value))) + msg = "REQ: {}({})".format(fname, ",".join(args_list)) + logger.debug(msg) + return tgen_fwrite(msg) + + +def tgen_log_resp(fname, text): + msg = 'RESP: {} {}'.format(fname, text) + logger.debug(msg) + return tgen_fwrite(msg) + analyzer_filter = {"ipv4Precedence0": "ip_precedence_tracking", "ipv4DefaultPhb0": "ip_dscp_tracking", @@ -91,36 +117,51 @@ def tgen_log_call(fname, **kwargs): "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 + def connect_retry(tg): - for i in range(0, 10): + if tg.skip_traffic: + return False + + for i in range(1, 11): ret_ds = tg.connect() - if ret_ds: + if ret_ds is not None: + if tg.tgen_config_file and ret_ds.get('status', '0') == '1': + return ret_ds msg = "UNKNOWN" if "log" not in ret_ds else ret_ds.get('log', '') - logger.warning('TG Connect Error: %s try: %d' % (msg, i)) - tgen_wait(10) + if i < 10: + logger.warning('TG Connect Fail: %s try: %d' % (msg, i)) + tgen_wait(10, "Wait to try reconnecting again") + else: + logger.error('TG Connect Error: %s' % (msg)) else: logger.info('TG Connection: Success') return True return False + class TGBase(TGStubs): - def __init__(self, tg_type, tg_version, tg_ip=None, tg_port_list=None): + def __init__(self, tg_type, tg_version, tg_ip=None, tg_port_list=None, tg_virtual=False, **kwargs): TGStubs.__init__(self, logger) - logger.info('TG Base Init...start') + logger.info('TG Base Init skip: {}'.format(skip_tgen)) self.tg_ns = "" self.tg_type = tg_type + self.tg_virtual = tg_virtual self.tg_version = tg_version self.tg_ip = tg_ip self.tg_port_list = tg_port_list @@ -128,12 +169,18 @@ def __init__(self, tg_type, tg_version, tg_ip=None, tg_port_list=None): self.tg_connected = False self.in_module_start_cleanup = False self.cached_interface_config_handles = OrderedDict() - self.tg_port_handle = dict() + self.tg_port_handle = SpyTestDict() self.tg_port_analyzer = dict() - if self.tg_ip is None or self.tg_port_list is None: - return - if self.skip_traffic: - return + self.ports_fec_disable = list() + self.topo_handle = dict() + self.tg_card = kwargs.get('card', '') + self.tgen_config_file = kwargs.get('config_file', '') + self.tg_port_speed = kwargs.get('port_speed', '') + self.auto_neg = kwargs.get('auto_neg', '') + self.phy_mode = kwargs.get('phy_mode', '') + self.fec = int(kwargs.get('fec', '0')) + self.skip_start_protocol = False + self.tg_link_params = kwargs.get('link_params', dict()) def manage_interface_config_handles(self, mode, port_handle, handle): logger.debug("manage_interface_config_handles: {} {} {}".format(mode, port_handle, handle)) @@ -172,7 +219,8 @@ def warn(self, dbg_msg): def fail(self, dbg_msg, msgid, *args): self.ensure_connected(dbg_msg) - self.collect_diagnosic(msgid) + self.get_session_errors() + self.collect_diagnosic(msgid, True) tgen_fail(dbg_msg, msgid, *args) def exception(self, exp): @@ -180,13 +228,25 @@ def exception(self, exp): self.ensure_connected(str(exp)) tgen_exception(exp) - def collect_diagnosic(self, fail_reason): + def collect_diagnosic(self, fail_reason, from_fail=False): + pass + + def get_capture_stats_state(self, port, capture_wait=None): + pass + + def get_emulation_handle_prefixes(self, ret_ds, **kwargs): pass - def get_capture_stats_state(self, port): + def get_port_connected_session(self, **kwargs): pass - def get_emulation_handle_prefixes(self, ret_ds, kwargs): + def get_session_errors(self, **kwargs): + return {} + + def ixia_eval(self, func, **kwargs): + return {} + + def tg_topology_test_control(self, stack=None, skip_wait=False, tg_wait=2, **kwargs): pass def has_disconnected(self, msg): @@ -197,12 +257,17 @@ def has_disconnected(self, msg): if "Connection reset by peer" in msg or "not connected" in msg: return True + if "SAL is not connected" in msg: + return True + return False def ensure_connected(self, msg): if os.getenv("SPYTEST_ENSURE_CONNECTED", "1") == "0": return + msg = str(msg) + if self.has_disconnected(msg): tgen_abort(msg, "tgen_failed_abort", msg) return @@ -212,12 +277,12 @@ def ensure_connected(self, msg): try: # try getting the ixnetwork build number to check connection status - get_ixiangpf().ixnet.getAttribute('::ixNet::OBJ-/globals', '-buildNumber') + get_ixnet().getAttribute('::ixNet::OBJ-/globals', '-buildNumber') except Exception: - tgen_abort(msg, "tgen_failed_abort", str(msg)) + tgen_abort(msg, "tgen_failed_abort", msg) def debug_show(self, ph, msg=""): - stats = self.tg_traffic_stats(port_handle=ph,mode="aggregate") + 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)) @@ -228,11 +293,13 @@ def tgen_eval(self, msg, func, **kwargs): (pid, ret_ds) = (0, dict()) try: pid = tgen_profiling_start(msg) + # nosemgrep-next-line ret_ds = eval(func)(**kwargs) tgen_profiling_stop(pid) except Exception as exp: tgen_profiling_stop(pid) logger.info('Error {} executing: {}'.format(msg, func)) + self.collect_diagnosic("tgen_eval_exception") if not self.in_module_start_cleanup: self.exception(exp) self.show_status() @@ -269,10 +336,27 @@ 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) + return self.tg_readvertise_bgp_routes(handle, route_handle) + + def pre_interface_config(self, **kwargs): + intf_kwrgs = {} + intf_params = ['port_handle', 'ipv6_intf_addr', 'ipv6_prefix_length', 'ipv6_resolve_gateway_mac', + 'ipv6_gateway', 'src_mac_addr', 'arp_send_req', 'vlan', 'vlan_id', 'count', 'netmask', + 'vlan_id_step', 'vlan_id_count', 'intf_ip_addr', 'gateway', 'resolve_gateway_mac'] + for param in intf_params: + if kwargs.get(param) is not None: + intf_kwrgs[param] = kwargs.pop(param, '') + intf_kwrgs['mode'] = 'config' + intf_kwrgs['vlan'] = '1' if intf_kwrgs.get('vlan_id') else '0' + intf_kwrgs['skip_start_protocol'] = True + han = self.tg_interface_config(**intf_kwrgs) + if han.get('status', '0') != '1': + logger.error('Host is not created properly') + return {}, kwargs + return han, kwargs def map_field(self, src, dst, d): - if d.get(src) != None: + if d.get(src) is not None: if dst: d[dst] = d[src] else: @@ -286,23 +370,6 @@ def modify_tgen_return_params(self, res, actual, 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 _ 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 @@ -314,81 +381,135 @@ def trgen_pre_proc(self, fname, **kwargs): kwargs_port_handle = kwargs.get('port_handle') kwargs_handle = kwargs.get('handle') kwargs = self.trgen_adjust_mismatch_params(fname, **kwargs) + self.skip_start_protocol = kwargs.pop('skip_start_protocol', self.skip_start_protocol) func = self.get_hltapi_name(fname) if self.tg_version == 8.40 and fname == 'tg_traffic_config': - if kwargs.get('rate_pps') != None: + if kwargs.get('rate_pps') is not 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') == 'modify': + if self.tg_type == 'stc' and kwargs.get('handle'): + func = self.get_hltapi_name('tg_emulation_device_config') + self.map_field("port_handle", None, kwargs) + self.map_field("arp_send_req", None, kwargs) + self.map_field("create_host", None, kwargs) + self.map_field("vlan", None, kwargs) + self.map_field("interface_handle", "handle", kwargs) + self.map_field("src_mac_addr", "mac_addr", kwargs) + self.map_field("gateway", "gateway_ip_addr", kwargs) + self.map_field("ipv6_intf_addr", "intf_ipv6_addr", kwargs) + self.map_field("ipv6_prefix_length", "intf_ipv6_prefix_len", kwargs) + self.map_field("ipv6_gateway", "gateway_ipv6_addr", kwargs) + self.map_field("gateway_step", "gateway_ip_addr_step", kwargs) + self.map_field("ipv6_gateway_step", "gateway_ipv6_addr_step", kwargs) + self.map_field("src_mac_addr_step", "mac_addr_step", kwargs) + if kwargs.get('netmask') is not None: + kwargs['intf_prefix_len'] = IPAddress(kwargs.pop('netmask', '255.255.255.0')).netmask_bits() 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' + 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) + han = re.search(r'.*deviceGroup:(\d)+', han).group(0) logger.debug("Starting Destroy ... {}".format(han)) self.tg_test_control(handle=han, action='stop_protocol') tgen_wait(10) kwargs['topology_handle'] = han - kwargs.pop('handle','') - kwargs.pop('port_handle','') + 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' + 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' + 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.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' - + kwargs.pop('mode', '') + desired_status = kwargs.pop('desired_status', '') + kwargs['action'] = 'check_link_state' + if fname == 'tg_emulation_bgp_route_config': + if self.tg_type == 'ixia': + if kwargs.get('mode') in ['remove', 'delete']: + func = self.get_hltapi_name('tg_network_group_config') + handle = re.search(r'.*networkGroup:(\d)+', kwargs['handle']).group(0) + kwargs = dict() + kwargs['mode'] = 'delete' + kwargs['protocol_handle'] = handle + if fname == 'tg_emulation_bgp_control': + action = kwargs.pop('action', 'enable') + if self.tg_type == 'ixia': + if kwargs.get('mode') in ['link_flap', 'full_route_flap']: + ret_val = {} + if kwargs.get('mode') == 'link_flap': + down_time = kwargs.get('link_flap_down_time', '0') if action == 'enable' else '0' + up_time = kwargs.get('link_flap_up_time', '0') if action == 'enable' else '0' + flap = '1' if action == 'enable' else '0' + ret_val = self.ixia_eval('emulation_bgp_config', mode='modify', handle=kwargs['handle'], + enable_flap=flap, flap_up_time=up_time, flap_down_time=down_time) + if kwargs.get('mode') == 'full_route_flap': + down_time = kwargs.get('route_flap_down_time', '0') + up_time = kwargs.get('route_flap_up_time', '0') + flap = '1' if action == 'enable' else '0' + handle = re.search(r'.*IPRouteProperty:(\d)+', kwargs['route_handle']).group(0) + ret_val = self.ixia_eval('emulation_bgp_route_config', mode='modify', handle=handle, + enable_route_flap=flap, flap_up_time=up_time, flap_down_time=down_time) + self.tg_topology_test_control(action='apply_on_the_fly_changes') + return ret_val + logger.info('Applying changes for IXIA before starting BGP') + self.tg_topology_test_control(action='apply_on_the_fly_changes', tg_wait=10) + if self.tg_type == 'stc' and action != 'enable': + return if fname == 'tg_traffic_control': if self.tg_type == 'ixia' and kwargs.get('action') == 'reset': - ret_ds = get_ixiangpf().traffic_control(action='poll') + ret_ds = self.ixia_eval('traffic_control', action='poll') if ret_ds.get('stopped') == '0': - traffic_items = get_ixiangpf().session_info(mode='get_traffic_items') - if traffic_items.get('traffic_config') != None: + traffic_items = self.ixia_eval('session_info', mode='get_traffic_items') + if traffic_items.get('traffic_config') is not 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) + op_type = kwargs.pop('output_type', None) + capture_wait = kwargs.pop('capture_wait', 120) if self.tg_type == 'ixia': - self.get_capture_stats_state(kwargs.get('port_handle')) + self.get_capture_stats_state(kwargs.get('port_handle'), capture_wait) if op_type == 'hex': func = self.get_hltapi_name('self.local_get_captured_packets') else: kwargs.pop('var_num_frames', '') if fname == 'tg_packet_control': - if self.tg_type == 'stc' and kwargs['action'] in ['start', 'stop']: - port_handle=kwargs.get('port_handle') - if isinstance(port_handle,list): + if self.tg_type == 'stc' and kwargs['action'] in ['start', 'stop', 'cumulative_start']: + if kwargs.get('action') == 'cumulative_start': + kwargs['action'] = 'start' + port_handle = kwargs.get('port_handle') + if isinstance(port_handle, list): ret_ds = None - kwargs.pop('port_handle','') + kwargs.pop('port_handle', '') for ph in port_handle: ret_ds = self.tg_packet_control(port_handle=ph, **kwargs) return ret_ds @@ -396,9 +517,36 @@ def trgen_pre_proc(self, fname, **kwargs): if fname == 'tg_traffic_stats' and self.tg_type == 'ixia': self.ensure_traffic_stats(**kwargs) - if fname == 'tg_emulation_igmp_control': + if fname == 'tg_emulation_dhcp_config': + if self.tg_type == 'ixia': + if kwargs.get('mode') == 'create': + topo_han = self.topo_handle[kwargs.get('port_handle')] + if topo_han is None: + res = self.ixia_eval('topology_config', port_handle=kwargs.get('port_handle')) + topo_han = res['topology_handle'] + self.topo_handle[kwargs.get('port_handle')] = topo_han + logger.info(self.topo_handle) + return {'handles': topo_han} + elif kwargs.get('mode') == 'reset': + kwargs.pop('ip_version', '') + func = self.get_hltapi_name('tg_topology_config') + han = kwargs['handle'] + han = han[0] if type(han) is list else han + logger.debug("Starting Destroy ... {}".format(han)) + self.tg_test_control(handle=han, action='stop_protocol') + tgen_wait(10) + out = self.verify_session_status(kwargs['port_handle']) + kwargs['topology_handle'] = han + kwargs['mode'] = 'destroy' + if out: + self.topo_handle[kwargs['port_handle']] = None + kwargs.pop('handle', '') + kwargs.pop('port_handle', '') + + if fname in ['tg_emulation_igmp_control', 'tg_emulation_mld_control']: if self.tg_type == 'stc': - if kwargs.get('mode') in ['start', 'stop']: return + if kwargs.get('mode') in ['start', 'stop']: + return msg = "{} {}".format(func, kwargs) ret_ds = self.tgen_eval(msg, func, **kwargs) @@ -411,7 +559,7 @@ def trgen_pre_proc(self, fname, **kwargs): msg = "{} {}".format(func, kwargs) ret_ds = self.tgen_eval(msg, func, **kwargs) logger.info(ret_ds) - if ret_ds.get('status') != None: + if ret_ds.get('status') is not None: break if ret_ds.get('status') is None: logger.error('Traffic stats not collected properly, even after waiting for 15 sec...') @@ -430,9 +578,10 @@ def trgen_pre_proc(self, fname, **kwargs): self.ensure_traffic_control(**kwargs) if fname == 'tg_traffic_config': stream_id = ret_ds.get('stream_id', '') - logger.info('STREAM HANDLE: "{}"'.format(stream_id)) + if stream_id: + logger.info('STREAM HANDLE: "{}"'.format(stream_id)) if 'emulation_src_handle' in kwargs or 'emulation_dst_handle' in kwargs: - self.get_emulation_handle_prefixes(ret_ds, kwargs) + self.get_emulation_handle_prefixes(ret_ds, **kwargs) if fname == 'tg_traffic_config' and self.tg_type == 'ixia': self.manage_traffic_config_handles(ret_ds, **kwargs) if fname == 'tg_connect': @@ -441,7 +590,7 @@ def trgen_pre_proc(self, fname, **kwargs): 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: + if kwargs.get('enable_ping_response') is not None and kwargs.get('netmask') is not 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']): @@ -450,8 +599,8 @@ def trgen_pre_proc(self, fname, **kwargs): 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): + if re.search('cleanup_session', func): + if re.search('sth', func): get_sth().invoke("stc::apply") tgen_wait(1) elif kwargs.get('mode') == 'destroy': @@ -462,12 +611,16 @@ def trgen_pre_proc(self, fname, **kwargs): 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] + if not self.skip_start_protocol: + 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']) + elif kwargs.get('mode') == 'modify': + tgen_wait(2) + self.tg_topology_test_control(action='apply_on_the_fly_changes', skip_wait=True) else: ret_ds = self.tg_interface_handle(ret_ds) if fname == 'tg_emulation_bgp_config': @@ -507,13 +660,30 @@ def trgen_pre_proc(self, fname, **kwargs): 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_mld_config': + if self.tg_type == 'ixia': + ret_ds = self.modify_tgen_return_params(ret_ds, 'mld_host_handle', 'host_handle') + if self.tg_type == 'stc': + ret_ds = self.modify_tgen_return_params(ret_ds, 'handles', 'host_handle') + ret_ds['ipv6_handle'] = kwargs['handle'] + if fname == 'tg_emulation_mld_group_config': + if self.tg_type == 'ixia': + ret_ds = self.modify_tgen_return_params(ret_ds, 'mld_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_mld_querier_config': + if self.tg_type == 'ixia': + ret_ds = self.modify_tgen_return_params(ret_ds, 'mld_querier_handle', 'handle') + ret_ds['ipv6_handle'] = kwargs['handle'] + if fname == 'tg_emulation_dotonex_config': + ret_ds = self.modify_tgen_return_params(ret_ds, 'dotonex_device_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']) + 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'} @@ -523,8 +693,8 @@ def trgen_pre_proc(self, fname, **kwargs): 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: + if ret_ds.get('handle') is not None: + if ret_ds['handle'].get('dhcp_handle') is not None: ret_ds['dhcp_handle'] = ret_ds['handle']['dhcp_handle'] else: ret_ds['dhcp_handle'] = ret_ds['handle']['dhcpv6_handle'] @@ -535,13 +705,20 @@ def trgen_pre_proc(self, fname, **kwargs): 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') + ret_ds = self.modify_tgen_return_params(ret_ds, 'topology_handle', 'handles') if fname == 'tg_emulation_dhcp_group_config': + if self.tg_type == 'stc': + if ret_ds.get('dhcpv6_handle'): + ret_ds = self.modify_tgen_return_params(ret_ds, 'dhcpv6_handle', 'handle') 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') + # self.tg_topology_test_control(handle=kwargs['handle'], stack='deviceGroup', action='start_protocol') + # self.tg_topology_test_control(handle=kwargs['handle'], stack='deviceGroup', action='stop_protocol') + tgen_wait(2) + if fname == 'tg_emulation_dhcp_stats': + if self.tg_type == 'ixia': + if 'log' in ret_ds and "Couldn't find statistics for" in ret_ds['log']: + self.fail(ret_ds['log'], "tgen_failed_statistics") + if fname == 'tg_cleanup_session': self.tg_connected = False self.tg_port_handle.clear() @@ -550,15 +727,21 @@ def trgen_pre_proc(self, fname, **kwargs): 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 + 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'): + if kwargs.get('op_mode') == 'normal' and kwargs.get('port_handle', '') in self.ports_fec_disable: + self.ixia_eval('interface_config', port_handle=kwargs.get('port_handle'), mode="modify", autonegotiation=0, + ieee_media_defaults=0, enable_rs_fec=0) + self.ixia_eval('interface_config', port_handle=kwargs.get('port_handle'), mode="modify", autonegotiation=0) + if func == self.get_hltapi_name('tg_test_control') and desired_status: + ret_ds = bool(ret_ds.get(kwargs['port_handle'], {}).get('state', '').lower() == desired_status.lower()) + else: 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': + 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)) @@ -575,14 +758,16 @@ def trgen_pre_proc(self, fname, **kwargs): 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') + self.ixia_eval('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 "Protocols cannot be added or removed while protocols are running" 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']: @@ -599,37 +784,99 @@ def trgen_pre_proc(self, fname, **kwargs): 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 "::ixia::traffic_config: Could not create traffic item" 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") - if "Sorry we could not process this start within specified time" in ret_ds['log']: + if "Device port_address has invalid Network Stack" in ret_ds['log']: + tgen_abort(ret_ds['log'], "tgen_failed_abort", ret_ds['log']) + if "Sorry we could not process this" in ret_ds['log']: self.warn(ret_ds['log']) + try: + if self.tg_type == 'ixia': + stream_handles = utils.make_list(kwargs.get('handle')) + status = self.get_session_errors(stream_handle=stream_handles)[1] + + msg = '' + trafficitems = get_ixnet().getList('/traffic', 'trafficItem') + err_msg = ['One or more destination MACs or VPNs are invalid', 'Too Many Flow Groups'] + for each_ti in trafficitems: + ti_name = get_ixnet().getAttribute(each_ti, '-name') + if ti_name in kwargs.get('handle'): + logger.info('Fetching errors or warnings in Traffic handle: {}'.format(ti_name)) + for log_type in ['errors', 'warnings']: + log_msg = get_ixnet().getAttribute(each_ti, '-' + log_type) + if log_msg: + logger.error('{} in {}: {}'.format(log_type.upper(), ti_name, log_msg)) + else: + logger.info('{} in {}: {}'.format(log_type.upper(), ti_name, log_msg)) + if ti_name in stream_handles: + for e_msg in err_msg: + for message in log_msg: + if e_msg in message: + msg = e_msg + break + if not msg: + if status.get('status') == '0': + msg = 'One or more TGen connceted ports status showing as down' + else: + msg = ret_ds['log'] + if msg in err_msg: + self.fail(ret_ds['log'], "tgen_failed_api", msg) + self.warn(msg) + except Exception as ex: + self.fail(ex, "tgen_failed_api", ret_ds['log']) + if "Unable to set attributes" in ret_ds['log']: self.fail(ret_ds['log'], "tgen_failed_set_attrib") + if "Port already used" in ret_ds['log']: + self.fail(ret_ds['log'], "tgen_failed_port_in_used") + if "Unable to connect to IxNetwork" in ret_ds['log']: + tgen_abort(ret_ds['log'], "tgen_failed_abort", ret_ds['log']) + if "Please provide a valid traffic item handle or name" in ret_ds['log']: + self.fail(ret_ds['log'], "tgen_failed_api", ret_ds['log']) + if "Error in" in ret_ds['log']: + self.fail(ret_ds['log'], "tgen_failed_api", ret_ds['log']) + if "started Protocol stack is not permitted" in ret_ds['log']: + self.fail(ret_ds['log'], "tgen_failed_configure_stack") + if "Unable to add" in ret_ds['log']: + self.fail(ret_ds['log'], "tgen_failed_configure_stack") + if "Ixnetwork error occured" in ret_ds['log']: + tgen_abort(ret_ds['log'], "tgen_failed_abort", ret_ds['log']) + if "Couldn't find statistics for" in ret_ds['log']: + self.fail(ret_ds['log'], "tgen_failed_statistics") # 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'): + if func == self.get_hltapi_name('tg_test_control') and desired_status: + ret_ds = bool(ret_ds.get(kwargs['port_handle'], {}).get('state', '').lower() == desired_status.lower()) + else: ret_ds = bool("log" not in ret_ds) return ret_ds def trgen_post_proc(self, fname, **kwargs): pass + def trgen_adjust_mismatch_params(self, fname, **kwargs): - pass - def local_stc_tapi_call(self,param): - pass + return kwargs + + def local_stc_tapi_call(self, param): + return None + + def verify_session_status(self, port_handle, tg_wait=2, ses_ns=True, retry=10): + return None + 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) - self.tg_connected = connect_retry(self) + def __init__(self, tg_type, tg_version, tg_ip=None, tg_port_list=None, **kwargs): + TGBase.__init__(self, tg_type, tg_version, tg_ip, tg_port_list, **kwargs) logger.info('TG STC Init...done') def clean_all(self): @@ -639,10 +886,13 @@ def clean_all(self): 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) + pre_wait = 2 for port_handle, handle_list in self.cached_interface_config_handles.items(): for handle in handle_list: + if pre_wait: + tgen_wait(pre_wait) + pre_wait = 0 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() @@ -650,39 +900,78 @@ def clean_all(self): logger.debug("TG CLEAN ALL FINISHED") def get_port_status(self, port_list): - port_handle_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): + 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 tg_save_config(self, file_name=None, file_path=None): + file_path = tgen_get_logs_path_folder() if not file_path else file_path + file_name = 'stc_tgen_config.xml' if not file_name else file_name + filename = os.path.join(file_path, file_name) + logger.info('The config file is saved at {}'.format(filename)) + self.tg_save_xml(filename=filename) + 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) + self.ports_fec_disable = {} + logger.info("TGen: Trying to connect") + msg = "Executing: stc::connect" + pid = tgen_profiling_start(msg, max_time=900) + ret_ds = get_sth().connect(device=self.tg_ip, port_list=self.tg_port_list, break_locks=1) + tgen_profiling_stop(pid) + logger.info("TGen: connect status: {}".format(ret_ds)) if ret_ds.get('status') != '1': return ret_ds - port_handle_list=[] + 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) if port_details_all.get('status', '0') == '1' and port_details_all.get('intf_speed', '') != '': 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") + for intf_speed, port_handle in zip(intf_speed_list, port_handle_list): + if not self.ports_fec_disable.get(intf_speed, []): + self.ports_fec_disable[intf_speed] = [] + self.ports_fec_disable[intf_speed].append(port_handle) + for speed in ['100000', '50000', '25000']: + if speed == '100000' and self.ports_fec_disable.get(speed): + logger.info('Disabling FEC on ports {} is of speed {}'.format(self.ports_fec_disable[speed], speed)) + self.tg_interface_config(port_handle=self.ports_fec_disable[speed], + mode="modify", forward_error_correct="false") + if speed in ['50000', '25000'] and self.ports_fec_disable.get(speed): + fec_opt = 'disable_fec_50g' if speed == '50000' else 'disable_fec' + logger.info('Disabling FEC on ports {} is of speed {}'.format(self.ports_fec_disable[speed], speed)) + self.tg_interface_config(port_handle=self.ports_fec_disable[speed], + mode="modify", fec_option=fec_opt, autonegotiation='1') + self.tg_interface_config(port_handle=self.ports_fec_disable[speed], + mode="modify", fec_option=fec_opt, autonegotiation='0') return None + def get_card_type(self, port_list): + card_type = {} + + chassisHandle = self.local_stc_tapi_call('stc::get ' + 'system1' + ' -children-PhysicalChassisManager') + mgrChassis = self.local_stc_tapi_call('stc::get ' + chassisHandle + ' -children-PhysicalChassis') + modHandles = self.local_stc_tapi_call('stc::get ' + mgrChassis + ' -children-PhysicalTestModule') + + port_list = utils.make_list(port_list) + for modHandle in modHandles.split(' '): + moduleData = self.local_stc_tapi_call('stc::get ' + modHandle) + for port in port_list: + if port.split('/')[0] == re.search(r'-Index\s(\d+)', moduleData).group(1): + # logger.info('Card Type for port {} is {}'.format(port, re.search(r'-Model\s([\S]+)',moduleData).group())) + card_type[port] = re.search(r'-Model\s([\S]+)', moduleData).group(1) + logger.info('Card Type: {}'.format(card_type)) + return card_type + def trgen_adjust_mismatch_params(self, fname, **kwargs): if fname == 'tg_traffic_config': self.map_field("ethernet_value", "ether_type", kwargs) @@ -693,15 +982,19 @@ def trgen_adjust_mismatch_params(self, fname, **kwargs): self.map_field("data_pattern_mode", None, kwargs) self.map_field("global_stream_control", None, kwargs) self.map_field("global_stream_control_iterations", None, kwargs) - if kwargs.get('custom_pattern') != None: - kwargs['custom_pattern'] = kwargs['custom_pattern'].replace(" ","") - kwargs['disable_signature'] = '1' + self.map_field("vlan_protocol_tag_id", "vlan_tpid", kwargs) + if kwargs.get('vlan_tpid') is not None: + if len(str(kwargs['vlan_tpid'])) == 4: + kwargs['vlan_tpid'] = int('0x{}'.format(str(kwargs['vlan_tpid']).lstrip('0x')), 0) + if kwargs.get('custom_pattern') is not None: + kwargs['custom_pattern'] = kwargs['custom_pattern'].replace(" ", "") + kwargs['disable_signature'] = kwargs.get('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) self.map_field("icmp_code", "icmpv6_code", kwargs) self.map_field("icmp_target_addr", "icmpv6_target_address", kwargs) - if kwargs.get('vlan_id') != None: + if kwargs.get('vlan_id') is not None: if kwargs.get('l2_encap') is None: kwargs['l2_encap'] = 'ethernet_ii_vlan' if type(kwargs.get('vlan_id')) != list: @@ -712,9 +1005,11 @@ def trgen_adjust_mismatch_params(self, fname, **kwargs): vlan_list = kwargs.get('vlan_id') kwargs['vlan_id'] = vlan_list[0] kwargs['vlan_id_outer'] = vlan_list[1] + if len(x) > 2: + kwargs['vlan_id_other'] = vlan_list[2:] for param in ('enable_time_stamp', 'enable_pgid', 'vlan', 'duration'): - if kwargs.get(param) != None: + if kwargs.get(param) is not None: kwargs.pop(param) for param in ('udp_src_port_mode', 'udp_dst_port_mode', @@ -723,47 +1018,47 @@ def trgen_adjust_mismatch_params(self, fname, **kwargs): 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') is None: + if (kwargs.get('transmit_mode') is not None + or kwargs.get('l3_protocol') is not None) and \ + kwargs.get('length_mode') is None: kwargs['length_mode'] = 'fixed' - if kwargs.get('port_handle2') != None: + if kwargs.get('port_handle2') is not None: kwargs['dest_port_list'] = kwargs.pop('port_handle2') - if kwargs.get('high_speed_result_analysis') != None and \ - kwargs.get('track_by') != None: + if kwargs.get('high_speed_result_analysis') is not None and \ + kwargs.get('track_by') is not 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: + if kwargs.get('circuit_endpoint_type') is not 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) + 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_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'): + '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) + kwargs.pop(param, None) - if kwargs.get('mac_dst_mode') != None: + if kwargs.get('mac_dst_mode') is not 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. + # 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') is None: kwargs['high_speed_result_analysis'] = 0 @@ -775,53 +1070,116 @@ def trgen_adjust_mismatch_params(self, fname, **kwargs): self.map_field("max_wait_timer", None, kwargs) if kwargs.get('db_file') is None: kwargs['db_file'] = 0 - if kwargs.get('handle') != None: - kwargs['stream_handle'] = kwargs['handle'] - kwargs.pop('handle') + if kwargs.get('handle') is not None: + kwargs['stream_handle'] = kwargs.pop('handle', '') + if kwargs.get('stream_handle'): + kwargs['stream_handle'] = kwargs['stream_handle'] if isinstance(kwargs['stream_handle'], str) else list(kwargs['stream_handle']) elif fname == 'tg_interface_config': self.map_field("ipv4_resolve_gateway", "resolve_gateway_mac", kwargs) + self.map_field("ipv6_resolve_gateway", "ipv6_resolve_gateway_mac", kwargs) self.map_field("transmit_mode", None, kwargs) self.map_field("ignore_link", None, kwargs) - if kwargs.get("resolve_gateway_mac") != None: - kwargs['resolve_gateway_mac'] = 'false' if kwargs['resolve_gateway_mac'] == 0 else 'true' + self.map_field("vlan_custom_config", None, kwargs) + for param in ['resolve_gateway_mac', 'ipv6_resolve_gateway_mac']: + if kwargs.get(param) is not None: + kwargs[param] = 'false' if str(kwargs[param]) in ['false', '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' if kwargs.get('create_host') == 'false': - if kwargs.get('netmask') != None: + if kwargs.get('netmask') is not None: kwargs['intf_prefix_len'] = IPAddress(kwargs.pop('netmask', '255.255.255.0')).netmask_bits() if kwargs.get('mode') != 'destroy' and kwargs.get('enable_ping_response') is None: kwargs['enable_ping_response'] = 1 elif fname == 'tg_emulation_bgp_config': - if kwargs.get('enable_4_byte_as') != None: + if kwargs.get('enable_4_byte_as') is not None: l_as = int(int(kwargs['local_as']) / 65536) l_nn = int(kwargs['local_as']) - (l_as * 65536) r_as = int(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) + 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: + if kwargs.get('ip_addr_step') is not 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: + if kwargs.get('source_pool_handle') is not None: kwargs['device_group_mapping'] = 'MANY_TO_MANY' kwargs['enable_user_defined_sources'] = '1' kwargs['specify_sources_as_list'] = '0' elif fname == 'tg_emulation_ospf_config': kwargs.pop('validate_received_mtu', '') kwargs.pop('max_mtu', '') + elif fname == 'tg_emulation_dhcp_server_config': + self.map_field("ipaddress_pool_prefix_length", None, kwargs) + self.map_field("subnet", None, kwargs) + self.map_field("pool_count", None, kwargs) + self.map_field("ipaddress_pool_step", None, kwargs) + if kwargs.get('mode') == 'reset': + kwargs.pop('port_handle', '') + elif fname == 'tg_emulation_dhcp_config': + if kwargs.get('mode') == 'reset': + kwargs.pop('port_handle', '') + elif fname == 'tg_emulation_dhcp_server_relay_agent_config': + kwargs.pop('assign_strategy', '') + elif fname == 'tg_emulation_dot1x_config': + if int(kwargs.get('num_sessions', 1)) > 1: + for entry in ['username', 'password']: + if kwargs.get(entry): + kwargs[entry] = str(kwargs[entry]) + '@s' + elif fname == 'tg_emulation_dot1x_control': + if kwargs.get('mode') == 'stop': + kwargs['mode'] = 'logout' + elif fname == 'tg_emulation_mld_group_config': + self.map_field("g_filter_mode", None, kwargs) + if kwargs.get('source_pool_handle') is not None: + kwargs['device_group_mapping'] = 'MANY_TO_MANY' + kwargs['user_defined_src'] = 'true' + elif fname == 'tg_emulation_mld_querier_config': + self.map_field("active", None, kwargs) + if kwargs.get('mode') == 'create': + handles, ret_kwargs = self.pre_interface_config(**kwargs) + kwargs = ret_kwargs + kwargs['handle'] = handles['handle'] + elif fname == 'tg_emulation_mld_config': + self.map_field("active", None, kwargs) + if kwargs.get('mode') == 'create': + handles, ret_kwargs = self.pre_interface_config(**kwargs) + kwargs = ret_kwargs + kwargs['handle'] = handles['handle'] + elif fname == 'tg_emulation_bgp_route_config': + if kwargs.get('prefix_from') is not None: + prefix_len = kwargs.pop('prefix_from', '24') + kwargs['netmask'] = IPNetwork('0.0.0.0/{}'.format(prefix_len)).netmask.format() + elif fname == 'tg_emulation_ptp_config': + self.map_field("vlan_id", "vlan_id1", kwargs) + self.map_field("profile", "", kwargs) + self.map_field("arp_send_req", "", kwargs) + self.map_field("vlan", "", kwargs) + self.map_field("announce_current_utc_offset_valid", "", kwargs) + self.map_field("announce_ptp_timescale", "", kwargs) + self.map_field("path_trace_tlv", "", kwargs) + kwargs['local_ip_prefix_len'] = IPAddress(kwargs.get('local_ip_prefix_len', '255.255.255.0')).netmask_bits() + log_intv_map = {'-9': '{"-511"}', '-8': '{"-255"}', '-7': '{"-127"}', '-6': '{"-63"}', '-5': '{"-31"}', + '-4': '{"-15"}', '-3': '{"-7"}', '-2': '{"-3"}', '-1': '{"-1"}', '0': '{"0"}', '1': '{"1"}', + '2': '{"3"}', '3': '{"7"}', '4': '{"15"}', '5': '{"31"}', '6': '{"63"}', '7': '{"127"}', + '8': '{"255"}', '9': '{"511"}'} + kwargs['log_sync_message_interval'] = log_intv_map.get(kwargs.get('log_sync_message_interval', '-3')) + kwargs['log_minimum_delay_request_interval'] = log_intv_map.get(kwargs.get('log_minimum_delay_request_interval', '0')) + kwargs['log_announce_message_interval'] = log_intv_map.get(kwargs.get('log_announce_message_interval', '0')) + kwargs['time_source'] = kwargs.get('time_source', 'gps') + kwargs['master_clock_class'] = kwargs.get('master_clock_class', '6') return kwargs def tg_interface_handle(self, ret_ds): @@ -833,7 +1191,7 @@ def tg_interface_handle(self, ret_ds): elif "handles" in ret_ds: temp = ret_ds['handles'] if type(temp) == list: - temp = temp[0] if len(temp)==1 else temp + temp = temp[0] if len(temp) == 1 else temp ret_ds['handle'] = temp return ret_ds @@ -852,6 +1210,11 @@ def tg_igmp_querier_control(self, mode, handle): logger.info("IGMP Querier action completed: {}".format(result)) return result if result['status'] == '1' else None + def tg_mld_querier_control(self, mode, handle): + result = self.tg_emulation_mld_querier_control(mode=mode, handle=handle) + logger.info("MLD 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)) @@ -862,24 +1225,25 @@ def tg_ospf_lsa_config(self, **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)) + 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.get('status') == '1': - logger.debug('TG API Run Status: Success') + logger.debug('TGen: API Run Status: Success') else: - logger.warning('TG API Error: %s' % ret_ds.get('log', '')) + logger.warning('TGen: API Error: %s' % ret_ds.get('log', '')) self.tg_connected = False self.tg_port_handle.clear() - def local_stc_tapi_call(self,param): + def local_stc_tapi_call(self, param): res = get_sth().invoke(param) return res - def _custom_filter_delete(self,port_handle): + 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() @@ -894,8 +1258,7 @@ def _custom_filter_delete(self,port_handle): self.tg_port_analyzer[port_handle] = {} self.tg_port_analyzer[port_handle]['pattern_list'] = list() - - def _custom_filter_config(self,**kwargs): + def _custom_filter_config(self, **kwargs): ret_dict = dict() ret_dict['status'] = '1' @@ -913,30 +1276,30 @@ def _custom_filter_config(self,**kwargs): return ret_dict project_handle = 'Project1' - #This is the default name/handle. Things might not work if it is different. + # 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): + 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 + 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 + logger.info('offset_list and pattern_list must be of same length') + ret_dict['status'] = '0' + return ret_dict - #Delete existing filters, if any + # 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 + # 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') @@ -945,7 +1308,7 @@ def _custom_filter_config(self,**kwargs): f_index = 1 for offset in kwargs['offset_list']: - filter_name = 'CustomFilter'+str(f_index) + 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 @@ -954,7 +1317,7 @@ def _custom_filter_config(self,**kwargs): else: current_analyzer = self.tg_port_analyzer[port_handle]['analyzer_handle'] - if mode in ['start','stop']: + 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) @@ -967,11 +1330,11 @@ def _custom_filter_config(self,**kwargs): 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: + if self.tg_port_analyzer[port_handle]['pattern_list'] is not 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): + if not isinstance(p_list, list): logger.info('Each pattern must be a list') ret_dict['status'] = '0' return ret_dict @@ -997,34 +1360,34 @@ def _custom_filter_config(self,**kwargs): 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}) + ret_dict['status'] = '1' for rxresult in rx_result_handle_list: - logger.debug('rxresult row: {}'.format(rxresult)) + logger.info('rxresult row: {}'.format(rxresult)) rx_result_hash = self.local_stc_tapi_call('stc::get ' + rxresult) - logger.debug('RX Result: {}'.format(rx_result_hash)) + logger.info('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") + # 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): + 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)) + 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)) + 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: @@ -1035,8 +1398,7 @@ def _custom_filter_config(self,**kwargs): return ret_dict - - def tg_custom_filter_config(self,**kwargs): + def tg_custom_filter_config(self, **kwargs): ret_dict = dict() ret_dict['status'] = '1' stc_kwargs = dict() @@ -1054,16 +1416,16 @@ def tg_custom_filter_config(self,**kwargs): offset_count = 0 offset_list = list() pattern_list = list() - for offset,pattern in zip(['pattern_offset1'],['pattern1']): + 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)) + 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']): + 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]) @@ -1071,17 +1433,23 @@ def tg_custom_filter_config(self,**kwargs): 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)) + 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) + 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) + result = self._custom_filter_config(mode='getstats', port_handle=port_handle) + if result['status'] == '0': + logger.info('No packets matching filter criteria is received') + ret_dict[port_handle]['custom_filter'].update({'filtered_frame_count': 0}) + ret_dict[port_handle]['custom_filter'].update({'total_rx_count': 0}) + return ret_dict + 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}) @@ -1089,8 +1457,7 @@ def tg_custom_filter_config(self,**kwargs): return ret_dict - - def get_emulation_handle_prefixes(self, ret_ds, kwargs): + def get_emulation_handle_prefixes(self, ret_ds, **kwargs): ip_dict = dict() for emu_handle in ['emulation_src_handle', 'emulation_dst_handle']: handle_list = utils.make_list(kwargs.get(emu_handle)) @@ -1100,10 +1467,11 @@ def get_emulation_handle_prefixes(self, ret_ds, kwargs): temp = dict() if handle.startswith('host') or handle.startswith('emulateddevice'): device_obj = self.local_stc_tapi_call('stc::get ' + handle + ' -toplevelif-Targets') - if device_obj.startswith('eth') or device_obj.startswith('vlan'): continue + if device_obj.startswith('eth') or device_obj.startswith('vlan'): + continue temp['start_addr'] = self.local_stc_tapi_call( 'stc::get ' + device_obj.split(' ')[0] + ' -Address') - temp['hadle'] = handle + temp['handle'] = handle else: if ret_ds.get('stream_id', ''): emu_type = 'src' if emu_handle == 'emulation_src_handle' else 'dst' @@ -1111,7 +1479,7 @@ def get_emulation_handle_prefixes(self, ret_ds, kwargs): device = device_obj.split(' ')[index] temp['start_addr'] = self.local_stc_tapi_call('stc::get ' + device + ' -StartIpList') temp['count'] = self.local_stc_tapi_call('stc::get ' + device + ' -NetworkCount') - temp['hadle'] = handle + temp['handle'] = handle ip_dict[emu_handle].append(temp) except Exception: logger.error("Couldn't get ip prefix for handle: {}".format(handle)) @@ -1119,24 +1487,31 @@ def get_emulation_handle_prefixes(self, ret_ds, kwargs): 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) + def __init__(self, tg_type, tg_version, tg_ip=None, tg_port_list=None, tg_ix_server=None, tg_ix_port=8009, tg_virtual=False, **kwargs): + self.ix_server = tg_ix_server + self.ix_port = str(tg_ix_port) self.topo_handle = {} self.traffic_config_handles = {} - TGBase.__init__(self, tg_type, tg_version, tg_ip, tg_port_list) - self.tg_connected = connect_retry(self) + TGBase.__init__(self, tg_type, tg_version, tg_ip, tg_port_list, tg_virtual, **kwargs) logger.info('TG Ixia Init...done') def clean_all(self): + if self.skip_traffic: + return 0 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: + traffic_items = self.ixia_eval('session_info', mode='get_traffic_items') + if traffic_items.get('traffic_config') is not 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.info("TG CLEAN ALL: stop and remove all streams on {}".format(items)) + ret_ds = self.ixia_eval('traffic_control', action='poll') + if ret_ds.get('stopped') == '0': + logger.debug("stopping streams before reset") + ret_ds = self.ixia_eval('traffic_control', action='stop', handle=items) + logger.debug(ret_ds) + tgen_wait(2) + ret_ds = self.ixia_eval('traffic_control', action="reset", handle=items) logger.debug(ret_ds) tgen_wait(2) else: @@ -1146,40 +1521,137 @@ def clean_all(self): 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.ixia_eval('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] + 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(15) + logger.info("Removing topology handles: {}".format(self.topo_handle)) + self.tg_topology_test_control(action='stop_all_protocols', tg_wait=10) + self.verify_session_status(ph_list) + reconnect = False + for ph in ph_list: + topo_handle = self.topo_handle[ph] + ret_ds = {} + if topo_handle: + logger.info("removing cached topology {} on port {}".format(topo_handle, ph)) + try: + ret_ds = self.ixia_eval('topology_config', topology_handle=topo_handle, mode='destroy') + except Exception: + self.collect_diagnosic("tgen_eval_exception") + reconnect = True + logger.debug(ret_ds) + if 'Unable to delete' in ret_ds.get('log', ''): + self.collect_diagnosic("tgen_eval_exception") + reconnect = True + self.topo_handle[ph] = None + if reconnect: + break + tgen_wait(2) + if reconnect: + self.tg_disconnect() + self.tg_connected = False + self.topo_handle.clear() + tgen_wait(5, 'Wait before reconnect tgen...') + connect_tgen() - logger.debug("TG CLEAN ALL FINISHED") + logger.info("TG CLEAN ALL FINISHED") + + def ixia_eval(self, func, **kwargs): + + msg = "{} {}".format(func, kwargs) + logger.info('Executing: {}'.format(msg)) + (pid, ret_ds) = (0, dict()) + try: + pid = tgen_profiling_start(msg) + ret_ds = getattr(get_ixiangpf(), func)(**kwargs) + tgen_profiling_stop(pid) + except Exception as exp: + tgen_profiling_stop(pid) + logger.info('Error {} executing: {}'.format(msg, func)) + self.collect_diagnosic("tgen_eval_exception") + if not self.in_module_start_cleanup: + self.exception(exp) + self.show_status() + + return ret_ds + + def verify_session_status(self, port_handle, tg_wait=2, ses_ns=True, retry=10): + try: + ret_list = [] + ph_list = utils.make_list(port_handle) + res = self.ixia_eval('protocol_info', mode='global_per_port') + logger.debug(res) + if res.get('status') == '0' and 'Make sure protocol is started' in res.get('log', ''): + logger.debug('All device groups were deleted from the configured topologies') + for ph in ph_list: + topo_handle = self.topo_handle[ph] + ret_val = False + if topo_handle: + for loop in range(1, retry + 1): + if res.get('status') == '0' and 'Make sure protocol is started' in res.get('log', ''): + _ = 'All device groups were deleted from the topologies' + elif not res['global_per_port'].get(ph): + if loop == retry: + logger.debug('No topologies configured on the port {}'.format(ph)) + logger.debug('Topology {} not found on port {}..try..{}'.format(topo_handle, ph, loop)) + tgen_wait(tg_wait) + res = self.ixia_eval('protocol_info', mode='global_per_port') + logger.debug(res) + continue + else: + total = res['global_per_port'][ph]['sessions_total'] + total_ns = res['global_per_port'][ph]['sessions_not_started'] + total_s = res['global_per_port'][ph]['sessions_up'] + if ses_ns and total != total_ns: + continue + if not ses_ns and total != total_s: + continue + ret_val = True + break + ret_list.append(ret_val) + return False if False in ret_list else True + except Exception as exp: + logger.debug(exp) + + def tg_topology_test_control(self, stack=None, skip_wait=False, tg_wait=2, **kwargs): + if kwargs.get('handle') is not None and stack is not None: + found = re.search(r'.*{}:(\d)+'.format(stack), kwargs['handle']) + if found: + kwargs['handle'] = found.group(0) + kwargs.pop('tg_wait', '') + skip_start_protocol = kwargs.pop('skip_start_protocol', self.skip_start_protocol) + if skip_start_protocol: + tgen_wait(2, 'skipping the start protocol') + return + for _ in range(1, 30): + if kwargs.get('action') == 'apply_on_the_fly_changes': + res = self.ixia_eval('test_control', action='apply_on_the_fly_changes') + else: + res = self.ixia_eval('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 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)) + ret_ds = self.ixia_eval('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')) + 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 @@ -1189,16 +1661,17 @@ def get_ixnetwork_status(self,**kwargs): 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) + # nosemgrep-next-line + 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']): + 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']) + 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'])) @@ -1221,56 +1694,288 @@ 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)) + if ret_ds: + logger.info('Connection Manager Info: {}'.format(ret_ds)) + return ret_ds + + def get_session_errors(self, **kwargs): + try: + root = get_ixnet().getRoot() + globals = get_ixnet().getList(root, 'globals') + appErrors = get_ixnet().getList(globals[0], 'appErrors') + errorMsgList = get_ixnet().getList(appErrors[0], 'error') + logger.error('Global API Errors: {}'.format(errorMsgList)) + errorDesc = dict() + for errorMsg in errorMsgList: + errorDesc[errorMsg] = dict() + errorDesc[errorMsg]['description'] = get_ixnet().getAttribute(errorMsg, '-description') + errorDesc[errorMsg]['name'] = get_ixnet().getAttribute(errorMsg, '-name') + if 'Too Many Flow Groups' in errorDesc[errorMsg]['name']: + msg = errorDesc[errorMsg]['description'] + tgen_abort(msg, "tgen_failed_abort", msg) + if 'Port is Unassigned or Port CPU not ready' in errorDesc[errorMsg]['name']: + msg = errorDesc[errorMsg]['description'] + stream_handle = kwargs.get('stream_handle') + self.get_port_connected_session(stream_handle=stream_handle) + tgen_abort(msg, "tgen_failed_abort", msg) + logger.info('Global API Description: {}'.format(errorDesc)) + ret_ds = self.get_port_connected_session(port_handle=list(self.tg_port_handle.values())) + except Exception as exp: + self.fail(exp, "tgen_failed_api", str(exp)) return ret_ds + def get_port_connected_session(self, **kwargs): + port_handles = [] + if kwargs.get('stream_handle'): + stream_handles = utils.make_list(kwargs.get('stream_handle')) + ports = self.get_port_handle_from_stream_handle(stream_handle=stream_handles) + if ports: + for stream in stream_handles: + port_handles.extend(ports[stream]['sources']) + port_handles.extend(ports[stream]['destinations']) + + if kwargs.get('port_handle'): + p_handles = utils.make_list(kwargs.get('port_handle')) + port_handles.extend(p_handles) + + port_handles = list(set(port_handles)) + cur_session = self.ixnet_connect_status['connection']['username'] + + if port_handles: + for port in port_handles: + ch, card, po = port.split('/') + tg_ip = utils.make_list(self.tg_ip) + parent = '::ixNet::OBJ-/availableHardware/chassis:' + try: + s_owner = get_ixnet().getAttribute(parent + '"' + tg_ip[int(ch) - 1] + '"' + '/card:' + card + '/' + 'port:' + po, '-owner') + if s_owner != cur_session: + logger.info('The port "{}" is connected to IxNetwork session "{}". The current IxNetwork session is "{}"'.format(port, s_owner, cur_session)) + msg = 'The port ownership is lost. In use by {}'.format(s_owner) + tgen_abort(msg, "tgen_failed_abort", msg) + except Exception as exp: + self.fail(exp, "tgen_failed_api", str(exp)) + status = self.ixia_eval('test_control', action='check_link_state', port_handle=list(self.tg_port_handle.values())) + logger.info('TGen connected Port status: {}'.format(status)) + return port_handles, status + + def get_port_handle_from_stream_handle(self, **kwargs): + + traffic_data = {} + if not kwargs.get('stream_handle'): + return traffic_data + + try: + stream_list = kwargs.get('stream_handle') + trafficitems = get_ixnet().getList('/traffic', 'trafficItem') + + for each_ti in trafficitems: + ti_name = get_ixnet().getAttribute(each_ti, '-name') + traffic_data[ti_name] = {} + endpointsets = get_ixnet().getList(each_ti, 'endpointSet') + + if ti_name in stream_list: + for each_endpoint in endpointsets: + vport_source = [] + vport_destination = [] + + sources = get_ixnet().getAttribute(each_endpoint, '-sources') + for each_source in sources: + porthandlefetch_status = get_ixiangpf().convert_vport_to_porthandle( + vport=each_source.split('/protocols')[0]) + if porthandlefetch_status['status'] != '1': + continue + else: + vport = porthandlefetch_status['handle'] + vport_source.append(vport) + + destinations = get_ixnet().getAttribute(each_endpoint, '-destinations') + for each_destination in destinations: + porthandlefetch_status = get_ixiangpf().convert_vport_to_porthandle( + vport=each_destination.split('/protocols')[0]) + if porthandlefetch_status['status'] != '1': + continue + else: + vport = porthandlefetch_status['handle'] + vport_destination.append(vport) + + traffic_data[ti_name]['sources'] = list(set(vport_source)) + traffic_data[ti_name]['destinations'] = list(set(vport_destination)) + logger.info('traffic_data: {}'.format(traffic_data)) + return traffic_data + except Exception: + return traffic_data + def connect(self): self.tg_ns = 'ixiangpf' self.ixnetwork_os = None + self.ixnet_connect_status = None ret_ds = self.show_status() if ret_ds and ret_ds['status'] == '1' and ret_ds['session_in_use'] > ret_ds['total_session'] - 1: msg = 'Max recommended connection is reached, should abort the run' tgen_abort(msg, "tgen_failed_abort", msg) params = SpyTestDict(device=self.tg_ip, port_list=self.tg_port_list, connect_timeout=60, - ixnetwork_tcl_server=self.ix_server, break_locks=1, reset=1) + ixnetwork_tcl_server=self.ix_server, break_locks=1, reset=1) + if self.tgen_config_file: + params.config_file = self.tgen_config_file + params.pop('reset', '') if self.ix_port == "443": # ixnetwork linux VM params.user_name = "admin" params.user_password = "admin" - ret_ds = get_ixiangpf().connect(**params) + if self.tg_virtual: + params.ixnetwork_license_servers = "10.59.135.10" + params.ixnetwork_license_type = "subscription_tier1" + try: + logger.info("TGen: Trying to connect") + ret_ds = get_ixiangpf().connect(**params) + except Exception: + return {} - logger.info(ret_ds) - if ret_ds['status'] != '1': + logger.info("TGen: connect status: {}".format(ret_ds)) + if ret_ds.get('status') != '1': return ret_ds + self.ixnet_connect_status = ret_ds if 'connection' in ret_ds and ('api_key_file' in ret_ds['connection'] or 'api_key' in ret_ds['connection']): self.ixnetwork_os = 'linux' else: self.ixnetwork_os = 'windows' - 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) + if not ret_ds.get('port_handle') and not self.tg_port_list: + logger.info('No TGEN ports specificed in the testbed') + return None + self.ports_fec_disable = [] + res = dict() + for _ in range(3): + res = self.ixia_eval('traffic_stats') + if res.get('status') == '1': + break + if not isinstance(self.tg_ip, list): + 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] + if self.tg_port_handle[port] is None: + logger.info('Port Handle Info: {}'.format(self.tg_port_handle)) + logger.info("Port handle for port {} is None...retrying connect again".format(port)) + self.tg_disconnect() + return {} + # For topology_handle. + self.topo_handle[self.tg_port_handle[port]] = None + # To get 100G ports. + if not res.get(self.tg_port_handle[port]): + logger.info('Traffic Stats Info: {}'.format(res)) + logger.info("Failed to get port {} from the traffic stats info..retrying connect again".format(self.tg_port_handle[port])) + self.tg_disconnect() + return ret_ds + if res[self.tg_port_handle[port]]['aggregate']['tx']['line_speed'] in ['100GE', '25GE']: + self.ports_fec_disable.append(self.tg_port_handle[port]) + else: + tg_ip = utils.make_list(self.tg_ip) + tg_port_list = utils.make_list(self.tg_port_list) + out = ret_ds['port_handle'] + tg_port_handles = dict() + for ip in tg_ip: + key1, key2, key3, key4 = ip.split('.') + for k1 in out: + if k1 != key1: + continue + for k2 in out[k1]: + if k2 != key2: + continue + for k3 in out[k1][k2]: + if k3 != key3: + continue + for k4 in out[k1][k2][k3]: + if k4 != key4: + continue + tg_port_handles[ip] = out[k1][k2][k3][k4] + for index, port_list in enumerate(tg_port_list): + # ixia output is different for key 'port_handle': {'10': {'59': {'130': {'4': {'1/5': '1/1/5'}}}}} + for port in port_list: + vport = str(index + 1) + '/' + port + self.tg_port_handle[vport] = tg_port_handles[tg_ip[index]][port] + if self.tg_port_handle[vport] is None: + logger.info('Port Handle Info: {}'.format(tg_port_handles)) + logger.info("Port handle for port {} is None...retrying connect again".format(port)) + self.tg_disconnect() + return {} + # For topology_handle. + self.topo_handle[self.tg_port_handle[vport]] = None + # To get 100G ports. + if not res.get(self.tg_port_handle[vport]): + logger.info('Traffic Stats Info: {}'.format(ret_ds)) + logger.info("Failed to get port {} from the traffic stats info..retrying connect again".format(self.tg_port_handle[vport])) + self.tg_disconnect() + return ret_ds + if res[self.tg_port_handle[vport]]['aggregate']['tx']['line_speed'] in ['100GE', '25GE']: + self.ports_fec_disable.append(self.tg_port_handle[vport]) + + if not params.get('config_file'): + if self.ports_fec_disable: + logger.info('Disabling FEC for ports: {}'.format(self.ports_fec_disable)) + res = self.ixia_eval('interface_config', port_handle=list(self.tg_port_handle.values()), mode="modify", + autonegotiation=0, ieee_media_defaults=0, enable_rs_fec=0, force_enable_rs_fec='0', + firecode_force_on='0') + self.ixia_eval('interface_config', port_handle=self.ports_fec_disable, mode="modify", autonegotiation=0) + logger.info(res) + self.auto_neg = 1 if self.auto_neg else 0 + if self.tg_link_params.get('port_speed') and self.tg_link_params.get('auto_neg'): + if self.tg_link_params.get('port_speed')[0] == self.tg_link_params.get('auto_neg')[0]: + self.auto_neg = self.tg_link_params.get('auto_neg')[1] + + def p_han(x): + return [self.get_port_handle(p) for p in x] + port_speed = [] + if self.tg_link_params.get('port_speed'): + port_speed = [p_han(self.tg_link_params['port_speed'][0]), self.tg_link_params['port_speed'][1]] + elif self.tg_port_speed: + port_speed = [list(self.tg_port_handle.values()), utils.make_list(self.tg_port_speed)] + if port_speed: + logger.info("Changing the TGEN ports speed to '{}' on ports {}".format(port_speed[1], port_speed[0])) + speed_map = {'ether2500': 'ether2.5Gig', 'ether10000': 'ether10Gig'} + speeds = [speed_map[i] if speed_map.get(i) else i for i in port_speed[1]] + res = self.ixia_eval('interface_config', port_handle=port_speed[0], mode="modify", speed=speeds, autonegotiation=self.auto_neg) + logger.info(res) + if res.get('status') != '1': + logger.info('Example speed options: ether10, ether100, ether1000, ether2500, ether10000, ether5Gig, ether25Gig, ether100Gig, ether40Gig, ether50Gig') + logger.info("Failed to change the TGEN port speed to '{}'. Please provide valid supported speed on the ports".format(speeds)) + self.tg_disconnect() + if self.auto_neg and not port_speed: + res = self.ixia_eval('interface_config', port_handle=list(self.tg_port_handle.values()), mode="modify", autonegotiation=self.auto_neg) + phy_mode = [] + if self.tg_link_params.get('phy_mode'): + phy_mode = [p_han(self.tg_link_params['phy_mode'][0]), self.tg_link_params['phy_mode'][1]] + elif self.phy_mode: + phy_mode = [list(self.tg_port_handle.values()), self.phy_mode] + if phy_mode: + res = self.ixia_eval('interface_config', port_handle=phy_mode[0], mode="modify", phy_mode=phy_mode[1]) + logger.info(res) + if res.get('status') != '1': + logger.info("Failed to change the PHY mode of TGEN ports to '{}'".format(phy_mode[0])) + fec = [] + self.fec = 1 if self.fec else 0 + if self.tg_link_params.get('fec'): + fec = [p_han(self.tg_link_params['fec'][0]), self.tg_link_params['fec'][1]] + elif self.fec: + fec = [list(self.tg_port_handle.values()), self.fec] + if fec: + res = self.ixia_eval('interface_config', port_handle=fec[0], mode="modify", ieee_media_defaults=0, enable_rs_fec=fec[1]) + logger.info(res) + if res.get('status') != '1': + logger.info("Failed to change the fec mode of TGEN ports to '{}'".format(fec[0])) + # Initial setting for ARP/ND. + if not isinstance(self.tg_ip, list): + h1 = self.ixia_eval('topology_config', port_handle=self.tg_port_handle[self.tg_port_list[0]], mode='config') + else: + port = list(self.tg_port_handle.keys())[0] + h1 = self.ixia_eval('topology_config', port_handle=port, mode='config') + logger.info(h1) + res = self.ixia_eval('interface_config', protocol_handle='/globals', single_arp_per_gateway=0, single_ns_per_gateway=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 + res = self.ixia_eval('topology_config', topology_handle=h1['topology_handle'], mode='destroy') + logger.info(res) + return None + return ret_ds def trgen_adjust_mismatch_params(self, fname, **kwargs): if fname == 'tg_traffic_config': @@ -1283,39 +1988,55 @@ def trgen_adjust_mismatch_params(self, fname, **kwargs): self.map_field("icmpv6_code", "icmp_code", kwargs) self.map_field("icmpv6_type", "icmp_type", kwargs) self.map_field("icmpv6_target_address", "icmp_target_addr", kwargs) + self.map_field("ipv6_srcprefix", "", kwargs) + self.map_field("ipv6_dstprefix", "", kwargs) + self.map_field("disable_signature", "", kwargs) if kwargs.get("l4_protocol") == "icmpv6": kwargs['l4_protocol'] = 'icmp' - 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_protocol_tag_id') is not None: + if len(str(kwargs['vlan_protocol_tag_id'])) != 4: + kwargs['vlan_protocol_tag_id'] = hex(int(kwargs['vlan_protocol_tag_id'])).lstrip('0x') - if kwargs.get('vlan_id_outer') != None: - # If vlan-id_outer is present then vlan_id will also be there + if type(kwargs.get('vlan_id')) == list: + # script : [inner vlan id, outer vlan id, other vlan ids] + # Ixia config: Vlan tag closest to the payload is the INNER tag, and the tag closest to the MAC header + # is the OUTER tag. + if len(kwargs['vlan_id']) > 1: + kwargs['vlan_id'][0], kwargs['vlan_id'][1] = kwargs['vlan_id'][1], kwargs['vlan_id'][0] + + if kwargs.get('vlan_id_outer') is not 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['vlan_id'] = [outer_vlan_id, vlan_id] kwargs.pop('vlan_id_outer') - - if kwargs.get('vlan_id') != None and kwargs.get('vlan') is None: + if kwargs.get('vlan_id_other') is not None: + # If vlan_id_other is present then vlan_id_outer will also be there + # vlan headers built as following order: vlan_id_other, vlan_id_outer, vlan_id + other_vlan_id = utils.make_list(kwargs['vlan_id_other']) + other_vlan_id.reverse() + other_vlan_id.extend(kwargs['vlan_id']) + kwargs['vlan_id'] = other_vlan_id + kwargs.pop('vlan_id_other') + + if kwargs.get('vlan_id') is not None and kwargs.get('vlan') is None: kwargs['vlan'] = 'enable' # for stream level stats, circuit_type and track_by arguments required - if kwargs.get('port_handle2') != None: + if kwargs.get('port_handle2') is not None: if kwargs.get('track_by') is None: kwargs['track_by'] = 'trackingenabled0' if kwargs.get('circuit_type') is None: kwargs['circuit_type'] = 'raw' - if kwargs.get('emulation_src_handle') != None and kwargs.get('emulation_dst_handle') != None: + if kwargs.get('emulation_src_handle') is not None and kwargs.get('emulation_dst_handle') is not 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: + if kwargs.get(param) is not None: kwargs.pop(param) for param in ('udp_src_port_mode', 'udp_dst_port_mode', @@ -1325,22 +2046,22 @@ def trgen_adjust_mismatch_params(self, fname, **kwargs): if kwargs.get(param) == 'decrement': kwargs[param] = 'decr' - if kwargs.get('ip_tos_field') != None: + if kwargs.get('ip_tos_field') is not 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) + 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['ip_reserved'] = kwargs.get('ip_mbz', 0) kwargs.pop('ip_tos_field') - kwargs.pop('ip_mbz',None) + kwargs.pop('ip_mbz', None) - if kwargs.get('emulation_dst_handle') != None: + if kwargs.get('emulation_dst_handle') is not None: foundIgmp = 1 if isinstance(kwargs['emulation_dst_handle'], list): for ele in kwargs['emulation_dst_handle']: @@ -1360,15 +2081,16 @@ def trgen_adjust_mismatch_params(self, fname, **kwargs): 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') + if kwargs.get('stream_handle') is not None: + kwargs['handle'] = kwargs.pop('stream_handle', '') + if kwargs.get('handle'): + kwargs['handle'] = kwargs['handle'] if isinstance(kwargs['handle'], str) else list(kwargs['handle']) for param in ('get', 'enable_arp', 'duration'): - if kwargs.get(param) != None: + if kwargs.get(param) is not None: kwargs.pop(param) if kwargs.get('action') in ['run', 'stop'] and kwargs.get('port_handle') is None: - #kwargs['max_wait_timer'] = 120 - #temp change to roll back the HF from ixia + # 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: @@ -1376,83 +2098,104 @@ def trgen_adjust_mismatch_params(self, fname, **kwargs): if fname == 'tg_interface_config': self.map_field("resolve_gateway_mac", "ipv4_resolve_gateway", kwargs) + self.map_field("ipv6_resolve_gateway_mac", "ipv6_resolve_gateway", kwargs) self.map_field("control_plane_mtu", "mtu", kwargs) self.map_field("flow_control", "enable_flow_control", kwargs) self.map_field("arp_target", None, kwargs) + wait = 5 if self.skip_start_protocol else 10 + for param in ['ipv4_resolve_gateway', 'ipv6_resolve_gateway']: + if kwargs.get(param) is not None: + kwargs[param] = '0' if str(kwargs[param]) in ['false', '0'] else '1' + if kwargs.get('mode') == 'modify': + self.map_field("handle", "interface_handle", kwargs) if kwargs.get('mode') == 'config': - topo_han=self.topo_handle[kwargs.get('port_handle')] + topo_han = self.topo_handle[kwargs.get('port_handle')] if topo_han is None: - res=self.tg_topology_config(port_handle=kwargs.get('port_handle')) + res = self.ixia_eval('topology_config', port_handle=kwargs.get('port_handle')) logger.info(res) + if res.get('status') == '0': + self.fail(res.get('log'), "tgen_failed_api", res.get('log')) 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') + tgen_wait(wait) + 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) + mul = kwargs.get('vlan_id_count', '1') + res = self.ixia_eval('topology_config', topology_handle=topo_han, device_group_multiplier=mul) logger.info(res) - tgen_wait(10) + if res.get('status') == '0': + self.fail(res.get('log'), "tgen_failed_api", res.get('log')) + tgen_wait(wait) kwargs['protocol_handle'] = res['device_group_handle'] kwargs.pop('port_handle') - if kwargs.get('enable_flow_control') != None: + if kwargs.get('vlan_custom_config') is not None: + # vlan_custom_config = ['vlan_id', 'vlan_id_step', 'repeat_each' 'sequence'] + cust_val = kwargs.get('vlan_custom_config') + mul_status = self.tg_multivalue_config(pattern='custom', nest_step='1', nest_owner=topo_han, nest_enabled='0') + cust_status = self.tg_multivalue_config(multivalue_handle=mul_status['multivalue_handle'], custom_start=cust_val[0], custom_step='0') + incr_status = self.tg_multivalue_config(custom_handle=cust_status['custom_handle'], custom_increment_value=cust_val[1], custom_increment_count=cust_val[2]) + self.tg_multivalue_config(increment_handle=incr_status['increment_handle'], custom_increment_value='0', custom_increment_count=cust_val[3]) + kwargs['vlan_id'] = mul_status['multivalue_handle'] + kwargs['vlan_id_count'] = '1' + kwargs['vlan_id_step'] = '0' + kwargs.pop('vlan_custom_config', None) + if kwargs.get('enable_flow_control') is not 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: + if kwargs.get(param) is not 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 kwargs['action'] in ['start', 'cumulative_start']: + self.ixia_eval('traffic_control', action='apply') + # suggested by Ixia for more accurate results + logger.info('Enabling control and data plane options') + self.ixia_eval('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') is 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)) - topo_index = list(self.topo_handle.values()).index(topo) - tg_port = list(self.topo_handle.keys())[topo_index] - logger.debug('port_handle: {}'.format(tg_port)) - flag = 0 - for _ in range(1,30): - res = self.tg_protocol_info(mode='global_per_port') - logger.info(res) - if not res['global_per_port'].get(tg_port): + if kwargs.get('mode') not in ['remove', 'delete']: + if 'ipv6_prefix_length' in kwargs: + kwargs['prefix_from'] = kwargs['ipv6_prefix_length'] + kwargs.pop('ipv6_prefix_length') + if 'netmask' in kwargs: + kwargs['prefix_from'] = IPAddress(kwargs.pop('netmask', '255.255.255.0')).netmask_bits() + logger.info('Disabling protocol before adding the route') + # self.tg_topology_test_control(handle=kwargs['handle'], stack='deviceGroup', action='stop_protocol') + self.tg_topology_test_control(action='stop_all_protocols', tg_wait=10) + topo = re.search(r'.*topology:(\d)+', kwargs['handle']).group(0) + logger.debug('Topology: {}'.format(topo)) + topo_index = list(self.topo_handle.values()).index(topo) + tg_port = list(self.topo_handle.keys())[topo_index] + logger.debug('port_handle: {}'.format(tg_port)) + flag = 0 + for _ in range(1, 30): + res = self.ixia_eval('protocol_info', mode='global_per_port') + logger.info(res) + if not res['global_per_port'].get(tg_port): + self.tg_topology_test_control(action='apply_on_the_fly_changes', tg_wait=2) + continue + total = res['global_per_port'][tg_port]['sessions_total'] + total_ns = res['global_per_port'][tg_port]['sessions_not_started'] + logger.debug("sessions_total = {}".format(total)) + logger.debug("sessions_not_started = {}".format(total_ns)) + if total == total_ns: + flag = 1 + break tgen_wait(2) - continue - total=res['global_per_port'][tg_port]['sessions_total'] - total_ns=res['global_per_port'][tg_port]['sessions_not_started'] - logger.debug("sessions_total = {}".format(total)) - logger.debug("sessions_not_started = {}".format(total_ns)) - if total == total_ns: - flag = 1 - break - tgen_wait(2) - if not flag: - msg = "Failed to get port {} from the protocol info".format(tg_port) - self.fail(msg, "tgen_failed_api", msg) - 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 not flag: + msg = "Failed to get port {} from the protocol info".format(tg_port) + self.fail(msg, "tgen_failed_api", msg) + tgen_wait(10) if fname == 'tg_emulation_bgp_config': - if kwargs.get('local_as') != None and kwargs.get('remote_as') != None: + if kwargs.get('local_as') is not None and kwargs.get('remote_as') is not None: if int(kwargs['local_as']) != int(kwargs['remote_as']): kwargs['neighbor_type'] = 'external' else: @@ -1465,8 +2208,8 @@ def trgen_adjust_mismatch_params(self, fname, **kwargs): 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'] : + if fname in ['tg_emulation_igmp_control', 'tg_emulation_mld_control']: + if kwargs.get('mode') in ['join', 'leave']: kwargs['group_member_handle'] = kwargs['handle'] kwargs.pop('handle', None) @@ -1495,35 +2238,375 @@ def trgen_adjust_mismatch_params(self, fname, **kwargs): 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': + if fname == 'tg_emulation_mld_group_config': + if kwargs.get('source_pool_handle') is None and kwargs.get('mode') == 'create': + res = self.tg_emulation_multicast_source_config(mode='create', ip_addr_start='3000::1', 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_mld_querier_config': + if kwargs.get('mode') == 'create': + handles, ret_kwargs = self.pre_interface_config(**kwargs) + kwargs = ret_kwargs + kwargs['handle'] = handles['handle'] + ver_map = {'MLD_V1': 'version1', 'MLD_V2': 'version2'} + kwargs['version'] = ver_map[kwargs.pop('mld_version', '')] + elif kwargs.get('mode') == 'delete': + han = kwargs['handle'] + han = han[0] if type(han) is list else han + self.tg_topology_test_control(handle=han, stack='deviceGroup', action='stop_protocol') + + if fname == 'tg_emulation_mld_config': + if kwargs.get('mode') == 'create': + handles, ret_kwargs = self.pre_interface_config(**kwargs) + kwargs = ret_kwargs + kwargs['handle'] = handles['ipv6_handle'] + elif kwargs.get('mode') == 'delete': + han = kwargs['handle'] + han = han[0] if type(han) is list else han + self.tg_topology_test_control(handle=han, stack='deviceGroup', 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) self.map_field("vlan_ether_type", None, kwargs) self.map_field("gateway_addresses", None, kwargs) + self.map_field("retry_attempts", None, kwargs) + self.map_field("enable_auto_retry", None, kwargs) + topo = kwargs.get('handle') + topo_index = list(self.topo_handle.values()).index(topo) + tg_port = list(self.topo_handle.keys())[topo_index] + logger.debug('port_handle: {}'.format(tg_port)) + + intf_kwrgs = dict() + key_dict = {'count': 'num_sessions', 'src_mac_addr': 'mac_addr', 'vlan_id': 'vlan_id', + 'vlan_custom_config': 'vlan_custom_config', 'vlan_id_step': 'vlan_id_step', + 'vlan_id_count': 'vlan_id_count'} + for key, val in key_dict.items(): + if kwargs.get(val): + intf_kwrgs[key] = kwargs.pop(val, None) + if intf_kwrgs.get('count') is not None and intf_kwrgs.get('vlan_id_count') is not None: + if int(intf_kwrgs.get('count')) != int(intf_kwrgs.get('vlan_id_count')): + intf_kwrgs.pop('vlan_id_count', '') + if int(intf_kwrgs.get('count', 1)) > 1: + intf_kwrgs.pop('vlan_id_count', '') + intf_kwrgs['vlan'] = '1' if intf_kwrgs.get('vlan_id') else '0' + intf_kwrgs['mode'] = 'config' + intf_kwrgs['port_handle'] = tg_port + intf_kwrgs['skip_start_protocol'] = True + han = self.tg_interface_config(**intf_kwrgs) + group_kwargs = dict() + group_kwargs['handle'] = han['ethernet_handle'] if str(kwargs.get("dhcp_range_ip_type")) == '4': - kwargs['dhcp_range_ip_type'] = 'ipv4' + group_kwargs['dhcp_range_ip_type'] = 'ipv4' + group_kwargs['dhcp4_gateway_address'] = kwargs.get('dhcp4_gateway_address', '0.0.0.0') else: - kwargs['dhcp_range_ip_type'] = 'ipv6' + group_kwargs['dhcp_range_ip_type'] = 'ipv6' + group_kwargs['dhcp6_gateway_address'] = kwargs.get('dhcp6_gateway_address', '::') + kwargs = group_kwargs + kwargs['skip_start_protocol'] = True + del group_kwargs + if fname == 'tg_emulation_dhcp_server_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') self.map_field("gateway_ipv6_addr", "ipv6_gateway", kwargs) self.map_field("remote_mac", "manual_gateway_mac", kwargs) self.map_field("encapsulation", "", kwargs) + self.map_field("assign_strategy", "", kwargs) + self.map_field("mac_addr", "", kwargs) + if str(kwargs.get('ip_version')) == '6': + self.map_field("addr_pool_addresses_per_server", "ipaddress_count", kwargs) + self.map_field("prefix_pool_step", "", kwargs) + self.map_field("prefix_pool_start_addr", "", kwargs) + self.map_field("prefix_pool_per_server", "", kwargs) + self.map_field("prefix_pool_prefix_length", "", kwargs) + self.map_field("addr_pool_start_addr", "ipaddress_pool", kwargs) + self.map_field("addr_pool_prefix_length", "ipaddress_pool_prefix_length", kwargs) + self.map_field("addr_pool_step_per_server", "", kwargs) + self.map_field("addr_pool_host_step", "pool_address_increment", kwargs) + self.map_field("add_prefix_pool_start_addr", "ipaddress_pool", kwargs) + self.map_field("add_prefix_pool_prefix_length", "ipaddress_pool_prefix_length", kwargs) + self.map_field("add_prefix_pool_step_per_server", "ipaddress_pool_inside_step", kwargs) + self.map_field("add_prefix_pool_step", "pool_address_increment", kwargs) + self.map_field("server_emulation_mode", "", kwargs) + self.map_field("local_ipv6_prefix_len", "", kwargs) + self.map_field("local_ipv6_addr", "", kwargs) + + if kwargs.get('ipaddress_pool_inside_step') is None: + kwargs['ipaddress_pool_inside_step'] = '0:0:0:1::' + kwargs['skip_start_protocol'] = True + + if fname == 'tg_emulation_dhcp_server_control': + self.map_field("ip_version", "", kwargs) + if kwargs.get('action') == 'connect': + kwargs['action'] = 'collect' + + if fname == 'tg_emulation_dhcp_config': + ip_version = kwargs.get('ip_version', '4') + if 'retry_count' in kwargs or 'request_rate' in kwargs: + self.ixia_eval('emulation_dhcp_config', handle='/globals', retry_count=kwargs.get('retry_count', 3), + request_rate=kwargs.get('request_rate', 200), ip_version=ip_version) + + if fname == 'tg_emulation_dhcp_stats': + self.map_field("ip_version", "dhcp_version", kwargs) + if kwargs.get('dhcp_version') is not None: + kwargs['dhcp_version'] = 'dhcp4' if int(kwargs.get('dhcp_version')) == 4 else 'dhcp6' + if kwargs.get('mode') == 'aggregate': + kwargs['mode'] = 'aggregate_stats' + if kwargs.get('mode') == 'detailed_session': + kwargs['mode'] = 'session' + + if fname == 'tg_emulation_dhcp_control': + self.map_field("ip_version", "", kwargs) + self.map_field("port_handle", "", kwargs) return kwargs + def tg_emulation_ipv6_autoconfig(self, **kwargs): + self.map_field("mac_addr", "src_mac_addr", kwargs) + self.map_field("mac_addr_step", "src_mac_addr_step", kwargs) + self.map_field("router_solicit_retransmit_delay", "ipv6_autoconfiguration_send_rs_interval", kwargs) + self.map_field("router_solicit_retry", None, kwargs) + dict1 = {} + for param in ['ipv6_autoconfiguration_send_rs_interval']: + if param in kwargs: + dict1[param] = kwargs.pop(param, '') + self.tg_interface_config(protocol_handle='/globals', **dict1) + if kwargs.get('mode') == 'create': + handles, _ = self.pre_interface_config(**kwargs) + addr_mode = kwargs.get('ipv6_addr_mode', 'autoconfig') + handles = self.tg_interface_config(ipv6_addr_mode=addr_mode, protocol_handle=handles['ethernet_handle']) + + handles['handle'] = handles['ipv6autoconfiguration_handle'] + elif kwargs.get('mode') == 'reset': + kwargs['mode'] = 'destroy' + handles = self.tg_interface_config(**kwargs) + return handles if handles['status'] == '1' else None + + def tg_emulation_ipv6_autoconfig_control(self, **kwargs): + han = kwargs['handle'] + kwargs.get('port_handle', '') + if kwargs.get('action') == 'start': + self.tg_topology_test_control(handle=han, stack='ipv6Autoconfiguration', action='start_protocol', skip_wait=True) + if kwargs.get('action') == 'stop': + self.tg_topology_test_control(handle=han, stack='ipv6Autoconfiguration', action='stop_protocol', skip_wait=True) + + def tg_emulation_dot1x_config(self, **kwargs): + if int(kwargs.get('num_sessions', 1)) > 1: + for entry in ['username', 'password']: + if kwargs.get(entry): + pattern = str(kwargs[entry]) + '{Inc:0,1}' + ret_val = self.tg_multivalue_config(pattern="string", string_pattern=pattern) + kwargs[entry] = ret_val['multivalue_handle'] + self.map_field("username", "user_name", kwargs) + self.map_field("password", "user_pwd", kwargs) + self.map_field("eap_auth_method", "protocol_type", kwargs) + self.map_field("username_wildcard", "", kwargs) + self.map_field("password_wildcard", "", kwargs) + self.map_field("wildcard_pound_start", "", kwargs) + self.map_field("wildcard_question_start", "", kwargs) + self.map_field("encapsulation", "", kwargs) + self.map_field("local_ip_addr", "", kwargs) + self.map_field("ip_version", "", kwargs) + self.map_field("gateway_ip_addr", "", kwargs) + self.map_field("auth_retry_interval", "", kwargs) + self.map_field("auth_retry_count", "", kwargs) + self.map_field("retransmit_count", "", kwargs) + self.map_field("retransmit_interval", "", kwargs) + + if kwargs.get('mode') == 'create': + port_han = kwargs.pop('port_handle', '') + intf_kwrgs = dict() + key_dict = {'count': 'num_sessions', 'src_mac_addr': 'mac_addr', 'vlan_id': 'vlan_id', + 'vlan_id_step': 'vlan_id_step', + 'vlan_id_count': 'vlan_id_count', 'src_mac_address_step': 'mac_addr_step'} + for key, val in key_dict.items(): + if kwargs.get(val): + intf_kwrgs[key] = kwargs.pop(val, None) + intf_kwrgs['vlan'] = '1' if intf_kwrgs.get('vlan_id') else '0' + intf_kwrgs['mode'] = 'config' + intf_kwrgs['port_handle'] = port_han + intf_kwrgs['skip_start_protocol'] = True + han = self.tg_interface_config(**intf_kwrgs) + kwargs['handle'] = han['ethernet_handle'] + elif kwargs.get('mode') == 'delete': + han = kwargs['handle'] + han = han[0] if type(han) is list else han + self.tg_topology_test_control(handle=han, stack='deviceGroup', action='stop_protocol') + kwargs['skip_start_protocol'] = True + result = self.tg_emulation_dotonex_config(**kwargs) + return result if result['status'] == '1' else None + + def tg_emulation_dot1x_control(self, **kwargs): + if kwargs.get('mode') == 'logout': + kwargs['mode'] = 'stop' + result = self.tg_emulation_dotonex_control(**kwargs) + return result if result['status'] == '1' else None + + def emulation_dot1x_stats(self, **kwargs): + if kwargs.get('mode') == 'aggregate': + kwargs['mode'] = 'per_port_stats' + if kwargs.get('mode') == 'sessions': + kwargs['mode'] = 'per_session_stats' + result = self.tg_emulation_dotonex_info(**kwargs) + return result if result['status'] == '1' else None + + def tg_emulation_ptp_control(self, **kwargs): + if re.search(r'.*ipv(4|6):(\d)+', kwargs.get('handle')): + result = self.tg_ptp_over_ip_control(**kwargs) + else: + result = self.tg_ptp_over_mac_control(**kwargs) + return result if result['status'] == '1' else None + + def tg_emulation_ptp_stats(self, **kwargs): + kwargs.pop('port_handle', '') + if not kwargs.get('handle'): + logger.info('Provide argument handle to fetch the stats') + return {} + if re.search(r'.*ipv(4|6):(\d)+', kwargs.get('handle')): + result = self.tg_ptp_over_ip_stats(**kwargs) + else: + result = self.tg_ptp_over_mac_stats(**kwargs) + return result if result['status'] == '1' else None + + def tg_emulation_ptp_config(self, **kwargs): + self.map_field("master_clock_priority1", "priority1", kwargs) + self.map_field("master_clock_priority2", "priority2", kwargs) + self.map_field("log_sync_message_interval", "log_sync_interval", kwargs) + self.map_field("path_delay_mechanism", "delay_mechanism", kwargs) + self.map_field("device_type", "role", kwargs) + self.map_field("log_minimum_delay_request_interval", "log_delay_req_interval", kwargs) + self.map_field("log_announce_message_interval", "log_announce_interval", kwargs) + self.map_field("ptp_port_number", "port_number", kwargs) + self.map_field("ptp_session_mode", "communication_mode", kwargs) + self.map_field("master_clock_class", "clock_class", kwargs) + self.map_field("ptp_domain_number", "domain", kwargs) + self.map_field("local_mac_addr", "src_mac_addr", kwargs) + self.map_field("vlan_id1", "vlan_id", kwargs) + self.map_field("local_ip_addr", "intf_ip_addr", kwargs) + self.map_field("local_ip_prefix_len", "netmask", kwargs) + self.map_field("remote_ip_addr", "gateway", kwargs) + self.map_field("frequency_traceable", "announce_frequency_traceable", kwargs) + self.map_field("custom_clock_accuracy", "clock_accuracy", kwargs) + self.map_field("utc_offset", "current_utc_offset", kwargs) + self.map_field("time_traceable", "announce_time_traceable", kwargs) + self.map_field("local_ipv6_addr", "ipv6_intf_addr", kwargs) + self.map_field("local_ipv6_prefix_len", "ipv6_prefix_length", kwargs) + self.map_field("remote_ipv6_addr", "ipv6_gateway", kwargs) + + transport_type = kwargs.pop('transport_type', '') + time_source_map = {'atomic-clock': '0X10', 'gps': '0X20', 'terrestrial-radio': '0X30', 'ptp': '0X40', + 'ntp': '0X50', 'handset': '0X60', 'other': '0X90', 'internal-oscillator': '0XA0'} + if kwargs.get('time_source') in ['ptp-profile', 'reserved']: + logger.info('Provided invalid value for time sorce') + return False + if kwargs.get('time_source'): + kwargs['time_source'] = time_source_map.get(kwargs.get('time_source', 'gps')) + log_intv_map = {'-9': '247', '-8': '248', '-7': '249', '-6': '250', '-5': '251', '-4': '252', '-3': '253', + '-2': '254', '-1': '255', '0': '0', '1': '1', '2': '2', '3': '3', '4': '4', '5': '5', '6': '6', + '7': '7', '8': '8', '9': '9'} + for param in ['log_sync_interval', 'log_delay_req_interval', 'log_announce_interval']: + if kwargs.get(param): + kwargs[param] = log_intv_map.get(kwargs.get(param, '0')) + if kwargs.get('delay_mechanism') == 'end-to-end': + kwargs['delay_mechanism'] = 'E2E' + if kwargs.get('delay_mechanism') == 'peer-to-peer': + kwargs['delay_mechanism'] = 'P2P' + sync_two_step_flag = kwargs.pop('sync_two_step_flag', '') + if sync_two_step_flag == 'on': + kwargs['step_mode'] = 'two-step' + if sync_two_step_flag == 'off': + kwargs['step_mode'] = 'one-step' + if kwargs.get('role') == 'ptpMaster': + kwargs['role'] = 'master' + if kwargs.get('role') == 'ptpSlave': + kwargs['role'] = 'slave' + leap_flag = kwargs.pop('leap_flag', '') + if leap_flag == 'leap59': + kwargs['announce_leap59'] = '1' + if leap_flag == 'leap61': + kwargs['announce_leap61'] = '1' + clock_acc_map = {'less_025_0ns': '32', 'less_100_0ns': '33', 'less_250_0ns': '34', 'less_001_0us': '35', + 'less_002_5us': '36', 'less_010_0us': '37', 'less_025_0us': '38', 'less_100_0us': '39', + 'less_250_0us': '40', 'less_001_0ms': '41', 'less_002_5ms': '42', 'less_010_0ms': '43', + 'less_025_0ms': '44', 'less_100_0ms': '45', 'less_250_0ms': '46', 'less_001_0s': '47', + 'less_010_0s': '48', 'greater_010_0s': '49'} + if kwargs.get('clock_accuracy'): + kwargs['clock_accuracy'] = clock_acc_map.get(kwargs.get('clock_accuracy', 'less_001_0us')) + res_dict = {'true': '1', 'false': '0', '1': '1', '0': '0'} + for param in ['announce_frequency_traceable', 'announce_time_traceable', 'announce_current_utc_offset_valid', 'announce_ptp_timescale', 'path_trace_tlv']: + if kwargs.get(param): + kwargs[param] = res_dict.get(kwargs.get(param, '0')) + if kwargs.get('mode') == 'create': + if not transport_type: + logger.info('Mandatory argument transport_type not provided') + return False + handles, ret_kwargs = self.pre_interface_config(**kwargs) + kwargs = ret_kwargs + if transport_type == 'ethernet_ii': + kwargs['parent_handle'] = handles['ethernet_handle'] + if transport_type == 'ipv4': + kwargs['parent_handle'] = handles['ipv4_handle'] + if transport_type == 'ipv6': + kwargs['parent_handle'] = handles['ipv6_handle'] + elif kwargs.get('mode') == 'delete': + han = kwargs['handle'] + han = han[0] if type(han) is list else han + self.tg_topology_test_control(handle=han, stack='deviceGroup', action='stop_protocol') + if not transport_type: + ret_val = re.search(r'.*ipv(4|6):(\d)+', han) + if ret_val: + if ret_val.group(1) == '4': + transport_type = 'ipv4' + if ret_val.group(1) == '6': + transport_type = 'ipv6' + else: + transport_type = 'ethernet_ii' + if transport_type == 'ethernet_ii': + result = self.tg_ptp_over_mac_config(**kwargs) + elif transport_type in ['ipv4', 'ipv6']: + result = self.tg_ptp_over_ip_config(**kwargs) + else: + logger.info('Invlaid transport_tye provided') + return {} + return result if result['status'] == '1' else None + + def tg_emulation_dhcp_server_relay_agent_config(self, **kwargs): + self.map_field("relay_agent_pool_count", "pool_count", kwargs) + self.map_field("relay_agent_ipaddress_pool", "ipaddress_pool", kwargs) + self.map_field("prefix_length", "ipaddress_pool_prefix_length", kwargs) + self.map_field("relay_agent_pool_step", "ipaddress_pool_inside_step", kwargs) + self.map_field("relay_agent_ipaddress_step", "pool_address_increment", kwargs) + self.map_field("relay_agent_ipaddress_count", "ipaddress_count", kwargs) + self.map_field("vpn_id_type", "", kwargs) + kwargs.pop('vpn_id', None) + assign_strategy = kwargs.pop('assign_strategy', None) + if assign_strategy == 'link_selection': + kwargs['subnet_addr_assign'] = 1 + kwargs['subnet'] = "relay link_selection" + kwargs['mode'] = 'modify' + if kwargs.get('ipaddress_pool_prefix_length') is not None and kwargs.get('ipaddress_count') is None: + kwargs['ipaddress_count'] = 2**(32 - int(kwargs['ipaddress_pool_prefix_length'])) - 3 + + self.tg_emulation_dhcp_server_config(**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[:int(len(temp)/2)] - ret_ds['handle'] = temp[0] if len(temp)==1 else temp + temp = temp[:int(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 @@ -1534,6 +2617,12 @@ def tg_igmp_querier_control(self, mode, handle): logger.info("IGMP Querier action completed: {}".format(result)) return result if result['status'] == '1' else None + def tg_mld_querier_control(self, mode, handle): + result = self.tg_emulation_mld_control(mode=mode, handle=handle) + tgen_wait(10) + logger.info("MLD 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)) @@ -1610,8 +2699,11 @@ def tg_emulation_ospf_route_config(self, **kwargs): 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 kwargs.get('mode') in ['remove', 'delete']: + self.tg_topology_test_control(action='apply_on_the_fly_changes') + else: + 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': @@ -1632,96 +2724,135 @@ def trgen_post_proc(self, fname, **kwargs): 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') + if fname == 'tg_emulation_dotonex_config': + tgen_wait(2) + self.skip_start_protocol = False + 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) + 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)) + else: + result = None + 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)) + 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) + ret_ds = self.ixia_eval('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') + logger.debug('TGen: API Run Status: Success') else: - logger.warning('TG API Error: %s' % ret_ds['log']) + logger.warning('TGen: API Error: %s' % ret_ds['log']) self.tg_connected = False self.tg_port_handle.clear() - def collect_diagnosic(self, fail_reason): - # Default Location of collected diags for Windows :: C:\Program Files (x86)\Ixia\IxNetwork\9.10.2007.7\diagnostic - # Default Location of collected diags for Linux:: /opt/ixia/IxNetwork/9.10.2007.7/aptixia/api/logcollector - file_location = '' - if self.tg_version in ["8.4", "8.40", "8.42", "8.42"]: - file_location = get_ixnet().getAttribute('::ixNet::OBJ-/globals', '-persistencePath') + '\\' + def tg_save_config(self, file_name=None, file_path=None, fail_reason=''): + datetime = utils.get_current_datetime(fmt='%Y_%m_%d_%H_%M_%S') + config_file = 'ixNetconfig_' + fail_reason + '_' + datetime + '.ixncfg' + file_name = config_file if not file_name else file_name + # file_path = tgen_get_logs_path_folder() if not file_path else file_path + # filename = os.path.join(file_path, file_name) + _, config_path = self._ixnet_config_file_location() + logger.info('Saving Ixia session configuration....') + logger.info('Config File Path: API server {}@{}'.format(self.ix_server.split(':')[0], config_path)) + logger.info('Configuration File: {}'.format(file_name)) + get_ixnet().execute('saveConfig', get_ixnet().writeTo(file_name, '-ixNetRelative')) + + def _ixnet_config_file_location(self): if self.ixnetwork_os == 'linux': file_path = '/opt/ixia/IxNetwork/9.10.2007.7/aptixia/api/logcollector' config_path = '/opt/ixia/IxNetwork/9.10.2007.7/' else: file_path = r'C:\Program Files (x86)\Ixia\IxNetwork\9.10.2007.7\diagnostic' config_path = r'C:\Program Files (x86)\Ixia\IxNetwork\9.10.2007.7' - datetime = utils.get_current_datetime(fmt='%Y_%m_%d_%H_%M_%S') + return file_path, config_path - logger.info('Saving Ixia session configuration....') - logger.info('Config File Path: API server {}@{}'.format(self.ix_server.split(':')[0], config_path)) - config_file = 'ixNetconfig_' + fail_reason + '_' + datetime + '.ixncfg' - logger.info('Configuration File: {}'.format(config_file)) - get_ixnet().execute('saveConfig', get_ixnet().writeTo(config_file, '-ixNetRelative')) - - logger.info('Collecting Ixia diagnostics....started') - logger.info('Diagnostics File Path: API server {}@{}'.format(self.ix_server.split(':')[0], file_path)) - diags_file = file_location + 'ixNetDiag_' + fail_reason + '_' + datetime + '_Collect.zip' - logger.info('Diagnostics File: {}'.format(diags_file)) - get_ixnet().execute('collectLogs', get_ixnet().writeTo(diags_file, '-ixNetRelative'), 'currentInstance') - logger.info('Collecting Ixia diagnostics....completed') - - def local_ixnet_call(self,method,*args): - #::ixNet::OK' - #IxNetError: - func_call = 'ixiangpf.ixnet.'+method + def collect_diagnosic(self, fail_reason, from_fail=False): + # Default Location of collected diags for Windows :: C:\Program Files (x86)\Ixia\IxNetwork\9.10.2007.7\diagnostic + # Default Location of collected diags for Linux:: /opt/ixia/IxNetwork/9.10.2007.7/aptixia/api/logcollector + if os.getenv("SPYTEST_TGEN_COLLECT_DIAGNOSTICS", "0") == "0": + return + try: + file_location = '' + if self.tg_version in ["7.4", "7.40"]: + return + if self.tg_version in ["8.4", "8.40", "8.42", "8.42"]: + file_location = get_ixnet().getAttribute('::ixNet::OBJ-/globals', '-persistencePath') + '\\' + file_path, _ = self._ixnet_config_file_location() + datetime = utils.get_current_datetime(fmt='%Y_%m_%d_%H_%M_%S') + self.tg_save_config(fail_reason=fail_reason) + logger.info('Collecting Ixia diagnostics....started') + logger.info('Diagnostics File Path: API server {}@{}'.format(self.ix_server.split(':')[0], file_path)) + diags_file = file_location + 'ixNetDiag_' + fail_reason + '_' + datetime + '_Collect.zip' + logger.info('Diagnostics File: {}'.format(diags_file)) + get_ixnet().execute('collectLogs', get_ixnet().writeTo(diags_file, '-ixNetRelative'), 'currentInstance') + logger.info('Collecting Ixia diagnostics....completed') + except Exception as ex: + if not from_fail: + self.fail(ex, "tgen_failed_api", str(ex)) + else: + logger.error(str(ex)) + + def local_ixnet_call(self, method, *args): + # ::ixNet::OK' + # IxNetError: + func_call = 'ixiangpf.ixnet.' + method + # nosemgrep-next-line res = eval(func_call)(*args) - if re.search(r'Error',res): - logger.info('Error in ixNet call {}: {}'.format(func_call,res)) + 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') + 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' - var_num_frames = kwargs.get('var_num_frames', 20) - #get vport info + # 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) <= int(var_num_frames) else int(var_num_frames) - 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 + var_num_frames = kwargs.get('var_num_frames', 20) + for i in range(3): + try: + captured_packets = dict() + cap_pkt_count = self.local_ixnet_call('getAttribute', vport_handle + '/capture', '-' + packet_type + 'PacketCounter') + logger.debug('Capture packet count on wire: {}'.format(cap_pkt_count)) + pkts_in_buffer = int(cap_pkt_count) if int(cap_pkt_count) <= int(var_num_frames) else int(var_num_frames) + 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 + ret_dict['status'] = '1' + ret_dict.pop('log', '') + break + except Exception as exp: + if captured_packets.get('frame') and len(captured_packets['frame']) > 0: + logger.debug('No. of capture packets fetched: {}'.format(len(captured_packets['frame']))) + ret_dict[kwargs['port_handle']] = captured_packets + ret_dict['status'] = '1' + break + else: + ret_dict['log'] = str(exp) + logger.error('Capture packet get failed:: {}: Trying again....{}'.format(str(exp), i)) return ret_dict - def tg_custom_filter_config(self,**kwargs): + def tg_custom_filter_config(self, **kwargs): ret_dict = dict() ret_dict['status'] = '1' ixia_kwargs = dict() @@ -1732,13 +2863,14 @@ def tg_custom_filter_config(self,**kwargs): return ret_dict mode = kwargs.get('mode').lower() + action = kwargs.get('action', 'cumulative_start') 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']): + 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)) + logger.info('Missing Mandatory parameter {} or {}'.format(offset, pattern)) ret_dict['status'] = '0' return ret_dict offset_count += 1 @@ -1746,7 +2878,7 @@ def tg_custom_filter_config(self,**kwargs): ixia_kwargs['pattern1'] = kwargs['pattern1'] capture_filter_pattern = 'pattern1' - for offset,pattern in zip(['pattern_offset2'],['pattern2']): + 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'] @@ -1756,39 +2888,40 @@ def tg_custom_filter_config(self,**kwargs): elif not kwargs.get(offset) and not kwargs.get(pattern): pass else: - logger.info('Both parameter {} and {} need to be provided'.format(offset,pattern)) + 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') + # 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_control(port_handle=port_handle, action='reset') + 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') + 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=action) if mode == 'getstats': - self.tg_packet_control(port_handle=port_handle,action='stop') + 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) + self.get_capture_stats_state(port_handle, capture_wait=kwargs.get('capture_wait', 120)) result = self.tg_packet_stats(port_handle=port_handle) if result['status'] != '1': - for i in range(1,5): + 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) + logger.info(result) if result['status'] == '1': - filtered_frame_count = result[port_handle]['aggregate']['num_frames'] - total_rx_count = result[port_handle]['aggregate']['uds5_frame_count'] + filtered_frame_count = result[port_handle]['aggregate']['uds4_frame_count'] + total_rx_count = result[port_handle]['aggregate']['uds3_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}) @@ -1802,7 +2935,7 @@ def ensure_traffic_stats(self, timeout=60, skip_fail=False, **kwargs): mode = kwargs.get('mode') traffic_mode = {'traffic_item': 'Traffic Item Statistics', 'flow': 'Flow Statistics', 'aggregate': 'Port Statistics'} - if traffic_mode.get(mode) != None: + if traffic_mode.get(mode) is not None: if mode == 'all': view_traffic_page = get_ixnet().getList('/statistics', 'view') else: @@ -1829,8 +2962,8 @@ 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') + handle = kwargs.get('handle') + port_handle = kwargs.get('port_handle') duration = utils.integer_parse(kwargs.get('duration', 0)) traffic_elems = [] if handle: @@ -1848,10 +2981,12 @@ def ensure_traffic_control(self, timeout=180, skip_fail=False, **kwargs): elif action == "stop": self._ensure_traffic_elem_stop(traffic_elem, duration, timeout, skip_fail) if action == 'reset': - if not port_handle: return + 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) + if han in self.traffic_config_handles: + self.traffic_config_handles.pop(han) def _get_traffic_elem_from_stream_id(self, stream_id): retval = [] @@ -1883,13 +3018,13 @@ def _ensure_traffic_elem_start(self, traffic_elem, duration, timeout=180, skip_f # 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']) + res = self.ixia_eval('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']) + traffic_elem['traffic_item']) logger.debug(msg) while True: try: @@ -1904,8 +3039,8 @@ def _ensure_traffic_elem_start(self, traffic_elem, duration, timeout=180, skip_f 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) + # msg = "traffic is not configured" + # self.fail(msg, "tgen_failed_api", msg) return False time.sleep(1) if time.time() > end_time: @@ -1955,7 +3090,8 @@ def manage_traffic_config_handles(self, ret_ds, **kwargs): port_handle = kwargs.get('port_handle') mode = kwargs.get('mode') if mode == "create": - if not port_handle: return + 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)} @@ -1966,30 +3102,34 @@ def manage_traffic_config_handles(self, ret_ds, **kwargs): 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) + if not port_values: + self.traffic_config_handles.pop(port_handle) - def get_capture_stats_state(self, port): + def get_capture_stats_state(self, port, capture_wait=120): res = self.tg_convert_porthandle_to_vport(port_handle=port) - capture = get_ixnet().getList(res['handle'], 'capture')[0] - count = 0 - start = time.time() - for cap_type in ['-dataCaptureState', '-controlCaptureState']: - try: - state = get_ixnet().getAttribute(capture, cap_type) - except Exception as exp: - self.fail(exp, "tgen_failed_api", exp) - while state != 'ready': - time.sleep(1) - count = count + 1 - if count > 60: - diff = time.time() - start - logger.error('Capture did not become ready even after {} sec'.format(diff)) - break + try: + capture = get_ixnet().getList(res['handle'], 'capture')[0] + count = 0 + start = time.time() + for cap_type in ['-dataCaptureState', '-controlCaptureState']: + state = get_ixnet().getAttribute(capture, cap_type) + while state != 'ready': + time.sleep(1) + count = count + 1 + if count > int(capture_wait): + diff = time.time() - start + logger.error('Capture did not become ready even after {} sec'.format(diff)) + break + state = get_ixnet().getAttribute(capture, cap_type) + except Exception as exp: + self.fail(exp, "tgen_failed_api", str(exp)) + logger.info("Total time taken to capture ready {} sec".format(time.time() - start)) - if self.ix_port == '443': tgen_wait(5, 'waiting to stabilize the captured packets') + if self.ix_port == '443': + tgen_wait(5, 'waiting to stabilize the captured packets') - def get_emulation_handle_prefixes(self, ret_ds, kwargs): + def get_emulation_handle_prefixes(self, ret_ds, **kwargs): ip_dict = dict() for emu_handle in ['emulation_src_handle', 'emulation_dst_handle']: handle_list = utils.make_list(kwargs.get(emu_handle)) @@ -2004,7 +3144,7 @@ def get_emulation_handle_prefixes(self, ret_ds, kwargs): else: values = get_ixnet().getAttribute(handle, '-address') temp['start_addr'] = values - temp['hadle'] = handle + temp['handle'] = handle ip_dict[emu_handle].append(temp) except Exception: logger.error("Couldn't get ip prefix for handle: {}".format(handle)) @@ -2014,9 +3154,8 @@ def get_emulation_handle_prefixes(self, ret_ds, kwargs): class TGScapy(TGBase): def __init__(self, tg_type, tg_version, tg_ip=None, tg_port=8009, tg_port_list=None): logger.info('TG Scapy Init') - TGBase.__init__(self, tg_type, tg_version, tg_ip, tg_port_list) + TGBase.__init__(self, tg_type, tg_version, tg_ip, tg_port_list, True) self.sc = ScapyClient(logger, tg_ip, tg_port, tg_port_list, self) - self.tg_connected = connect_retry(self) def __getattribute__(self, name): try: @@ -2033,17 +3172,26 @@ def show_status(self): def instrument(self, phase, context): self.server_control(phase, context) + def alert(self, msg): + workarea.alert(msg) + def log_call(self, fname, **kwargs): - text = tgen_log_call(fname, **kwargs) + msg = tgen_log_call(fname, **kwargs) if not tgen_log_lvl_is_debug(): - logger.info('REQ: {}'.format(text)) + logger.info(msg) + + def log_resp(self, fname, text): + msg = tgen_log_resp(fname, text) + if not tgen_log_lvl_is_debug(): + logger.info(msg) def api_fail(self, msg): tgen_fail("", "tgen_failed_api", msg) def save_log(self, name, data): try: - lfile = tgen_get_logs_path(name) + logs_path = tgen_get_logs_path() + lfile = os.path.join(logs_path, name) utils.write_file(lfile, data) except Exception as exp: logger.error('TG: Failed to save log: %s' % str(exp)) @@ -2057,7 +3205,9 @@ def tg_arp_control(self, **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)) + else: + result = None + logger.info('Sending ARP completed: {}'.format(result)) return result def tg_withdraw_bgp_routes(self, route_handle): @@ -2070,9 +3220,16 @@ def tg_readvertise_bgp_routes(self, handle, route_handle): 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) + tgen_wait(10) + logger.info("IGMP Querier 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)) + # logger.info("creating wrapper for {}".format(func)) dummy_func_name = 'tg_' + func[0] if tg_type == 'ixia': real_func_name = 'ixiangpf.' + func[0] @@ -2090,18 +3247,22 @@ def generate_tg_methods(tg_type, afnl): tg_wrapper_func = re.sub( r'real_func_name', real_func_name, tg_wrapper_func) - #exec tg_wrapper_func in globals() + # nosemgrep-next-line exec(tg_wrapper_func, globals()) if tg_type == 'ixia': + # nosemgrep-next-line setattr(TGIxia, dummy_func_name, eval(dummy_func_name)) else: + # nosemgrep-next-line setattr(TGStc, dummy_func_name, eval(dummy_func_name)) + def close_tgen(): for _, tg in tgen_obj_dict.items(): tg.tg_disconnect() return True + def init_tgen(workarea_in, logger_in, skip_tgen_in): global workarea, logger, skip_tgen workarea = workarea_in @@ -2110,18 +3271,79 @@ def init_tgen(workarea_in, logger_in, skip_tgen_in): hltApiLog = tgen_get_logs_path('hltApiLog.txt') utils.delete_file(hltApiLog) + def instrument_tgen(phase, context): for _, tg in tgen_obj_dict.items(): 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, skip_tgen + +def connect_tgen(): + tg = get_chassis() + tg.tg_connected = connect_retry(tg) + logger.info('TG Connect...done') + return tg.tg_connected + + +def get_tgen_link_params(dut, port, param, default=None): + port_list = utils.make_list(port) + param_list = utils.make_list(param) + param_dict = dict() + for ent in param_list: + p, v = [], [] + for intf in port_list: + ret = workarea.get_link_param(dut, intf, ent, default) + if ret: + p.append(intf) + v.append(ret) + if p and v: + param_dict[ent] = [p, v] + return param_dict + + +def load_tgen(tgen_dict, phase): + + dconnect = int(os.getenv("SPYTEST_TGEN_DELAYED_CONNECT", "0")) + + do_init, do_connect = False, False + if dconnect == 1: + # load during session create and connect during session init + if phase != 1: + do_init = True + if phase != 0: + do_connect = True + elif dconnect == 2: + # load and connect during session init + if phase != 0: + do_init = True + do_connect = True + else: + # load and connect during session create + if phase != 1: + do_init = True + do_connect = True + + if do_init: + rv = load_tgen_int(tgen_dict) + if not rv: + return rv + if do_connect: + return connect_tgen() + + return True + + +def load_tgen_int(tgen_dict): + global tg_stc_pkg_loaded, tg_ixia_pkg_loaded, tg_scapy_pkg_loaded, skip_tgen file_prefix = os.getenv("SPYTEST_FILE_PREFIX", "results") + config_file = tgen_dict.get('config_file', '') + config_file = os.getenv("SPYTEST_TGEN_CONFIG_FILE", config_file) # Abort if same TG type are having different version tg_type = tgen_dict['type'] tg_version = tgen_dict['version'] - + tg_virtual = bool(tgen_dict.get('virtual', 0)) + if tg_type in ['ixia']: + tgen_dict['config_file'] = config_file if tg_type not in ["stc", "ixia", "scapy"]: logger.error("Unknown TGen Type {}".format(tg_type)) return False @@ -2135,14 +3357,41 @@ def load_tgen(tgen_dict): 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 skip_tgen and not utils.ipcheck(tg_ip): - logger.error("TGEN IP Address: {} is not reachable".format(tg_ip)) - return False - - if skip_tgen and os.getenv("SPYTEST_DRYRUN_FORCE_SCAPY", "0") != "0": + logger.info("Loading {}:{} {} Ports: {} skip: {}".format( + tg_type, tg_version, tg_ip, tg_port_list, skip_tgen)) + if config_file: + logger.info("Loading config file: {}".format(config_file)) + link_params = get_tgen_link_params(tgen_dict['name'], tg_port_list, ['phy_mode', 'port_speed', 'auto_neg', 'fec']) + if link_params: + tgen_dict['link_params'] = link_params + + dryrun_force_scapy = bool(os.getenv("SPYTEST_DRYRUN_FORCE_SCAPY", "0") != "0") + + if isinstance(tg_ip, list): + chassis_ports = {} + for port in tg_port_list: + parts = port.split("/") + if len(parts) != 3: + logger.error("Invalid port {}".format(port)) + return False + chassis = int(parts[0]) + if chassis not in chassis_ports: + chassis_ports[chassis] = [] + chassis_ports[chassis].append("/".join(parts[1:])) + new_port_list = [] + for index, _ in enumerate(tg_ip): + port_list = chassis_ports.get(index + 1, list()) + new_port_list.append(port_list) + tg_port_list = new_port_list + elif not skip_tgen and not dryrun_force_scapy: + reachable = bool(os.getenv("SPYTEST_TGEN_SKIP_REACHABLE_CHECK", "0") != "0") + if not reachable: + if utils.ipcheck(tg_ip, 10, logger.warning, "TGEN "): + reachable = True + if not reachable: + return False + + if skip_tgen and dryrun_force_scapy: tg_type = 'scapy' tg_version = '1.0' skip_tgen = False @@ -2154,22 +3403,23 @@ def load_tgen(tgen_dict): if not tg_stc_load(tg_version, logger, tgen_get_logs_path()): return False code = "import sth \n" - exec (code, globals(), globals()) + # nosemgrep-next-line + exec(code, globals(), globals()) if tgen_log_lvl_is_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')) + 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()) + 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) @@ -2181,7 +3431,7 @@ def load_tgen(tgen_dict): 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) + tg_obj = TGStc(tg_type, tg_version, tg_ip, tg_port_list, **tgen_dict) if tg_type == 'ixia': for ix_server in utils.make_list(tgen_dict['ix_server']): @@ -2202,6 +3452,7 @@ def load_tgen(tgen_dict): "ixiahlt = IxiaHlt(ixiatcl) \n" + \ "ixiangpf = IxiaNgpf(ixiahlt) \n" + # nosemgrep-next-line exec(code, globals(), globals()) if tgen_log_lvl_is_debug(): logger.info("Setting Ixia Debugs...") @@ -2218,7 +3469,7 @@ def load_tgen(tgen_dict): 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) + tg_obj = TGIxia(tg_type, tg_version, tg_ip, tg_port_list, tg_ix_server, tg_ix_port, tg_virtual, **tgen_dict) if tg_obj.tg_connected: break @@ -2231,42 +3482,62 @@ def load_tgen(tgen_dict): 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 + return True + def module_init(): retval = True for _, tg in tgen_obj_dict.items(): tg.in_module_start_cleanup = True - try: tg.clean_all() - except Exception: retval = False + try: + tg.clean_all() + except Exception: + retval = False tg.in_module_start_cleanup = False return retval + 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_chassis(name=None): try: if name is None: - name = tgen_obj_dict.keys()[0] + name = list(tgen_obj_dict.keys())[0] return tgen_obj_dict[name] except Exception: return None + def get_tgen(port, name=None): tg = get_chassis(name) - if not tg: return (None, None) + if not tg: + return (None, None) ph = tg.get_port_handle(port) return (tg, ph) + def is_soft_tgen(name=None): tg = get_chassis(name) - return (tg and tg.tg_type == "scapy") + if not tg: + return False + if tg.tg_type == "scapy": + return True + return tg.tg_virtual + + +def get_tg_type(name=None): + tg = get_chassis(name) + return tg.tg_type if tg else "ixia" + if __name__ == "__main__": + tg_stc_load("4.91", None, None) + # tg_ixia_load("8.42", None, None) tg_ixia_load("9.10", None, None) code = \ "from ixiatcl import IxiaTcl \n" + \ @@ -2277,5 +3548,5 @@ def is_soft_tgen(name=None): "ixiahlt = IxiaHlt(ixiatcl) \n" + \ "ixiangpf = IxiaNgpf(ixiahlt) \n" + # nosemgrep-next-line exec(code, globals(), globals()) - diff --git a/spytest/spytest/tgen/tg_scapy.py b/spytest/spytest/tgen/tg_scapy.py index a2acfabdac..2e7d260fe0 100644 --- a/spytest/spytest/tgen/tg_scapy.py +++ b/spytest/spytest/tgen/tg_scapy.py @@ -3,8 +3,10 @@ import copy import time import logging +from collections import OrderedDict + +from spytest import paths -import spytest.paths as paths class ScapyClient(object): @@ -13,12 +15,13 @@ def __init__(self, logger, tg_ip=None, tg_port=8009, tg_port_list=None, base=Non self.conn = None self.logger = logger or logging.getLogger() self.use_pyro = True + self.use_pyro_ns = False self.node_name = "" self.filemode = bool(os.getenv("SPYTEST_FILE_MODE", "0") != "0") self.tg_ip = tg_ip self.tg_port = tg_port self.tg_port_list = tg_port_list or [] - self.tg_port_handle = {} + self.tg_port_handle = OrderedDict() root = os.path.join(os.path.dirname(__file__), '..') root = os.path.abspath(root) @@ -37,9 +40,15 @@ def _log_call(self, fname, **kwargs): self.base.log_call(fname, **kwargs) else: opts = ', '.join(['{}={!r}'.format(k, v) for k, v in kwargs.items()]) - self.logger.info("TODO {} {}".format(fname, opts)) + self.logger.info("REQ: {} {}".format(fname, opts)) - def save_log(self, name, data): + def _log_resp(self, fname, text): + if self.base: + self.base.log_resp(fname, text) + else: + self.logger.info("RESP: {} {}".format(fname, text)) + + def _save_log(self, name, data): if self.base: self.base.save_log(name, data) else: @@ -51,7 +60,7 @@ def _api_fail(self, msg): else: self.logger.info("TODO {}".format(msg)) - def log_info(self, *args): + def _log_info(self, *args): self.logger.info(*args) def log_remote_alerts(self, phase): @@ -60,84 +69,143 @@ def log_remote_alerts(self, phase): errs2 = self._execute(func_name, self.conn.server_control, "get-alerts", "") errs3 = "===============================================================" if errs2.strip(): - self.logger.info("\n".join(["\n",errs1, errs2, errs3,"\n"])) + msg = "\n".join(["\n", errs1, errs2, errs3, "\n"]) + self.logger.info(msg) + if self.base: + self.base.alert(msg) def server_control(self, phase, context): func_name = "server_control" if self.filemode: return elif phase == "clean-all": + self._log_info("ScapyClient instrument: {} {}".format(phase, context)) self._execute(func_name, self.conn.server_control, "clean-all", "") - elif phase == "pre-test": + elif phase in ["pre-test", "pre-function-prolog"]: + self._log_info("ScapyClient instrument: {} {}".format(phase, context)) self._execute(func_name, self.conn.server_control, "add-log", "test-start {} {}".format(context, self.node_name)) self.log_remote_alerts(phase) - elif phase == "post-test": + elif phase in ["post-test", "post-function-epilog"]: + self._log_info("ScapyClient instrument: {} {}".format(phase, context)) self._execute(func_name, self.conn.server_control, "add-log", "test-finish {} {}".format(context, self.node_name)) self.log_remote_alerts(phase) elif phase == "pre-module-prolog": - local_file = "{}.tgen".format(paths.get_mlog_name(context)) + self._log_info("ScapyClient instrument: {} {}".format(phase, context)) + local_file = paths.get_mtgen_path(context) path = self._execute(func_name, self.conn.server_control, "init-log", local_file) self.logger.info("Server Logs Path {}".format(path)) self.log_remote_alerts(phase) elif phase == "post-module-epilog": - self.log_info("ScapyClient instrument: {} {}".format(phase, context)) - local_file = "{}.tgen".format(paths.get_mlog_name(context)) - self.log_info("ScapyClient instrument: {} {}".format(phase, local_file)) + local_file = paths.get_mtgen_path(context) + self._log_info("ScapyClient instrument ({}): {} {}".format(context, phase, local_file)) try: data = self._execute(func_name, self.conn.server_control, "read-log", local_file) - self.save_log(local_file, data) + self._save_log(local_file, data) + except Exception as exp: + self._log_info("Failed to read log {} {}".format(context, str(exp))) + try: + local_file = local_file.replace(".tgen", ".pcap") + data = self._execute(func_name, self.conn.server_control, "read-pcap", local_file) + self._save_log(local_file, data) except Exception as exp: - self.log_info("Failed to read log {} {}".format(context, str(exp))) + self._log_info("Failed to read pcap {} {}".format(context, str(exp))) self.log_remote_alerts(phase) else: - self.log_info("ScapyClient instrument: ignored {} {}".format(phase, context)) + 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} + 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' + def pyro_connect(self): + import Pyro4 + if self.use_pyro_ns: + uri = "PYRONAME:scapy-tgen@{}".format(self.tg_ip) + else: + uri = "PYRO:scapy-tgen@{}:{}".format(self.tg_ip, self.tg_port) + Pyro4.config.SERIALIZER = "serpent" + Pyro4.config.SERIALIZER = "pickle" + Pyro4.config.PICKLE_PROTOCOL_VERSION = 2 + conn = Pyro4.Proxy(uri) + import uuid + conn._pyroHandshake = uuid.uuid4() + conn._pyroBind() + return conn + def scapy_disconnect(self, dry_run=False): if self.filemode: return None if self.use_pyro: - import Pyro4 - #uri = "PYRO:scapy-tgen@{}:{}".format(self.tg_ip, self.tg_port) - uri = "PYRONAME:scapy-tgen@{}".format(self.tg_ip) - Pyro4.config.SERIALIZER = "pickle" - self.conn = Pyro4.Proxy(uri) + pass else: - self.conn2 = self.rpyc_connect() - self.conn = self.conn2.root + self.conn2.close() + + def scapy_connect(self, dry_run=False): + self.tg_ns = 'scapy' + + if self.filemode: + return None + + last_exception = None + for _ in range(5): + try: + if self.use_pyro: + self.conn = self.pyro_connect() + else: + self.conn2 = self.rpyc_connect() + self.conn = self.conn2.root + last_exception = None + break + except Exception as exp: + last_exception = exp + time.sleep(10) + if last_exception is not None: + retval = {"status": "Failed to connect: {}".format(last_exception)} + self.logger.warning(retval["status"]) + return retval - try: dbg_lvl = int(os.getenv("SPYTEST_SCAPY_DBG_LVL", "1")) - except Exception: dbg_lvl = 1 - try: max_pps = int(os.getenv("SPYTEST_SCAPY_MAX_PPS", "100")) - except Exception: max_pps = 100 + try: + dbg_lvl = int(os.getenv("SPYTEST_SCAPY_DBG_LVL", "1")) + except Exception: + dbg_lvl = 1 + try: + max_pps = int(os.getenv("SPYTEST_SCAPY_MAX_PPS", "100")) + except Exception: + max_pps = 100 model = os.getenv("SCAPY_TGEN_PORTMAP", "eth1") self.node_name = os.getenv("PYTEST_XDIST_WORKER", "") func_name = "server_control" + last_exception = None for _ in range(5): try: self._execute(func_name, self.conn.server_control, "set-name", self.node_name) + last_exception = None break - except Exception: + except Exception as exp: + last_exception = exp time.sleep(10) - path = self._execute(func_name, self.conn.server_control, "init-log", "default") + if last_exception is not None: + retval = {"status": "Failed to connect: {}".format(last_exception)} + self.logger.warning(retval["status"]) + return retval + path = self._execute(func_name, self.conn.server_control, "init-log", "default.log") self.logger.info("Server Logs Path {}".format(path)) self._execute(func_name, self.conn.server_control, "set-dbg-lvl", dbg_lvl) self._execute(func_name, self.conn.server_control, "set-dry-run", dry_run) self._execute(func_name, self.conn.server_control, "set-max-pps", max_pps) self._execute(func_name, self.conn.server_control, "set-model", model) + self._execute(func_name, self.conn.server_control, "set-env", + "SPYTEST_SCAPY_DOT1X_IMPL", os.getenv("SPYTEST_SCAPY_DOT1X_IMPL", "1")) + self._execute(func_name, self.conn.server_control, "set-env", + "SPYTEST_SCAPY_USE_BRIDGE", os.getenv("SPYTEST_SCAPY_USE_BRIDGE", "1")) res = self.tg_connect(port_list=self.tg_port_list) self._set_port_handle(None, None) for port in self.tg_port_list: @@ -148,125 +216,356 @@ def log_api(self, *args, **kwargs): func = sys._getframe(1).f_code.co_name self._log_call(func, **kwargs) - def fix_newstr(self, kwargs): - from future.types import newstr + def fix_dict_values(self, kwargs): for key, value in kwargs.items(): - if isinstance(value, newstr): + if type(value) is {}.values().__class__: + if value != list(value): + self._log_info("TGen -- Change {} from {} to {}".format(key, value, list(value))) + kwargs[key] = list(value) + elif type(value) is {}.keys().__class__: + if value != list(value): + self._log_info("TGen --- Change {} from {} to {}".format(key, value, list(value))) + kwargs[key] = list(value) + elif isinstance(value, int): + kwargs[key] = str(value) + elif isinstance(value, float): + kwargs[key] = str(value) + elif not isinstance(value, list): + if value != str(value): + self._log_info("TGen - Change {} type {} from {} to {}".format(key, type(value), value, str(value))) kwargs[key] = str(value) def execute(self, func, *args, **kwargs): func_name = sys._getframe(1).f_code.co_name - return self._execute(func_name, func, *args, **kwargs) + self.fix_dict_values(kwargs) + retval = self._execute(func_name, func, *args, **kwargs) + self._log_resp(func_name, retval) + return retval def _execute(self, func_name, func, *args, **kwargs): - #msg1 = " ".join(map(str,args)) - #msg2 = ','.join(['{}={!r}'.format(k,v) for k,v in kwargs.items()]) - #self.log_info("ScapyClient: {} {} {} {}".format(self.node_name, func_name, msg1, msg2)) - try: - res = func(self.node_name, *args, **kwargs) - 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 + for _ in range(3): + try: + res = func(self.node_name, *args, **kwargs) + return copy.copy(res) + except Exception as exp: + msg = "\n{}\n{}".format(self.tg_ip, exp) + if self.use_pyro: + import Pyro4 + msg = msg + "\n" + "".join(Pyro4.util.getPyroTraceback()) + self._api_fail(msg) + if "Failed to locate the nameserver" in msg: + time.sleep(10) + continue + raise exp def sim_execute(self, *args, **kwargs): func_name = sys._getframe(1).f_code.co_name sim_id = getattr(self, "sim_id", 0) + 1 self.sim_id = sim_id if func_name == "tg_traffic_config": - return {"stream_id" : str(sim_id)} + return {"stream_id": str(sim_id)} elif func_name in ["tg_interface_config"]: - return {"handle" : str(sim_id)} + return {"handle": str(sim_id)} + elif func_name in ["tg_emulation_dhcp_server_config"]: + return {"dhcp_handle": str(sim_id)} + elif func_name in ["tg_emulation_dhcp_config"]: + return {"handles": [str(sim_id)], "handle": str(sim_id)} + elif func_name in ["tg_emulation_dhcp_group_config"]: + return {"handle": str(sim_id)} + elif func_name in ["tg_emulation_igmp_group_config"]: + return {"group_handle": str(sim_id)} + elif func_name in ["tg_emulation_mld_group_config"]: + return {"group_handle": str(sim_id)} elif func_name in ["tg_emulation_bgp_config", "tg_emulation_bgp_route_config"]: - return {"handle" : str(sim_id)} + return {"handle": str(sim_id)} + elif func_name in ["tg_emulation_dot1x_config"]: + return {"handle": str(sim_id)} elif func_name in ["tg_emulation_igmp_config"]: - return {"host_handle" : str(sim_id)} + return {"host_handle": str(sim_id)} + elif func_name in ["tg_emulation_mld_config"]: + return {"host_handle": str(sim_id)} elif func_name in ["tg_emulation_multicast_group_config"]: - return {"mul_group_handle" : str(sim_id)} + return {"mul_group_handle": str(sim_id)} elif func_name in ["tg_emulation_multicast_source_config"]: - return {"mul_source_handle" : str(sim_id)} + return {"mul_source_handle": str(sim_id)} elif func_name == "tg_traffic_stats": - rv = {"status" : "1", "traffic_item": {}} - tx_ph = kwargs.get("port_handle", None) + rv = {"status": "1", "traffic_item": {}} + ph = kwargs.get("port_handle", None) mode = kwargs.get("mode", "aggregate") - tx_ph_value = {mode: {}} - tx_ph_value[mode]["tx"] = {"raw_pkt_count":0, "total_pkts":0, "pkt_byte_count":0} - tx_ph_value[mode]["rx"] = {"total_pkts":0, "pkt_byte_count":0} - rv[tx_ph] = tx_ph_value + ph_value = {mode: {}} + ph_value[mode]["tx"] = {"raw_pkt_count": 0, "total_pkts": 0, "pkt_byte_count": 0} + ph_value[mode]["rx"] = {"total_pkts": 0, "pkt_byte_count": 0} + rv[ph] = ph_value + return rv + elif func_name == "tg_emulation_dhcp_stats": + ph = kwargs.get("port_handle", None) + mode = kwargs.get("mode", "aggregate") + rv = {"status": "1", "traffic_item": {}, "ipv6": {ph: {}}} + rv[mode] = {"currently_bound": 0} + rv["ipv6"][ph][mode] = {"currently_bound": 0} return rv return None def tg_connect(self, *args, **kwargs): self.log_api(*args, **kwargs) - if self.filemode: return self.sim_execute(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) return self.execute(self.conn.tg_connect, *args, **kwargs) + def tg_disconnect(self, *args, **kwargs): self.log_api(*args, **kwargs) - if self.filemode: return self.sim_execute(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) return self.execute(self.conn.tg_disconnect, *args, **kwargs) + def tg_traffic_control(self, *args, **kwargs): self.log_api(*args, **kwargs) - if self.filemode: return self.sim_execute(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) return self.execute(self.conn.tg_traffic_control, *args, **kwargs) + def tg_interface_control(self, *args, **kwargs): self.log_api(*args, **kwargs) - if self.filemode: return self.sim_execute(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) return self.execute(self.conn.tg_interface_control, *args, **kwargs) + def tg_packet_control(self, *args, **kwargs): self.log_api(*args, **kwargs) - if self.filemode: return self.sim_execute(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) return self.execute(self.conn.tg_packet_control, *args, **kwargs) + def tg_packet_stats(self, *args, **kwargs): self.log_api(*args, **kwargs) - if self.filemode: return self.sim_execute(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) return self.execute(self.conn.tg_packet_stats, *args, **kwargs) + def tg_traffic_config(self, *args, **kwargs): self.log_api(*args, **kwargs) - if self.filemode: return self.sim_execute(*args, **kwargs) - self.fix_newstr(kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) return self.execute(self.conn.tg_traffic_config, *args, **kwargs) + def tg_interface_config(self, *args, **kwargs): self.log_api(*args, **kwargs) - if self.filemode: return self.sim_execute(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) return self.execute(self.conn.tg_interface_config, *args, **kwargs) + def tg_traffic_stats(self, *args, **kwargs): self.log_api(*args, **kwargs) - if self.filemode: return self.sim_execute(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) return self.execute(self.conn.tg_traffic_stats, *args, **kwargs) + def tg_emulation_bgp_config(self, *args, **kwargs): self.log_api(*args, **kwargs) - if self.filemode: return self.sim_execute(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) return self.execute(self.conn.tg_emulation_bgp_config, *args, **kwargs) + def tg_emulation_bgp_route_config(self, *args, **kwargs): self.log_api(*args, **kwargs) - if self.filemode: return self.sim_execute(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) return self.execute(self.conn.tg_emulation_bgp_route_config, *args, **kwargs) + def tg_emulation_bgp_control(self, *args, **kwargs): self.log_api(*args, **kwargs) - if self.filemode: return self.sim_execute(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) return self.execute(self.conn.tg_emulation_bgp_control, *args, **kwargs) - def tg_emulation_igmp_config(self, *args, **kwargs): - self.log_api(*args, **kwargs) - if self.filemode: return self.sim_execute(*args, **kwargs) - return self.execute(self.conn.tg_emulation_igmp_config, *args, **kwargs) + def tg_emulation_multicast_group_config(self, *args, **kwargs): self.log_api(*args, **kwargs) - if self.filemode: return self.sim_execute(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) return self.execute(self.conn.tg_emulation_multicast_group_config, *args, **kwargs) + def tg_emulation_multicast_source_config(self, *args, **kwargs): self.log_api(*args, **kwargs) - if self.filemode: return self.sim_execute(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) return self.execute(self.conn.tg_emulation_multicast_source_config, *args, **kwargs) + + def tg_emulation_igmp_config(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_emulation_igmp_config, *args, **kwargs) + def tg_emulation_igmp_group_config(self, *args, **kwargs): self.log_api(*args, **kwargs) - if self.filemode: return self.sim_execute(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) return self.execute(self.conn.tg_emulation_igmp_group_config, *args, **kwargs) + + def tg_emulation_igmp_querier_config(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_emulation_igmp_querier_config, *args, **kwargs) + def tg_emulation_igmp_control(self, *args, **kwargs): self.log_api(*args, **kwargs) - if self.filemode: return self.sim_execute(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) return self.execute(self.conn.tg_emulation_igmp_control, *args, **kwargs) + def tg_emulation_igmp_querier_control(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_emulation_igmp_querier_control, *args, **kwargs) + + def tg_emulation_mld_config(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_emulation_mld_config, *args, **kwargs) + + def tg_emulation_mld_group_config(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_emulation_mld_group_config, *args, **kwargs) + + def tg_emulation_mld_querier_config(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_emulation_mld_querier_config, *args, **kwargs) + + def tg_emulation_mld_control(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_emulation_mld_control, *args, **kwargs) + + def tg_emulation_mld_querier_control(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_emulation_mld_querier_control, *args, **kwargs) + + def tg_ospf_lsa_config(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_emulation_ospf_lsa_config, *args, **kwargs) + + def tg_emulation_ospf_config(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_emulation_ospf_config, *args, **kwargs) + + def tg_emulation_ospf_control(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_emulation_ospf_control, *args, **kwargs) + + def tg_emulation_ospf_route_config(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_emulation_ospf_route_config, *args, **kwargs) + + def tg_emulation_ospf_lsa_config(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_emulation_ospf_lsa_config, *args, **kwargs) + + def tg_emulation_ospf_network_group_config(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_emulation_ospf_network_group_config, *args, **kwargs) + + def tg_emulation_ospf_topology_route_config(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_emulation_ospf_topology_route_config, *args, **kwargs) + + def tg_emulation_dhcp_config(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_emulation_dhcp_config, *args, **kwargs) + + def tg_emulation_dhcp_control(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_emulation_dhcp_control, *args, **kwargs) + + def tg_emulation_dhcp_group_config(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_emulation_dhcp_group_config, *args, **kwargs) + + def tg_emulation_dhcp_server_config(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_emulation_dhcp_server_config, *args, **kwargs) + + def tg_emulation_dhcp_server_control(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_emulation_dhcp_server_control, *args, **kwargs) + + def tg_emulation_dhcp_server_stats(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_emulation_dhcp_server_stats, *args, **kwargs) + + def tg_emulation_dhcp_server_relay_agent_config(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_emulation_dhcp_server_relay_agent_config, *args, **kwargs) + + def tg_emulation_dhcp_stats(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_emulation_dhcp_stats, *args, **kwargs) + + def tg_custom_filter_config(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_custom_filter_config, *args, **kwargs) + + def tg_emulation_dot1x_config(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_emulation_dot1x_config, *args, **kwargs) + + def tg_emulation_dot1x_control(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_emulation_dot1x_control, *args, **kwargs) + + def tg_emulation_ipv6_autoconfig(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_emulation_ipv6_autoconfig, *args, **kwargs) + + def tg_emulation_ipv6_autoconfig_control(self, *args, **kwargs): + self.log_api(*args, **kwargs) + if self.filemode: + return self.sim_execute(*args, **kwargs) + return self.execute(self.conn.tg_emulation_ipv6_autoconfig_control, *args, **kwargs) diff --git a/spytest/spytest/tgen/tg_stubs.py b/spytest/spytest/tgen/tg_stubs.py index cbbabe6dc3..096a144f32 100644 --- a/spytest/spytest/tgen/tg_stubs.py +++ b/spytest/spytest/tgen/tg_stubs.py @@ -1,66 +1,179 @@ import sys + class TGStubs(object): def __init__(self, logger): self.logger = logger + def override(self, name=None): name = name or sys._getframe(1).f_code.co_name self.logger.error("{} should be overriden".format(name)) return {} + def clean_all(self): return self.override() + def show_status(self): return self.override() + def tg_interface_handle(self, ret_ds): return self.override() + def tg_interface_config(self, *args, **kwargs): return self.override() + + def tg_save_xml(self, **kwargs): + return self.override() + def tg_test_control(self, **kwargs): return self.override() + def tg_packet_control(self, *args, **kwargs): return self.override() + def tg_traffic_control(self, *args, **kwargs): return self.override() + def tg_topology_config(self, **kwargs): return self.override() + def tg_packet_stats(self, *args, **kwargs): return self.override() + def tg_traffic_stats(self, *args, **kwargs): return self.override() + def tg_interface_stats(self, **kwargs): return self.override() + def tg_packet_config_buffers(self, **kwargs): return self.override() + def tg_packet_config_filter(self, **kwargs): return self.override() + def tg_packet_config_triggers(self, **kwargs): return self.override() + def tg_convert_porthandle_to_vport(self, **kwargs): return self.override() + def tg_protocol_info(self, **kwargs): return self.override() + def tg_withdraw_bgp_routes(self, route_handle): return self.override() + def tg_readvertise_bgp_routes(self, handle, route_handle): return self.override() + def tg_emulation_bgp_config(self, **kwargs): return self.override() + def tg_emulation_bgp_control(self, **kwargs): return self.override() + def tg_emulation_bgp_route_config(self, **kwargs): return self.override() + def tg_emulation_ospf_config(self, **kwargs): return self.override() + + def tg_emulation_ospf_control(self, **kwargs): + return self.override() + + def tg_emulation_ospf_route_config(self, **kwargs): + return self.override() + def tg_emulation_ospf_lsa_config(self, **kwargs): return self.override() + def tg_emulation_ospf_network_group_config(self, **kwargs): return self.override() + def tg_emulation_ospf_topology_route_config(self, **kwargs): return self.override() + + def tg_emulation_igmp_config(self, **kwargs): + return self.override() + + def tg_emulation_igmp_group_config(self, **kwargs): + return self.override() + + def tg_emulation_igmp_querier_config(self, **kwargs): + return self.override() + def tg_emulation_igmp_control(self, **kwargs): return self.override() + def tg_emulation_igmp_querier_control(self, **kwargs): return self.override() + + def tg_emulation_mld_querier_control(self, **kwargs): + return self.override() + + def tg_emulation_mld_control(self, **kwargs): + return self.override() + def tg_emulation_multicast_source_config(self, **kwargs): return self.override() + def tg_multivalue_config(self, **kwargs): + return self.override() + + def tg_emulation_dhcp_config(self, **kwargs): + return self.override() + + def tg_emulation_dhcp_control(self, **kwargs): + return self.override() + + def tg_emulation_dhcp_group_config(self, **kwargs): + return self.override() + + def tg_emulation_dhcp_server_config(self, **kwargs): + return self.override() + + def tg_emulation_dhcp_server_control(self, **kwargs): + return self.override() + + def tg_emulation_dhcp_server_stats(self, **kwargs): + return self.override() + + def tg_emulation_dhcp_server_relay_agent_config(self, **kwargs): + return self.override() + + def tg_emulation_dhcp_stats(self, **kwargs): + return self.override() + + def tg_emulation_dotonex_config(self, **kwargs): + return self.override() + + def tg_emulation_dotonex_control(self, **kwargs): + return self.override() + + def tg_emulation_dotonex_info(self, **kwargs): + return self.override() + + def tg_ptp_over_ip_control(self, **kwargs): + return self.override() + + def tg_ptp_over_mac_control(self, **kwargs): + return self.override() + + def tg_ptp_over_ip_config(self, **kwargs): + return self.override() + + def tg_ptp_over_mac_config(self, **kwargs): + return self.override() + + def tg_ptp_over_ip_stats(self, **kwargs): + return self.override() + + def tg_ptp_over_mac_stats(self, **kwargs): + return self.override() + + def tg_emulation_ipv6_autoconfig(self, **kwargs): + return self.override() + + def tg_emulation_ipv6_autoconfig_control(self, **kwargs): + return self.override() diff --git a/spytest/spytest/tgen/tgen_utils.py b/spytest/spytest/tgen/tgen_utils.py index 8f58f78a4b..7d939c9f96 100644 --- a/spytest/spytest/tgen/tgen_utils.py +++ b/spytest/spytest/tgen/tgen_utils.py @@ -1,24 +1,27 @@ import re +import os import copy -from spytest import st, SpyTestDict +from spytest import st, SpyTestDict, cutils from spytest.tgen_api import get_chassis -from apis.system.basic import get_techsupport +from spytest.tgen_api import is_soft_tgen + def _log_call(fname, **kwargs): - args_list=[] + args_list = [] for key, value in kwargs.items(): - args_list.append("%s=%s" %(key, value)) + 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): + 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) is None: - st.error('{} parameter missing in traffic pair: {}'.format(param,tr_pair)) + st.error('{} 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.error('tx_ports, tx_obj and exp_ratio must be of same length, in traffic pair: {}'.format(tr_pair)) @@ -26,15 +29,15 @@ def _validate_parameters(tr_details): if len(tr_details[tr_pair]['rx_ports']) != len(tr_details[tr_pair]['rx_obj']): st.error('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 tr_details[tr_pair].get('stream_list', None) is not None: if len(tr_details[tr_pair]['tx_ports']) != len(tr_details[tr_pair]['stream_list']): st.error('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 tr_details[tr_pair].get('filter_param', None) is not None and tr_details[tr_pair].get('stream_list', None) is not None: if len(tr_details[tr_pair]['filter_param']) != len(tr_details[tr_pair]['stream_list']): st.error('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 tr_details[tr_pair].get('filter_val', None) is not None and tr_details[tr_pair].get('filter_param', None) is not None: if len(tr_details[tr_pair]['filter_val']) != len(tr_details[tr_pair]['filter_param']): st.error('filer value and filter list must be of same length, in traffic pair: {}'.format(tr_pair)) return False @@ -42,7 +45,7 @@ def _validate_parameters(tr_details): return True -def get_counter_name(mode,tg_type,comp_type,direction,logger=True): +def get_counter_name(mode, tg_type, comp_type, direction, logger=True): tg_type2 = "ixia" if tg_type == "scapy" else tg_type traffic_counters = { 'aggregate': { @@ -51,79 +54,79 @@ def get_counter_name(mode,tg_type,comp_type,direction,logger=True): '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' : { + }, + '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' : { + }, + '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_type2][direction][comp_type] if logger: @@ -132,15 +135,36 @@ def get_counter_name(mode,tg_type,comp_type,direction,logger=True): def _get_debug_dump(name='tg_stats_failed'): - tg=get_chassis() - get_techsupport(filename=name) + tg = get_chassis() tg.collect_diagnosic(fail_reason=name) -def _fetch_stats(obj, port, mode, comp_type, direction, stream_elem=None): +def _stats_error(which, stats, exp=None): + line = cutils.get_line_number(1) + st.error('{} Could not get {} from TGEN, is traffic started?'.format(line, which)) + st.error("{} {}".format(stats, exp)) + + +def _tx_fail(counter, name, value, stats, port_status=None): + _get_debug_dump() + line = cutils.get_line_number(1) + msg = '{} TX Counter {} is zero for {}: {}'.format(line, counter, name, value) + st.error("{} {}".format(msg, stats)) + if is_soft_tgen(): + st.error(cutils.stack_trace(None, True)) + ret_val = [] + if port_status: + for port in port_status[0]: + ret_val.append(bool(port_status[1][port]['state'] != 'down')) + if not all(ret_val): + msg = 'One of the traffic item endpoint is down: {}'.format(port_status[0]) + st.report_tgen_fail('tgen_failed_api', msg) + + +def _fetch_stats(obj, port, mode, comp_type, direction, **kwargs): """ @author: Lakshminarayana D(lakshminarayana.d@broadcom.com) - Fuction will fetch traffic stats based inputs and classify TgenFail based on stats available. + Function will fetch traffic stats based inputs and classify TgenFail based on stats available. :param obj: TG object :param port: Port handler :param mode: Mode of the stats to be fetched(Ex: streams, aggregate, traffic_items) @@ -150,52 +174,88 @@ def _fetch_stats(obj, port, mode, comp_type, direction, stream_elem=None): :return: stats: A Dictionary object with tx/rx packets/bytes stats """ + stream_elem = kwargs.get('stream_elem') + scale_mode = kwargs.get('scale_mode') stats_mode = 'streamblock' if mode in ['streams', 'traffic_item'] else mode counter_name = get_counter_name(stats_mode, obj.tg_type, comp_type, direction, logger=False) - tx_counter = int() + counter = 0 stats = dict() for loop in range(0, 4): if loop > 0: - st.log('TG stats are not fully ready. Trying to fetch stats again.... iteration {}'.format(loop)) + st.warn('TG stats are not fully ready. Trying to fetch stats again.... iteration {}'.format(loop)) st.wait(2, 'waiting before fetch stats again') - stats = obj.tg_traffic_stats(port_handle=port, mode=mode) + stats_dict = {'port_handle': port, 'mode': mode} + if obj.tg_type == 'stc' and scale_mode: + stats_dict.update({'scale_mode': scale_mode}) + stats = obj.tg_traffic_stats(**stats_dict) if obj.tg_type == 'stc': if mode == 'streams': - tx_counter = float(stats[port]['stream'][stream_elem][direction][counter_name]) + counter = float(stats[port]['stream'][stream_elem][direction][counter_name]) else: - tx_counter = int(stats[port][mode][direction][counter_name]) - if direction == 'rx' or tx_counter != 0: break + counter = int(stats[port][mode][direction][counter_name]) + if direction == 'rx' or counter != 0: + break elif obj.tg_type in ['ixia', 'scapy']: try: if mode == 'traffic_item': - tx_counter = float(stats[mode][stream_elem][direction][counter_name]) + counter = int(float(stats[mode][stream_elem][direction][counter_name])) + elif mode == 'streams': + counter = int(stats[port]['stream'][stream_elem][direction][counter_name]) else: - tx_counter = int(stats[port][mode][direction][counter_name]) - except Exception: - st.error('Could not get traffic_stats from the TGEN, Please check if traffic was started') - tx_counter = 0 - if stats.get('waiting_for_stats', '1') == '0' and (direction == 'rx' or tx_counter != 0): - break - if direction == 'tx' and not tx_counter: + counter = int(float(stats[port][mode][direction][counter_name])) + except Exception as exp: + _stats_error("traffic_stats", stats, exp) + counter = 'N/A' + if stats.get('waiting_for_stats', '1') == '0' and (direction == 'rx' or counter not in [0, 'N/A']): + break + if counter == 'N/A' or direction == 'tx' and not counter: (name, value) = ('stream handle', stream_elem) if stream_elem else ('port', port) - msg = 'TX Counter {} is 0 (zero) for {}: {}'.format(counter_name, name, value) - _get_debug_dump() - st.warn(msg) - st.report_tgen_fail('tgen_failed_api', msg) + port_status = obj.get_session_errors(port_handle=port, stream_handle=stream_elem) + _tx_fail(counter_name, name, value, stats, port_status) return stats -def _verify_aggregate_stats(tr_details,mode,comp_type,tolerance_factor,delay_factor,retry,return_all): + +def _build_pair_info(tr_pair, tr_details): + pair_info = tr_details[str(tr_pair)] + dut_tx_ports = pair_info.get('dut_tx_ports', "") + dut_rx_ports = pair_info.get('dut_rx_ports', "") + msg = "Pair: {}".format(tr_pair) + msg = msg + " TX: {}".format(pair_info['tx_ports']) + if dut_tx_ports: + msg = msg + " ({})".format(dut_tx_ports) + msg = msg + " RX: {}".format(pair_info['rx_ports']) + if dut_rx_ports: + msg = msg + " ({})".format(dut_rx_ports) + return msg + + +def _log_validation(cmsg, result, exp_val, real_rx_val, diff, strelem=None, fpelem=None, fvelem=None): + msg = "Traffic Validation: {} {} Expected: {} Actual: {} diff%: {}" + msg = msg.format(result, cmsg, exp_val, real_rx_val, round(diff, 6)) + if strelem: + msg = msg + " streamid: {}".format(strelem) + if fpelem: + msg = msg + " filter param: {} value: {}".format(fpelem, fvelem) + st.log(msg) + + +def _verify_aggregate_stats(tr_details, **kwargs): return_value = True - ret_all=[] - delay = 5 * float(delay_factor) - tolerance = 5 * float(tolerance_factor) + ret_all = [] + delay = 5 * float(kwargs.get('delay_factor')) + tolerance = 5 * float(kwargs.get('tolerance_factor')) + retry = kwargs.get('retry') + mode = kwargs.get('mode') + comp_type = kwargs.get('comp_type') + return_all = kwargs.get('return_all') + scale_mode = kwargs.get('scale_mode') st.tg_wait(delay, "aggregate_stats") port_stats = dict() - for tr_pair in range(1,len(tr_details)+1): + 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'] @@ -203,75 +263,77 @@ def _verify_aggregate_stats(tr_details,mode,comp_type,tolerance_factor,delay_fac rx_ports = tr_details[tr_pair]['rx_ports'] rx_obj = tr_details[tr_pair]['rx_obj'] - cmsg = 'Pair: {}, Transmit Ports: {}, Receive Ports: {}'.format(tr_pair, tx_ports, rx_ports) - st.log('Validating Traffic, Pair: {}, Transmit Ports: {}, Receive Ports: {}'.format(tr_pair,tx_ports,rx_ports)) - retry_count = retry + 1 if not retry and comp_type == 'packet_rate' else retry + cmsg = _build_pair_info(tr_pair, tr_details) + st.log('Validating Traffic, {}'.format(cmsg)) + retry_count = retry + 1 if not retry else retry tx_ph, rx_ph = None, None for loop in range(0, int(retry_count) + 1): if loop > 0: st.log('The difference is not in the given tolerance. So, retrying the stats fetch once again....{}'.format(loop)) st.wait(2, 'waiting to before fetch stats again') - for port in [tx_ph, rx_ph]: port_stats.pop(port, '') + for port in [tx_ph, rx_ph]: + port_stats.pop(port, '') exp_val = 0 - for port,obj,ratio in zip(tx_ports,tx_obj,exp_ratio): + 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) if tx_ph not in port_stats: - port_stats[tx_ph] = _fetch_stats(obj, tx_ph, mode, comp_type, 'tx') + port_stats[tx_ph] = _fetch_stats(obj, tx_ph, mode, comp_type, 'tx', scale_mode=scale_mode) tx_stats = port_stats[tx_ph] - #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)) + # st.debug(tx_stats) + counter_name = get_counter_name(mode, obj.tg_type, comp_type, 'tx') + cur_tx_val = int(float(tx_stats[tx_ph][mode]['tx'][counter_name])) + st.log('Transmit counter_name: {}, counter_val: {}'.format(counter_name, cur_tx_val)) exp_val += cur_tx_val * ratio - st.log('Total Tx from ports {}: {}'.format(tx_ports,exp_val)) + st.log('Total Tx from ports {}: {}'.format(tx_ports, exp_val)) - for port,obj in zip(rx_ports,rx_obj): + 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) if rx_ph not in port_stats: - port_stats[rx_ph] = _fetch_stats(obj, rx_ph, mode, comp_type, 'rx') + port_stats[rx_ph] = _fetch_stats(obj, rx_ph, mode, comp_type, 'rx', scale_mode=scale_mode) rx_stats = port_stats[rx_ph] - #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.debug(rx_stats) + counter_name = get_counter_name(mode, obj.tg_type, comp_type, 'rx') + real_rx_val = int(float(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: {}, {}'.format('Success',cmsg)) - st.log('Got expected values; Expected: {}, Actual: {}, diff%: {}'.format(exp_val,real_rx_val,diff)) + _log_validation(cmsg, True, exp_val, real_rx_val, diff) ret_all.append(True) break elif loop != retry_count: - if not retry and comp_type == 'packet_rate' and diff > (tolerance + 5): - msg = 'The traffic difference is in not between {} and {}. Skipping the retry'.format(tolerance, tolerance + 5) - st.debug(msg) - else: - st.log('Traffic Varification: {}, {}'.format('Failure', cmsg)) - st.log('Expected: {}, Actual: {}, diff%: {}'.format(exp_val, real_rx_val, diff)) + if retry or diff <= tolerance + 5: + _log_validation(cmsg, False, exp_val, real_rx_val, diff) continue + # msg = 'The traffic difference is in not between {} and {}. Skipping the retry'.format(tolerance, tolerance + 5) + # st.debug(msg) return_value = False ret_all.append(False) - st.log('Traffic Validation: {}, {}'.format('Failure', cmsg)) - st.log('Expected: {}, Actual: {}, diff%: {}'.format(exp_val,real_rx_val,diff)) + _log_validation(cmsg, False, exp_val, real_rx_val, diff) break - return return_value if return_all==0 else (return_value, ret_all) + 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): +def _verify_streamlevel_stats(tr_details, **kwargs): return_value = True - ret_all=[] - delay = 5 * float(delay_factor) - tolerance = 5 * float(tolerance_factor) + ret_all = [] + delay = 5 * float(kwargs.get('delay_factor')) + tolerance = 5 * float(kwargs.get('tolerance_factor')) + retry = kwargs.get('retry') + mode = kwargs.get('mode') + comp_type = kwargs.get('comp_type') + return_all = kwargs.get('return_all') + scale_mode = kwargs.get('scale_mode') st.tg_wait(delay, "streamlevel_stats") stream_stats = dict() - for tr_pair in range(1,len(tr_details)+1): + 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'] @@ -280,97 +342,96 @@ def _verify_streamlevel_stats(tr_details,mode,comp_type,tolerance_factor,delay_f rx_obj = tr_details[tr_pair]['rx_obj'] stream_list = tr_details[tr_pair]['stream_list'] - cmsg = 'Pair: {}, TX Ports: {}, RX Ports: {}'.format(tr_pair,tx_ports,rx_ports) - st.log('Validating Traffic, {}, stream_list: {}'.format(cmsg,stream_list)) + cmsg = 'Pair: {}, TX Ports: {}, RX Ports: {}'.format(tr_pair, tx_ports, rx_ports) + st.log('Validating Traffic, {}, stream_list: {}'.format(cmsg, 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): + 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): - retry_count = retry + 1 if not retry and comp_type == 'packet_rate' else retry - for loop in range(0, int(retry_count)+1): + for strelem, ratelem in zip(stream, ratio): + retry_count = retry + 1 if not retry else retry + for loop in range(0, int(retry_count) + 1): if loop > 0: - st.log('The difference is not in the given tolerance. So, retrying the stats fetch once again....{}'.format(loop)) + st.warn('The difference is not in the given tolerance. So, retrying the stats fetch once again....{}'.format(loop)) st.wait(2, 'waiting to before fetch stats again') - if rx_obj.tg_type == 'stc': stream_stats.pop(tx_ph, '') - if rx_obj.tg_type in ['ixia', 'scapy']: stream_stats.pop(rx_ph, '') + if rx_obj.tg_type == 'stc': + stream_stats.pop(tx_ph, '') + if rx_obj.tg_type in ['ixia', 'scapy']: + stream_stats.pop(rx_ph, '') exp_val = 0 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') if rx_obj.tg_type == 'stc': - #rx_stats = rx_obj.tg_traffic_stats(port_handle=rx_ph,mode='streams',streams=strelem) if tx_ph not in stream_stats: - stream_stats[tx_ph] = _fetch_stats(txObj, tx_ph, 'streams', comp_type, 'tx', stream_elem=strelem) + stream_stats[tx_ph] = _fetch_stats(txObj, tx_ph, 'streams', comp_type, 'tx', stream_elem=strelem, scale_mode=scale_mode) rx_stats = stream_stats[tx_ph] - exp_val = int(rx_stats[tx_ph]['stream'][strelem]['tx'][tx_counter_name]) - tx_val=exp_val - exp_val = int(exp_val * float(ratelem)) + tx_val = int(rx_stats[tx_ph]['stream'][strelem]['tx'][tx_counter_name]) + exp_val = int(tx_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']: - #rx_stats = rx_obj.tg_traffic_stats(port_handle=rx_ph, mode='traffic_item') if rx_ph not in stream_stats: stream_stats[rx_ph] = _fetch_stats(rx_obj, rx_ph, 'traffic_item', comp_type, 'tx', stream_elem=strelem) rx_stats = stream_stats[rx_ph] - #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 + # 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.error('Could not get traffic_stats from the TGEN, Please check if traffic was started') + _stats_error("traffic_stats", stream_stats) return False + try: - exp_val = float(rx_stats['traffic_item'][strelem]['tx'][tx_counter_name]) - except Exception: - st.error('Could not get tx counter from the TGEN, Please check if traffic was started') + tx_val = int(float(rx_stats['traffic_item'][strelem]['tx'][tx_counter_name])) + except Exception as exp: + _stats_error("tx counter", stream_stats, exp) return False - - tx_val=exp_val - exp_val = int(exp_val * float(ratelem)) - + exp_val = int(tx_val * float(ratelem)) try: - real_rx_val = float(rx_stats['traffic_item'][strelem]['rx'][rx_counter_name]) - except Exception: - st.error('Could not get rx counter from the TGEN, Please check if traffic was started') + real_rx_val = int(float(rx_stats['traffic_item'][strelem]['rx'][rx_counter_name])) + except Exception as exp: + _stats_error("rx counter", stream_stats, exp) return False - st.log('RX counter {} = {}'.format(rx_counter_name, real_rx_val)) + st.debug('RX counter {} = {} on {} stream_list: {}'.format(rx_counter_name, real_rx_val, rx_port, stream_list)) + st.debug('TX counter {} = {} on {} stream_list: {}'.format(tx_counter_name, tx_val, txPort, stream_list)) if tx_val > 0: 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: {}, {} streamid: {}'.format('Success',cmsg, strelem)) - st.log('Got expected values; Expected: {}, Actual: {}, diff%: {}'.format(exp_val,real_rx_val,diff)) + _log_validation(cmsg, True, exp_val, real_rx_val, diff, strelem) ret_all.append(True) break if loop != retry_count: - if not retry and comp_type == 'packet_rate' and diff > (tolerance + 5): - msg = 'The traffic difference is in not between {} and {}. Skipping the retry'.format(tolerance, tolerance + 5) - st.debug(msg) - else: - st.log('Traffic Varification: {}, {} streamid: {}'.format('Failure', cmsg, strelem)) - st.log('Expected: {}, Actual: {}, diff%: {}'.format(exp_val, real_rx_val, diff)) + if retry or diff <= tolerance + 5: + _log_validation(cmsg, False, exp_val, real_rx_val, diff, strelem) continue + # msg = 'The traffic difference is in not between {} and {}. Skipping the retry'.format(tolerance, tolerance + 5) + # st.debug(msg) return_value = False ret_all.append(False) - st.log('Traffic Validation: {}, {} streamid: {}'.format('Failure',cmsg, strelem)) - st.log('Expected: {}, Actual: {}, diff%: {}'.format(exp_val,real_rx_val,diff)) + _log_validation(cmsg, False, exp_val, real_rx_val, diff, strelem) break - return return_value if return_all==0 else (return_value, ret_all) + 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): +def _verify_analyzer_filter_stats(tr_details, **kwargs): return_value = True - ret_all=[] - delay = 5 * float(delay_factor) - tolerance = 5 * float(tolerance_factor) + ret_all = [] + delay = 5 * float(kwargs.get('delay_factor')) + tolerance = 5 * float(kwargs.get('tolerance_factor')) + mode = kwargs.get('mode') + comp_type = kwargs.get('comp_type') + return_all = kwargs.get('return_all') + scale_mode = kwargs.get('scale_mode') st.tg_wait(delay, "analyzer_filter_stats") - for tr_pair in range(1,len(tr_details)+1): + 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'] @@ -380,12 +441,14 @@ def _verify_analyzer_filter_stats(tr_details,mode,comp_type,tolerance_factor,del 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)) + + cmsg = _build_pair_info(tr_pair, tr_details) + st.log('Validating Traffic, {}, stream_list: {}, filter_param: {}, filter_val: {}'.format(cmsg, 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): + 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) @@ -394,68 +457,62 @@ def _verify_analyzer_filter_stats(tr_details,mode,comp_type,tolerance_factor,del stream = [stream] fparam = [fparam] fvalue = [fvalue] - for strelem,fpelem,fvelem in zip(stream,fparam,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) + 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, scale_mode=scale_mode) exp_val = int(tx_stats[tx_ph]['stream'][strelem]['tx'][tx_counter_name]) - tx_val=exp_val + tx_val = exp_val if exp_val == 0: - msg = 'TX Counter {} is 0 (zero) for port: {}'.format(tx_counter_name, strelem) - st.warn(msg) - st.report_tgen_fail('tgen_failed_api', msg) + _tx_fail(tx_counter_name, "stream", strelem, tx_stats) exp_val = exp_val * ratio - rx_stats = rx_obj.tg_traffic_stats(port_handle=rx_ph,mode='aggregate') + 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)) + 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) + 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 + tx_val = exp_val if exp_val == 0: - msg = 'TX Counter {} is 0 (zero) for port: {}'.format(tx_counter_name, strelem) - st.warn(msg) - st.report_tgen_fail('tgen_failed_api', msg) + _tx_fail(tx_counter_name, "stream", strelem, tx_stats) 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') + 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)) + 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)) + _log_validation(cmsg, True, exp_val, real_rx_val, diff, strelem, fpelem, fvelem) 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) + _log_validation(cmsg, False, exp_val, real_rx_val, diff, strelem, fpelem, fvelem) + 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): +def _verify_custom_filter_stats(tr_details, **kwargs): return_value = True - ret_all=[] - delay = 5 * float(delay_factor) - tolerance = 5 * float(tolerance_factor) + ret_all = [] + delay = 5 * float(kwargs.get('delay_factor')) + tolerance = 5 * float(kwargs.get('tolerance_factor')) + comp_type = kwargs.get('comp_type') + return_all = kwargs.get('return_all') st.tg_wait(delay, "custom_filter_stats") - for tr_pair in range(1,len(tr_details)+1): + 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'] @@ -463,49 +520,45 @@ def _verify_custom_filter_stats(tr_details,mode,comp_type,tolerance_factor,delay 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)) + cmsg = _build_pair_info(tr_pair, tr_details) + st.log('Validating Traffic, {}'.format(cmsg)) exp_val = 0 - for port,obj,ratio in zip(tx_ports,tx_obj,exp_ratio): + 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') + 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 = 'TX Counter {} is 0 (zero) for port: {}'.format(counter_name, port) - st.warn(msg) - st.report_tgen_fail('tgen_failed_api', msg) + _tx_fail(counter_name, "port", port, tx_stats) + st.log('Transmit counter_name: {}, counter_val: {}'.format(counter_name, cur_tx_val)) exp_val += cur_tx_val * ratio # st.log(exp_val) - st.log('Total Tx from ports {}: {}'.format(tx_ports,exp_val)) + st.log('Total Tx from ports {}: {}'.format(tx_ports, exp_val)) - for port,obj in zip(rx_ports,rx_obj): + 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) + rx_stats = obj.tg_custom_filter_config(mode='getStats', port_handle=rx_ph, capture_wait=kwargs.get('capture_wait')) st.log(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('Receive counter_name: {}, counter_val: {}'.format(counter_name, real_rx_val)) - st.log('Total Rx on ports {}: {}'.format(rx_ports,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)) + _log_validation(cmsg, True, 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) + _log_validation(cmsg, False, 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' : { @@ -541,42 +594,46 @@ def validate_tgen_traffic(**kwargs): return False traffic_details = kwargs['traffic_details'] - #st.log(traffic_details) + # st.log(traffic_details) if not _validate_parameters(traffic_details): return False - delay_factor = kwargs.get('delay_factor',1) - delay_factor = delay_factor if float(delay_factor) >= 1 else 1 - tolerance_factor = kwargs.get('tolerance_factor',1) - retry = kwargs.get('retry',0) - 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, return_index=0): + tgen_dict = dict() + delay_factor = kwargs.get('delay_factor', 1) + tgen_dict['delay_factor'] = delay_factor if float(delay_factor) >= 1 else 1 + tgen_dict['tolerance_factor'] = kwargs.get('tolerance_factor', 1) + tgen_dict['retry'] = kwargs.get('retry', 0) + tgen_dict['comp_type'] = kwargs.get('comp_type', 'packet_count') + tgen_dict['return_all'] = kwargs.get('return_all', 0) + tgen_dict['mode'] = kwargs.get('mode', 'aggregate').lower() + tgen_dict['scale_mode'] = kwargs.get('scale_mode', 0) + tgen_dict['capture_wait'] = kwargs.get('capture_wait', 120) + + # force the validation to packet count for soft TGEN + if is_soft_tgen(): + tgen_dict['comp_type'] = 'packet_count' + + if tgen_dict['mode'] == 'aggregate': + return _verify_aggregate_stats(traffic_details, **tgen_dict) + elif tgen_dict['mode'] == 'streamblock': + return _verify_streamlevel_stats(traffic_details, **tgen_dict) + elif tgen_dict['mode'] == 'filter': + return _verify_analyzer_filter_stats(traffic_details, **tgen_dict) + elif tgen_dict['mode'] == 'custom_filter': + return _verify_custom_filter_stats(traffic_details, **tgen_dict) + + +def _verify_packet_capture(pkt_dict, offset_list, value_list, port_handle, max_count=20, return_index=0): 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)) + 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): + for offset, value in zip(offset_list, value_list): + value = str(value) if ":" in value: value = value.split(':') elif "." in value: @@ -584,10 +641,11 @@ def _verify_packet_capture(pkt_dict, offset_list, value_list,port_handle, max_co 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)] + 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): + # CHECK - Can this ever be a list? + if not isinstance(value, list): value = [value] start_range = offset @@ -597,25 +655,26 @@ def _verify_packet_capture(pkt_dict, offset_list, value_list,port_handle, max_co found_value = pkt_dict[port_handle]['frame'][str(pkt_num)]['frame_pylist'][start_range:end_range] except Exception: found_value = [] - + found_value = [cutils.to_string(val) for val in found_value] if found_value == value: - st.log('Match found in packet: {} for {} at offset: {}'.format(pkt_num,value,offset)) + 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)) + st.log('Match not found in packet: {} at offset: {}, Expected: {}, Found: {}'.format(pkt_num, offset, value, found_value)) if ret_val == 0 and return_index: return pkt_num, True elif ret_val == 0: - return True + return True return (-1, False) if return_index else False -def _parse_ixia_packet(pkt_dict,header,field,value,offset='not_set'): - for k,v in pkt_dict.items(): - if isinstance(v,dict): +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 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) @@ -627,107 +686,112 @@ def _parse_ixia_packet(pkt_dict,header,field,value,offset='not_set'): 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) + _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,return_index=0): - 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'], - } - } - - global ixia_pckt_cap_ret_val - 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)) - try: - p_d = pkt_dict[port_handle]['frame']['data'][str(pkt_num)] - except Exception: - st.error('The given indexed packet not found in the capture buffer') - st.report_tgen_fail('tgen_failed_capture_buffer') - #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' - ixia_pckt_cap_ret_val = [] - 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)) - st.log("{} {} {}".format(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 +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'], + } +} + + +def _verify_packet_capture_ixia(pkt_dict, header_list, value_list, port_handle, return_index=0): + + global ixia_pckt_cap_ret_val + 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)) + try: + p_d = pkt_dict[port_handle]['frame']['data'][str(pkt_num)] + except Exception: + st.error('The given indexed packet not found in the capture buffer') + st.report_tgen_fail('tgen_failed_capture_buffer') + # 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' + ixia_pckt_cap_ret_val = [] + 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: - st.log('Match not found in packet: {} for {} in {}:{} header'.format(pkt_num,value,header,field)) - st.log('Packet: {}'.format(p_d)) + value = str(int(value, 16)) + st.log("{} {} {}".format(header, field, value)) - if ret_val == 0 and return_index: - return pkt_num, True - elif ret_val == 0: - return True + 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 and return_index: + return pkt_num, True + elif ret_val == 0: + return True def validate_packet_capture(**kwargs): pkt_dict = kwargs['pkt_dict'] - header_list = kwargs.get('header_list','new_ixia_format') + header_list = kwargs.get('header_list', 'new_ixia_format') offset_list = kwargs['offset_list'] value_list = kwargs['value_list'] - num_frames = kwargs.get('var_num_frames', 20) - return_index =kwargs.get('return_index', 0) + num_frames = int(kwargs.get('var_num_frames', 20)) + return_index = kwargs.get('return_index', 0) _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') + st.warn('Packets have caputred on more than one port. Pass packet info for only one port') return False for key in pkt_dict: @@ -735,39 +799,46 @@ def validate_packet_capture(**kwargs): port_handle = key if pkt_dict[port_handle]['aggregate']['num_frames'] in ['0', 'N/A']: - st.log("No packets were captured") + st.warn("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,num_frames,return_index) + return _verify_packet_capture(pkt_dict, offset_list, value_list, port_handle, num_frames, return_index) if kwargs['tg_type'] in ['scapy']: - return _verify_packet_capture(pkt_dict,offset_list,value_list,port_handle, 0, return_index) + return _verify_packet_capture(pkt_dict, offset_list, value_list, port_handle, 0, return_index) 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,num_frames,return_index) - else: - return _verify_packet_capture_ixia(pkt_dict,header_list,value_list,port_handle,return_index) + return _verify_packet_capture(pkt_dict, offset_list, value_list, port_handle, num_frames, return_index) + + # _verify_packet_capture_ixia is obsolete from now on, it is there only for legacy script. + return _verify_packet_capture_ixia(pkt_dict, header_list, value_list, port_handle, return_index) + + st.warn("Unknown tg_type {}".format(kwargs['tg_type'])) + return False -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) + +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) + 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 + 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 _ in range(ping_count): - result = src_obj.tg_interface_config(protocol_handle=dev_handle,send_ping='1',ping_dst=dst_ip) + result = src_obj.tg_interface_config(protocol_handle=dev_handle, send_ping='1', ping_dst=dst_ip) st.log("ping output: {}".format(result)) if port_handle not in result: st.warn("port_handle details not found in o/p") elif "ping_details" not in result[port_handle]: st.warn("ping_details details not found in o/p") + elif 'No sessions were started' in result[port_handle]['ping_details']: + src_obj.get_session_errors() + st.report_tgen_fail('tgen_failed_api', result[port_handle]['ping_details']) else: try: result = result[port_handle]['ping_details'] @@ -775,7 +846,7 @@ def verify_ping(src_obj,port_handle,dev_handle,dst_ip,ping_count=5,exp_count=5): 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) + tx_pkt, rx_pkt = ping_out.group(1), ping_out.group(2) if int(tx_pkt) == int(rx_pkt): count += 1 except AttributeError: @@ -785,8 +856,8 @@ def verify_ping(src_obj,port_handle,dev_handle,dst_ip,ping_count=5,exp_count=5): st.error("Need to add code for this tg type: {}".format(src_obj.tg_type)) return False -def tg_bgp_config(**kwargs): +def tg_bgp_config(**kwargs): """ Description: Configures the BGP parameters on the already created host. @@ -829,13 +900,13 @@ def tg_bgp_config(**kwargs): """ ret = {} - def_conf_var = { 'mode' : 'enable', - 'active_connect_enable' : '1' - } - def_route_var = { 'mode' : 'add', - } - def_ctrl_var = { 'mode' : 'start', - } + 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) @@ -861,39 +932,39 @@ def tg_bgp_config(**kwargs): # Copying default values to var. for k in def_conf_var.keys(): - conf_var[k] = conf_var.get(k,def_conf_var[k]) + 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]) + 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]) + ctrl_var[k] = ctrl_var.get(k, def_ctrl_var[k]) - bgp_conf={} + bgp_conf = {} bgp_route = [] - bgp_ctrl={} + bgp_ctrl = {} if 'conf_var' in kwargs: bgp_conf = tg.tg_emulation_bgp_config(handle=handle, **conf_var) st.log(bgp_conf) hand = bgp_conf['handle'] if 'route_var' in kwargs: - for i,var in enumerate(route_var): + for i, var in enumerate(route_var): bgp_route.append(tg.tg_emulation_bgp_route_config(handle=hand, **var)) st.log(bgp_route) if 'ctrl_var' in kwargs: - bgp_ctrl = tg.tg_emulation_bgp_control(handle=hand,**ctrl_var) + bgp_ctrl = tg.tg_emulation_bgp_control(handle=hand, **ctrl_var) st.log(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)) + st.log('BGP Return Status : ' + str(ret)) return ret -def tg_igmp_config(**kwargs): +def tg_igmp_config(**kwargs): """ Description: Configures the IGMP parameters on the already created host. @@ -939,18 +1010,18 @@ def tg_igmp_config(**kwargs): """ ret = {} - def_session_var = { 'mode' : 'create', - 'igmp_version' : 'v2' - } - def_group_var = { 'mode' : 'create', - 'active' : '1' - } + 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' } + def_igmp_group_var = {'mode': 'create'} _log_call("tg_igmp_config", **kwargs) @@ -979,18 +1050,18 @@ def tg_igmp_config(**kwargs): # Copying default values to var. for k in def_session_var.keys(): - session_var[k] = session_var.get(k,def_session_var[k]) + 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]) + 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]) + 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 ={} + igmp_config = {} if 'session_var' in kwargs: igmp_session = tg.tg_emulation_igmp_config(handle=handle, **session_var) @@ -1016,7 +1087,7 @@ def tg_igmp_config(**kwargs): ret['group'] = copy.deepcopy(igmp_group) ret['source'] = copy.deepcopy(igmp_source) ret['config'] = copy.deepcopy(igmp_config) - st.log('Return Status : '+str(ret)) + st.log('IGMP Return Status : ' + str(ret)) return ret @@ -1029,27 +1100,56 @@ def get_traffic_stats(tg_obj, **kwargs): :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 + Mode Aggregate: + for_tx_pkt = tgapi.get_traffic_stats(tg1, mode='aggregate', port_handle=tg_ph_1, direction='tx') + for_rx_pkt = tgapi.get_traffic_stats(tg2, mode='aggregate', port_handle=tg_ph_2, direction='rx') + + Mode Streams: + for_both_tx_rx = tgapi.get_traffic_stats(tg1, mode='streams', port_handle=tg_ph_1, direction='tx', stream_handle=stream_id) """ - stats = SpyTestDict() - stats.tx = SpyTestDict() - stats.rx = SpyTestDict() if "port_handle" not in kwargs: st.error("Please provide the port handler") return False + stats = SpyTestDict() + stats.tx = SpyTestDict() + stats.rx = SpyTestDict() port_handle = kwargs["port_handle"] - mode = kwargs["mode"] if "mode" in kwargs else "aggregate" + stream_handle = kwargs.get("stream_handle") + tgen_dict = dict() + tgen_dict.update({'stream_elem': stream_handle}) + tgen_dict.update({'scale_mode': kwargs.get("scale_mode", 0)}) + mode = kwargs.get("mode", "aggregate") + scapy_streams_support = bool(os.getenv('SPYTEST_SCAPY_STREAM_STATS', "0") != '0') + if mode == 'streams': + if tg_obj.tg_type == "ixia": + mode = 'traffic_item' + elif tg_obj.tg_type != "scapy": + pass + elif not scapy_streams_support: + mode = 'traffic_item' direction = kwargs.get('direction', 'rx') - stats_tg = _fetch_stats(tg_obj, port_handle, mode, 'packet_count', direction) - 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']) - st.log("*****TG STATS******") + stats_tg = _fetch_stats(tg_obj, port_handle, mode, 'packet_count', direction, **tgen_dict) + if mode == 'aggregate': + entry = stats_tg[port_handle][mode] + stats.tx.total_packets = cutils.integer_parse(entry['tx'].get('total_pkts', 0), 0) + stats.tx.total_bytes = cutils.integer_parse(entry['tx'].get('pkt_byte_count', 0), 0) + stats.rx.total_packets = cutils.integer_parse(entry['rx'].get('total_pkts', 0), 0) + stats.rx.total_bytes = cutils.integer_parse(entry['rx'].get('pkt_byte_count', 0), 0) + stats.rx.oversize_count = cutils.integer_parse(entry['rx'].get('oversize_count', 0), 0) + elif mode in ['streams', 'traffic_item']: + if tg_obj.tg_type == 'stc': + entry = stats_tg[port_handle]['stream'][stream_handle] + elif tg_obj.tg_type == "ixia": + entry = stats_tg['traffic_item'][stream_handle] + elif scapy_streams_support: + entry = stats_tg[port_handle]['stream'][stream_handle] + else: + entry = stats_tg['traffic_item'][stream_handle] + stats.tx.total_packets = cutils.integer_parse(entry['tx'].get('total_pkts', 0), 0) + stats.rx.total_packets = cutils.integer_parse(entry['rx'].get('total_pkts', 0), 0) + + st.banner("{} TG STATS PORT={} STREAM={}".format(mode, port_handle, stream_handle)) st.log(stats) - st.log("***********") return stats @@ -1062,4 +1162,3 @@ def port_traffic_control(action, *args, **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 index f0cb21322f..c0142838fc 100644 --- a/spytest/spytest/tgen_api.py +++ b/spytest/spytest/tgen_api.py @@ -78,8 +78,10 @@ def get_chassis_byname(name): def is_soft_tgen(vars=None): tg = get_chassis(vars) - if not tg: return False - if tg.tg_type == "scapy": return True + if not tg: + return False + if tg.tg_type == "scapy": + return True return tg.tg_virtual @@ -185,18 +187,21 @@ def get_max(v1, v2): def normalize_pps(value): from utilities.common import get_env_int - if not is_soft_tgen(): return value + if not is_soft_tgen(): + return value max_value = get_env_int("SPYTEST_SCAPY_MAX_RATE_PPS", 100) return get_min(int(value), max_value) def normalize_mtu(value): - if not is_soft_tgen(): return value + if not is_soft_tgen(): + return value return get_min(int(value), 9000) def normalize_hosts(value): - if not is_soft_tgen(): return value + if not is_soft_tgen(): + return value return get_min(int(value), 256)