1a13fedbeSBrenda J. Butlerimport os 2a13fedbeSBrenda J. Butlerimport signal 3a13fedbeSBrenda J. Butlerfrom string import Template 4a13fedbeSBrenda J. Butlerimport subprocess 5a13fedbeSBrenda J. Butlerimport time 6ac9b8293SPedro Tammelafrom multiprocessing import Pool 798cfbe42SPedro Tammelafrom functools import cached_property 8a13fedbeSBrenda J. Butlerfrom TdcPlugin import TdcPlugin 9a13fedbeSBrenda J. Butler 10a13fedbeSBrenda J. Butlerfrom tdc_config import * 11a13fedbeSBrenda J. Butler 12fa63d353SPedro Tammelatry: 13fa63d353SPedro Tammela from pyroute2 import netns 14fa63d353SPedro Tammela from pyroute2 import IPRoute 15fa63d353SPedro Tammela netlink = True 16fa63d353SPedro Tammelaexcept ImportError: 17fa63d353SPedro Tammela netlink = False 18fa63d353SPedro Tammela print("!!! Consider installing pyroute2 !!!") 19fa63d353SPedro Tammela 20a13fedbeSBrenda J. Butlerclass SubPlugin(TdcPlugin): 21a13fedbeSBrenda J. Butler def __init__(self): 22a13fedbeSBrenda J. Butler self.sub_class = 'ns/SubPlugin' 23a13fedbeSBrenda J. Butler super().__init__() 24a13fedbeSBrenda J. Butler 2598cfbe42SPedro Tammela def pre_suite(self, testcount, testlist): 2698cfbe42SPedro Tammela super().pre_suite(testcount, testlist) 2798cfbe42SPedro Tammela 2850a5988aSPedro Tammela def prepare_test(self, test): 2950a5988aSPedro Tammela if 'skip' in test and test['skip'] == 'yes': 3050a5988aSPedro Tammela return 3198cfbe42SPedro Tammela 3250a5988aSPedro Tammela if 'nsPlugin' not in test['plugins']: 3350a5988aSPedro Tammela return 3498cfbe42SPedro Tammela 3550a5988aSPedro Tammela if netlink == True: 3650a5988aSPedro Tammela self._nl_ns_create() 3750a5988aSPedro Tammela else: 3856e16bc6SPedro Tammela self._ipr2_ns_create() 3950a5988aSPedro Tammela 4050a5988aSPedro Tammela # Make sure the netns is visible in the fs 414b480cfbSPedro Tammela ticks = 20 4250a5988aSPedro Tammela while True: 434b480cfbSPedro Tammela if ticks == 0: 444b480cfbSPedro Tammela raise TimeoutError 4550a5988aSPedro Tammela self._proc_check() 4650a5988aSPedro Tammela try: 4750a5988aSPedro Tammela ns = self.args.NAMES['NS'] 4850a5988aSPedro Tammela f = open('/run/netns/{}'.format(ns)) 4950a5988aSPedro Tammela f.close() 5050a5988aSPedro Tammela break 5150a5988aSPedro Tammela except: 5250a5988aSPedro Tammela time.sleep(0.1) 534b480cfbSPedro Tammela ticks -= 1 5450a5988aSPedro Tammela continue 5550a5988aSPedro Tammela 5650a5988aSPedro Tammela def pre_case(self, test, test_skip): 57a13fedbeSBrenda J. Butler if self.args.verbose: 5898cfbe42SPedro Tammela print('{}.pre_case'.format(self.sub_class)) 5998cfbe42SPedro Tammela 6098cfbe42SPedro Tammela if test_skip: 6198cfbe42SPedro Tammela return 6298cfbe42SPedro Tammela 6350a5988aSPedro Tammela self.prepare_test(test) 6450a5988aSPedro Tammela 6598cfbe42SPedro Tammela def post_case(self): 6698cfbe42SPedro Tammela if self.args.verbose: 6798cfbe42SPedro Tammela print('{}.post_case'.format(self.sub_class)) 68a13fedbeSBrenda J. Butler 693d5026fcSPedro Tammela if netlink == True: 703d5026fcSPedro Tammela self._nl_ns_destroy() 713d5026fcSPedro Tammela else: 7256e16bc6SPedro Tammela self._ipr2_ns_destroy() 73a13fedbeSBrenda J. Butler 7498cfbe42SPedro Tammela def post_suite(self, index): 7598cfbe42SPedro Tammela if self.args.verbose: 7698cfbe42SPedro Tammela print('{}.post_suite'.format(self.sub_class)) 7798cfbe42SPedro Tammela 7898cfbe42SPedro Tammela # Make sure we don't leak resources 79*501679f5SPedro Tammela cmd = self._replace_keywords("$IP -a netns del") 8098cfbe42SPedro Tammela 8198cfbe42SPedro Tammela if self.args.verbose > 3: 8298cfbe42SPedro Tammela print('_exec_cmd: command "{}"'.format(cmd)) 8398cfbe42SPedro Tammela 8498cfbe42SPedro Tammela subprocess.run(cmd, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) 8598cfbe42SPedro Tammela 86a13fedbeSBrenda J. Butler def adjust_command(self, stage, command): 87a13fedbeSBrenda J. Butler super().adjust_command(stage, command) 88a13fedbeSBrenda J. Butler cmdform = 'list' 89a13fedbeSBrenda J. Butler cmdlist = list() 90a13fedbeSBrenda J. Butler 91a13fedbeSBrenda J. Butler if self.args.verbose: 92a13fedbeSBrenda J. Butler print('{}.adjust_command'.format(self.sub_class)) 93a13fedbeSBrenda J. Butler 94a13fedbeSBrenda J. Butler if not isinstance(command, list): 95a13fedbeSBrenda J. Butler cmdform = 'str' 96a13fedbeSBrenda J. Butler cmdlist = command.split() 97a13fedbeSBrenda J. Butler else: 98a13fedbeSBrenda J. Butler cmdlist = command 99a13fedbeSBrenda J. Butler if stage == 'setup' or stage == 'execute' or stage == 'verify' or stage == 'teardown': 100a13fedbeSBrenda J. Butler if self.args.verbose: 101a13fedbeSBrenda J. Butler print('adjust_command: stage is {}; inserting netns stuff in command [{}] list [{}]'.format(stage, command, cmdlist)) 102a13fedbeSBrenda J. Butler cmdlist.insert(0, self.args.NAMES['NS']) 103a13fedbeSBrenda J. Butler cmdlist.insert(0, 'exec') 104a13fedbeSBrenda J. Butler cmdlist.insert(0, 'netns') 10502a3f0d5SDavide Caratti cmdlist.insert(0, self.args.NAMES['IP']) 106a13fedbeSBrenda J. Butler else: 107a13fedbeSBrenda J. Butler pass 108a13fedbeSBrenda J. Butler 109a13fedbeSBrenda J. Butler if cmdform == 'str': 110a13fedbeSBrenda J. Butler command = ' '.join(cmdlist) 111a13fedbeSBrenda J. Butler else: 112a13fedbeSBrenda J. Butler command = cmdlist 113a13fedbeSBrenda J. Butler 114a13fedbeSBrenda J. Butler if self.args.verbose: 115a13fedbeSBrenda J. Butler print('adjust_command: return command [{}]'.format(command)) 116a13fedbeSBrenda J. Butler return command 117a13fedbeSBrenda J. Butler 118fa63d353SPedro Tammela def _nl_ns_create(self): 119fa63d353SPedro Tammela ns = self.args.NAMES["NS"]; 120fa63d353SPedro Tammela dev0 = self.args.NAMES["DEV0"]; 121fa63d353SPedro Tammela dev1 = self.args.NAMES["DEV1"]; 122fa63d353SPedro Tammela dummy = self.args.NAMES["DUMMY"]; 12398cfbe42SPedro Tammela 124fa63d353SPedro Tammela if self.args.verbose: 125fa63d353SPedro Tammela print('{}._nl_ns_create'.format(self.sub_class)) 12698cfbe42SPedro Tammela 127fa63d353SPedro Tammela netns.create(ns) 128fa63d353SPedro Tammela netns.pushns(newns=ns) 129fa63d353SPedro Tammela with IPRoute() as ip: 130fa63d353SPedro Tammela ip.link('add', ifname=dev1, kind='veth', peer={'ifname': dev0, 'net_ns_fd':'/proc/1/ns/net'}) 131fa63d353SPedro Tammela ip.link('add', ifname=dummy, kind='dummy') 1324b480cfbSPedro Tammela ticks = 20 133fa63d353SPedro Tammela while True: 1344b480cfbSPedro Tammela if ticks == 0: 1354b480cfbSPedro Tammela raise TimeoutError 136fa63d353SPedro Tammela try: 137fa63d353SPedro Tammela dev1_idx = ip.link_lookup(ifname=dev1)[0] 138fa63d353SPedro Tammela dummy_idx = ip.link_lookup(ifname=dummy)[0] 139fa63d353SPedro Tammela ip.link('set', index=dev1_idx, state='up') 140fa63d353SPedro Tammela ip.link('set', index=dummy_idx, state='up') 141fa63d353SPedro Tammela break 142fa63d353SPedro Tammela except: 143fa63d353SPedro Tammela time.sleep(0.1) 1444b480cfbSPedro Tammela ticks -= 1 145fa63d353SPedro Tammela continue 146fa63d353SPedro Tammela netns.popns() 14798cfbe42SPedro Tammela 148fa63d353SPedro Tammela with IPRoute() as ip: 1494b480cfbSPedro Tammela ticks = 20 150fa63d353SPedro Tammela while True: 1514b480cfbSPedro Tammela if ticks == 0: 1524b480cfbSPedro Tammela raise TimeoutError 153fa63d353SPedro Tammela try: 154fa63d353SPedro Tammela dev0_idx = ip.link_lookup(ifname=dev0)[0] 155fa63d353SPedro Tammela ip.link('set', index=dev0_idx, state='up') 156fa63d353SPedro Tammela break 157fa63d353SPedro Tammela except: 158fa63d353SPedro Tammela time.sleep(0.1) 1594b480cfbSPedro Tammela ticks -= 1 160fa63d353SPedro Tammela continue 16198cfbe42SPedro Tammela 16256e16bc6SPedro Tammela def _ipr2_ns_create_cmds(self): 16398cfbe42SPedro Tammela cmds = [] 16498cfbe42SPedro Tammela 16598cfbe42SPedro Tammela ns = self.args.NAMES['NS'] 16698cfbe42SPedro Tammela 16798cfbe42SPedro Tammela cmds.append(self._replace_keywords('netns add {}'.format(ns))) 168fa63d353SPedro Tammela cmds.append(self._replace_keywords('link add $DEV1 type veth peer name $DEV0')) 16998cfbe42SPedro Tammela cmds.append(self._replace_keywords('link set $DEV1 netns {}'.format(ns))) 170fa63d353SPedro Tammela cmds.append(self._replace_keywords('link add $DUMMY type dummy'.format(ns))) 17198cfbe42SPedro Tammela cmds.append(self._replace_keywords('link set $DUMMY netns {}'.format(ns))) 17298cfbe42SPedro Tammela cmds.append(self._replace_keywords('netns exec {} $IP link set $DEV1 up'.format(ns))) 17398cfbe42SPedro Tammela cmds.append(self._replace_keywords('netns exec {} $IP link set $DUMMY up'.format(ns))) 174fa63d353SPedro Tammela cmds.append(self._replace_keywords('link set $DEV0 up'.format(ns))) 17598cfbe42SPedro Tammela 17698cfbe42SPedro Tammela if self.args.device: 17798cfbe42SPedro Tammela cmds.append(self._replace_keywords('link set $DEV2 netns {}'.format(ns))) 17898cfbe42SPedro Tammela cmds.append(self._replace_keywords('netns exec {} $IP link set $DEV2 up'.format(ns))) 17998cfbe42SPedro Tammela 18098cfbe42SPedro Tammela return cmds 181489ce2f4SLucas Bates 18256e16bc6SPedro Tammela def _ipr2_ns_create(self): 183a13fedbeSBrenda J. Butler ''' 184a13fedbeSBrenda J. Butler Create the network namespace in which the tests will be run and set up 185a13fedbeSBrenda J. Butler the required network devices for it. 186a13fedbeSBrenda J. Butler ''' 18756e16bc6SPedro Tammela self._exec_cmd_batched('pre', self._ipr2_ns_create_cmds()) 18898cfbe42SPedro Tammela 1893d5026fcSPedro Tammela def _nl_ns_destroy(self): 1903d5026fcSPedro Tammela ns = self.args.NAMES['NS'] 1913d5026fcSPedro Tammela netns.remove(ns) 1923d5026fcSPedro Tammela 19356e16bc6SPedro Tammela def _ipr2_ns_destroy_cmd(self): 19498cfbe42SPedro Tammela return self._replace_keywords('netns delete {}'.format(self.args.NAMES['NS'])) 195a13fedbeSBrenda J. Butler 19656e16bc6SPedro Tammela def _ipr2_ns_destroy(self): 197a13fedbeSBrenda J. Butler ''' 198a13fedbeSBrenda J. Butler Destroy the network namespace for testing (and any associated network 199a13fedbeSBrenda J. Butler devices as well) 200a13fedbeSBrenda J. Butler ''' 20156e16bc6SPedro Tammela self._exec_cmd('post', self._ipr2_ns_destroy_cmd()) 20298cfbe42SPedro Tammela 20398cfbe42SPedro Tammela @cached_property 20498cfbe42SPedro Tammela def _proc(self): 20598cfbe42SPedro Tammela ip = self._replace_keywords("$IP -b -") 20698cfbe42SPedro Tammela proc = subprocess.Popen(ip, 20798cfbe42SPedro Tammela shell=True, 20898cfbe42SPedro Tammela stdin=subprocess.PIPE, 20998cfbe42SPedro Tammela env=ENVIR) 21098cfbe42SPedro Tammela 21198cfbe42SPedro Tammela return proc 21298cfbe42SPedro Tammela 21398cfbe42SPedro Tammela def _proc_check(self): 21498cfbe42SPedro Tammela proc = self._proc 21598cfbe42SPedro Tammela 21698cfbe42SPedro Tammela proc.poll() 21798cfbe42SPedro Tammela 21898cfbe42SPedro Tammela if proc.returncode is not None and proc.returncode != 0: 21998cfbe42SPedro Tammela raise RuntimeError("iproute2 exited with an error code") 220a13fedbeSBrenda J. Butler 221a13fedbeSBrenda J. Butler def _exec_cmd(self, stage, command): 222a13fedbeSBrenda J. Butler ''' 223a13fedbeSBrenda J. Butler Perform any required modifications on an executable command, then run 224a13fedbeSBrenda J. Butler it in a subprocess and return the results. 225a13fedbeSBrenda J. Butler ''' 226a13fedbeSBrenda J. Butler 22798cfbe42SPedro Tammela if self.args.verbose > 3: 228a13fedbeSBrenda J. Butler print('_exec_cmd: command "{}"'.format(command)) 229a13fedbeSBrenda J. Butler 23098cfbe42SPedro Tammela proc = self._proc 231a13fedbeSBrenda J. Butler 23298cfbe42SPedro Tammela proc.stdin.write((command + '\n').encode()) 23398cfbe42SPedro Tammela proc.stdin.flush() 23498cfbe42SPedro Tammela 23598cfbe42SPedro Tammela if self.args.verbose > 3: 23698cfbe42SPedro Tammela print('_exec_cmd proc: {}'.format(proc)) 23798cfbe42SPedro Tammela 23898cfbe42SPedro Tammela self._proc_check() 23998cfbe42SPedro Tammela 24098cfbe42SPedro Tammela def _exec_cmd_batched(self, stage, commands): 24198cfbe42SPedro Tammela for cmd in commands: 24298cfbe42SPedro Tammela self._exec_cmd(stage, cmd) 243a13fedbeSBrenda J. Butler 244a13fedbeSBrenda J. Butler def _replace_keywords(self, cmd): 245a13fedbeSBrenda J. Butler """ 246a13fedbeSBrenda J. Butler For a given executable command, substitute any known 247a13fedbeSBrenda J. Butler variables contained within NAMES with the correct values 248a13fedbeSBrenda J. Butler """ 249a13fedbeSBrenda J. Butler tcmd = Template(cmd) 250a13fedbeSBrenda J. Butler subcmd = tcmd.safe_substitute(self.args.NAMES) 251a13fedbeSBrenda J. Butler return subcmd 252