1823b8294SYaroslav Brustinov#!/router/bin/python
289a2be82Simaromimport sys
389a2be82Simaromif sys.version_info >= (3, 0):
489a2be82Simarom    import configparser
589a2be82Simaromelse:
689a2be82Simarom    import ConfigParser
7823b8294SYaroslav Brustinov
8823b8294SYaroslav Brustinovimport outer_packages
9823b8294SYaroslav Brustinovimport yaml
10823b8294SYaroslav Brustinovfrom collections import namedtuple
11823b8294SYaroslav Brustinovimport subprocess, shlex
12823b8294SYaroslav Brustinovimport os
13823b8294SYaroslav Brustinov
14823b8294SYaroslav BrustinovTRexConfig = namedtuple('TRexConfig', 'trex, router, tftp')
15823b8294SYaroslav Brustinov
16823b8294SYaroslav Brustinov# debug/development purpose, lists object's attributes and their values
17823b8294SYaroslav Brustinovdef print_r(obj):
18823b8294SYaroslav Brustinov    for attr in dir(obj):
1989a2be82Simarom        print('obj.%s %s' % (attr, getattr(obj, attr)))
20823b8294SYaroslav Brustinov
21823b8294SYaroslav Brustinovdef mix_string (str):
22823b8294SYaroslav Brustinov    """Convert all string to lowercase letters, and replaces spaces with '_' char"""
23823b8294SYaroslav Brustinov    return str.replace(' ', '_').lower()
24823b8294SYaroslav Brustinov
25823b8294SYaroslav Brustinov# executes given command, returns tuple (return_code, stdout, stderr)
263f747bcfSYaroslav Brustinovdef run_command(cmd, background = False):
273f747bcfSYaroslav Brustinov    if background:
283804a5ddSYaroslav Brustinov        print('Running command in background: %s' % cmd)
293f747bcfSYaroslav Brustinov        with open(os.devnull, 'w') as tempf:
303f747bcfSYaroslav Brustinov            subprocess.Popen(shlex.split(cmd), stdin=tempf, stdout=tempf, stderr=tempf)
313f747bcfSYaroslav Brustinov        return (None,)*3
323f747bcfSYaroslav Brustinov    else:
333804a5ddSYaroslav Brustinov        print('Running command: %s' % cmd)
343f747bcfSYaroslav Brustinov        proc = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
353f747bcfSYaroslav Brustinov        (stdout, stderr) = proc.communicate()
36f91190c7SYaroslav Brustinov        stdout = stdout.decode()
37f91190c7SYaroslav Brustinov        stderr = stderr.decode()
383f747bcfSYaroslav Brustinov        if stdout:
3989a2be82Simarom            print('Stdout:\n%s' % stdout)
403f747bcfSYaroslav Brustinov        if proc.returncode:
413f747bcfSYaroslav Brustinov            if stderr:
4289a2be82Simarom                print('Stderr:\n%s' % stderr)
4389a2be82Simarom            print('Return code: %s' % proc.returncode)
443f747bcfSYaroslav Brustinov        return (proc.returncode, stdout, stderr)
453f747bcfSYaroslav Brustinov
463f747bcfSYaroslav Brustinov
471f749d9bSYaroslav Brustinovdef run_remote_command(host, command_string, background = False, timeout = 20):
481f749d9bSYaroslav Brustinov    cmd = 'ssh -tt %s \'sudo%s sh -ec "%s"\'' % (host, (' timeout %s' % timeout) if (timeout and not background) else '', command_string)
493f747bcfSYaroslav Brustinov    return run_command(cmd, background)
50823b8294SYaroslav Brustinov
51823b8294SYaroslav Brustinov
52823b8294SYaroslav Brustinovdef generate_intf_lists (interfacesList):
53823b8294SYaroslav Brustinov    retDict = {
54823b8294SYaroslav Brustinov        'relevant_intf'       : [],
55823b8294SYaroslav Brustinov        'relevant_ip_addr'    : [],
56823b8294SYaroslav Brustinov        'relevant_mac_addr'   : [],
57823b8294SYaroslav Brustinov        'total_pairs'         : None
58823b8294SYaroslav Brustinov        }
59823b8294SYaroslav Brustinov
60823b8294SYaroslav Brustinov    for intf in interfacesList:
61823b8294SYaroslav Brustinov        retDict['relevant_intf'].append(intf['client'])
62823b8294SYaroslav Brustinov        retDict['relevant_ip_addr'].append(intf['client_config']['ip_addr'])
63823b8294SYaroslav Brustinov        retDict['relevant_mac_addr'].append(intf['client_config']['mac_addr'])
64823b8294SYaroslav Brustinov        retDict['relevant_intf'].append(intf['server'])
65823b8294SYaroslav Brustinov        retDict['relevant_ip_addr'].append(intf['server_config']['ip_addr'])
66823b8294SYaroslav Brustinov        retDict['relevant_mac_addr'].append(intf['server_config']['mac_addr'])
67823b8294SYaroslav Brustinov
68823b8294SYaroslav Brustinov    retDict['total_pairs'] = len(interfacesList)
69823b8294SYaroslav Brustinov
70823b8294SYaroslav Brustinov    return retDict
71823b8294SYaroslav Brustinov
72823b8294SYaroslav Brustinovdef get_single_net_client_addr (ip_addr, octetListDict = {'3' : 1}, ip_type = 'ipv4'):
73823b8294SYaroslav Brustinov    """ get_single_net_client_addr(ip_addr, octetListDict, ip_type) -> str
74823b8294SYaroslav Brustinov
75823b8294SYaroslav Brustinov        Parameters
76823b8294SYaroslav Brustinov        ----------
77823b8294SYaroslav Brustinov        ip_addr : str
78823b8294SYaroslav Brustinov            a string an IP address (by default, of type A.B.C.D)
79823b8294SYaroslav Brustinov        octetListDict : dict
80823b8294SYaroslav Brustinov            a ditionary representing the octets on which to act such that ip[octet_key] = ip[octet_key] + octet_value
81823b8294SYaroslav Brustinov        ip_type : str
82823b8294SYaroslav Brustinov            a string that defines the ip type to parse. possible inputs are 'ipv4', 'ipv6'
83823b8294SYaroslav Brustinov
84823b8294SYaroslav Brustinov        By default- Returns a new ip address - A.B.C.(D+1)
85823b8294SYaroslav Brustinov    """
86823b8294SYaroslav Brustinov    if ip_type == 'ipv4':
87823b8294SYaroslav Brustinov        ip_lst = ip_addr.split('.')
88823b8294SYaroslav Brustinov
8989a2be82Simarom        for octet,increment in octetListDict.items():
90823b8294SYaroslav Brustinov            int_octet = int(octet)
91823b8294SYaroslav Brustinov            if ((int_octet < 0) or (int_octet > 3)):
92823b8294SYaroslav Brustinov                raise ValueError('the provided octet is not legal in {0} format'.format(ip_type) )
93823b8294SYaroslav Brustinov            else:
94823b8294SYaroslav Brustinov                if (int(ip_lst[int_octet]) + increment) < 255:
95823b8294SYaroslav Brustinov                    ip_lst[int_octet] = str(int(ip_lst[int_octet]) + increment)
96823b8294SYaroslav Brustinov                else:
97823b8294SYaroslav Brustinov                    raise ValueError('the requested increment exceeds 255 client address limit')
98823b8294SYaroslav Brustinov
99823b8294SYaroslav Brustinov        return '.'.join(ip_lst)
100823b8294SYaroslav Brustinov
101823b8294SYaroslav Brustinov    else: # this is a ipv6 address, handle accordingly
102823b8294SYaroslav Brustinov        ip_lst = ip_addr.split(':')
103823b8294SYaroslav Brustinov
10489a2be82Simarom        for octet,increment in octetListDict.items():
105823b8294SYaroslav Brustinov            int_octet = int(octet)
106823b8294SYaroslav Brustinov            if ((int_octet < 0) or (int_octet > 7)):
107823b8294SYaroslav Brustinov                raise ValueError('the provided octet is not legal in {0} format'.format(ip_type) )
108823b8294SYaroslav Brustinov            else:
109823b8294SYaroslav Brustinov                if (int(ip_lst[int_octet]) + increment) < 65535:
110823b8294SYaroslav Brustinov                    ip_lst[int_octet] = format( int(ip_lst[int_octet], 16) + increment, 'X')
111823b8294SYaroslav Brustinov                else:
112823b8294SYaroslav Brustinov                    raise ValueError('the requested increment exceeds 65535 client address limit')
113823b8294SYaroslav Brustinov
114823b8294SYaroslav Brustinov        return ':'.join(ip_lst)
115823b8294SYaroslav Brustinov
116823b8294SYaroslav Brustinov
117823b8294SYaroslav Brustinovdef load_complete_config_file (filepath):
118823b8294SYaroslav Brustinov    """load_complete_config_file(filepath) -> list
119823b8294SYaroslav Brustinov
120823b8294SYaroslav Brustinov    Loads a configuration file (.yaml) for both trex config and router config
121823b8294SYaroslav Brustinov    Returns a list with a dictionary to each of the configurations
122823b8294SYaroslav Brustinov    """
123823b8294SYaroslav Brustinov
124823b8294SYaroslav Brustinov    # create response dictionaries
125823b8294SYaroslav Brustinov    trex_config = {}
126823b8294SYaroslav Brustinov    rtr_config  = {}
127823b8294SYaroslav Brustinov    tftp_config = {}
128823b8294SYaroslav Brustinov
129823b8294SYaroslav Brustinov    try:
130823b8294SYaroslav Brustinov        with open(filepath, 'r') as f:
13179005906SYaroslav Brustinov            config = yaml.safe_load(f)
132dbff547fSYaroslav Brustinov
133d279c8c1SYaroslav Brustinov            # Handle TRex configuration
134823b8294SYaroslav Brustinov            trex_config['trex_name']         = config["trex"]["hostname"]
135823b8294SYaroslav Brustinov            trex_config['trex_cores']        = int(config["trex"]["cores"])
136823b8294SYaroslav Brustinov            trex_config['modes']          = config['trex'].get('modes', [])
137dbff547fSYaroslav Brustinov            for key, val in config['trex'].items():
138dbff547fSYaroslav Brustinov                trex_config[key] = val
139823b8294SYaroslav Brustinov
140823b8294SYaroslav Brustinov            if 'loopback' not in trex_config['modes']:
141823b8294SYaroslav Brustinov                trex_config['router_interface']  = config["router"]["ip_address"]
142823b8294SYaroslav Brustinov
143823b8294SYaroslav Brustinov                # Handle Router configuration
144823b8294SYaroslav Brustinov                rtr_config['model']              = config["router"]["model"]
145823b8294SYaroslav Brustinov                rtr_config['hostname']           = config["router"]["hostname"]
146823b8294SYaroslav Brustinov                rtr_config['ip_address']         = config["router"]["ip_address"]
147823b8294SYaroslav Brustinov                rtr_config['image']              = config["router"]["image"]
148823b8294SYaroslav Brustinov                rtr_config['line_pswd']          = config["router"]["line_password"]
149823b8294SYaroslav Brustinov                rtr_config['en_pswd']            = config["router"]["en_password"]
150823b8294SYaroslav Brustinov                rtr_config['interfaces']         = config["router"]["interfaces"]
151823b8294SYaroslav Brustinov                rtr_config['clean_config']       = config["router"]["clean_config"]
152823b8294SYaroslav Brustinov                rtr_config['intf_masking']       = config["router"]["intf_masking"]
153823b8294SYaroslav Brustinov                rtr_config['ipv6_mask']          = config["router"]["ipv6_mask"]
154823b8294SYaroslav Brustinov                rtr_config['mgmt_interface']     = config["router"]["mgmt_interface"]
155823b8294SYaroslav Brustinov
156823b8294SYaroslav Brustinov                # Handle TFTP configuration
157823b8294SYaroslav Brustinov                tftp_config['hostname']          = config["tftp"]["hostname"]
158823b8294SYaroslav Brustinov                tftp_config['ip_address']        = config["tftp"]["ip_address"]
159823b8294SYaroslav Brustinov                tftp_config['images_path']       = config["tftp"]["images_path"]
160823b8294SYaroslav Brustinov
161823b8294SYaroslav Brustinov                if rtr_config['clean_config'] is None:
162823b8294SYaroslav Brustinov                    raise ValueError('A clean router configuration wasn`t provided.')
163823b8294SYaroslav Brustinov
164823b8294SYaroslav Brustinov    except ValueError:
16589a2be82Simarom        print("")
166823b8294SYaroslav Brustinov        raise
167823b8294SYaroslav Brustinov
168823b8294SYaroslav Brustinov    except Exception as inst:
16989a2be82Simarom        print("\nBad configuration file provided: '{0}'\n".format(filepath))
170823b8294SYaroslav Brustinov        raise inst
171823b8294SYaroslav Brustinov
172823b8294SYaroslav Brustinov    return TRexConfig(trex_config, rtr_config, tftp_config)
173823b8294SYaroslav Brustinov
174823b8294SYaroslav Brustinovdef load_object_config_file (filepath):
175823b8294SYaroslav Brustinov    try:
176823b8294SYaroslav Brustinov        with open(filepath, 'r') as f:
17779005906SYaroslav Brustinov            config = yaml.safe_load(f)
178823b8294SYaroslav Brustinov            return config
179823b8294SYaroslav Brustinov    except Exception as inst:
18089a2be82Simarom        print("\nBad configuration file provided: '{0}'\n".format(filepath))
18189a2be82Simarom        print(inst)
182823b8294SYaroslav Brustinov        exit(-1)
183823b8294SYaroslav Brustinov
184823b8294SYaroslav Brustinov
185823b8294SYaroslav Brustinovdef query_yes_no(question, default="yes"):
186823b8294SYaroslav Brustinov    """Ask a yes/no question via raw_input() and return their answer.
187823b8294SYaroslav Brustinov
188823b8294SYaroslav Brustinov    "question" is a string that is presented to the user.
189823b8294SYaroslav Brustinov    "default" is the presumed answer if the user just hits <Enter>.
190823b8294SYaroslav Brustinov        It must be "yes" (the default), "no" or None (meaning
191823b8294SYaroslav Brustinov        an answer is required of the user).
192823b8294SYaroslav Brustinov
193823b8294SYaroslav Brustinov    The "answer" return value is True for "yes" or False for "no".
194823b8294SYaroslav Brustinov    """
195823b8294SYaroslav Brustinov    valid = { "yes": True, "y": True, "ye": True,
196823b8294SYaroslav Brustinov              "no": False, "n": False }
197823b8294SYaroslav Brustinov    if default is None:
198823b8294SYaroslav Brustinov        prompt = " [y/n] "
199823b8294SYaroslav Brustinov    elif default == "yes":
200823b8294SYaroslav Brustinov        prompt = " [Y/n] "
201823b8294SYaroslav Brustinov    elif default == "no":
202823b8294SYaroslav Brustinov        prompt = " [y/N] "
203823b8294SYaroslav Brustinov    else:
204823b8294SYaroslav Brustinov        raise ValueError("invalid default answer: '%s'" % default)
205823b8294SYaroslav Brustinov
206823b8294SYaroslav Brustinov    while True:
207823b8294SYaroslav Brustinov        sys.stdout.write(question + prompt)
20889a2be82Simarom        choice = input().lower()
209823b8294SYaroslav Brustinov        if default is not None and choice == '':
210823b8294SYaroslav Brustinov            return valid[default]
211823b8294SYaroslav Brustinov        elif choice in valid:
212823b8294SYaroslav Brustinov            return valid[choice]
213823b8294SYaroslav Brustinov        else:
214823b8294SYaroslav Brustinov            sys.stdout.write("Please respond with 'yes' or 'no' "
215823b8294SYaroslav Brustinov                             "(or 'y' or 'n').\n")
216823b8294SYaroslav Brustinov
217823b8294SYaroslav Brustinov
218823b8294SYaroslav Brustinovdef load_benchmark_config_file (filepath):
219823b8294SYaroslav Brustinov    """load_benchmark_config_file(filepath) -> list
220823b8294SYaroslav Brustinov
221823b8294SYaroslav Brustinov    Loads a configuration file (.yaml) for both trex config and router config
222823b8294SYaroslav Brustinov    Returns a list with a dictionary to each of the configurations
223823b8294SYaroslav Brustinov    """
224823b8294SYaroslav Brustinov
225823b8294SYaroslav Brustinov    # create response dictionary
226dbff547fSYaroslav Brustinov    benchmark_config = {}
227823b8294SYaroslav Brustinov
228823b8294SYaroslav Brustinov    try:
229dbff547fSYaroslav Brustinov        with open(filepath) as f:
23079005906SYaroslav Brustinov            benchmark_config = yaml.safe_load(f)
231823b8294SYaroslav Brustinov
232823b8294SYaroslav Brustinov    except Exception as inst:
23389a2be82Simarom        print("\nBad configuration file provided: '{0}'\n".format(filepath))
23489a2be82Simarom        print(inst)
235823b8294SYaroslav Brustinov        exit(-1)
236823b8294SYaroslav Brustinov
237823b8294SYaroslav Brustinov    return benchmark_config
238823b8294SYaroslav Brustinov
239823b8294SYaroslav Brustinov
240823b8294SYaroslav Brustinovdef get_benchmark_param (benchmark_path, test_name, param, sub_param = None):
241823b8294SYaroslav Brustinov
242823b8294SYaroslav Brustinov    config = load_benchmark_config_file(benchmark_path)
243823b8294SYaroslav Brustinov    if sub_param is None:
244823b8294SYaroslav Brustinov        return config[test_name][param]
245823b8294SYaroslav Brustinov    else:
246823b8294SYaroslav Brustinov        return config[test_name][param][sub_param]
247823b8294SYaroslav Brustinov
248823b8294SYaroslav Brustinovdef gen_increment_dict (dual_port_mask):
249823b8294SYaroslav Brustinov    addr_lst    = dual_port_mask.split('.')
250823b8294SYaroslav Brustinov    result      = {}
251823b8294SYaroslav Brustinov    for idx, octet_increment in enumerate(addr_lst):
252823b8294SYaroslav Brustinov        octet_int = int(octet_increment)
253823b8294SYaroslav Brustinov        if octet_int>0:
254823b8294SYaroslav Brustinov            result[str(idx)] = octet_int
255823b8294SYaroslav Brustinov
256823b8294SYaroslav Brustinov    return result
257823b8294SYaroslav Brustinov
258823b8294SYaroslav Brustinov
259823b8294SYaroslav Brustinovdef get_network_addr (ip_type = 'ipv4'):
260823b8294SYaroslav Brustinov    ipv4_addr = [1, 1, 1, 0]  # base ipv4 address to start generating from- 1.1.1.0
261823b8294SYaroslav Brustinov    ipv6_addr = ['2001', 'DB8', 0, '2222', 0, 0, 0, 0]  # base ipv6 address to start generating from- 2001:DB8:1111:2222:0:0
262823b8294SYaroslav Brustinov    while True:
263823b8294SYaroslav Brustinov        if ip_type == 'ipv4':
264823b8294SYaroslav Brustinov            if (ipv4_addr[2] < 255):
265823b8294SYaroslav Brustinov                yield [".".join( map(str, ipv4_addr) ), '255.255.255.0']
266823b8294SYaroslav Brustinov                ipv4_addr[2] += 1
267823b8294SYaroslav Brustinov            else:   # reached defined maximum limit of address allocation
268823b8294SYaroslav Brustinov                return
269823b8294SYaroslav Brustinov        else:   # handling ipv6 addressing
270823b8294SYaroslav Brustinov            if (ipv6_addr[2] < 4369):
271823b8294SYaroslav Brustinov                tmp_ipv6_addr = list(ipv6_addr)
272823b8294SYaroslav Brustinov                tmp_ipv6_addr[2] = hex(tmp_ipv6_addr[2])[2:]
273823b8294SYaroslav Brustinov                yield ":".join( map(str, tmp_ipv6_addr) )
274823b8294SYaroslav Brustinov                ipv6_addr[2] += 1
275823b8294SYaroslav Brustinov            else:   # reached defined maximum limit of address allocation
276823b8294SYaroslav Brustinov                return
277823b8294SYaroslav Brustinov
278823b8294SYaroslav Brustinov
279823b8294SYaroslav Brustinov
280823b8294SYaroslav Brustinov
281823b8294SYaroslav Brustinovif __name__ == "__main__":
282823b8294SYaroslav Brustinov    pass
283