Пример #1
0
 def slave_del(self, dev_id, slave_dev_id):
     netdev = self._config[dev_id]
     if not "slaves" in netdev or not slave_dev_id in netdev["slaves"]:
         return False
     netdev["slaves"].remove(slave_dev_id)
     device = NetConfigDevice(netdev, self._config)
     device.slave_del(slave_dev_id)
     return True
Пример #2
0
    def set_configuration(self, conf):
        self.clear_configuration()
        if "name" not in conf or conf["name"] == None:
            conf["name"] = self._name
        self._conf_dict = conf
        self._conf = NetConfigDevice(conf, self._if_manager)

        if not self._initialized:
            self._name = conf["name"]
Пример #3
0
 def slave_add(self, dev_id, slave_dev_id):
     netdev = self._config[dev_id]
     if not "slaves" in netdev:
         netdev["slaves"] = []
     elif slave_dev_id in netdev["slaves"]:
         return False
     netdev["slaves"].append(slave_dev_id)
     device = NetConfigDevice(netdev, self._config)
     device.slave_add(slave_dev_id)
     return True
Пример #4
0
    def add_interface_config(self, if_id, config):
        dev_type = config["type"]
        class_initialized = dev_type in self._get_used_types()

        self._config[if_id] = config

        self._devnames.rescan_netdevs()
        self._devnames.assign_name(if_id, self._config)

        dev_config = NetConfigDevice(config, self._config)
        self._dev_configs[if_id] = dev_config

        if not class_initialized:
            logging.info("Initializing '%s' device class", dev_type)
            dev_config.type_init()
Пример #5
0
    def set_configuration(self, conf):
        self.clear_configuration()
        if "name" not in conf or conf["name"] == None:
            conf["name"] = self._name
        self._conf_dict = conf
        self._conf = NetConfigDevice(conf, self._if_manager)

        if not self._initialized:
            self._name = conf["name"]
