Example #1
0
class MIXTT():
    """ Mininet IXP topology tester. 
    
        Takes json with topology information and builds a network based on this.
        It will check the reachability of all hosts as well as redundancy is 
        working by turning off each link between switches and validates that 
        hosts are still able to communicate. """
    def __init__(self):
        self.net = None
        self.network_matrix = None
        self.hosts_matrix = None
        self.link_matrix = None
        self.switch_dps = None
        self.vlan_matrix = {}
        self.vlan_to_host_id = []
        self.p4_switches = []
        self.ping_count = 1
        self.no_redundancy = False
        self.logger = None

    def build_network(self):
        """ Builds a mininet network based on the network matrix that's been 
            given """

        topo = self.MyTopo(hosts_matrix=self.hosts_matrix,
                           switch_matrix=self.link_matrix,
                           switch_dps=self.switch_dps,
                           p4_switches=self.p4_switches,
                           logger=self.logger)
        self.net = Mininet(topo=topo,
                           controller=RemoteController(name="faucet",
                                                       ip="127.0.0.1",
                                                       port=6653))

    def test_network(self):
        """ Sets up the network and tests that all hosts can ping each other in
            ipv4 and ipv6. Also tests failover by disabling links between 
            switches """
        self.ping_vlan_v4()
        # Compensates for ipv6 taking some time to set up
        time.sleep(1)
        self.ping_vlan_v6()
        # No redundancy mode until p4 redundancy has been tested more
        if self.no_redundancy or self.p4_switches:
            return
        for link in self.link_matrix:
            s1, s2 = link[0], link[2]
            self.log_info(f"Setting link between {s1} and {s2} down")
            self.net.configLinkStatus(s1, s2, "down")
            self.ping_vlan_v4()
            self.ping_vlan_v6()
            self.log_info(f"Setting link between {s1} and {s2} up")
            self.net.configLinkStatus(s1, s2, "up")
            self.ping_vlan_v4()
            self.ping_vlan_v6()

    def cleanup_ips(self):
        """ Cleans up ip addresses, in particular hosts with multiple interfaces
            and vlans """

        self.vlan_matrix["none"] = []
        for iface in self.hosts_matrix:
            h = {"name": iface["name"]}
            h["port"] = f"h{iface['id']}-eth0"
            h["id"] = iface["id"]
            if "ipv4" in iface:
                h["ipv4"] = iface["ipv4"]
            if "ipv6" in iface:
                h["ipv6"] = iface["ipv6"]
                self.add_ipv6(iface['id'], f"h{iface['id']}-eth0", iface)
            if "vlan" not in iface:
                self.vlan_matrix["none"].append(h)
        for iface in self.vlan_to_host_id:
            h = {"name": iface["name"]}
            h["port"] = "eth-0"
            if "ipv4" in iface:
                h["ipv4"] = iface["ipv4"]
            if "ipv6" in iface:
                h["ipv6"] = iface["ipv6"]
            hnode = self.net.getNodeByName(f"h{iface['id']}")
            if hnode.IP() == "127.0.0.1":
                hnode.cmd(f"ip addr del dev h{iface['id']}-eth0 127.0.0.1")
                hnode.cmd(f"ip -6 addr flush dev h{iface['id']}-eth0")
            hnode.cmd(f"ip link set dev h{iface['id']}-eth0 {iface['mac']}")
            self.add_vlan(iface["id"], iface, 0)

    def add_vlan(self, hostname, iface, port):
        """ Adds a vlan address to the specified port """
        self.vlan_matrix.setdefault(iface["vlan"], [])
        h = self.net.getNodeByName(f"h{hostname}")
        phase = f"h{hostname}-eth{port}"
        vid = iface["vlan"]
        vlan_port_name = f"eth{port}.{vid}"
        host = {"name": iface["name"]}
        host["port"] = vlan_port_name
        host["id"] = iface["id"]
        h.cmd(
            f'ip link add link {phase} name {vlan_port_name} type vlan id {vid}'
        )
        if "ipv4" in iface:
            h.cmd(f"ip addr add dev {vlan_port_name} {iface['ipv4']}")
            host["ipv4"] = iface["ipv4"]
        if "ipv6" in iface:
            self.add_ipv6(hostname, vlan_port_name, iface)
            host["ipv6"] = iface["ipv6"]
        self.vlan_matrix[iface["vlan"]].append(host)
        h.cmd(f"ip link set dev {vlan_port_name} up")

    def add_ipv6(self, hostname, portname, iface):
        """ Removes the default ipv6 address from hosts and adds the ip based on
            the hosts matrix """
        h = self.net.getNodeByName(f"h{hostname}")
        h.cmd(f"ip -6 addr flush dev {portname}")
        h.cmd(f"ip -6 addr add dev {portname} {iface['ipv6']}")

    def ping_vlan_v4(self):
        """ Uses the hosts matrix and pings all the ipv6 addresses, similar to
            mininet's pingall format """
        self.log_info('*** Ping: testing ping4 reachability')
        packets = 0
        lost = 0
        ploss = None
        for vlan in self.vlan_matrix:
            self.log_info(f"Testing reachability for hosts with vlan: {vlan}")
            for host in self.vlan_matrix[vlan]:
                results = []
                h = self.net.getNodeByName(f"h{host['id']}")
                # self.logger.info(f'{host["name"]} -> ')
                self.to_console(f'{host["name"]} -> ')
                for dst in self.vlan_matrix[vlan]:
                    if dst is host:
                        continue
                    if "ipv4" not in host:
                        continue
                    addr = dst['ipv4'].split('/')[0]
                    result = h.cmd(
                        f'ping -I {host["port"]} -c{self.ping_count} -i 0.01 {addr}'
                    )
                    self.logger.debug(result)
                    sent, received = self.net._parsePing(result)
                    packets += sent
                    lost += sent - received
                    out = 'X'
                    if received:
                        out = dst["name"]
                    self.to_console(f'{out} ')
                    results.append(out)
                output('\n')
        if packets > 0:
            ploss = 100.0 * lost / packets
            received = packets - lost
            self.log_info("*** Results: %i%% dropped (%d/%d received)" %
                          (ploss, received, packets))

    def ping_vlan_v6(self):
        """ Uses the hosts matrix and pings all the ipv6 addresses, similar to
            mininet's pingall format """
        self.log_info('*** Ping: testing ping6 reachability')
        packets = 0
        lost = 0
        ploss = None
        for vlan in self.vlan_matrix:
            self.log_info(f"Testing reachability for hosts with vlan: {vlan}")
            for host in self.vlan_matrix[vlan]:
                h = self.net.getNodeByName(f"h{host['id']}")
                output(f'{host["name"]} -> ')
                for dst in self.vlan_matrix[vlan]:
                    if dst is host:
                        continue
                    if "ipv6" not in host:
                        continue
                    addr = dst['ipv6'].split('/')[0]
                    result = h.cmd(
                        f'ping6 -I {host["port"]} -c{self.ping_count} -i 0.01 {addr}'
                    )
                    self.logger.debug(result)
                    sent, received = self.net._parsePing(result)
                    packets += sent
                    lost += sent - received
                    out = 'X'
                    if received:
                        out = dst["name"]
                    output(f'{out} ')
                output('\n')
        if packets > 0:
            ploss = 100.0 * lost / packets
            received = packets - lost
            self.log_info("*** Results: %i%% dropped (%d/%d received)" %
                          (ploss, received, packets))

    # Possibly redundant code, keeping for testing purpose
    def pingAllV4(self):
        """ Uses the hosts matrix and pings all the ipv6 addresses, similar to
            mininet's pingall format """
        self.logger.info('*** Ping: testing ping4 reachability\n')
        packets = 0
        lost = 0
        ploss = None
        for host in self.hosts_matrix:
            h = self.net.getNodeByName(host[0])
            self.logger.info(f'{host[0]} -> ')
            for dst in self.hosts_matrix:
                if dst is host:
                    continue
                addr6 = dst[1].split('/')[0]
                result = h.cmd(f'ping -c{self.ping_count} -i 0.01 {addr6}')
                sent, received = self.net._parsePing(result)
                packets += sent
                lost += sent - received
                out = 'X'
                if received:
                    out = dst[0]
                self.logger.info(f'{out} ')
            self.logger.info('\n')
        if packets > 0:
            ploss = 100.0 * lost / packets
            received = packets - lost
            self.logger.info("*** Results: %i%% dropped (%d/%d received)\n" %
                             (ploss, received, packets))

    # Possibly redundant code, keeping for testing purpose
    def pingAllV6(self):
        """ Uses the hosts matrix and pings all the ipv6 addresses, similar to
            mininet's pingall format """
        self.logger.info('*** Ping: testing ping6 reachability\n')
        packets = 0
        lost = 0
        ploss = None
        for host in self.hosts_matrix:
            h = self.net.getNodeByName(host[0])
            self.logger.info(f'{host[0]} -> ')
            for dst in self.hosts_matrix:
                if dst is host:
                    continue
                addr6 = dst[2].split('/')[0]
                result = h.cmd(f'ping -c{self.ping_count} -i 0.01 -6 {addr6}')
                sent, received = self.net._parsePing(result)
                packets += sent
                lost += sent - received
                out = 'X'
                if received:
                    out = dst[0]
                self.logger.info(f'{out} ')
            self.logger.info('\n')
        if packets > 0:
            ploss = 100.0 * lost / packets
            received = packets - lost
            self.logger.info("*** Results: %i%% dropped (%d/%d received)\n" %
                             (ploss, received, packets))

    def start(self, args, logger):
        """ Starts the program """

        self.logger = logger

        info(f"{datetime.now().strftime('%b %d %H:%M:%S')}\n")
        info('Starting new Testing instance\n')
        nw_matrix = None
        if args.json_topology:
            self.logger.error("Direct JSON is not yet supported")
            sys.exit()
            # network_matrix = self.parse_json(args.json)
        if args.topology_file:
            nw_matrix = self.open_file(args.topology_file)

        if not args.json_topology and not args.topology_file:
            nw_matrix = self.open_file(DEFAULT_INPUT_FILE)

        try:
            args.ping = int(args.ping)
            self.ping_count = args.ping
        except:
            self.log_error(
                'Ping input is not a number, using the default ping count of 1'
            )

        if not nw_matrix:
            self.log_error("No topology discovered. Please check input files")

        if args.thrift_port:
            self.thrift_port = args.thrift_port

        if args.no_redundancy:
            self.no_redundancy = True
            self.log_info('No redundancy testing mode')

        if nw_matrix:
            self.check_matrix(nw_matrix)
            self.build_network()
            self.net.start()
            self.cleanup_ips()

            if (args.cli):
                CLI(self.net)
            else:
                self.test_network()
            self.net.stop()

    def parse_json(self, json_string):
        """ Parses json string entered through cli """
        data = None
        try:
            data = json.loads(json_string)
        except ValueError as err:
            self.log_error(f"Error in the input json string\n")
        return data

    def open_file(self, input_file):
        """ Opens the json file that contains the network topology """
        data = None
        try:
            with open(input_file) as jsonFile:
                data = json.load(jsonFile)
        except (UnicodeDecodeError, PermissionError, ValueError) as err:
            self.logger.error(f"Error in the file {input_file}\n")
        except FileNotFoundError as err:
            self.logger.error(f"File not found: {input_file}\n")
            if input_file is DEFAULT_INPUT_FILE:
                self.logger.error(
                    "Please specify a default topology in " +
                    "/etc/mxitt/topology.json or specify a topology file " +
                    "using the -i --input option\n")
                sys.exit()

        return data

    def check_matrix(self, nw_matrix):
        """ Checks and validates the network matrix format """
        err_msg = "Malformed config detected! Please check config: "

        if "hosts_matrix" not in nw_matrix:
            self.logger.error(f"{err_msg}No \"hosts_matrix\" is detected\n")
            sys.exit()
        if not nw_matrix["hosts_matrix"]:
            self.logger.error(f"{err_msg}hosts_matrix doesn't have content\n")
            sys.exit()
        for host in nw_matrix["hosts_matrix"]:
            malformed = False
            if "name" not in host:
                self.logger.error(f"{err_msg} Entry detected without a name\n")
                malformed = True

            if "interfaces" not in host:
                self.logger.error(
                    f"{err_msg} Entry detected without any interfaces\n")
                malformed = True

            if malformed:
                sys.exit()
            for iface in host["interfaces"]:
                if "ipv4" in iface:
                    if "." not in iface["ipv4"] or "/" not in iface["ipv4"]:
                        self.logger.error(
                            f"{err_msg}Host: {host['name']} has an self.logger.error in the ipv4 section\n"
                        )
                        self.logger.error(f"IPv4 section: {iface['ipv4']}\n")
                        malformed = True
                if "ipv6" in iface:
                    if ":" not in iface["ipv6"] or "/" not in iface["ipv6"]:
                        self.logger.error(
                            f"{err_msg}Host: {host['name']} has an self.logger.error in ipv6 section\n"
                        )
                        self.logger.error(f"IPv6 section: {iface['ipv6']}\n")
                        malformed = True
                if "ipv4" not in iface and "ipv6" not in iface:
                    self.logger.error(
                        f"{err_msg}Host: {host['name']} has neither an IPv4 or IPv6 address\n"
                    )
                    malformed = True
                if "mac" not in iface:
                    mac = ""
                    for other_iface in host["interfaces"]:
                        if iface is other_iface:
                            continue

                        if not malformed and \
                           iface["switch"] == other_iface["switch"] and \
                           iface["swport"] == other_iface["swport"] and \
                           "mac" in other_iface:

                            mac = other_iface["mac"]
                            iface["mac"] = mac

                    if not mac:
                        self.logger.error(
                            f"{err_msg}Host: {host['name']} does not have a mac address\n"
                        )
                        malformed = True

                if "mac" in iface:
                    if ":" not in iface["mac"]:
                        self.logger.error(
                            f"{err_msg}Host: {host['name']} has an self.logger.error in mac section\n"
                        )
                        self.logger.error(f"mac section: {iface['mac']}\n")
                        malformed = True
                if "swport" not in iface:
                    self.logger.error(
                        f"{err_msg}Host: {host['name']} does not have a switch port\n"
                    )
                    malformed = True
                if "switch" not in iface:
                    self.logger.error(
                        f"{err_msg}Host: {host['name']} does not have a switch property\n"
                    )
                    malformed = True
                if "vlan" in iface:
                    vid = int(iface["vlan"])
                    if vid < 0 or vid > 4095:
                        self.logger.error(
                            f"{err_msg}Host: {host['name']} has an interface" +
                            f"an invalid vlan id. Vid should be between 1" +
                            f" and 4095. Vid :{vid} detected\n")
                if malformed:
                    sys.exit()

        if "switch_matrix" not in nw_matrix:
            self.logger.error(f"{err_msg}No \"switch_matrix\" detected\n")
            sys.exit()
        if not nw_matrix["switch_matrix"]:
            self.logger.error(f"{err_msg}switch matrix doesn't have content\n")
            sys.exit()
        if "links" not in nw_matrix["switch_matrix"]:
            self.logger.error(
                f'{err_msg}switch matrix is missing a links section\n')
            sys.exit()

        for switch in nw_matrix["switch_matrix"]["links"]:
            if len(switch) != 4:
                self.logger.error(
                    f"{err_msg}The switch matrix seems to be missing parts. " +
                    "please ensure format is as follows:\n" +
                    "[switch1_name,\tport_connecting_switch1_to_switch2," +
                    "\tswitch2_name,\tport_connecting_switch2_to_switch1]\n")
                sys.exit()

        if "dp_ids" not in nw_matrix["switch_matrix"]:
            self.logger.warning(
                "No \"switch_dps\" detected, dps generated in Mininet might" +
                " not match dps in faucet config\n")
        else:
            self.switch_dps = nw_matrix["switch_matrix"]["dp_ids"]

        if "p4" in nw_matrix["switch_matrix"]:
            self.p4_switches = nw_matrix["switch_matrix"]["p4"]
        self.hosts_matrix = self.flatten_nw_matrix(nw_matrix)
        self.link_matrix = nw_matrix["switch_matrix"]["links"]

    def flatten_nw_matrix(self, nw_matrix):
        """ Flattens out the topology matrix turning each interface into a 
            separate namespace """
        flattened_matrix = []
        id = 1
        for host in nw_matrix["hosts_matrix"]:
            hname = host["name"]
            connected_sw = {}
            ifaces = []
            vlan_ifaces = []
            untagged_ids = []
            tagged_ids = []
            for iface in host["interfaces"]:
                switch = iface["switch"]
                swport = iface["swport"]
                if switch not in connected_sw:
                    connected_sw[switch] = {swport: id}
                    h = iface
                    h["name"] = hname
                    h["id"] = id
                    if "vlan" not in iface:
                        ifaces.append(h)
                        untagged_ids.append(id)
                    else:
                        vlan_ifaces.append(h)
                        tagged_ids.append(id)
                    id += 1
                    continue
                if swport not in connected_sw[switch]:
                    connected_sw[switch][swport] = id
                    h = iface
                    h["name"] = hname
                    h["id"] = id
                    if "vlan" not in iface:
                        ifaces.append(h)
                        untagged_ids.append(id)
                    else:
                        vlan_ifaces.append(h)
                        tagged_ids.append(id)
                    id += 1
                    continue

                tempid = connected_sw[switch][swport]
                h = iface
                h["name"] = hname
                h["id"] = tempid
                if "vlan" not in iface:
                    ifaces.append(h)
                    untagged_ids.append(tempid)
                else:
                    vlan_ifaces.append(h)
                    tagged_ids.append(tempid)
                id += 1
                continue

            for iface in vlan_ifaces:
                if iface["id"] not in untagged_ids:
                    # To prevent interference with multiple vlans on same iface
                    untagged_ids.append(iface["id"])
                    ifaces.append(iface)
            self.vlan_to_host_id.extend(vlan_ifaces)
            flattened_matrix.extend(ifaces)

        return flattened_matrix

    def to_console(self, message):
        output(message)

    def log_info(self, message):

        info(f'{message}\n')
        self.logger.info(message)

    def log_error(self, message):

        error(f'{message}\n')
        self.logger.error('message')

    class MyTopo(Topo):
        """ Custom topology generator """
        def __init__(self,
                     hosts_matrix=None,
                     switch_matrix=None,
                     switch_dps=None,
                     p4_switches=None,
                     sw_path=DEFAULT_P4_SWITCH,
                     p4_json=DEFAULT_UMBRELLA_JSON,
                     logger=None):
            """ Create a topology based on input JSON"""

            # Initialize topology
            Topo.__init__(self)
            switch_list = []
            self.logger = logger

            for sw in switch_dps:
                dp_id = switch_dps[sw]
                switch_list.append(sw)
                self.addSwitch(sw, dpid='%x' % dp_id)
            if p4_switches:
                self.log_info('Adding p4 switches:')
                i = 0
                for sw in p4_switches:
                    self.log_info(f'{sw}')
                    # Need to allow for multiple p4 switches to be used
                    # Can't use 9090 due to promethues clash
                    t_port = 9190 + i
                    i = +1
                    # TODO: Change to variables
                    self.addSwitch(sw,
                                   cls=P4Switch,
                                   sw_path=sw_path,
                                   json_path=p4_json,
                                   thrift_port=t_port)
                    switch_list.append(sw)
                # logger.info('\n')
            for switch in switch_matrix:
                self.addLink(switch[0], switch[2], int(switch[1]),
                             int(switch[3]))

            for host in hosts_matrix:
                self.hostAdd(host)

        def hostAdd(self, host):
            """ Adds the host to the network """
            hname = f"h{host['id']}"
            if "ipv4" in host and "vlan" not in host:
                self.addHost(hname,
                             ip=host["ipv4"],
                             mac=host["mac"],
                             intf="eth-0")
            else:
                self.addHost(hname,
                             ip="127.0.0.1/32",
                             mac=host["mac"],
                             intf="eth-0")
            self.addLink(host["switch"], hname, host["swport"])

        def to_console(self, message):
            output(message)

        def log_info(self, message):

            info(f'{message}\n')
            self.logger.info(message)

        def log_error(self, message):

            error(f'{message}\n')
            self.logger.error('message')
