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
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 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
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()
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)
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
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)
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)
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)