Пример #6
0
class Device(object):
    def __init__(self, if_manager):
        self._initialized = False
        self._configured = False
        self._created = False
        self._addr_setup = False

        self._if_index = None
        self._hwaddr = None
        self._name = None
        self._conf = None
        self._conf_dict = None
        self._ip_addrs = []
        self._ifi_type = None
        self._state = None
        self._master = {"primary": None, "other": []}
        self._slaves = []
        self._netns = None
        self._peer = None
        self._mtu = None
        self._driver = None
        self._devlink = None

        self._if_manager = if_manager

    def set_devlink(self, devlink_port_data):
        self._devlink = devlink_port_data

    def init_netlink(self, nl_msg):
        self._if_index = nl_msg['index']
        self._ifi_type = nl_msg['ifi_type']
        self._hwaddr = normalize_hwaddr(nl_msg.get_attr("IFLA_ADDRESS"))
        self._name = nl_msg.get_attr("IFLA_IFNAME")
        self._state = nl_msg.get_attr("IFLA_OPERSTATE")
        self._ip_addrs = []
        self.set_master(nl_msg.get_attr("IFLA_MASTER"), primary=True)
        self._netns = None
        self._mtu = nl_msg.get_attr("IFLA_MTU")

        if self._driver is None:
            self._driver = self._ethtool_get_driver()

        self._initialized = True

        #return an update message that will be sent to the controller
        return {"type": "if_update",
                "if_data": self.get_if_data()}

    def update_netlink(self, nl_msg):
        if self._if_index != nl_msg['index']:
            return None
        if nl_msg['header']['type'] == RTM_NEWLINK:
            self._ifi_type = nl_msg['ifi_type']
            self._hwaddr = normalize_hwaddr(nl_msg.get_attr("IFLA_ADDRESS"))
            self._name = nl_msg.get_attr("IFLA_IFNAME")
            self._state = nl_msg.get_attr("IFLA_OPERSTATE")
            self.set_master(nl_msg.get_attr("IFLA_MASTER"), primary=True)
            self._mtu = nl_msg.get_attr("IFLA_MTU")

            link = nl_msg.get_attr("IFLA_LINK")
            if link != None:
                # IFLA_LINK is an index of device that's closer to physical
                # interface in the stack, e.g. index of eth0 for eth0.100
                # so to properly deconfigure the stack we have to save
                # parent index in the child device; this is the opposite
                # to IFLA_MASTER
                link_dev = self._if_manager.get_device(link)
                if link_dev != None:
                    link_dev.set_master(self._if_index, primary=False)
                # This reference shouldn't change - you can't change the realdev
                # of a vlan, you need to create a new vlan. Therefore the
                # the following add_slave shouldn't be a problem.
                self.add_slave(link)

            if self._conf_dict:
                self._conf_dict["name"] = self._name

            if self._driver is None:
                self._driver = self._ethtool_get_driver()

            self._initialized = True
        elif nl_msg['header']['type'] == RTM_NEWADDR:
            scope = nl_msg['scope']
            addr_val = nl_msg.get_attr('IFA_ADDRESS')
            prefix_len = str(nl_msg['prefixlen'])
            addr = {"addr": addr_val,
                    "prefix": prefix_len,
                    "scope": scope}
            if self.find_addrs(addr) == []:
                self._ip_addrs.append(addr)
        elif nl_msg['header']['type'] == RTM_DELADDR:
            scope = nl_msg['scope']
            addr_val = nl_msg.get_attr('IFA_ADDRESS')
            prefix_len = str(nl_msg['prefixlen'])
            addr = {"addr": addr_val,
                    "prefix": prefix_len,
                    "scope": scope}
            matching_addrs = self.find_addrs(addr)
            for ip_addr in matching_addrs:
                self._ip_addrs.remove(ip_addr)

        #return an update message that will be sent to the controller
        return {"type": "if_update",
                "if_data": self.get_if_data()}

    def del_link(self):
        if self._master["primary"]:
            primary_id = self._master["primary"]
            primary_dev = self._if_manager.get_device(primary_id)
            if primary_dev:
                primary_dev.del_slave(self._if_index)

        for m_id in self._master["other"]:
            m_dev = self._if_manager.get_device(m_id)
            if m_dev:
                m_dev.del_slave(self._if_index)

        for dev_id in self._slaves:
            dev = self._if_manager.get_device(dev_id)
            if dev != None:
                dev.del_master(self._if_index)

    def find_addrs(self, addr_spec):
        ret = []
        for addr in self._ip_addrs:
            if addr_spec.items() <= addr.items():
                ret.append(addr)
        return ret

    def get_if_index(self):
        return self._if_index

    def get_hwaddr(self):
        return self._hwaddr

    def get_name(self):
        return self._name

    def get_ips(self):
        return self._ip_addrs

    def clear_ips(self):
        self._ip_addrs = []

    def is_configured(self):
        return self._configured

    def get_conf_dict(self):
        return self._conf_dict

    def set_peer(self, dev):
        self._peer = dev

    def get_peer(self):
        return self._peer

    def set_configuration(self, conf):
        self.clear_configuration()
        if "name" not in conf or conf["name"] == None:
            conf["name"] = self._name
        self._conf_dict = conf
        self._conf = NetConfigDevice(conf, self._if_manager)

        if not self._initialized:
            self._name = conf["name"]

    def get_configuration(self):
        return self._conf

    def del_configuration(self):
        self._conf = None
        self._conf_dict = None

    def _clear_tc_qdisc(self):
        try:
            #checks device existence as it might have been removed by
            #calling self.deconfigure()
            exec_cmd("ip l show %s" % self._name, log_outputs=False)
        except:
            return

        exec_cmd("tc qdisc del dev %s root" % self._name, die_on_err=False)
        out, _ = exec_cmd("tc filter show dev %s" % self._name)
        ingress_handles = re.findall("ingress (\\d+):", out)
        for ingress_handle in ingress_handles:
            exec_cmd("tc qdisc del dev %s handle %s: ingress" %
                     (self._name, ingress_handle))
        out, _ = exec_cmd("tc qdisc show dev %s" % self._name)
        ingress_qdiscs = re.findall("qdisc ingress (\\w+):", out)
        if len(ingress_qdiscs) != 0:
                exec_cmd("tc qdisc del dev %s ingress" % self._name)

    def _clear_tc_filters(self):
        try:
            #checks device existence as it might have been removed by
            #calling self.deconfigure()
            exec_cmd("ip l show %s" % self._name, log_outputs=False)
        except:
            return

        out, _ = exec_cmd("tc filter show dev %s" % self._name)
        egress_prefs = re.findall("pref (\\d+) .* handle", out)

        for egress_pref in egress_prefs:
            exec_cmd("tc filter del dev %s pref %s" % (self._name,
                     egress_pref))

    def clear_configuration(self):
        if self._master["primary"]:
            primary_id = self._master["primary"]
            primary_dev = self._if_manager.get_device(primary_id)
            if primary_dev:
                primary_dev.clear_configuration()

        for m_id in self._master["other"]:
            m_dev = self._if_manager.get_device(m_id)
            if m_dev and self._if_index not in m_dev.get_master()["other"]:
                m_dev.clear_configuration()

        if self._conf != None and self._configured:
            self.address_cleanup()
            self.down()
            self.deconfigure()
            self._clear_tc_qdisc()
            self._clear_tc_filters()
            self.destroy()
            self._conf = None
            self._conf_dict = None

    def set_master(self, if_index, primary=True):
        if primary:
            prev_master_id = self._master["primary"]
            if prev_master_id != None and if_index != prev_master_id:
                prev_master_dev = self._if_manager.get_device(prev_master_id)
                if prev_master_dev != None:
                    prev_master_dev.del_slave(self._if_index)

            self._master["primary"] = if_index
            if self._master["primary"] != None:
                master_id = self._master["primary"]
                master_dev = self._if_manager.get_device(master_id)
                if master_dev != None:
                    master_dev.add_slave(self._if_index)
        elif if_index not in self._master["other"]:
            self._master["other"].append(if_index)

    def get_master(self):
        return self._master

    def del_master(self, if_index):
        if self._master["primary"] == if_index:
            self._master["primary"] = None
        elif if_index in self._master["other"]:
            self._master["other"].remove(if_index)

    def add_slave(self, if_index):
        if if_index not in self._slaves:
            self._slaves.append(if_index)

    def del_slave(self, if_index):
        if if_index in self._slaves:
            self._slaves.remove(if_index)

    def create(self):
        if self._conf != None and not self._created:
            self._conf.create()
            self._created = True
            return True
        return False

    def destroy(self):
        if self._conf != None and self._created:
            self._conf.destroy()
            self._created = False
            return True
        return False

    def configure(self):
        if self._conf != None and not self._configured:
            self._conf.configure()
            self._configured = True

    def deconfigure(self):
        if self._master["primary"]:
            primary_id = self._master["primary"]
            primary_dev = self._if_manager.get_device(primary_id)
            if primary_dev:
                primary_dev.deconfigure()

        for m_id in self._master["other"]:
            m_dev = self._if_manager.get_device(m_id)
            if m_dev and self._if_index not in m_dev.get_master()["other"]:
                m_dev.deconfigure()

        if self._conf != None and self._configured:
            self._conf.deconfigure()
            self._configured = False

    def up(self):
        if self._conf != None:
            self._conf.up()
        else:
            exec_cmd("ip link set %s up" % self._name)

    def down(self):
        if self._conf != None:
            self._conf.down()
        else:
            exec_cmd("ip link set %s down" % self._name)

    def address_setup(self):
        if self._conf != None and self._configured and not self._addr_setup:
            self._conf.address_setup()
            self._addr_setup = True

    def address_cleanup(self):
        if self._conf != None and self._addr_setup:
            self._conf.address_cleanup()
            self._addr_setup = False

    def link_up(self):
        exec_cmd("ip link set %s up" % self._name)

    def link_down(self):
        exec_cmd("ip link set %s down" % self._name)

    def link_stats(self):
        stats = {"devname": self._name,
                 "hwaddr": self._hwaddr}
        try:
            out, _ = exec_cmd("ip -s link show %s" % self._name)
        except:
            return {}

        lines = iter(out.split("\n"))
        for line in lines:
            if (len(line.split()) == 0):
                continue
            if (line.split()[0] == 'vf'):
                break
            if (line.split()[0] == "RX:"):
                rx_stats = map(int, lines.next().split())
                stats.update({"rx_bytes"  : rx_stats[0],
                              "rx_packets": rx_stats[1],
                              "rx_errors" : rx_stats[2],
                              "rx_dropped": rx_stats[3],
                              "rx_overrun": rx_stats[4],
                              "rx_mcast"  : rx_stats[5]})
            if (line.split()[0] == "TX:"):
                tx_stats = map(int, lines.next().split())
                stats.update({"tx_bytes"  : tx_stats[0],
                              "tx_packets": tx_stats[1],
                              "tx_errors" : tx_stats[2],
                              "tx_dropped": tx_stats[3],
                              "tx_carrier": tx_stats[4],
                              "tx_collsns": tx_stats[5]})
        return stats

    def link_cpu_ifstat(self):
        stats = {"devname": self._name,
                 "hwaddr": self._hwaddr}
        try:
            out, _ = exec_cmd("ifstat -x c %s" % self._name)
        except:
            return {}
        lines = iter(out.split("\n"))
        line_first = ""
        line_decond = ""
        for line in lines:
            if (len(line.split()) == 0):
                continue
            if (line.split()[0] == self._name):
                break
        else:
            return {}
        stats_data = line.split()[1:]
        for i in range(len(stats_data)):
            stats_data[i] = stats_data[i].replace("K",  "000")
            stats_data[i] = stats_data[i].replace("M", "000000")

        stats_data = map(int, stats_data)
        stats["rx_packets"] = stats_data[0]
        stats["tx_packets"] = stats_data[2]
        stats["rx_bytes"] = stats_data[4]
        stats["tx_bytes"] = stats_data[6]
        return stats

    def set_addresses(self, ips):
        self._conf.set_addresses(ips)
        exec_cmd("ip addr flush %s scope global" % self._name)
        for address in ips:
            exec_cmd("ip addr add %s dev %s" % (address, self._name))

    def add_route(self, dest, ipv6):
        exec_cmd("ip %s route add %s dev %s" % ("-6" if ipv6 else "", dest, self._name))

    def del_route(self, dest, ipv6):
        exec_cmd("ip %s route del %s dev %s" % ("-6" if ipv6 else "", dest, self._name))

    def route_cmd(self, cmd, dest, nhs, ipv6):
        cmd = "ip %s route %s %s" % ("-6" if ipv6 else "", cmd, dest)
        for ns in nhs:
            cmd = cmd + (" \\\n   nexthop via %s" % ns)
        exec_cmd(cmd)

    def add_nhs_route(self, dest, nhs, ipv6):
        self.route_cmd("add", dest, nhs, ipv6)

    def del_nhs_route(self, dest, nhs, ipv6):
        self.route_cmd("del", dest, nhs, ipv6)

    def set_netns(self, netns):
        self._netns = netns
        return

    def get_netns(self):
        return self._netns

    def _ethtool_get_driver(self):
        if self._ifi_type == 772:  #loopback ifi type
            return 'loopback'
        out, _ = exec_cmd("ethtool -i %s" % self._name, False, False, False)
        match = re.search("^driver: (.*)$", out, re.MULTILINE)
        if match is not None:
            return match.group(1)
        else:
            return None

    def get_if_data(self):
        if_data = {"if_index": self._if_index,
                   "hwaddr": self._hwaddr,
                   "name": self._name,
                   "ip_addrs": self._ip_addrs,
                   "ifi_type": self._ifi_type,
                   "state": self._state,
                   "master": self._master,
                   "slaves": self._slaves,
                   "netns": self._netns,
                   "peer": self._peer.get_if_index() if self._peer else None,
                   "mtu": self._mtu,
                   "driver": self._driver,
                   "devlink": self._devlink}
        return if_data

    def set_speed(self, speed):
        exec_cmd("ethtool -s %s speed %s autoneg off" % (self._name, speed))

    def set_autoneg(self):
        exec_cmd("ethtool -s %s autoneg on" % self._name)

    def slave_add(self, if_id):
        if self._conf != None:
            self._conf.slave_add(if_id)

    def slave_del(self, if_id):
        if self._conf != None:
            self._conf.slave_del(if_id)

    def get_ethtool_stats(self):
        stdout, _ = exec_cmd("ethtool -S %s" % self._name)

        d = {}
        # First and last lines don't contain stats.
        for line in stdout.split('\n')[1:-1]:
            stat, count = line.split(':')
            d[stat.strip()] = int(count)
        return d

    def enable_lldp(self):
        self._conf.enable_lldp()
        exec_cmd("lldptool -i %s -L adminStatus=rxtx" % self._name)

    def set_pause_on(self):
        exec_cmd("ethtool -A %s rx on tx on autoneg off" % self._name,
                 die_on_err=False)

    def set_pause_off(self):
        exec_cmd("ethtool -A %s rx off tx off autoneg off" % self._name,
                 die_on_err=False)

    def set_mcast_flood(self, on = True):
        cmd = "ip link set dev %s type bridge_slave mcast_flood " % self._name
        if on:
            cmd += "on"
        else:
            cmd += "off"
        exec_cmd(cmd)

    def set_mcast_router(self, state):
        cmd = "ip link set dev %s type bridge_slave mcast_router %d" % \
                   (self._name, state)
        exec_cmd(cmd)

    def get_coalesce(self):
        try:
            return ethtool.get_coalesce(self._name)
        except IOError as e:
            logging.error("Failed to get coalesce settings: %s", e)
            return {}

    def set_coalesce(self, cdata):
        try:
            ethtool.set_coalesce(self._name, cdata)
        except IOError as e:
            logging.error("Failed to set coalesce settings: %s", e)