Example #2
0
class ATHOS():
    """ Mininet IXP topology tester.

        Takes json with topology information and builds a network based on this.
        It will check the reachability of all hosts as well as redundancy is
        working by turning off each link between switches and validates that
        hosts are still able to communicate. """

    def __init__(self):
        self.net = None
        self.network_matrix = None
        self.hosts_matrix = None
        self.link_matrix = None
        self.switch_dps = None
        self.vlan_matrix = {}
        self.vlan_to_host_id = []
        self.p4_switches = []
        self.logger = None
        self.unmanaged_switches = []
        self.ploss_threshold = DEFAULT_PLOSS_THRESHOLD


    def build_network(self, thrift_port_base=9190):
        """ Builds a mininet network based on the network matrix that's been
            given """

        topo = self.MyTopo(hosts_matrix=self.hosts_matrix,
                           switch_matrix=self.link_matrix,
                           switch_dps=self.switch_dps,
                           p4_switches=self.p4_switches,
                           unmanaged_switches=self.unmanaged_switches,
                           logger=self.logger,
                           thrift_port_base=thrift_port_base)
        self.net = Mininet(
            topo=topo,
            controller=RemoteController(
                name="cerberus-controller",
                ip="127.0.0.1",
                port=6653
            ))


    def test_network(self, no_redundancy=False, ping_count=1):
        """ Sets up the network and tests that all hosts can ping each other in
            ipv4 and ipv6. Also tests failover by disabling links between
            switches """
        v4_loss = self.ping_vlan_v4(ping_count)
        # Compensates for ipv6 taking some time to set up
        time.sleep(1)
        v6_loss = self.ping_vlan_v6(ping_count)
        ploss_passed = self.packet_loss_threshold_passed(v4_loss, v6_loss)
        if not ploss_passed:
            return
        # No redundancy mode until p4 redundancy has been tested more
        if no_redundancy or self.p4_switches:
            return
        for link in (l for l in self.link_matrix if self.backup_exists(l)):
            source_switch, destination_switch = link[0], link[2]
            info(f"Setting link between {source_switch} and "
                 f"{destination_switch} down\n")
            self.net.configLinkStatus(source_switch, destination_switch, "down")
            self.ping_vlan_v4(ping_count)
            self.ping_vlan_v6(ping_count)
            ploss_passed = self.packet_loss_threshold_passed(v4_loss, v6_loss,
                                    source_switch, destination_switch, "down")
            if not ploss_passed:
                return
            info(f"Setting link between {source_switch} and "
                 f"{destination_switch} up\n")
            self.net.configLinkStatus(source_switch, destination_switch, "up")
            self.ping_vlan_v4(ping_count)
            self.ping_vlan_v6(ping_count)
            ploss_passed = self.packet_loss_threshold_passed(v4_loss, v6_loss,
                                    source_switch, destination_switch, "up")
            if not ploss_passed:
                return


    def cleanup_ips(self):
        """ Cleans up ip addresses, in particular hosts with multiple interfaces
            and vlans """
        self.vlan_matrix["none"] = []
        for iface in self.hosts_matrix:
            host = {"name": iface["name"]}
            host["port"] = f"h{iface['id']}-eth0"
            host["id"] = iface["id"]
            if "ipv4" in iface:
                host["ipv4"] = iface["ipv4"]
            if "ipv6" in iface:
                host["ipv6"] = iface["ipv6"]
                self.add_ipv6(iface['id'], f"h{iface['id']}-eth0", iface)
            if "vlan" not in iface:
                self.vlan_matrix["none"].append(host)
        for iface in self.vlan_to_host_id:
            host = {"name": iface["name"]}
            host["port"] = "eth-0"
            if "ipv4" in iface:
                host["ipv4"] = iface["ipv4"]
            if "ipv6" in iface:
                host["ipv6"] = iface["ipv6"]
            hnode = self.net.getNodeByName(f"h{iface['id']}")
            if hnode.IP() == "127.0.0.1":
                hnode.cmd(f"ip addr del dev h{iface['id']}-eth0 127.0.0.1")
                hnode.cmd(f"ip -6 addr flush dev h{iface['id']}-eth0")
            hnode.cmd(f"ip link set dev h{iface['id']}-eth0 {iface['mac']}")
            self.add_vlan(iface["id"], iface, 0)


    def add_vlan(self, hostname, iface, port):
        """ Adds a vlan address to the specified port """
        self.vlan_matrix.setdefault(iface["vlan"], [])
        host_node = self.net.getNodeByName(f"h{hostname}")
        phase = f"h{hostname}-eth{port}"
        vid = iface["vlan"]
        vlan_port_name = f"eth{port}.{vid}" if iface['tagged'] else f'h{iface["id"]}-eth0'
        host = {"name": iface["name"]}
        host["port"] = vlan_port_name
        host["id"] = iface["id"]
        if iface["tagged"]:
            host_node.cmd(f'ip link add link {phase} name ' +
                        f'{vlan_port_name} type vlan id {vid}')
        if "ipv4" in iface:
            host_node.cmd(f"ip addr add dev {vlan_port_name} {iface['ipv4']}")
            host["ipv4"] = iface["ipv4"]
        if "ipv6" in iface:
            self.add_ipv6(hostname, vlan_port_name, iface)
            host["ipv6"] = iface["ipv6"]
        if iface["tagged"]:
            host_node.cmd(f"ip link set dev {vlan_port_name} up")
            host_node.cmd(f"ip link set address {iface['mac']} dev {vlan_port_name}")
        self.vlan_matrix[iface["vlan"]].append(host)


    def add_ipv6(self, hostname, portname, iface):
        """ Removes the default ipv6 address from hosts and adds the ip based on
            the hosts matrix """
        host_node = self.net.getNodeByName(f"h{hostname}")
        host_node.cmd(f"ip -6 addr flush dev {portname}")
        host_node.cmd(f"ip -6 addr add dev {portname} {iface['ipv6']}")

    def ping_vlan_v4(self, ping_count=1):
        """ Uses the hosts matrix and pings all the ipv6 addresses, similar to
            mininet's pingall format """
        info('*** Ping: testing ping4 reachability\n')
        packets = 0
        lost = 0
        ploss = None
        for vlan in self.vlan_matrix:
            info(f"Testing reachability for hosts with vlan: {vlan}\n")
            for host in self.vlan_matrix[vlan]:
                results = []
                if "ipv4" not in host:
                    continue
                host_node = self.net.getNodeByName(f"h{host['id']}")
                output(f'{host["name"]} -> ')
                for dst in self.vlan_matrix[vlan]:
                    if dst is host:
                        continue
                    if "ipv4" not in dst:
                        continue
                    addr = dst['ipv4'].split('/')[0]
                    result = host_node.cmd(f'ping -I {host["port"]}' +
                                           f' -c{ping_count}  -W 1 -i 0.01 {addr}')
                    self.logger.debug(result)
                    sent, received = self.net._parsePing(result)
                    packets += sent
                    lost += sent - received
                    out = 'X'
                    if received:
                        out = dst["name"]
                    output(f'{out} ')
                    results.append(out)
                output('\n')
        if packets > 0:
            ploss = 100.0 * lost / packets
            received = packets - lost
            info(f"*** Results: {round(ploss, 2)}% dropped "
                 f"({received}/{packets} received)\n")
            return ploss


    def ping_vlan_v6(self, ping_count=1):
        """ Uses the hosts matrix and pings all the ipv6 addresses, similar to
            mininet's pingall format """
        info('*** Ping: testing ping6 reachability\n')
        packets = 0
        lost = 0
        ploss = None
        for vlan in self.vlan_matrix:
            info(f"Testing reachability for hosts with vlan: {vlan}\n")
            for host in self.vlan_matrix[vlan]:
                if "ipv6" not in host:
                    continue
                host_node = self.net.getNodeByName(f"h{host['id']}")
                output(f'{host["name"]} -> ')
                for dst in self.vlan_matrix[vlan]:
                    if dst is host:
                        continue
                    if "ipv6" not in dst:
                        continue
                    addr = dst['ipv6'].split('/')[0]
                    result = host_node.cmd(
                        f'ping6 -I {host["port"]} -c{ping_count} -W 1 -i 0.01 {addr}')
                    sent, received = self.net._parsePing(result)
                    packets += sent
                    lost += sent - received
                    out = 'X'
                    if received:
                        out = dst["name"]
                    output(f'{out} ')
                output('\n')
        if packets > 0:
            ploss = 100.0 * lost / packets
            received = packets - lost
            info(f"*** Results: {round(ploss, 2)}% dropped "
                 f"({received}/{packets} received)\n")
            return ploss


    def backup_exists(self, link):
        """ Checks if the switches have a redundant path setup """
        src_switch, dst_switch = link[0], link[2]

        src_has_other_link = False
        dst_has_other_link = False

        for l in (i for i in self.link_matrix if i != link):
            src_has_other_link = True if src_switch in l else src_has_other_link
            dst_has_other_link = True if dst_switch in l else dst_has_other_link

        if not src_has_other_link:
            warn(f"Warning: {src_switch} does not have another core link, the "
                 f"link between {src_switch} and {dst_switch} will not be "
                 f"turned off\n")
            return False
        if not dst_has_other_link:
            warn(f"Warning: {dst_switch} does not have another core link, the "
                 f"link between {dst_switch} and {src_switch} will not be "
                 f"turned off\n")
            return False
        return True


    def start(self, args, logger):
        """ Starts the program """

        self.logger = logger
        ping_count = 1

        info(f"{datetime.now().strftime('%b %d %H:%M:%S')}\n")
        info('Starting new Testing instance\n')
        nw_matrix = None
        if args.json_topology:
            error("Direct JSON is not yet supported\n")
            sys.exit()
        if args.topology_file:
            nw_matrix = self.open_file(args.topology_file)

        if not args.json_topology and not args.topology_file:
            nw_matrix = self.open_file(DEFAULT_INPUT_FILE)

        if not nw_matrix:
            error("No topology discovered. Please check input files\n")

        try:
            ping_count = int(args.ping)
        except TypeError as err:
            error('Ping input is not a number, using the default ping '
                  'count of 1\n{err}')

        t_port = None
        if args.thrift_port:
            t_port = args.thrift_port

        if nw_matrix:
            self.parse_config(nw_matrix)
            self.build_network(t_port)
            self.net.start()
            self.cleanup_ips()

            if args.script:
                ATHOS.run_start_script(args.script)

            if args.cli:
                CLI(self.net)
            else:
                self.test_network(args.no_redundancy, ping_count)
            self.net.stop()


    def parse_json(self, json_string):
        """ Parses json string entered through cli """
        data = None
        try:
            data = json.loads(json_string)
        except ValueError as err:
            error(f"Error in the input json string\n{err}")
        return data


    def open_file(self, input_file):
        """ Opens the json file that contains the network topology """
        data = None
        try:
            with open(input_file) as json_file:
                data = json.load(json_file)
        except (UnicodeDecodeError, PermissionError, ValueError) as err:
            error(f"Error in the file {input_file}\n{err}\n")
        except FileNotFoundError as err:
            error(f"File not found: {input_file}\n")
            if input_file is DEFAULT_INPUT_FILE:
                error("Please specify a default topology in "
                      "/etc/mxitt/topology.json or specify a topology file "
                      f"using the -i --input option\n{err}\n")
                sys.exit()

        return data

    @staticmethod
    def run_start_script(script):
        """ Runs specified startup script before continuing. Typical use cases
            would be starting controllers or loading switches with rules """
        call(script, shell=True)


    def parse_config(self, nw_matrix):
        """ Parses and validates the config """
        err_msg = "Malformed config detected! "
        try:
            if "hosts_matrix" not in nw_matrix:
                raise ConfigError(f"{err_msg}No 'hosts_matrix' found\n")
            if "switch_matrix" not in nw_matrix:
                raise ConfigError(f"{err_msg}No 'hosts_matrix' found\n")
        except ConfigError as err:
            error(err)
            sys.exit()
        self.check_hosts_config(nw_matrix["hosts_matrix"])
        self.check_switch_config(nw_matrix["switch_matrix"])
        self.hosts_matrix = self.flatten_nw_matrix(nw_matrix)


    def check_hosts_config(self, host_matrix):
        """ Parses and validates the hosts matrix """
        err_msg = ("Malformed config detected in the hosts section!\n" +
                   "Please check the config:\n")
        if not host_matrix:
            error(f"{err_msg}The hosts_matrix doesn't have any content\n")

        for host in host_matrix:
            malformed = False
            if "name" not in host:
                error(f"{err_msg}Entry detected without a name\n")
                malformed = True

            if "interfaces" not in host:
                error(f"{err_msg}Entry detected without any interfaces\n")
                malformed = True
            if malformed:
                sys.exit()

            self.check_host_interfaces(err_msg, host)


    def check_host_interfaces(self, err_msg, host):
        """ Parse and validates the host's interfaces """
        err_msg = err_msg + f"Host: {host['name']} has an error"

        try:
            if not host["interfaces"]:
                raise ConfigError(f"{err_msg} interfaces section is empty")

            for iface in host["interfaces"]:
                if "swport" not in iface:
                    raise ConfigError(f"{err_msg}. It does not have a " +
                                      "switch port\n")
                if "switch" not in iface:
                    raise ConfigError(f"{err_msg}. It does not have an " +
                                      "assigned switch\n")
                if "ipv4" in iface:
                    self.check_ipv4_address(err_msg, iface["ipv4"])
                if "ipv6" in iface:
                    self.check_ipv6_address(err_msg, iface["ipv6"])
                if "ipv4" not in iface and "ipv6" not in iface:
                    raise ConfigError(f"{err_msg}. It has neither an IPv4" +
                                      " or IPv6 address\n")
                if "mac" not in iface:
                    iface["mac"] = \
                        self.check_for_available_mac(err_msg, iface,
                                                     host["interfaces"])
                if "mac" in iface:
                    self.check_mac_address(err_msg, iface["mac"])
                if "vlan" in iface:
                    self.check_vlan_validity(err_msg, iface["vlan"])

        except ConfigError as err:
            error(err + "\n")
            sys.exit()

    def check_ipv4_address(self, err_msg, v4_address):
        """ Checks validity of ipv4 address """
        try:
            if not v4_address:
                raise ConfigError(f"{err_msg} please check that ipv4 sections "
                                  "have addresses assigned\n")
            if "." not in v4_address or "/" not in v4_address:
                raise ConfigError(f"{err_msg} in the ipv4 section\n"
                                  f"IPv4 section: {v4_address}\n")
        except ConfigError as err:
            error(err)
            sys.exit()


    def check_ipv6_address(self, err_msg, v6_address):
        """ Checks validity of ipv6 address """
        try:
            if not v6_address:
                raise ConfigError(f"{err_msg} please check that ipv6 sections" +
                                  "have addresses assigned\n")
            if ":" not in v6_address or "/" not in v6_address:
                raise ConfigError(f"{err_msg} in the ipv6 section\n" +
                                  f"IPv6 section: {v6_address}\n")
        except ConfigError as err:
            error(err)
            sys.exit()


    def check_mac_address(self, err_msg, mac_address):
        """ Checks validity of MAC address """
        try:
            if not mac_address:
                raise ConfigError(f"{err_msg} please check that MAC sections" +
                                  "have addresses assigned\n")
            if ":" not in mac_address:
                raise ConfigError(f"{err_msg} in the MAC section. Currently "
                                  "only addresses seperated with : is "
                                  f"supported.\nMAC section: {mac_address}\n")
        except ConfigError as err:
            error(err)
            sys.exit()


    def check_for_available_mac(self, err_msg, iface, host_interfaces):
        """ Checks if port another mac address is assigned to the port """
        mac = ""
        try:
            for other_iface in host_interfaces:
                if iface is other_iface:
                    continue

                if iface["switch"] == other_iface["switch"] and \
                iface["swport"] == other_iface["swport"] and \
                "mac" in other_iface:

                    mac = other_iface["mac"]

            if not mac:
                raise ConfigError(f"{err_msg} in the mac section. " +
                                  "No mac address was provided\n")
        except ConfigError as err:
            error(err)

        return mac


    def check_vlan_validity(self, err_msg, vlan):
        """ Checks that the assigned vlan is a valid value """
        try:
            vid = int(vlan)
            if vid < 0 or vid > 4095:
                raise ConfigError(f"{err_msg}. Invalid vlan id(vid) detected. "
                                  "A valid vid should be between 1 and 4095.\n"
                                  f"Found vid: {vid}\n")
        except (ConfigError, ValueError) as err:
            error(err)
            sys.exit()

    def packet_loss_threshold_passed(self, v4_loss, v6_loss, src_switch=None,
                                     dst_switch=None, status=""):
        """ Helper to check packet loss and if it is more than the
            maximum allowed """
        link_error_msg = ""
        if src_switch and dst_switch:
            link_error_msg = (f" when the link between {src_switch} and "
                              f"{dst_switch} was set {status}.")
        else:
            link_error_msg = (f" before any links were changed.")

        if v4_loss and v4_loss > self.ploss_threshold \
            and v6_loss and v6_loss > self.ploss_threshold:
            error(f"FAIL: Reachability failed for both IPv4 and IPv6"
                  f"{link_error_msg}")
            error(f"Packet loss threshold: {self.ploss_threshold}\n"
                  f"IPv4 loss: {v4_loss}\tIPv6 loss: {v6_loss}")
            return False
        elif v4_loss and v4_loss > self.ploss_threshold:
            error(f"FAIL: Reachability failed for IPv4{link_error_msg}\n")
            error(f"Packet loss threshold: {self.ploss_threshold}\n"
                  f"IPv4 loss: {v4_loss}")
            return False
        elif v6_loss and v6_loss > self.ploss_threshold:
            error(f"FAIL: Reachability failed for IPv6{link_error_msg}\n")
            error(f"Packet loss threshold: {self.ploss_threshold}\t"
                  f"IPv6 loss: {v6_loss}")
            return False
        else:
            return True


    def check_switch_config(self, sw_matrix):
        """ Parses and validates the switch matrix """
        err_msg = ("Malformed config detected in the switch section!\n" +
                   "Please check the config:\n")
        try:
            if not sw_matrix:
                raise ConfigError(f"{err_msg}Switch matrix is empty\n")
            if "links" not in sw_matrix:
                raise ConfigError(f"{err_msg}No links section found\n")
            for link in sw_matrix["links"]:
                if len(link) != 4:
                    raise ConfigError(f"{err_msg}Invalid link found. The "
                        "expected link format should be:\n"
                        "[switchA,portA,switchB,portB]\nWhere portA is the port"
                        " on switchA connected to switchB, and vice versa "
                        f"for portB\nLink found: {link}\n")
                port_a = int(link[1])
                port_b = int(link[3])

                if port_a < 0 or port_a > 255 or port_b < 0 or port_b > 255:
                    raise ConfigError("Invalid port number detected. Ensure "
                                      "that port numbers are between 0 and 255 "
                                      f"sw1_port: {port_a}\t sw2_port:{port_b}\n")
            if "dp_ids" not in sw_matrix:
                warn(f"{err_msg}No dp_id section found, dp_ids generated in "
                     "Mininet might not match those in controller config\n")
            else:
                for _, dp_id in sw_matrix["dp_ids"].items():
                    if not hex(dp_id):
                        raise ConfigError(f"{err_msg}Please ensure that dp_ids "
                                          "are valid numbers\n")
                self.switch_dps = sw_matrix["dp_ids"]
            if "p4" in sw_matrix:
                self.p4_switches = sw_matrix["p4"]
            if "unmanaged_switches" in sw_matrix:
                self.unmanaged_switches = sw_matrix["unmanaged_switches"]
            self.link_matrix = sw_matrix["links"]

        except ConfigError as err:
            error(err)
            sys.exit()
        except ValueError as err:
            error(f"{err_msg}Please check value of port numbers and vlan ids\n")
            error(err)
            sys.exit()


    def flatten_nw_matrix(self, nw_matrix):
        """ Flattens out the topology matrix turning each interface into a
            separate namespace """
        flattened_matrix = []
        id = 1
        for host in nw_matrix["hosts_matrix"]:
            hname = host["name"]
            connected_sw = {}
            ifaces = []
            vlan_ifaces = []
            untagged_ids = []
            tagged_ids = []
            for iface in host["interfaces"]:
                switch = iface["switch"]
                swport = iface["swport"]
                if switch not in connected_sw:
                    connected_sw[switch] = {swport:id}
                    h = iface
                    h["name"] = hname
                    h["id"] = id
                    if "vlan" not in iface:
                        ifaces.append(h)
                        untagged_ids.append(id)
                    else:
                        vlan_ifaces.append(h)
                        tagged_ids.append(id)
                    id += 1
                    continue
                if swport not in connected_sw[switch]:
                    connected_sw[switch][swport] = id
                    h = iface
                    h["name"] = hname
                    h["id"] = id
                    if "vlan" not in iface:
                        ifaces.append(h)
                        untagged_ids.append(id)
                    else:
                        vlan_ifaces.append(h)
                        tagged_ids.append(id)
                    id += 1
                    continue

                tempid = connected_sw[switch][swport]
                h = iface
                h["name"] = hname
                h["id"] = tempid
                if "vlan" not in iface:
                    ifaces.append(h)
                    untagged_ids.append(tempid)
                else:
                    vlan_ifaces.append(h)
                    tagged_ids.append(tempid)
                id += 1
                continue

            for iface in vlan_ifaces:
                if iface["id"] not in untagged_ids:
                    # To prevent interference with multiple vlans on same iface
                    untagged_ids.append(iface["id"])
                    ifaces.append(iface)
            self.vlan_to_host_id.extend(vlan_ifaces)
            flattened_matrix.extend(ifaces)

        return flattened_matrix


    class MyTopo(Topo):
        """ Custom topology generator """

        def __init__(self, hosts_matrix=None, switch_matrix=None,
                     switch_dps=None, p4_switches=None,
                     unmanaged_switches=None,
                     sw_path=DEFAULT_P4_SWITCH,
                     p4_json=DEFAULT_UMBRELLA_JSON,
                     logger=None,
                     thrift_port_base=9190):
            """ Create a topology based on input JSON"""

            # Initialize topology
            Topo.__init__(self)
            switch_list = []
            self.logger = logger

            for sw in switch_dps:
                dp_id = switch_dps[sw]
                switch_list.append(sw)
                self.addSwitch(sw, dpid='%x' % dp_id)
            if p4_switches:
                info('Adding p4 switches:')
                i = 0
                for sw in p4_switches:
                    info(f'{sw}')
                    # Need to allow for multiple p4 switches to be used
                    # Can't use 9090 due to promethues clash
                    t_port = int(thrift_port_base) + int(i)
                    i += 1
                    self.addSwitch(sw, cls=P4Switch,
                                   sw_path=sw_path,
                                   json_path=p4_json,
                                   thrift_port=t_port
                                   )
                    switch_list.append(sw)
            if unmanaged_switches:
                for sw in unmanaged_switches:
                    self.addSwitch(sw, failMode="standalone")
            for switch in switch_matrix:
                self.addLink(switch[0], switch[2],
                             int(switch[1]), int(switch[3]))
            for host in hosts_matrix:
                self.host_add(host)


        def host_add(self, host):
            """ Adds the host to the network """
            hname = f"h{host['id']}"
            if "ipv4" in host and "vlan" not in host:
                self.addHost(hname, ip=host["ipv4"], mac=host["mac"],
                             intf="eth-0")
            if "ipv4" in host and "tagged" in host and not host["tagged"]:
                self.addHost(hname, ip=host["ipv4"], mac=host["mac"],
                             intf="eth-0")
            else:
                self.addHost(hname, ip="127.0.0.1/32", mac=host["mac"],
                             intf="eth-0")
            self.addLink(host["switch"], hname, host["swport"])
