def clear(remotes=default_remotes): check_access(remotes) for remote in remotes: exec(remote, 'ip -all netns delete || true') # removal of all l2tp tunnels - removes all sessions as well exec( remote, 'ip l2tp show tunnel | grep Tunnel | tr "," " " | cut -d" " -f2 | xargs -r -n1 ip l2tp del tunnel tunnel_id' )
def show(remotes=default_remotes): check_access(remotes) for remote_id, remote in enumerate(remotes): nodes = exec(remote, 'ip netns list', get_output=True)[0].count('ns-') veth = int( exec(remote, 'ip netns exec switch ip addr list | grep -c "@ve-" || true', get_output=True)[0]) // 2 l2tp = int( exec(remote, 'ip l2tp show session | grep -c "ve-" || true', get_output=True)[0]) label = remote.address or 'local' print(f'{label}: {nodes} nodes, {veth} veth links, {l2tp} l2tp links')
def apply(state={}, node_command=None, link_command=None, remotes=default_remotes): check_access(remotes) new_state = state (cur_state, cur_state_rmap) = get_current_state(remotes) # handle different new_state types if isinstance(new_state, str): if new_state == 'none': new_state = {} else: if not os.path.isfile(new_state): eprint(f'File not found: {new_state}') stop_all_terminals() exit(1) with open(new_state) as file: new_state = json.load(file) # map each node to a remote or local computer # distribute evenly with minimized interconnects rmap = _get_remote_mapping(cur_state, new_state, remotes, cur_state_rmap) data = _get_task(cur_state, new_state) beg_ms = millis() # add "switch" namespace if state_empty(cur_state): for remote in remotes: # add switch if it does not exist yet exec(remote, 'ip netns add "switch" || true') # disable IPv6 in switch namespace (no need, less overhead) exec( remote, 'ip netns exec "switch" sysctl -q -w net.ipv6.conf.all.disable_ipv6=1' ) for node in data.nodes_update: update_node(node, node_command, rmap) for link in data.links_update: update_link(link, link_command, rmap) for node in data.nodes_create: create_node(node, node_command, rmap) for link in data.links_create: create_link(link, link_command, rmap) for link in data.links_remove: remove_link(link, rmap) for node in data.nodes_remove: remove_node(node, rmap) # remove "switch" namespace if state_empty(new_state): for remote in remotes: exec(remote, 'ip netns del "switch" || true') # wait for tasks to complete wait_for_completion() end_ms = millis() if verbosity != 'quiet': print('Network setup in {}:'.format(format_duration(end_ms - beg_ms))) print( f' nodes: {len(data.nodes_create)} created, {len(data.nodes_remove)} removed, {len(data.nodes_update)} updated' ) print( f' links: {len(data.links_create)} created, {len(data.links_remove)} removed, {len(data.links_update)} updated' ) return new_state
#!/usr/bin/env python3 import os import sys import glob sys.path.append('../../') from shared import Remote import shared import ping import software import network remotes = [Remote()] #[Remote('192.168.44.133'), Remote('192.168.44.137')] shared.check_access(remotes) software.clear(remotes) network.clear(remotes) prefix = os.environ.get('PREFIX', '') # 100MBit LAN cable def get_tc_command(link, ifname): return f'tc qdisc replace dev "{ifname}" root tbf rate 100mbit burst 8192 latency 1ms' def run(protocol, csvfile): for path in sorted(glob.glob(f'../../data/grid4/*.json')): state = shared.load_json(path) (node_count, link_count) = shared.json_count(state)
def check_access(remotes): shared.check_access(remotes)
def main(): parser = argparse.ArgumentParser() parser.add_argument('--verbosity', choices=['verbose', 'normal', 'quiet'], default='normal', help='Set verbosity.') parser.add_argument( '--remotes', help='Distribute nodes and links on remotes described in the JSON file.' ) parser.add_argument('--ip-protocol', choices=['4', '6'], help='Use IPv4/IPv6 only.') parser.set_defaults(to_state=None) subparsers = parser.add_subparsers(dest='action', required=True, help='Action') parser_start = subparsers.add_parser( 'start', help='Start protocol daemons in every namespace.') parser_start.add_argument('protocol', choices=protocol_choices, help='Routing protocol to start.') parser_start.add_argument('to_state', nargs='?', default=None, help='To state') parser_stop = subparsers.add_parser( 'stop', help='Stop protocol daemons in every namespace.') parser_stop.add_argument('protocol', choices=protocol_choices, help='Routing protocol to stop.') parser_stop.add_argument('to_state', nargs='?', default=None, help='To state') parser_change = subparsers.add_parser( 'apply', help='Stop/Start protocol daemons in every namespace.') parser_change.add_argument('protocol', choices=protocol_choices, help='Routing protocol to change.') parser_change.add_argument('to_state', nargs='?', default=None, help='To state') parser_run = subparsers.add_parser( 'run', help='Execute any command in every namespace.') parser_run.add_argument( 'command', nargs=argparse.REMAINDER, help='Shell command that is run. {name} is replaced by the nodes name.' ) parser_run.add_argument('--quiet', action='store_true', help='Do not output stdout and stderr.') parser_run.add_argument('to_state', nargs='?', default=None, help='To state') parser_clear = subparsers.add_parser('clear', help='Stop all routing protocols.') args = parser.parse_args() global ip_protocol ip_protocol = args.ip_protocol if args.remotes: with open(args.remotes) as file: args.remotes = json.load(file) else: args.remotes = default_remotes check_access(args.remotes) global verbosity verbosity = args.verbosity # get nodes that have been added or will be removed (old_ids, new_ids, rmap) = _get_update(args.to_state, args.remotes) if args.action == 'start': if args.to_state: start_routing_protocol(args.protocol, rmap, new_ids) else: all = list(rmap.keys()) start_routing_protocol(args.protocol, rmap, all) elif args.action == 'stop': if args.to_state: stop_routing_protocol(args.protocol, rmap, old_ids) else: all = list(rmap.keys()) stop_routing_protocol(args.protocol, rmap, all) elif args.action == 'apply': stop_routing_protocol(args.protocol, rmap, old_ids) start_routing_protocol(args.protocol, rmap, new_ids) elif args.action == 'clear': clear(args.remotes) elif args.action == 'run': run(' '.join(args.command), rmap, args.quiet) else: eprint('Unknown action: {}'.format(args.action)) exit(1) stop_all_terminals()
def main(): parser = argparse.ArgumentParser() parser.add_argument('--verbosity', choices=['verbose', 'normal', 'quiet'], default='normal', help='Set verbosity.') parser.add_argument( '--remotes', help='Distribute nodes and links on remotes described in the JSON file.' ) parser.set_defaults(to_state=None) subparsers = parser.add_subparsers(dest='action', required=True, help='Action') parser_start = subparsers.add_parser( 'start', help='Run start script in every namespace.') parser_start.add_argument('protocol', help='Routing protocol script prefix.') parser_start.add_argument('to_state', nargs='?', default=None, help='To state') parser_stop = subparsers.add_parser( 'stop', help='Run stop script in every namespace.') parser_stop.add_argument('protocol', help='Routing protocol script prefix.') parser_stop.add_argument('to_state', nargs='?', default=None, help='To state') parser_change = subparsers.add_parser( 'apply', help='Run stop/start scripts in every namespace.') parser_change.add_argument('protocol', help='Routing protocol script prefix.') parser_change.add_argument('to_state', nargs='?', default=None, help='To state') parser_run = subparsers.add_parser( 'run', help='Execute any command in every namespace.') parser_run.add_argument( 'command', nargs=argparse.REMAINDER, help= 'Shell command that is run. Remote address and namespace id is added to call arguments.' ) parser_run.add_argument('to_state', nargs='?', default=None, help='To state') parser_copy = subparsers.add_parser('copy', help='Copy to all remotes.') parser_copy.add_argument('source', nargs='+') parser_copy.add_argument('destination') parser_clear = subparsers.add_parser( 'clear', help='Run all stop scripts in every namespaces.') args = parser.parse_args() if args.remotes: if not os.path.isfile(args.remotes): eprint(f'File not found: {args.remotes}') stop_all_terminals() exit(1) with open(args.remotes) as file: args.remotes = [Remote.from_json(obj) for obj in json.load(file)] else: args.remotes = default_remotes check_access(args.remotes) global verbosity verbosity = args.verbosity # get nodes that have been added or will be removed (old_ids, new_ids, rmap) = _get_update(args.to_state, args.remotes) if args.action == 'start': ids = new_ids if args.to_state else list(rmap.keys()) beg_ms = millis() _start_protocol(args.protocol, rmap, ids) end_ms = millis() if verbosity != 'quiet': print('started {} in {} namespaces in {}'.format( args.protocol, len(ids), format_duration(end_ms - beg_ms))) elif args.action == 'stop': ids = old_ids if args.to_state else list(rmap.keys()) beg_ms = millis() _stop_protocol(args.protocol, rmap, ids) end_ms = millis() if verbosity != 'quiet': print('stopped {} in {} namespaces in {}'.format( args.protocol, len(ids), format_duration(end_ms - beg_ms))) elif args.action == 'apply': beg_ms = millis() _stop_protocol(args.protocol, rmap, old_ids) _start_protocol(args.protocol, rmap, new_ids) end_ms = millis() if verbosity != 'quiet': print('applied {} in {} namespaces in {}'.format( args.protocol, len(rmap.keys()), format_duration(end_ms - beg_ms))) elif args.action == 'clear': beg_ms = millis() clear(args.remotes) end_ms = millis() if verbosity != 'quiet': print('cleared on {} remotes in {}'.format( len(args.remotes), format_duration(end_ms - beg_ms))) elif args.action == 'copy': beg_ms = millis() copy(args.remotes, args.source, args.destination) end_ms = millis() if verbosity != 'quiet': print('copied on {} remotes in {}'.format( len(args.remotes), format_duration(end_ms - beg_ms))) elif args.action == 'run': ids = new_ids if args.to_state else list(rmap.keys()) for id in ids: remote = rmap[id] label = remote.address or 'local' cmd = f'ip netns exec ns-{id} {" ".join(args.command)} {label} {id}' _exec_verbose(remote, cmd) else: eprint('Unknown action: {}'.format(args.action)) stop_all_terminals() exit(1) stop_all_terminals()