Пример #7
0
class Device(object):
    def __init__(self, if_manager):
        self._initialized = False
        self._configured = False
        self._created = False

        self._if_index = None
        self._hwaddr = None
        self._name = None
        self._conf = None
        self._conf_dict = None
        self._ip = None
        self._ifi_type = None
        self._state = None
        self._master = {"primary": None, "other": []}
        self._slaves = []
        self._netns = None
        self._peer = None
        self._mtu = None
        self._driver = None

        self._if_manager = if_manager

    def init_netlink(self, nl_msg):
        self._if_index = nl_msg['index']
        self._ifi_type = nl_msg['ifi_type']
        self._hwaddr = normalize_hwaddr(nl_msg.get_attr("IFLA_ADDRESS"))
        self._name = nl_msg.get_attr("IFLA_IFNAME")
        self._state = nl_msg.get_attr("IFLA_OPERSTATE")
        self._ip = None #TODO
        self.set_master(nl_msg.get_attr("IFLA_MASTER"), primary=True)
        self._netns = None
        self._mtu = nl_msg.get_attr("IFLA_MTU")

        if self._driver is None:
            self._driver = self._ethtool_get_driver()

        self._initialized = True

    def update_netlink(self, nl_msg):
        if self._if_index == nl_msg['index']:
            self._ifi_type = nl_msg['ifi_type']
            self._hwaddr = normalize_hwaddr(nl_msg.get_attr("IFLA_ADDRESS"))
            self._name = nl_msg.get_attr("IFLA_IFNAME")
            self._state = nl_msg.get_attr("IFLA_OPERSTATE")
            self._ip = None #TODO
            self.set_master(nl_msg.get_attr("IFLA_MASTER"), primary=True)
            self._mtu = nl_msg.get_attr("IFLA_MTU")

            link = nl_msg.get_attr("IFLA_LINK")
            if link != None:
                # IFLA_LINK is an index of device that's closer to physical
                # interface in the stack, e.g. index of eth0 for eth0.100
                # so to properly deconfigure the stack we have to save
                # parent index in the child device; this is the opposite
                # to IFLA_MASTER
                link_dev = self._if_manager.get_device(link)
                if link_dev != None:
                    link_dev.set_master(self._if_index, primary=False)
                # This reference shouldn't change - you can't change the realdev
                # of a vlan, you need to create a new vlan. Therefore the
                # the following add_slave shouldn't be a problem.
                self.add_slave(link)

            if self._conf_dict:
                self._conf_dict["name"] = self._name

            if self._driver is None:
                self._driver = self._ethtool_get_driver()

            self._initialized = True

            #return an update message that will be sent to the controller
            return {"type": "if_update",
                    "if_data": self.get_if_data()}
        return None

    def del_link(self):
        if self._master["primary"]:
            primary_id = self._master["primary"]
            primary_dev = self._if_manager.get_device(primary_id)
            if primary_dev:
                primary_dev.del_slave(self._if_index)

        for m_id in self._master["other"]:
            m_dev = self._if_manager.get_device(m_id)
            if m_dev:
                m_dev.del_slave(self._if_index)

        for dev_id in self._slaves:
            dev = self._if_manager.get_device(dev_id)
            if dev != None:
                dev.del_master(self._if_index)

    def get_if_index(self):
        return self._if_index

    def get_hwaddr(self):
        return self._hwaddr

    def get_name(self):
        return self._name

    def get_ip_conf(self):
        return self._ip

    def is_configured(self):
        return self._configured

    def get_conf_dict(self):
        return self._conf_dict

    def set_peer(self, dev):
        self._peer = dev

    def get_peer(self):
        return self._peer

    def set_configuration(self, conf):
        self.clear_configuration()
        if "name" not in conf or conf["name"] == None:
            conf["name"] = self._name
        self._conf_dict = conf
        self._conf = NetConfigDevice(conf, self._if_manager)

        if not self._initialized:
            self._name = conf["name"]

    def get_configuration(self):
        return self._conf

    def del_configuration(self):
        self._conf = None
        self._conf_dict = None

    def clear_configuration(self):
        if self._master["primary"]:
            primary_id = self._master["primary"]
            primary_dev = self._if_manager.get_device(primary_id)
            if primary_dev:
                primary_dev.clear_configuration()

        for m_id in self._master["other"]:
            m_dev = self._if_manager.get_device(m_id)
            if m_dev:
                m_dev.clear_configuration()

        if self._conf != None:
            self.down()
            self.deconfigure()
            self.destroy()
            self._conf = None
            self._conf_dict = None

    def set_master(self, if_index, primary=True):
        if primary:
            prev_master_id = self._master["primary"]
            if prev_master_id != None and if_index != prev_master_id:
                prev_master_dev = self._if_manager.get_device(prev_master_id)
                if prev_master_dev != None:
                    prev_master_dev.del_slave(self._if_index)

            self._master["primary"] = if_index
            if self._master["primary"] != None:
                master_id = self._master["primary"]
                master_dev = self._if_manager.get_device(master_id)
                if master_dev != None:
                    master_dev.add_slave(self._if_index)
        elif if_index not in self._master["other"]:
            self._master["other"].append(if_index)

    def del_master(self, if_index):
        if self._master["primary"] == if_index:
            self._master["primary"] = None
        elif if_index in self._master["other"]:
            self._master["other"].remove(if_index)

    def add_slave(self, if_index):
        if if_index not in self._slaves:
            self._slaves.append(if_index)

    def del_slave(self, if_index):
        if if_index in self._slaves:
            self._slaves.remove(if_index)

    def create(self):
        if self._conf != None and not self._created:
            self._conf.create()
            self._created = True
            return True
        return False

    def destroy(self):
        if self._conf != None and self._created:
            self._conf.destroy()
            self._created = False
            return True
        return False

    def configure(self):
        if self._conf != None and not self._configured:
            self._conf.configure()
            self._configured = True

    def deconfigure(self):
        if self._master["primary"]:
            primary_id = self._master["primary"]
            primary_dev = self._if_manager.get_device(primary_id)
            if primary_dev:
                primary_dev.deconfigure()

        for m_id in self._master["other"]:
            m_dev = self._if_manager.get_device(m_id)
            if m_dev:
                m_dev.deconfigure()

        if self._conf != None and self._configured:
            self._conf.deconfigure()
            self._configured = False

    def up(self):
        if self._conf != None:
            self._conf.up()
        else:
            exec_cmd("ip link set %s up" % self._name)

    def down(self):
        if self._conf != None:
            self._conf.down()
        else:
            exec_cmd("ip link set %s down" % self._name)

    def link_up(self):
        exec_cmd("ip link set %s up" % self._name)

    def link_down(self):
        exec_cmd("ip link set %s down" % self._name)

    def set_netns(self, netns):
        self._netns = netns
        return

    def get_netns(self):
        return self._netns

    def _ethtool_get_driver(self):
        if self._ifi_type == 772:  #loopback ifi type
            return 'loopback'
        out, _ = exec_cmd("ethtool -i %s" % self._name, False, False, False)
        match = re.search("^driver: (.*)$", out, re.MULTILINE)
        if match is not None:
            return match.group(1)
        else:
            return None

    def get_if_data(self):
        if_data = {"devname": self._name,
                   "hwaddr": self._hwaddr,
                   "mtu": self._mtu,
                   "driver": self._driver}
        return if_data