Example #3
0
class SimulatedNetwork:
    def __init__(self, topo):
        self.net = Mininet(topo=topo)
        self.topo = topo

    def pingList(self, sourceList, destList):
        """Ping between all specified hosts.
           hosts: list of hosts
           returns: ploss packet loss percentage"""
        # should we check if running?
        packets = 0
        lost = 0
        ploss = None
        for node in sourceList:  #(self.routers + self.caches) :
            print '%s -> ' % node.name, ""
            for dest in destList:
                if node != dest:
                    opts = ''
                    if dest.intfs:
                        result = node.cmd('ping -c1 %s %s' % (opts, dest.IP()))
                        sent, received = self.net._parsePing(result)
                    else:
                        sent, received = 0, 0
                    packets += sent
                    if received > sent:
                        print('*** Error: received too many packets')
                        print '%s' % result, ""
                        node.cmdPrint('route')
                        exit(1)
                    lost += sent - received
                    print('%s ' % dest.name) if received else 'X ', ""
            print('\n')
        if packets > 0:
            ploss = 100.0 * lost / packets
            received = packets - lost
            print("*** Results: %i%% dropped (%d/%d received)\n" %
                  (ploss, received, packets))
        else:
            ploss = 0
            print("*** Warning: No packets sent\n")
        return ploss

    def buildRTable(self):
        for hostId in (self.topo.cacheMemoryDict.keys() + self.topo.clientIds):
            shortestPath = nx.dijkstra_path(self.topo.graph, "mainServer",
                                            hostId, "weight")
            for i in range(1, len(shortestPath) - 1):
                nodeId = shortestPath[i]
                next = nodeId
                for j in range(i - 1, -1, -1):
                    previousNodeId = shortestPath[j]
                    edgeData = self.topo.graph.get_edge_data(
                        previousNodeId, next)
                    if edgeData["id_1"] == previousNodeId:
                        previousNodeIP = edgeData["ip_1"].replace(
                            ".1/", ".0/").replace(".2/", ".0/").replace(
                                ".65/", ".64/").replace(".66/",
                                                        ".64/").split("/")[0]
                        previousNodeNetmask = "255.255.255.192" if edgeData[
                            "ip_1"].split("/")[1] == "26" else "255.255.255.0"
                        if j == i - 1:
                            nodeGateway = edgeData["ip_1"].split("/")[0]
                            nodeInterface = edgeData["interface_2"]
                    else:
                        previousNodeIP = edgeData["ip_2"].replace(
                            ".1/", ".0/").replace(".2/", ".0/").replace(
                                ".65/", ".64/").replace(".66/",
                                                        ".64/").split("/")[0]
                        previousNodeNetmask = "255.255.255.192" if edgeData[
                            "ip_2"].split("/")[1] == "26" else "255.255.255.0"
                        if j == i - 1:
                            nodeGateway = edgeData["ip_2"].split("/")[0]
                            nodeInterface = edgeData["interface_1"]
                    self.net[nodeId].cmd(
                        "route add -net {} netmask {} gw {} dev {}".format(
                            previousNodeIP, previousNodeNetmask, nodeGateway,
                            nodeInterface))
                    next = previousNodeId
                prev = nodeId
                for j in range(i + 1, len(shortestPath)):
                    nextNodeId = shortestPath[j]
                    edgeData = self.topo.graph.get_edge_data(prev, nextNodeId)
                    if edgeData["id_1"] == nextNodeId:
                        nextNodeIP = edgeData["ip_1"].replace(
                            ".1/", ".0/").replace(".2/", ".0/").replace(
                                ".65/", ".64/").replace(".66/",
                                                        ".64/").split("/")[0]
                        nextNodeNetmask = "255.255.255.192" if edgeData[
                            "ip_1"].split("/")[1] == "26" else "255.255.255.0"
                        if j == i + 1:
                            nodeGateway = edgeData["ip_1"].split("/")[0]
                            nodeInterface = edgeData["interface_2"]
                    else:
                        nextNodeIP = edgeData["ip_2"].replace(
                            ".1/", ".0/").replace(".2/", ".0/").replace(
                                ".65/", ".64/").replace(".66/",
                                                        ".64/").split("/")[0]
                        nextNodeNetmask = "255.255.255.192" if edgeData[
                            "ip_2"].split("/")[1] == "26" else "255.255.255.0"
                        if j == i + 1:
                            nodeGateway = edgeData["ip_2"].split("/")[0]
                            nodeInterface = edgeData["interface_1"]
                    self.net[nodeId].cmd(
                        "route add -net {} netmask {} gw {} dev {}".format(
                            nextNodeIP, nextNodeNetmask, nodeGateway,
                            nodeInterface))
                    prev = nextNodeId

    def buildNet(self):
        info('*** Building network\n')
        self.net.build()

    def startNet(self):
        info('*** Starting network\n')
        self.net.start()

    def postConfig(self):
        self.servers = [self.net["mainServer"]]
        self.caches = [
            self.net[cacheId] for cacheId in self.topo.cacheMemoryDict.keys()
        ]
        self.clients = [self.net[clientId] for clientId in self.topo.clientIds]
        self.routers = [self.net[routerId] for routerId in self.topo.routerIds]

        info(' *** Build Routing Table from mainServer to other hosts\n')
        self.buildRTable()

        info(' *** Test Ping from mainServer to other hosts\n')
        self.caches = [self.net["cache_1"], self.net["cache_2"]]
        self.clients = [self.net["client_1"], self.net["client_2"]]

        self.pingList(self.servers, (self.caches + self.clients))

        info(' *** Loading ClientId to CacheId Map\n')
        self.clientId2CacheIdMap = loadClientId2CacheIdMap(
            self.topo.clientIds, self.topo.graph, self.topo.routerCacheMap)

        info('*** Post configure switches and hosts\n')
        # set up directory
        setup_dir(self.servers)
        setup_dir(self.caches)
        setup_dir(self.clients)

        # get directory
        caches_dir = get_dir(self.caches)
        print(caches_dir)
        clients_dir = get_dir(self.clients)
        print(clients_dir)
        servers_dir = get_dir(self.servers)
        print(servers_dir)

        # open port 80 get data
        open_port(self.caches)
        open_port(self.servers)
        time.sleep(1)  # 1s

    def stopNet(self):
        deconstructor(self.caches)
        deconstructor(self.clients)
        deconstructor(self.servers)
        self.net.stop()

    def useCLI(self):
        CLI(self.net)
