orchestrator.py 5.36 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
#!/usr/bin/env python3

# Copyright (C) 2018 Simon Redman <sredman@cs.utah.edu>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# For future reference, here is the OSPFv6 RFC: https://tools.ietf.org/html/rfc2740.html

import add_routable_ipv6_addrs
import frr_configurator
22
import json
23
import ospf_sniffer_configurator
24
import ssh_helper
25
import sysctl_configurator
26 27 28 29 30
import topomap_parser

import argparse
import getpass
from netdiff import NetJsonParser
31
import re
32 33 34 35 36 37 38

if __name__ == "__main__":
    parser = argparse.ArgumentParser("Setup an Emulab experiment for Segment Routing")
    parser.add_argument("--topomap-file", action='store', default='/var/emulab/boot/topomap', type=str,
                        help="Path to the emulab topomap file to parse")
    parser.add_argument("--netgraph-file", action='store', type=str, required=False,
                        help="Path to the NetJSON file to parse. Skips parsing the netgrap from the topomap-file")
39 40
    parser.add_argument("--netgraph-write", action='store', type=str, required=False,
                        help="(Optional) Path to write the final NetJSON file")
41 42
    parser.add_argument("--username", action='store', type=str, default=getpass.getuser(),
                        help="Username to use on all hosts. Defaults to current user's username")
43
    parser.add_argument("--stop", action='store_true',
44
                        help="Stop all services (after writing config files)")
45 46 47 48
    parser.add_argument("--controller-name", action='store', type=str, default=ospf_sniffer_configurator.DEFAULT_CONTROLLER,
                        help="Hostname or IP of the node which is listening to the OSPF reports (Default: {default})".format(default=ospf_sniffer_configurator.DEFAULT_CONTROLLER))
    parser.add_argument("--controller-port", action='store', type=int, default=ospf_sniffer_configurator.DEFAULT_CONTROLLER_PORT,
                        help="Port number on the server listening for OSPF reports (Default: {default})".format(default=ospf_sniffer_configurator.DEFAULT_CONTROLLER_PORT))
49 50 51 52
    parser.add_argument("--ovs-regex", action='store', type=str, default='^ovs.*',
                        help="Regex to distinguish OVS nodes by label (Default \"^ovs.*\")")
    parser.add_argument("--host-regex", action='store', type=str, default='^host.*',
                        help="Regex to distinguish host nodes by label (Default \"^host.*\")")
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70

    args = parser.parse_args()

    if not args.netgraph_file:
        netgraph = NetJsonParser(data=topomap_parser.parse_topomap_to_netjson(args.topomap_file))
    else:
        netgraph = NetJsonParser(file=args.netgraph_file)

    ssh_helper.network_graph_login(netgraph.graph, args.username)

    # Skip adding ipv6 addresses to the management interfaces
    management_ips = [node["management-ip"] for node in netgraph.graph._node.values()]

    ULA_map = add_routable_ipv6_addrs.construct_ULAs(netgraph.graph, ignore_addrs=management_ips)

    add_routable_ipv6_addrs.add_ULAs_to_hosts(netgraph.graph, ULA_map)
    add_routable_ipv6_addrs.add_interfaces_to_netgraph(netgraph.graph, ULA_map)

71
    # Prepare a list of nodes which run OVS and should thus be ignore for router-related activites
72 73 74
    ovs_nodes = []
    for node in netgraph.graph.nodes:
        node_name = netgraph.graph._node[node]['label']
75
        if re.match(args.ovs_regex, node_name):
76
            ovs_nodes.append(node)
77

78
    # Prepare a list of nodes which are "customer host nodes" and should thus be ignored for core network-related activities
79 80 81 82 83 84 85 86
    host_nodes = []
    for node in netgraph.graph.nodes:
        node_name = netgraph.graph._node[node]['label']
        if re.match(args.host_regex, node_name):
            host_nodes.append(node)

    frr_configurator.configure_nodes(netgraph.graph, ignore_nodes=ovs_nodes + host_nodes)
    frr_configurator.start_frr_on_network(netgraph.graph, ignore_nodes=ovs_nodes + host_nodes)
87

88 89 90
    # Customer hosts would normally be DHCP clients, etc., but for now just give them a route
    add_routable_ipv6_addrs.add_default_routes(netgraph.graph, ULA_map, host_nodes)

91
    sysctl_configurator.configure_nodes(netgraph.graph,ignore_nodes=ovs_nodes)
92

93 94
    ospf_sniffer_configurator.clone_repo_on_network(netgraph.graph, ignore_nodes=ovs_nodes + host_nodes)
    ospf_sniffer_configurator.stop_sniffer_on_network(netgraph.graph, ignore_nodes=ovs_nodes + host_nodes) # Stopping with the app not running is not great, but better than starting twice
95 96 97 98
    ospf_sniffer_configurator.start_sniffer_on_network(netgraph.graph,
                                                       controller=args.controller_name,
                                                       port=args.controller_port,
                                                       ignore_nodes=ovs_nodes + host_nodes)
99

100
    ssh_helper.network_graph_logout(netgraph.graph)
101 102

    if args.netgraph_write:
103 104
        with open(args.netgraph_write, 'w') as outfile:
            json.dump(netgraph.json(dict=True), outfile)