Пример #8
0
class Device(object):
    def __init__(self, if_manager):
        self._initialized = False
        self._configured = False
        self._created = False
        self._addr_setup = False

        self._if_index = None
        self._hwaddr = None
        self._name = None
        self._conf = None
        self._conf_dict = None
        self._ip_addrs = []
        self._ifi_type = None
        self._state = None
        self._master = {"primary": None, "other": []}
        self._slaves = []
        self._netns = None
        self._peer = None
        self._mtu = None
        self._driver = None
        self._devlink = None

        self._if_manager = if_manager

    def set_devlink(self, devlink_port_data):
        self._devlink = devlink_port_data

    def init_netlink(self, nl_msg):
        self._if_index = nl_msg['index']
        self._ifi_type = nl_msg['ifi_type']
        self._hwaddr = normalize_hwaddr(nl_msg.get_attr("IFLA_ADDRESS"))
        self._name = nl_msg.get_attr("IFLA_IFNAME")
        self._state = nl_msg.get_attr("IFLA_OPERSTATE")
        self._ip_addrs = []
        self.set_master(nl_msg.get_attr("IFLA_MASTER"), primary=True)
        self._netns = None
        self._mtu = nl_msg.get_attr("IFLA_MTU")

        if self._driver is None:
            self._driver = self._ethtool_get_driver()

        self._initialized = True

        #return an update message that will be sent to the controller
        return {"type": "if_update",
                "if_data": self.get_if_data()}

    def update_netlink(self, nl_msg):
        if self._if_index != nl_msg['index']:
            return None
        if nl_msg['header']['type'] == RTM_NEWLINK:
            self._ifi_type = nl_msg['ifi_type']
            self._hwaddr = normalize_hwaddr(nl_msg.get_attr("IFLA_ADDRESS"))
            self._name = nl_msg.get_attr("IFLA_IFNAME")
            self._state = nl_msg.get_attr("IFLA_OPERSTATE")
            self.set_master(nl_msg.get_attr("IFLA_MASTER"), primary=True)
            self._mtu = nl_msg.get_attr("IFLA_MTU")

            if nl_msg.get_nested("IFLA_LINKINFO", "IFLA_INFO_KIND") == "vxlan":
                link = nl_msg.get_nested("IFLA_LINKINFO", "IFLA_INFO_DATA",
                                         "IFLA_VXLAN_LINK")
            elif nl_msg.get_nested("IFLA_LINKINFO", "IFLA_INFO_KIND") == "vti":
                link = nl_msg.get_nested("IFLA_LINKINFO", "IFLA_INFO_DATA",
                                         "IFLA_VTI_LINK")
            else:
                link = nl_msg.get_attr("IFLA_LINK")

            if link != None:
                # IFLA_LINK is an index of device that's closer to physical
                # interface in the stack, e.g. index of eth0 for eth0.100
                # so to properly deconfigure the stack we have to save
                # parent index in the child device; this is the opposite
                # to IFLA_MASTER
                link_dev = self._if_manager.get_device(link)
                if link_dev != None:
                    link_dev.set_master(self._if_index, primary=False)
                # This reference shouldn't change - you can't change the realdev
                # of a vlan, you need to create a new vlan. Therefore the
                # the following add_slave shouldn't be a problem.
                self.add_slave(link)

            if self._conf_dict:
                self._conf_dict["name"] = self._name

            if self._driver is None:
                self._driver = self._ethtool_get_driver()

            self._initialized = True
        elif nl_msg['header']['type'] == RTM_NEWADDR:
            scope = nl_msg['scope']
            addr_val = nl_msg.get_attr('IFA_ADDRESS')
            prefix_len = str(nl_msg['prefixlen'])
            addr = {"addr": addr_val,
                    "prefix": prefix_len,
                    "scope": scope}
            if self.find_addrs(addr) == []:
                self._ip_addrs.append(addr)
        elif nl_msg['header']['type'] == RTM_DELADDR:
            scope = nl_msg['scope']
            addr_val = nl_msg.get_attr('IFA_ADDRESS')
            prefix_len = str(nl_msg['prefixlen'])
            addr = {"addr": addr_val,
                    "prefix": prefix_len,
                    "scope": scope}
            matching_addrs = self.find_addrs(addr)
            for ip_addr in matching_addrs:
                self._ip_addrs.remove(ip_addr)

        #return an update message that will be sent to the controller
        return {"type": "if_update",
                "if_data": self.get_if_data()}

    def del_link(self):
        if self._master["primary"]:
            primary_id = self._master["primary"]
            primary_dev = self._if_manager.get_device(primary_id)
            if primary_dev:
                primary_dev.del_slave(self._if_index)

        for m_id in self._master["other"]:
            m_dev = self._if_manager.get_device(m_id)
            if m_dev:
                m_dev.del_slave(self._if_index)

        for dev_id in self._slaves:
            dev = self._if_manager.get_device(dev_id)
            if dev != None:
                dev.del_master(self._if_index)

    def find_addrs(self, addr_spec):
        ret = []
        for addr in self._ip_addrs:
            if addr_spec.items() <= addr.items():
                ret.append(addr)
        return ret

    def get_if_index(self):
        return self._if_index

    def get_hwaddr(self):
        return self._hwaddr

    def get_name(self):
        return self._name

    def get_ips(self):
        return self._ip_addrs

    def clear_ips(self):
        self._ip_addrs = []

    def is_configured(self):
        return self._configured

    def get_conf_dict(self):
        return self._conf_dict

    def set_peer(self, dev):
        self._peer = dev

    def get_peer(self):
        return self._peer

    def set_configuration(self, conf):
        self.clear_configuration()
        if "name" not in conf or conf["name"] == None:
            conf["name"] = self._name
        self._conf_dict = conf
        self._conf = NetConfigDevice(conf, self._if_manager)

        if not self._initialized:
            self._name = conf["name"]

    def get_configuration(self):
        return self._conf

    def del_configuration(self):
        self._conf = None
        self._conf_dict = None

    def _clear_tc_qdisc(self):
        try:
            #checks device existence as it might have been removed by
            #calling self.deconfigure()
            exec_cmd("ip l show %s" % self._name, log_outputs=False)
        except:
            return

        exec_cmd("tc qdisc del dev %s root" % self._name, die_on_err=False)
        out, _ = exec_cmd("tc filter show dev %s" % self._name)
        ingress_handles = re.findall("ingress (\\d+):", out)
        for ingress_handle in ingress_handles:
            exec_cmd("tc qdisc del dev %s handle %s: ingress" %
                     (self._name, ingress_handle))
        out, _ = exec_cmd("tc qdisc show dev %s" % self._name)
        ingress_qdiscs = re.findall("qdisc ingress (\\w+):", out)
        if len(ingress_qdiscs) != 0:
                exec_cmd("tc qdisc del dev %s ingress" % self._name)

    def _clear_tc_filters(self):
        try:
            #checks device existence as it might have been removed by
            #calling self.deconfigure()
            exec_cmd("ip l show %s" % self._name, log_outputs=False)
        except:
            return

        out, _ = exec_cmd("tc filter show dev %s" % self._name)
        egress_prefs = re.findall("pref (\\d+) .* handle", out)

        for egress_pref in egress_prefs:
            exec_cmd("tc filter del dev %s pref %s" % (self._name,
                     egress_pref))

    def clear_configuration(self):
        if self._master["primary"]:
            primary_id = self._master["primary"]
            primary_dev = self._if_manager.get_device(primary_id)
            if primary_dev:
                primary_dev.clear_configuration()

        for m_id in self._master["other"]:
            m_dev = self._if_manager.get_device(m_id)
            if m_dev and self._if_index not in m_dev.get_master()["other"]:
                m_dev.clear_configuration()

        if self._conf != None and self._configured:
            self.address_cleanup()
            self.down()
            self.deconfigure()
            self._clear_tc_qdisc()
            self._clear_tc_filters()
            self.destroy()
            self._conf = None
            self._conf_dict = None

    def set_master(self, if_index, primary=True):
        if primary:
            prev_master_id = self._master["primary"]
            if prev_master_id != None and if_index != prev_master_id:
                prev_master_dev = self._if_manager.get_device(prev_master_id)
                if prev_master_dev != None:
                    prev_master_dev.del_slave(self._if_index)

            self._master["primary"] = if_index
            if self._master["primary"] != None:
                master_id = self._master["primary"]
                master_dev = self._if_manager.get_device(master_id)
                if master_dev != None:
                    master_dev.add_slave(self._if_index)
        elif if_index not in self._master["other"]:
            self._master["other"].append(if_index)

    def get_master(self):
        return self._master

    def del_master(self, if_index):
        if self._master["primary"] == if_index:
            self._master["primary"] = None
        elif if_index in self._master["other"]:
            self._master["other"].remove(if_index)

    def add_slave(self, if_index):
        if if_index not in self._slaves:
            self._slaves.append(if_index)

    def del_slave(self, if_index):
        if if_index in self._slaves:
            self._slaves.remove(if_index)

    def create(self):
        if self._conf != None and not self._created:
            self._conf.create()
            self._created = True
            return True
        return False

    def destroy(self):
        if self._conf != None and self._created:
            self._conf.destroy()
            self._created = False
            return True
        return False

    def configure(self):
        if self._conf != None and not self._configured:
            self._conf.configure()
            self._configured = True

    def deconfigure(self):
        if self._master["primary"]:
            primary_id = self._master["primary"]
            primary_dev = self._if_manager.get_device(primary_id)
            if primary_dev:
                primary_dev.deconfigure()

        for m_id in self._master["other"]:
            m_dev = self._if_manager.get_device(m_id)
            if m_dev and self._if_index not in m_dev.get_master()["other"]:
                m_dev.deconfigure()

        if self._conf != None and self._configured:
            self._conf.deconfigure()
            self._configured = False

    def up(self):
        if self._conf != None:
            self._conf.up()
        else:
            exec_cmd("ip link set %s up" % self._name)

    def down(self):
        if self._conf != None:
            self._conf.down()
        else:
            exec_cmd("ip link set %s down" % self._name)

    def address_setup(self):
        if self._conf != None and self._configured and not self._addr_setup:
            self._conf.address_setup()
            self._addr_setup = True

    def address_cleanup(self):
        if self._conf != None and self._addr_setup:
            self._conf.address_cleanup()
            self._addr_setup = False

    def link_up(self):
        exec_cmd("ip link set %s up" % self._name)

    def link_down(self):
        exec_cmd("ip link set %s down" % self._name)

    def link_stats(self):
        stats = {"devname": self._name,
                 "hwaddr": self._hwaddr}
        try:
            out, _ = exec_cmd("ip -s link show %s" % self._name)
        except:
            return {}

        lines = iter(out.split("\n"))
        for line in lines:
            if (len(line.split()) == 0):
                continue
            if (line.split()[0] == 'vf'):
                break
            if (line.split()[0] == "RX:"):
                rx_stats = map(int, lines.next().split())
                stats.update({"rx_bytes"  : rx_stats[0],
                              "rx_packets": rx_stats[1],
                              "rx_errors" : rx_stats[2],
                              "rx_dropped": rx_stats[3],
                              "rx_overrun": rx_stats[4],
                              "rx_mcast"  : rx_stats[5]})
            if (line.split()[0] == "TX:"):
                tx_stats = map(int, lines.next().split())
                stats.update({"tx_bytes"  : tx_stats[0],
                              "tx_packets": tx_stats[1],
                              "tx_errors" : tx_stats[2],
                              "tx_dropped": tx_stats[3],
                              "tx_carrier": tx_stats[4],
                              "tx_collsns": tx_stats[5]})
        return stats

    def link_cpu_ifstat(self):
        stats = {"devname": self._name,
                 "hwaddr": self._hwaddr}
        try:
            out, _ = exec_cmd("ifstat -x c %s" % self._name)
        except:
            return {}
        lines = iter(out.split("\n"))
        line_first = ""
        line_decond = ""
        for line in lines:
            if (len(line.split()) == 0):
                continue
            if (line.split()[0] == self._name):
                break
        else:
            return {}
        stats_data = line.split()[1:]
        for i in range(len(stats_data)):
            stats_data[i] = stats_data[i].replace("K",  "000")
            stats_data[i] = stats_data[i].replace("M", "000000")

        stats_data = map(int, stats_data)
        stats["rx_packets"] = stats_data[0]
        stats["tx_packets"] = stats_data[2]
        stats["rx_bytes"] = stats_data[4]
        stats["tx_bytes"] = stats_data[6]
        return stats

    def set_addresses(self, ips):
        self._conf.set_addresses(ips)
        exec_cmd("ip addr flush %s scope global" % self._name)
        for address in ips:
            exec_cmd("ip addr add %s dev %s" % (address, self._name))

    def add_route(self, dest, ipv6):
        exec_cmd("ip %s route add %s dev %s" % ("-6" if ipv6 else "", dest, self._name))

    def del_route(self, dest, ipv6):
        exec_cmd("ip %s route del %s dev %s" % ("-6" if ipv6 else "", dest, self._name))

    def route_cmd(self, cmd, dest, nhs, ipv6):
        cmd = "ip %s route %s %s" % ("-6" if ipv6 else "", cmd, dest)
        for ns in nhs:
            cmd = cmd + (" \\\n   nexthop via %s" % ns)
        exec_cmd(cmd)

    def add_nhs_route(self, dest, nhs, ipv6):
        self.route_cmd("add", dest, nhs, ipv6)

    def del_nhs_route(self, dest, nhs, ipv6):
        self.route_cmd("del", dest, nhs, ipv6)

    def set_netns(self, netns):
        self._netns = netns
        return

    def get_netns(self):
        return self._netns

    def _ethtool_get_driver(self):
        if self._ifi_type == 772:  #loopback ifi type
            return 'loopback'
        out, _ = exec_cmd("ethtool -i %s" % self._name, False, False, False)
        match = re.search("^driver: (.*)$", out, re.MULTILINE)
        if match is not None:
            return match.group(1)
        else:
            return None

    def get_if_data(self):
        if_data = {"if_index": self._if_index,
                   "hwaddr": self._hwaddr,
                   "name": self._name,
                   "ip_addrs": self._ip_addrs,
                   "ifi_type": self._ifi_type,
                   "state": self._state,
                   "master": self._master,
                   "slaves": self._slaves,
                   "netns": self._netns,
                   "peer": self._peer.get_if_index() if self._peer else None,
                   "mtu": self._mtu,
                   "driver": self._driver,
                   "devlink": self._devlink}
        return if_data

    def set_speed(self, speed):
        exec_cmd("ethtool -s %s speed %s autoneg off" % (self._name, speed))

    def set_autoneg(self):
        exec_cmd("ethtool -s %s autoneg on" % self._name)

    def slave_add(self, if_id):
        if self._conf != None:
            self._conf.slave_add(if_id)

    def slave_del(self, if_id):
        if self._conf != None:
            self._conf.slave_del(if_id)

    def get_ethtool_stats(self):
        stdout, _ = exec_cmd("ethtool -S %s" % self._name)

        d = {}
        # First and last lines don't contain stats.
        for line in stdout.split('\n')[1:-1]:
            stat, count = line.split(':')
            d[stat.strip()] = int(count)
        return d

    def enable_lldp(self):
        self._conf.enable_lldp()
        exec_cmd("lldptool -i %s -L adminStatus=rxtx" % self._name)

    def set_pause_on(self):
        exec_cmd("ethtool -A %s rx on tx on autoneg off" % self._name,
                 die_on_err=False)

    def set_pause_off(self):
        exec_cmd("ethtool -A %s rx off tx off autoneg off" % self._name,
                 die_on_err=False)

    def set_mcast_flood(self, on = True):
        cmd = "ip link set dev %s type bridge_slave mcast_flood " % self._name
        if on:
            cmd += "on"
        else:
            cmd += "off"
        exec_cmd(cmd)

    def set_mcast_router(self, state):
        cmd = "ip link set dev %s type bridge_slave mcast_router %d" % \
                   (self._name, state)
        exec_cmd(cmd)

    def get_coalesce(self):
        try:
            return ethtool.get_coalesce(self._name)
        except IOError as e:
            logging.error("Failed to get coalesce settings: %s", e)
            return {}

    def set_coalesce(self, cdata):
        try:
            ethtool.set_coalesce(self._name, cdata)
        except IOError as e:
            logging.error("Failed to set coalesce settings: %s", e)