Example #4
0
class Functions:
    mini = 0

    def __init__(self):
        self.mini = Mininet(controller=Controller,
                            link=TCLink,
                            accessPoint=OVSKernelAP)

    def stop(self):
        self.mini.stopMobility()
        self.mini.stop()

    def create(self, count_st, count_ap):
        list_st = []
        for x in range(1, count_st + 1):
            list_st.append(
                self.mini.addStation("sta" + str(x),
                                     mac='00:00:00:00:00:0' + str(x),
                                     ip='10.0.0.' + str(x) + '/8',
                                     range=80))

        list_ap = []
        for y in range(1, count_ap + 1):
            list_ap.append(
                self.mini.addAccessPoint('ap' + str(y),
                                         ssid='test_ssid' + str(y),
                                         mode='g',
                                         channel=str(y + 1),
                                         position=str(randint(-200, 200)) +
                                         ',' + str(randint(-200, 200)) + ',0',
                                         range=160))

        c1 = self.mini.addController('c1', controller=Controller)

        self.mini.configureWifiNodes()
        '''self.mini.plotGraph(max_x=300, max_y=300)

        self.mini.seed(20)'''

        "*** Available models: RandomWalk, TruncatedLevyWalk, RandomDirection, RandomWayPoint, GaussMarkov, ReferencePoint, TimeVariantCommunity ***"
        self.mini.startMobility(time=0,
                                model='GaussMarkov',
                                max_x=200,
                                max_y=200,
                                min_x=-200,
                                min_y=-200,
                                min_v=0.3,
                                max_v=1.2)

        print "*** Starting network"
        self.mini.build()
        c1.start()
        for x in range(0, count_ap):
            list_ap[x].start([c1])

    def ping_nodes(self, src, dst):
        hosts = []
        for x in self.mini.stations:
            if x.name == src:
                hosts.append(x)
        for x in self.mini.accessPoints:
            if x.name == src:
                hosts.append(x)

        for x in self.mini.stations:
            if x.name == dst:
                hosts.append(x)
        for x in self.mini.accessPoints:
            if x.name == dst:
                hosts.append(x)
        return hosts

    def ping(self, src=None, dst=None, timeout=None):
        if src is None and dst is None:
            hosts = None
        else:
            hosts = self.ping_nodes(src, dst)
            if not hosts:
                return "Entered wrong set of nodes for pinging!"

        packets = 0
        lost = 0
        ploss = None
        out_string = ""
        if not hosts:
            hosts = self.mini.hosts + self.mini.stations
            out_string += '*** Ping: testing ping reachability\n'
        for node in hosts:
            out_string += '%s -> ' % node.name
            for dest in hosts:
                if node != dest:
                    opts = ''
                    if timeout:
                        opts = '-W %s' % timeout
                    if dest.intfs:
                        result = node.cmdPrint('ping -c1 %s %s' %
                                               (opts, dest.IP()))
                        sent, received = self.mini._parsePing(result)
                    else:
                        sent, received = 0, 0
                    packets += sent
                    if received > sent:
                        out_string += '*** Error: received too many packets\n'
                        out_string += '%s' % result
                        node.cmdPrint('route')
                        exit(1)
                    lost += sent - received
                    out_string += ('%s ' % dest.name) if received else 'X '
            out_string += '\n'
        if packets > 0:
            ploss = 100.0 * lost / packets
            received = packets - lost
            out_string += "*** Results: %i%% dropped (%d/%d received)\n" % (
                ploss, received, packets)
        else:
            ploss = 0
            out_string += "*** Warning: No packets sent\n"
        print out_string
        return out_string

    def ret_info(self):
        array = []

        for x in range(0, len(self.mini.accessPoints)):
            string_pos = str(
                self.mini.accessPoints[x].params['position'][0]) + " " + str(
                    self.mini.accessPoints[x].params['position'][1])
            string_name = self.mini.accessPoints[x].name
            node = make_node(string_name, string_pos, 'ap')
            array.append(node)

        for y in range(0, len(self.mini.stations)):
            string_pos = str(
                self.mini.stations[y].params['position'][0]) + " " + str(
                    self.mini.stations[y].params['position'][1])
            string_name = self.mini.stations[y].name
            node = make_node(string_name, string_pos, 'st')
            array.append(node)

        return array