Пример #9
0
class Device(object):
    def __init__(self, if_manager):
        self._initialized = False
        self._configured = False
        self._created = False
        self._addr_setup = False

        self._if_index = None
        self._hwaddr = None
        self._name = None
        self._conf = None
        self._conf_dict = None
        self._ip_addrs = []
        self._ifi_type = None
        self._state = None
        self._master = {"primary": None, "other": []}
        self._slaves = []
        self._netns = None
        self._peer = None
        self._mtu = None
        self._driver = None
        self._devlink = None

        self._if_manager = if_manager
        self._red_stats_collector = None

    def set_devlink(self, devlink_port_data):
        self._devlink = devlink_port_data

    def init_netlink(self, nl_msg):
        self._if_index = nl_msg['index']
        self._ifi_type = nl_msg['ifi_type']
        self._hwaddr = normalize_hwaddr(nl_msg.get_attr("IFLA_ADDRESS"))
        self._name = nl_msg.get_attr("IFLA_IFNAME")
        self._state = nl_msg.get_attr("IFLA_OPERSTATE")
        self._ip_addrs = []
        self.set_master(nl_msg.get_attr("IFLA_MASTER"), primary=True)
        self._netns = None
        self._mtu = nl_msg.get_attr("IFLA_MTU")

        if self._driver is None:
            self._driver = self._ethtool_get_driver()

        self._initialized = True

        #return an update message that will be sent to the controller
        return {"type": "if_update",
                "if_data": self.get_if_data()}

    def update_netlink(self, nl_msg):
        if self._if_index != nl_msg['index']:
            return None
        if nl_msg['header']['type'] == RTM_NEWLINK:
            self._ifi_type = nl_msg['ifi_type']
            self._hwaddr = normalize_hwaddr(nl_msg.get_attr("IFLA_ADDRESS"))
            self._name = nl_msg.get_attr("IFLA_IFNAME")
            self._state = nl_msg.get_attr("IFLA_OPERSTATE")
            self.set_master(nl_msg.get_attr("IFLA_MASTER"), primary=True)
            self._mtu = nl_msg.get_attr("IFLA_MTU")

            link = nl_msg.get_attr("IFLA_LINK")
            if link != None:
                # IFLA_LINK is an index of device that's closer to physical
                # interface in the stack, e.g. index of eth0 for eth0.100
                # so to properly deconfigure the stack we have to save
                # parent index in the child device; this is the opposite
                # to IFLA_MASTER
                link_dev = self._if_manager.get_device(link)
                if link_dev != None:
                    link_dev.set_master(self._if_index, primary=False)
                # This reference shouldn't change - you can't change the realdev
                # of a vlan, you need to create a new vlan. Therefore the
                # the following add_slave shouldn't be a problem.
                self.add_slave(link)

            if self._conf_dict:
                self._conf_dict["name"] = self._name

            if self._driver is None:
                self._driver = self._ethtool_get_driver()

            self._initialized = True
        elif nl_msg['header']['type'] == RTM_NEWADDR:
            scope = nl_msg['scope']
            addr_val = nl_msg.get_attr('IFA_ADDRESS')
            prefix_len = str(nl_msg['prefixlen'])
            addr = {"addr": addr_val,
                    "prefix": prefix_len,
                    "scope": scope}
            if self.find_addrs(addr) == []:
                self._ip_addrs.append(addr)
        elif nl_msg['header']['type'] == RTM_DELADDR:
            scope = nl_msg['scope']
            addr_val = nl_msg.get_attr('IFA_ADDRESS')
            prefix_len = str(nl_msg['prefixlen'])
            addr = {"addr": addr_val,
                    "prefix": prefix_len,
                    "scope": scope}
            matching_addrs = self.find_addrs(addr)
            for ip_addr in matching_addrs:
                self._ip_addrs.remove(ip_addr)

        #return an update message that will be sent to the controller
        return {"type": "if_update",
                "if_data": self.get_if_data()}

    def del_link(self):
        if self._master["primary"]:
            primary_id = self._master["primary"]
            primary_dev = self._if_manager.get_device(primary_id)
            if primary_dev:
                primary_dev.del_slave(self._if_index)

        for m_id in self._master["other"]:
            m_dev = self._if_manager.get_device(m_id)
            if m_dev:
                m_dev.del_slave(self._if_index)

        for dev_id in self._slaves:
            dev = self._if_manager.get_device(dev_id)
            if dev != None:
                dev.del_master(self._if_index)

    def find_addrs(self, addr_spec):
        ret = []
        for addr in self._ip_addrs:
            if addr_spec.items() <= addr.items():
                ret.append(addr)
        return ret

    def get_if_index(self):
        return self._if_index

    def get_hwaddr(self):
        return self._hwaddr

    def get_name(self):
        return self._name

    def get_ips(self):
        return self._ip_addrs

    def clear_ips(self):
        self._ip_addrs = []

    def is_configured(self):
        return self._configured

    def get_conf_dict(self):
        return self._conf_dict

    def set_peer(self, dev):
        self._peer = dev

    def get_peer(self):
        return self._peer

    def set_configuration(self, conf):
        self.clear_configuration()
        if "name" not in conf or conf["name"] == None:
            conf["name"] = self._name
        self._conf_dict = conf
        self._conf = NetConfigDevice(conf, self._if_manager)

        if not self._initialized:
            self._name = conf["name"]

    def get_configuration(self):
        return self._conf

    def del_configuration(self):
        self._conf = None
        self._conf_dict = None

    def _clear_tc_qdisc(self):
        try:
            #checks device existence as it might have been removed by
            #calling self.deconfigure()
            exec_cmd("ip l show %s" % self._name, log_outputs=False)
        except:
            return

        exec_cmd("tc qdisc del dev %s root" % self._name, die_on_err=False)
        out, _ = exec_cmd("tc filter show dev %s" % self._name)
        ingress_handles = re.findall("ingress (\\d+):", out)
        for ingress_handle in ingress_handles:
            exec_cmd("tc qdisc del dev %s handle %s: ingress" %
                     (self._name, ingress_handle))
        out, _ = exec_cmd("tc qdisc show dev %s" % self._name)
        ingress_qdiscs = re.findall("qdisc ingress (\\w+):", out)
        if len(ingress_qdiscs) != 0:
                exec_cmd("tc qdisc del dev %s ingress" % self._name)

    def _clear_tc_filters(self):
        try:
            #checks device existence as it might have been removed by
            #calling self.deconfigure()
            exec_cmd("ip l show %s" % self._name, log_outputs=False)
        except:
            return

        out, _ = exec_cmd("tc filter show dev %s" % self._name)
        egress_prefs = re.findall("pref (\\d+) .* handle", out)

        for egress_pref in egress_prefs:
            exec_cmd("tc filter del dev %s pref %s" % (self._name,
                     egress_pref))

    def clear_configuration(self):
        if self._red_stats_collector != None:
            self._red_stats_collector.stop()
        if self._master["primary"]:
            primary_id = self._master["primary"]
            primary_dev = self._if_manager.get_device(primary_id)
            if primary_dev:
                primary_dev.clear_configuration()

        for m_id in self._master["other"]:
            m_dev = self._if_manager.get_device(m_id)
            if m_dev and self._if_index not in m_dev.get_master()["other"]:
                m_dev.clear_configuration()

        if self._conf != None and self._configured:
            self.address_cleanup()
            self.down()
            self.deconfigure()
            self._clear_tc_qdisc()
            self._clear_tc_filters()
            self.destroy()
            self._conf = None
            self._conf_dict = None

    def set_master(self, if_index, primary=True):
        if primary:
            prev_master_id = self._master["primary"]
            if prev_master_id != None and if_index != prev_master_id:
                prev_master_dev = self._if_manager.get_device(prev_master_id)
                if prev_master_dev != None:
                    prev_master_dev.del_slave(self._if_index)

            self._master["primary"] = if_index
            if self._master["primary"] != None:
                master_id = self._master["primary"]
                master_dev = self._if_manager.get_device(master_id)
                if master_dev != None:
                    master_dev.add_slave(self._if_index)
        elif if_index not in self._master["other"]:
            self._master["other"].append(if_index)

    def get_master(self):
        return self._master

    def del_master(self, if_index):
        if self._master["primary"] == if_index:
            self._master["primary"] = None
        elif if_index in self._master["other"]:
            self._master["other"].remove(if_index)

    def add_slave(self, if_index):
        if if_index not in self._slaves:
            self._slaves.append(if_index)

    def del_slave(self, if_index):
        if if_index in self._slaves:
            self._slaves.remove(if_index)

    def create(self):
        if self._conf != None and not self._created:
            self._conf.create()
            self._created = True
            return True
        return False

    def destroy(self):
        if self._conf != None and self._created:
            self._conf.destroy()
            self._created = False
            return True
        return False

    def configure(self):
        if self._conf != None and not self._configured:
            self._conf.configure()
            self._configured = True

    def deconfigure(self):
        if self._master["primary"]:
            primary_id = self._master["primary"]
            primary_dev = self._if_manager.get_device(primary_id)
            if primary_dev:
                primary_dev.deconfigure()

        for m_id in self._master["other"]:
            m_dev = self._if_manager.get_device(m_id)
            if m_dev and self._if_index not in m_dev.get_master()["other"]:
                m_dev.deconfigure()

        if self._conf != None and self._configured:
            self._conf.deconfigure()
            self._configured = False

    def up(self):
        if self._conf != None:
            self._conf.up()
        else:
            exec_cmd("ip link set %s up" % self._name)

    def is_up(self, max_time):
        start_time = time()
        while(time() - start_time < max_time):
            out, _ = exec_cmd("ip l show dev %s up | grep 'state UP'" % self._name, die_on_err = False)
            if out == "":
                logging.info("sleep 20!")
                sleep(20)
            else:
                logging.info("port up!")
                return True
        raise IfMgrError("Time out - device %s not up." % self._name)

    def down(self):
        if self._conf != None:
            self._conf.down()
        else:
            exec_cmd("ip link set %s down" % self._name)

    def address_setup(self):
        if self._conf != None and self._configured and not self._addr_setup:
            self._conf.address_setup()
            self._addr_setup = True

    def address_cleanup(self):
        if self._conf != None and self._addr_setup:
            self._conf.address_cleanup()
            self._addr_setup = False

    def link_up(self):
        exec_cmd("ip link set %s up" % self._name)

    def link_down(self):
        exec_cmd("ip link set %s down" % self._name)

    def link_stats(self):
        stats = {"devname": self._name,
                 "hwaddr": self._hwaddr}
        try:
            out, _ = exec_cmd("ip -s link show %s" % self._name)
        except:
            return {}

        lines = iter(out.split("\n"))
        for line in lines:
            if (len(line.split()) == 0):
                continue
            if (line.split()[0] == 'vf'):
                break
            if (line.split()[0] == "RX:"):
                rx_stats = map(int, lines.next().split())
                stats.update({"rx_bytes"  : rx_stats[0],
                              "rx_packets": rx_stats[1],
                              "rx_errors" : rx_stats[2],
                              "rx_dropped": rx_stats[3],
                              "rx_overrun": rx_stats[4],
                              "rx_mcast"  : rx_stats[5]})
            if (line.split()[0] == "TX:"):
                tx_stats = map(int, lines.next().split())
                stats.update({"tx_bytes"  : tx_stats[0],
                              "tx_packets": tx_stats[1],
                              "tx_errors" : tx_stats[2],
                              "tx_dropped": tx_stats[3],
                              "tx_carrier": tx_stats[4],
                              "tx_collsns": tx_stats[5]})
        return stats

    def link_cpu_ifstat(self):
        stats = {"devname": self._name,
                 "hwaddr": self._hwaddr}
        if len(self._slaves) != 0:
            return self.link_cpu_ifstat_from_slaves()

        try:
            out, _ = exec_cmd("ifstat -x c %s" % self._name)
        except:
            return {}
        lines = iter(out.split("\n"))
        line_first = ""
        line_decond = ""
        for line in lines:
            if (len(line.split()) == 0):
                continue
            if (line.split()[0] == self._name):
                break
        else:
            return {}
        stats_data = line.split()[1:]
        for i in range(len(stats_data)):
            stats_data[i] = stats_data[i].replace("K",  "000")
            stats_data[i] = stats_data[i].replace("M", "000000")

        stats_data = map(int, stats_data)
        stats["rx_packets"] = stats_data[0]
        stats["tx_packets"] = stats_data[2]
        stats["rx_bytes"] = stats_data[4]
        stats["tx_bytes"] = stats_data[6]
        return stats

    def link_cpu_ifstat_from_slaves(self):
        stats = {"devname": self._name,
                 "hwaddr": self._hwaddr}
        fields_names = ["rx_packets", "tx_packets", "rx_bytes", "tx_bytes"]
        for field in fields_names:
            stats[field] = 0
        for slave_index in self._slaves:
            slave = self._if_manager.get_device(slave_index)
            if slave is None:
                pass
            slave_stats = slave.link_cpu_ifstat()
            for field in fields_names:
                if field in slave_stats:
                    stats[field] += slave_stats[field]
        return stats

    def set_qdisc_prio(self, bands = None, priomap = None, change = False):
        cmd = "change" if change else "add"
        cmd = "tc qdisc %s dev %s root handle 3: prio" % (cmd, self._name)
        if bands:
            cmd += " bands %d" % bands
        if priomap:
            cmd += " priomap %s" % priomap
        exec_cmd(cmd)

    def set_qdisc_red(self, limit, avpkt, _min, _max, prob = 0, ecn = False,
                      change = False, burst = None, parent = None):
        cmd = "change" if change else "add"
        if parent:
            parent = "parent 3:%d" % parent
        else:
            parent = "root"

        cmd = "tc qdisc %s dev %s %s red limit %d avpkt %d min %d max %d" % \
              (cmd, self._name, parent, limit, avpkt, _min, _max)
        if prob:
            cmd += " prob %d" % prob
        if ecn:
            cmd += " ecn"
        if burst:
            cmd += " burst %d" % burst
        exec_cmd(cmd)

    def unset_qdisc(self, parent = None):
        if parent:
            parent = "parent 3:%d" % parent
        else:
            parent = "root"
        exec_cmd("tc qdisc del dev %s %s" % (self._name, parent))

    def qdisc_red_stats(self, parent = None):
        try:
            out, _ = exec_cmd("tc -s qdisc show dev %s" % self._name)
        except ExecCmdFail:
            return {}

        if parent:
            parent = "parent 3:%d" % parent
        else:
            parent = "root"

        p = r"[.\n]*?qdisc red.+?%s.+?(offloaded)? limit.*?\n\s*Sent " \
            r"(?P<tx_bytes>\d+) bytes (?P<tx_packets>\d+) pkt \(dropped " \
            r"(?P<drops>\d+), overlimits (?P<overlimits>\d+).*\n\s*backlog " \
            r"(?P<backlog>\d+\w?)b.*\n.*?marked (?P<marked>\d+) early " \
            r"(?P<early>\d+) pdrop (?P<pdrop>\d+)" % parent
        red_pattern = re.compile(p, re.MULTILINE)
        stats_raw = red_pattern.search(out)
        if not stats_raw:
            return {}

        stats = {key: int(val.replace("K", "000").replace("M", "000000"))
                for key, val in stats_raw.groupdict().iteritems()}

        stats["offload"] = "offloaded" in stats_raw.groups()
        stats["devname"] = self._name
        stats["hwaddr"] = self._hwaddr
        return stats

    class QdiscRedStatCollector:
        def __init__(self, get_stats_func, parent):
            self._get_stats_func = get_stats_func
            self._stop_flag = Value("b", True)
            self._stats = Manager().dict()
            self._p = None
            self._old_stats = {}
            self._backlogs = None
            self._parent = parent

        def _collect_qdisc_red_stats(self, stop_flag, stats, backlogs):
            while not stop_flag.value:
                new_stats = self._get_stats_func(self._parent)
                if new_stats == {}:
                    continue
                if new_stats["tx_packets"] > stats["tx_packets"]:
                    stats.update(new_stats)
                    backlogs.append(stats["backlog"])
                sleep(0.25)

        def start(self):
            if not self._stop_flag.value:
                return

            self._stats.clear()
            self._backlogs = Manager().list()
            self._old_stats = self._get_stats_func(self._parent)
            self._stats.update(self._old_stats)
            self._stop_flag.value = False
            self._p = Process(target=self._collect_qdisc_red_stats,
                             args=(self._stop_flag, self._stats, self._backlogs))
            self._p.start()

        def stop(self):
            if self._stop_flag.value:
                return

            self._stop_flag.value = True
            self._p.join(0)
            backlogs = list(self._backlogs)
            stats = self._stats.copy()
            for key in self._old_stats.keys():
                if key not in stats:
                    continue
                if type(stats[key]) in (int, long):
                    stats[key] -= self._old_stats[key]
            return (backlogs, stats)

    def collect_qdisc_red_stats(self, parent = None):
        assert self._red_stats_collector is None
        self._red_stats_collector = self.QdiscRedStatCollector(self.qdisc_red_stats,
                                                               parent)
        self._red_stats_collector.start()

    def stop_collecting_qdisc_red_stats(self):
        ret = {}
        if self._red_stats_collector is not None:
            ret = self._red_stats_collector.stop()
            self._red_stats_collector = None
        return ret

    def set_addresses(self, ips):
        self._conf.set_addresses(ips)
        exec_cmd("ip addr flush %s scope global" % self._name)
        for address in ips:
            exec_cmd("ip addr add %s dev %s" % (address, self._name))

    def add_route(self, dest, ipv6):
        exec_cmd("ip %s route add %s dev %s" % ("-6" if ipv6 else "", dest, self._name))

    def del_route(self, dest, ipv6):
        exec_cmd("ip %s route del %s dev %s" % ("-6" if ipv6 else "", dest, self._name), die_on_err=False)

    def route_cmd(self, cmd, dest, nhs, ipv6, die_on_err=True):
        cmd = "ip %s route %s %s" % ("-6" if ipv6 else "", cmd, dest)
        for ns in nhs:
            cmd = cmd + (" \\\n   nexthop via %s" % ns)
        exec_cmd(cmd, die_on_err)

    def add_nhs_route(self, dest, nhs, ipv6):
        self.route_cmd("add", dest, nhs, ipv6)

    def del_nhs_route(self, dest, nhs, ipv6):
        self.route_cmd("del", dest, nhs, ipv6, die_on_err=False)

    def set_netns(self, netns):
        self._netns = netns
        return

    def get_netns(self):
        return self._netns

    def _ethtool_get_driver(self):
        if self._ifi_type == 772:  #loopback ifi type
            return 'loopback'
        out, _ = exec_cmd("ethtool -i %s" % self._name, False, False, False)
        match = re.search("^driver: (.*)$", out, re.MULTILINE)
        if match is not None:
            return match.group(1)
        else:
            return None

    def get_if_data(self):
        if_data = {"if_index": self._if_index,
                   "hwaddr": self._hwaddr,
                   "name": self._name,
                   "ip_addrs": self._ip_addrs,
                   "ifi_type": self._ifi_type,
                   "state": self._state,
                   "master": self._master,
                   "slaves": self._slaves,
                   "netns": self._netns,
                   "peer": self._peer.get_if_index() if self._peer else None,
                   "mtu": self._mtu,
                   "driver": self._driver,
                   "devlink": self._devlink}
        return if_data

    def set_speed(self, speed):
        exec_cmd("ethtool -s %s speed %s autoneg off" % (self._name, speed))

    def set_autoneg(self):
        exec_cmd("ethtool -s %s autoneg on" % self._name)

    def slave_add(self, if_id):
        if self._conf != None:
            self._conf.slave_add(if_id)

    def slave_del(self, if_id):
        if self._conf != None:
            self._conf.slave_del(if_id)

    def get_ethtool_stats(self):
        stdout, _ = exec_cmd("ethtool -S %s" % self._name)

        d = {}
        # First and last lines don't contain stats.
        for line in stdout.split('\n')[1:-1]:
            stat, count = line.split(':')
            d[stat.strip()] = int(count)
        return d

    def enable_lldp(self):
        self._conf.enable_lldp()
        exec_cmd("lldptool -i %s -L adminStatus=rxtx" % self._name)

    def set_pause_on(self):
        exec_cmd("ethtool -A %s rx on tx on autoneg off" % self._name,
                 die_on_err=False)

    def set_pause_off(self):
        exec_cmd("ethtool -A %s rx off tx off autoneg off" % self._name,
                 die_on_err=False)

    def set_mcast_flood(self, on = True):
        cmd = "ip link set dev %s type bridge_slave mcast_flood " % self._name
        if on:
            cmd += "on"
        else:
            cmd += "off"
        exec_cmd(cmd)

    def set_mcast_router(self, state):
        cmd = "ip link set dev %s type bridge_slave mcast_router %d" % \
                   (self._name, state)
        exec_cmd(cmd)
Пример #10
0
class Device(object):
    def __init__(self, if_manager):
        self._initialized = False
        self._configured = False
        self._created = False

        self._if_index = None
        self._hwaddr = None
        self._name = None
        self._conf = None
        self._conf_dict = None
        self._ip_addrs = []
        self._ifi_type = None
        self._state = None
        self._master = {"primary": None, "other": []}
        self._slaves = []
        self._netns = None
        self._peer = None
        self._mtu = None
        self._driver = None

        self._if_manager = if_manager

    def init_netlink(self, nl_msg):
        self._if_index = nl_msg['index']
        self._ifi_type = nl_msg['ifi_type']
        self._hwaddr = normalize_hwaddr(nl_msg.get_attr("IFLA_ADDRESS"))
        self._name = nl_msg.get_attr("IFLA_IFNAME")
        self._state = nl_msg.get_attr("IFLA_OPERSTATE")
        self._ip_addrs = []
        self.set_master(nl_msg.get_attr("IFLA_MASTER"), primary=True)
        self._netns = None
        self._mtu = nl_msg.get_attr("IFLA_MTU")

        if self._driver is None:
            self._driver = self._ethtool_get_driver()

        self._initialized = True

        #return an update message that will be sent to the controller
        return {"type": "if_update",
                "if_data": self.get_if_data()}

    def update_netlink(self, nl_msg):
        if self._if_index != nl_msg['index']:
            return None
        if nl_msg['header']['type'] == RTM_NEWLINK:
            self._ifi_type = nl_msg['ifi_type']
            self._hwaddr = normalize_hwaddr(nl_msg.get_attr("IFLA_ADDRESS"))
            self._name = nl_msg.get_attr("IFLA_IFNAME")
            self._state = nl_msg.get_attr("IFLA_OPERSTATE")
            self.set_master(nl_msg.get_attr("IFLA_MASTER"), primary=True)
            self._mtu = nl_msg.get_attr("IFLA_MTU")

            link = nl_msg.get_attr("IFLA_LINK")
            if link != None:
                # IFLA_LINK is an index of device that's closer to physical
                # interface in the stack, e.g. index of eth0 for eth0.100
                # so to properly deconfigure the stack we have to save
                # parent index in the child device; this is the opposite
                # to IFLA_MASTER
                link_dev = self._if_manager.get_device(link)
                if link_dev != None:
                    link_dev.set_master(self._if_index, primary=False)
                # This reference shouldn't change - you can't change the realdev
                # of a vlan, you need to create a new vlan. Therefore the
                # the following add_slave shouldn't be a problem.
                self.add_slave(link)

            if self._conf_dict:
                self._conf_dict["name"] = self._name

            if self._driver is None:
                self._driver = self._ethtool_get_driver()

            self._initialized = True
        elif nl_msg['header']['type'] == RTM_NEWADDR:
            scope = nl_msg['scope']
            addr_val = nl_msg.get_attr('IFA_ADDRESS')
            prefix_len = str(nl_msg['prefixlen'])
            addr = {"addr": addr_val,
                    "prefix": prefix_len,
                    "scope": scope}
            if self.find_addrs(addr) == []:
                self._ip_addrs.append(addr)
        elif nl_msg['header']['type'] == RTM_DELADDR:
            scope = nl_msg['scope']
            addr_val = nl_msg.get_attr('IFA_ADDRESS')
            prefix_len = str(nl_msg['prefixlen'])
            addr = {"addr": addr_val,
                    "prefix": prefix_len,
                    "scope": scope}
            matching_addrs = self.find_addrs(addr)
            for ip_addr in matching_addrs:
                self._ip_addrs.remove(ip_addr)

        #return an update message that will be sent to the controller
        return {"type": "if_update",
                "if_data": self.get_if_data()}

    def del_link(self):
        if self._master["primary"]:
            primary_id = self._master["primary"]
            primary_dev = self._if_manager.get_device(primary_id)
            if primary_dev:
                primary_dev.del_slave(self._if_index)

        for m_id in self._master["other"]:
            m_dev = self._if_manager.get_device(m_id)
            if m_dev:
                m_dev.del_slave(self._if_index)

        for dev_id in self._slaves:
            dev = self._if_manager.get_device(dev_id)
            if dev != None:
                dev.del_master(self._if_index)

    def find_addrs(self, addr_spec):
        ret = []
        for addr in self._ip_addrs:
            if addr_spec.items() <= addr.items():
                ret.append(addr)
        return ret

    def get_if_index(self):
        return self._if_index

    def get_hwaddr(self):
        return self._hwaddr

    def get_name(self):
        return self._name

    def get_ips(self):
        return self._ip_addrs

    def clear_ips(self):
        self._ip_addrs = []

    def is_configured(self):
        return self._configured

    def get_conf_dict(self):
        return self._conf_dict

    def set_peer(self, dev):
        self._peer = dev

    def get_peer(self):
        return self._peer

    def set_configuration(self, conf):
        self.clear_configuration()
        if "name" not in conf or conf["name"] == None:
            conf["name"] = self._name
        self._conf_dict = conf
        self._conf = NetConfigDevice(conf, self._if_manager)

        if not self._initialized:
            self._name = conf["name"]

    def get_configuration(self):
        return self._conf

    def del_configuration(self):
        self._conf = None
        self._conf_dict = None

    def clear_configuration(self):
        if self._master["primary"]:
            primary_id = self._master["primary"]
            primary_dev = self._if_manager.get_device(primary_id)
            if primary_dev:
                primary_dev.clear_configuration()

        for m_id in self._master["other"]:
            m_dev = self._if_manager.get_device(m_id)
            if m_dev:
                m_dev.clear_configuration()

        if self._conf != None:
            self.down()
            self.deconfigure()
            self.destroy()
            self._conf = None
            self._conf_dict = None

    def set_master(self, if_index, primary=True):
        if primary:
            prev_master_id = self._master["primary"]
            if prev_master_id != None and if_index != prev_master_id:
                prev_master_dev = self._if_manager.get_device(prev_master_id)
                if prev_master_dev != None:
                    prev_master_dev.del_slave(self._if_index)

            self._master["primary"] = if_index
            if self._master["primary"] != None:
                master_id = self._master["primary"]
                master_dev = self._if_manager.get_device(master_id)
                if master_dev != None:
                    master_dev.add_slave(self._if_index)
        elif if_index not in self._master["other"]:
            self._master["other"].append(if_index)

    def del_master(self, if_index):
        if self._master["primary"] == if_index:
            self._master["primary"] = None
        elif if_index in self._master["other"]:
            self._master["other"].remove(if_index)

    def add_slave(self, if_index):
        if if_index not in self._slaves:
            self._slaves.append(if_index)

    def del_slave(self, if_index):
        if if_index in self._slaves:
            self._slaves.remove(if_index)

    def create(self):
        if self._conf != None and not self._created:
            self._conf.create()
            self._created = True
            return True
        return False

    def destroy(self):
        if self._conf != None and self._created:
            self._conf.destroy()
            self._created = False
            return True
        return False

    def configure(self):
        if self._conf != None and not self._configured:
            self._conf.configure()
            self._configured = True

    def deconfigure(self):
        if self._master["primary"]:
            primary_id = self._master["primary"]
            primary_dev = self._if_manager.get_device(primary_id)
            if primary_dev:
                primary_dev.deconfigure()

        for m_id in self._master["other"]:
            m_dev = self._if_manager.get_device(m_id)
            if m_dev:
                m_dev.deconfigure()

        if self._conf != None and self._configured:
            self._conf.deconfigure()
            self._configured = False

    def up(self):
        if self._conf != None:
            self._conf.up()
        else:
            exec_cmd("ip link set %s up" % self._name)

    def down(self):
        if self._conf != None:
            self._conf.down()
        else:
            exec_cmd("ip link set %s down" % self._name)

    def link_up(self):
        exec_cmd("ip link set %s up" % self._name)

    def link_down(self):
        exec_cmd("ip link set %s down" % self._name)

    def link_stats(self):
        stats = {"devname": self._name,
                 "hwaddr": self._hwaddr}
        out, _ = exec_cmd("ip -s link show %s" % self._name)
        lines = iter(out.split("\n"))
        for line in lines:
            if (len(line.split()) == 0):
                continue
            if (line.split()[0] == "RX:"):
                rx_stats = map(int, lines.next().split())
                stats.update({"rx_bytes"  : rx_stats[0],
                              "rx_packets": rx_stats[1],
                              "rx_errors" : rx_stats[2],
                              "rx_dropped": rx_stats[3],
                              "rx_overrun": rx_stats[4],
                              "rx_mcast"  : rx_stats[5]})
            if (line.split()[0] == "TX:"):
                tx_stats = map(int, lines.next().split())
                stats.update({"tx_bytes"  : tx_stats[0],
                              "tx_packets": tx_stats[1],
                              "tx_errors" : tx_stats[2],
                              "tx_dropped": tx_stats[3],
                              "tx_carrier": tx_stats[4],
                              "tx_collsns": tx_stats[5]})
        return stats

    def set_addresses(self, ips):
        self._conf.set_addresses(ips)
        exec_cmd("ip addr flush %s" % self._name)
        for address in ips:
            exec_cmd("ip addr add %s dev %s" % (address, self._name))

    def add_route(self, dest):
        exec_cmd("ip route add %s dev %s" % (dest, self._name))

    def del_route(self, dest):
        exec_cmd("ip route del %s dev %s" % (dest, self._name))

    def set_netns(self, netns):
        self._netns = netns
        return

    def get_netns(self):
        return self._netns

    def _ethtool_get_driver(self):
        if self._ifi_type == 772:  #loopback ifi type
            return 'loopback'
        out, _ = exec_cmd("ethtool -i %s" % self._name, False, False, False)
        match = re.search("^driver: (.*)$", out, re.MULTILINE)
        if match is not None:
            return match.group(1)
        else:
            return None

    def get_if_data(self):
        if_data = {"if_index": self._if_index,
                   "hwaddr": self._hwaddr,
                   "name": self._name,
                   "ip_addrs": self._ip_addrs,
                   "ifi_type": self._ifi_type,
                   "state": self._state,
                   "master": self._master,
                   "slaves": self._slaves,
                   "netns": self._netns,
                   "peer": self._peer.get_if_index() if self._peer else None,
                   "mtu": self._mtu,
                   "driver": self._driver}
        return if_data

    def set_speed(self, speed):
        exec_cmd("ethtool -s %s speed %s autoneg off" % (self._name, speed))

    def set_autoneg(self):
        exec_cmd("ethtool -s %s autoneg on" % self._name)

    def slave_add(self, if_id):
        if self._conf != None:
            self._conf.slave_add(if_id)

    def slave_del(self, if_id):
        if self._conf != None:
            self._conf.slave_del(if_id)