def test_basic(self): from pyroute2 import IPRSocket from pyroute2.netlink import NLM_F_REQUEST from pyroute2.netlink import NLM_F_DUMP from pyroute2.netlink import NLM_F_ROOT from pyroute2.netlink import NLM_F_MATCH from pyroute2.netlink import NLMSG_DONE from pyroute2.netlink import NLMSG_ERROR from pyroute2.netlink import nlmsg from pyroute2.iproute import RTM_GETLINK from pyroute2.iproute import RTM_NEWLINK from pyroute2.netlink.rtnl.ifinfmsg import ifinfmsg ip = IPRSocket() ip.bind() # check the `socket` interface compliance poll = select.poll() poll.register(ip, select.POLLIN | select.POLLPRI) poll.unregister(ip) ip.close() assert issubclass(ifinfmsg, nlmsg) assert NLM_F_REQUEST == 1 assert NLM_F_ROOT == 0x100 assert NLM_F_MATCH == 0x200 assert NLM_F_DUMP == (NLM_F_ROOT | NLM_F_MATCH) assert NLMSG_DONE == 0x3 assert NLMSG_ERROR == 0x2 assert RTM_GETLINK == 0x12 assert RTM_NEWLINK == 0x10
def test_imports(self): from pyroute2 import IPRSocket from pyroute2.netlink import NLM_F_REQUEST from pyroute2.netlink import NLM_F_DUMP from pyroute2.netlink import NLM_F_ROOT from pyroute2.netlink import NLM_F_MATCH from pyroute2.netlink import NLMSG_DONE from pyroute2.netlink import NLMSG_ERROR from pyroute2.netlink.iproute import RTM_GETLINK from pyroute2.netlink.iproute import RTM_NEWLINK from pyroute2.netlink.generic import nlmsg from pyroute2.netlink.rtnl.ifinfmsg import ifinfmsg ip = IPRSocket() ip.bind() ip.close() assert issubclass(IPRSocket, socket.socket) assert issubclass(ifinfmsg, nlmsg) assert NLM_F_REQUEST == 1 assert NLM_F_ROOT == 0x100 assert NLM_F_MATCH == 0x200 assert NLM_F_DUMP == (NLM_F_ROOT | NLM_F_MATCH) assert NLMSG_DONE == 0x3 assert NLMSG_ERROR == 0x2 assert RTM_GETLINK == 0x12 assert RTM_NEWLINK == 0x10
def test_imports(self): from pyroute2 import IPRSocket from pyroute2.netlink import NLM_F_REQUEST from pyroute2.netlink import NLM_F_DUMP from pyroute2.netlink import NLM_F_ROOT from pyroute2.netlink import NLM_F_MATCH from pyroute2.netlink import NLMSG_DONE from pyroute2.netlink import NLMSG_ERROR from pyroute2.netlink import nlmsg from pyroute2.iproute import RTM_GETLINK from pyroute2.iproute import RTM_NEWLINK from pyroute2.netlink.rtnl.ifinfmsg import ifinfmsg ip = IPRSocket() ip.bind() ip.close() assert issubclass(IPRSocket, socket.socket) assert issubclass(ifinfmsg, nlmsg) assert NLM_F_REQUEST == 1 assert NLM_F_ROOT == 0x100 assert NLM_F_MATCH == 0x200 assert NLM_F_DUMP == (NLM_F_ROOT | NLM_F_MATCH) assert NLMSG_DONE == 0x3 assert NLMSG_ERROR == 0x2 assert RTM_GETLINK == 0x12 assert RTM_NEWLINK == 0x10
class NICMonitor(threading.Thread): def __init__(self, context): threading.Thread.__init__(self) self.logger = logging.getLogger(__name__) self.context = context self.ip = IPRSocket() self.ip.bind(rtnl.RTNLGRP_LINK | rtnl.RTNLGRP_NOTIFY) self.control = None self.state = '' self.carrier = -1 def setup(self): self.logger.info('NICMon:setup()') self.control = self.context.socket(zmq.PAIR) self.control.bind(const.fmNICMonitorEndpoint) return self.control def run(self): self.command = self.context.socket(zmq.PAIR) self.command.connect(const.fmNICMonitorEndpoint) self.poller = zmq.Poller() self.poller.register(self.ip._sock, zmq.POLLIN) self.poller.register(self.command, zmq.POLLIN) while True: sockets = dict(self.poller.poll()) if self.ip._sock.fileno() in sockets: infos = self.ip.get() for info in infos: attrs = dict(info['attrs']) ifname = attrs['IFLA_IFNAME'] state = attrs['IFLA_OPERSTATE'] carrier = attrs['IFLA_CARRIER'] if ifname == Config.NIC_NAME and state != self.state and carrier != self.carrier: self.logger.info( "NICMonitor: state has changed (%s,%d)" % (state, carrier)) if state != 'UP' or carrier != 1: # NIC down and/or carrier lost info = ('nic-', ) self.command.send_pyobj(info) elif state == 'UP' and carrier == 1: # NIC UP and carrier is on info = ('nic+', ) self.command.send_pyobj(info) self.state, self.carrier = state, carrier del sockets[self.ip._sock.fileno()] elif self.command in sockets: msg = self.command.recv_pyobj() if msg == 'stop': break del sockets[self.command] self.command.close() self.ip.close() def terminate(self): self.control.send_pyobj('stop')
def test_isinstance(self): from pyroute2 import IPRSocket from pyroute2 import IPRoute from pyroute2.iproute import IPRoute as IPRoute_real from pyroute2.netlink.rtnl.iprsocket import IPRSocket as IPRSocket_real ipr1 = IPRoute() ipr2 = IPRoute_real() ips1 = IPRSocket() ips2 = IPRSocket_real() # positive assert isinstance(ips1, IPRSocket) assert isinstance(ips2, IPRSocket) assert isinstance(ips1, IPRSocket_real) assert isinstance(ips2, IPRSocket_real) assert isinstance(ipr1, IPRoute) assert isinstance(ipr2, IPRoute) assert isinstance(ipr1, IPRoute_real) assert isinstance(ipr2, IPRoute_real) # negative assert not isinstance(ips1, IPRoute) assert not isinstance(ips2, IPRoute) assert not isinstance(ips1, IPRoute_real) assert not isinstance(ips2, IPRoute_real) # this must succeed -- IPRoute is a subclass of IPRSocket assert isinstance(ipr1, IPRSocket) assert isinstance(ipr2, IPRSocket) assert isinstance(ipr1, IPRSocket_real) assert isinstance(ipr2, IPRSocket_real) ips1.close() ips2.close() ipr1.close() ipr2.close()
def scan_netdevs(): scan = [] nl_socket = IPRSocket() msg = ifinfmsg() msg["family"] = socket.AF_UNSPEC msg["header"]["type"] = RTM_GETLINK msg["header"]["flags"] = NLM_F_REQUEST | NLM_F_DUMP msg["header"]["pid"] = os.getpid() msg["header"]["sequence_number"] = 1 msg.encode() nl_socket.sendto(msg.buf.getvalue(), (0,0)) finished = False while not finished: parts = nl_socket.get() for part in parts: if part["header"]["type"] in [NLMSG_DONE, NLMSG_ERROR]: finished = True continue if part["header"]["sequence_number"] != 1: continue if part["header"]["type"] == RTM_NEWLINK: new_link = {} new_link["netlink_msg"] = part new_link["index"] = part["index"] new_link["name"] = part.get_attr("IFLA_IFNAME") hwaddr = part.get_attr("IFLA_ADDRESS") new_link["hwaddr"] = normalize_hwaddr(hwaddr) scan.append(new_link) nl_socket.close() return scan
class InterfaceManager(object): def __init__(self, server_handler): self._devices = {} #if_index to device self._id_mapping = {} #id from the ctl to if_index self._tmp_mapping = {} #id from the ctl to newly created device self._nl_socket = IPRSocket() self._nl_socket.bind(groups=NL_GROUPS) self._dl_manager = DevlinkManager() self.rescan_devices() self._server_handler = server_handler def map_if(self, if_id, if_index): if if_id in self._id_mapping: raise IfMgrError("Interface already mapped.") elif if_index not in self._devices: raise IfMgrError("No interface with index %s found." % if_index) self._id_mapping[if_id] = if_index return def unmap_if(self, if_id): if if_id in self._id_mapping: del self._id_mapping[if_id] elif if_id in self._tmp_mapping: del self._tmp_mapping[if_id] else: pass def clear_if_mapping(self): self._id_mapping = {} def get_id_by_if_index(self, if_index): for if_id, index in self._id_mapping.iteritems(): if if_index == index: return if_id return None def reconnect_netlink(self): if self._nl_socket != None: self._nl_socket.close() self._nl_socket = None self._nl_socket = IPRSocket() self._nl_socket.bind(groups=NL_GROUPS) self.rescan_devices() def get_nl_socket(self): return self._nl_socket def rescan_devices(self): devices_to_remove = self._devices.keys() devs = scan_netdevs() for dev in devs: if dev['index'] not in self._devices: device = None for if_id, d in self._tmp_mapping.items(): d_cfg = d.get_conf_dict() if d_cfg["name"] == dev["name"]: device = d self._id_mapping[if_id] = dev['index'] del self._tmp_mapping[if_id] break if device == None: device = Device(self) device.init_netlink(dev['netlink_msg']) self._devices[dev['index']] = device else: self._devices[dev['index']].update_netlink(dev['netlink_msg']) devices_to_remove.remove(dev['index']) self._devices[dev['index']].clear_ips() for addr_msg in dev['ip_addrs']: self._devices[dev['index']].update_netlink(addr_msg) for i in devices_to_remove: if self._devices[i].get_netns() != None: continue dev_name = self._devices[i].get_name() logging.debug("Deleting Device with if_index %d, name %s because "\ "it doesn't exist anymore." % (i, dev_name)) del_msg = {"type": "if_deleted", "if_index": i} self._server_handler.send_data_to_ctl(del_msg) del self._devices[i] self._dl_manager.rescan_ports() for device in self._devices.values(): dl_port = self._dl_manager.get_port(device.get_name()) device.set_devlink(dl_port) def handle_netlink_msgs(self, msgs): for msg in msgs: self._handle_netlink_msg(msg) self._dl_manager.rescan_ports() for device in self._devices.values(): dl_port = self._dl_manager.get_port(device.get_name()) device.set_devlink(dl_port) def _handle_netlink_msg(self, msg): if msg['header']['type'] in [RTM_NEWLINK, RTM_NEWADDR, RTM_DELADDR]: if msg['index'] in self._devices: update_msg = self._devices[msg['index']].update_netlink(msg) if update_msg != None: for if_id, if_index in self._id_mapping.iteritems(): if if_index == msg['index']: update_msg["if_id"] = if_id break self._server_handler.send_data_to_ctl(update_msg) elif msg['header']['type'] == RTM_NEWLINK: dev = None for if_id, d in self._tmp_mapping.items(): d_cfg = d.get_conf_dict() if d_cfg["name"] == msg.get_attr("IFLA_IFNAME"): dev = d self._id_mapping[if_id] = msg['index'] del self._tmp_mapping[if_id] break if dev == None: dev = Device(self) update_msg = dev.init_netlink(msg) self._devices[msg['index']] = dev if update_msg != None: for if_id, if_index in self._id_mapping.iteritems(): if if_index == msg['index']: update_msg["if_id"] = if_id break self._server_handler.send_data_to_ctl(update_msg) elif msg['header']['type'] == RTM_DELLINK: if msg['index'] in self._devices: dev = self._devices[msg['index']] if dev.get_netns() == None and dev.get_conf_dict() == None: dev.del_link() del self._devices[msg['index']] del_msg = {"type": "if_deleted", "if_index": msg['index']} self._server_handler.send_data_to_ctl(del_msg) else: return def get_mapped_device(self, if_id): if if_id in self._id_mapping: if_index = self._id_mapping[if_id] return self._devices[if_index] elif if_id in self._tmp_mapping: return self._tmp_mapping[if_id] else: return None def get_mapped_devices(self): ret = {} for if_id, if_index in self._id_mapping.iteritems(): ret[if_id] = self._devices[if_index] for if_id in self._tmp_mapping: ret[if_id] = self._tmp_mapping[if_id] return ret def get_device(self, if_index): if if_index in self._devices: return self._devices[if_index] else: return None def get_devices(self): return self._devices.values() def get_device_by_hwaddr(self, hwaddr): for dev in self._devices.values(): if dev.get_hwaddr() == hwaddr: return dev return None def get_device_by_params(self, params): matched = None for dev in self._devices.values(): matched = dev dev_data = dev.get_if_data() for key, value in params.iteritems(): if key not in dev_data or dev_data[key] != value: matched = None break if matched: break return matched def deconfigure_all(self): for dev in self._devices.itervalues(): dev.clear_configuration() def create_device_from_config(self, if_id, config): if config["type"] == "eth": raise IfMgrError("Ethernet devices can't be created.") config["name"] = self.assign_name(config) device = Device(self) self._tmp_mapping[if_id] = device device.set_configuration(config) device.create() return config["name"] def create_device_pair(self, if_id1, config1, if_id2, config2): name1, name2 = self.assign_name(config1) config1["name"] = name1 config2["name"] = name2 config1["peer_name"] = name2 config2["peer_name"] = name1 device1 = Device(self) device2 = Device(self) self._tmp_mapping[if_id1] = device1 self._tmp_mapping[if_id2] = device2 device1.set_configuration(config1) device2.set_configuration(config2) device1.create() device1.set_peer(device2) device2.set_peer(device1) return name1, name2 def wait_interface_init(self): while len(self._tmp_mapping) > 0: rl, wl, xl = select.select([self._nl_socket], [], [], 1) if len(rl) == 0: continue msgs = recv_data(self._nl_socket)["data"] self.handle_netlink_msgs(msgs) def _is_name_used(self, name): self.rescan_devices() for device in self._devices.itervalues(): if name == device.get_name(): return True for device in self._tmp_mapping.itervalues(): if name == device.get_name(): return True return False def assign_name_generic(self, prefix): index = 0 while (self._is_name_used(prefix + str(index))): index += 1 return prefix + str(index) def _assign_name_pair(self, prefix): index1 = 0 index2 = 0 while (self._is_name_used(prefix + str(index1))): index1 += 1 index2 = index1 + 1 while (self._is_name_used(prefix + str(index2))): index2 += 1 return prefix + str(index1), prefix + str(index2) def assign_name(self, config): if "name" in config: return config["name"] dev_type = config["type"] if dev_type == "eth": if (not "hwaddr" in config or "name" in config): return hwaddr = normalize_hwaddr(config["hwaddr"]) for dev in self._devices: if dev.get_hwaddr() == hwaddr: return dev.get_name() elif dev_type == "bond": return self.assign_name_generic("t_bond") elif dev_type == "bridge" or dev_type == "ovs_bridge": return self.assign_name_generic("t_br") elif dev_type == "macvlan": return self.assign_name_generic("t_macvlan") elif dev_type == "team": return self.assign_name_generic("t_team") elif dev_type == "vlan": netdev_name = self.get_mapped_device(config["slaves"][0]).get_name() vlan_tci = get_option(config, "vlan_tci") prefix = "%s.%s_" % (netdev_name, vlan_tci) return self.assign_name_generic(prefix) elif dev_type == "veth": return self._assign_name_pair("veth") elif dev_type == "vti": return self.assign_name_generic("vti") elif dev_type == "vti6": return self.assign_name_generic("t_ip6vti") elif dev_type == "vxlan": return self.assign_name_generic("vxlan") else: return self.assign_name_generic("dev")
class InterfaceManager(object): def __init__(self, server_handler): self._device_classes = {} self._devices = {} #ifindex to device self._nl_socket = IPRSocket() self._nl_socket.bind(groups=NL_GROUPS) self._msg_queue = deque() #TODO split DevlinkManager away from the InterfaceManager #self._dl_manager = DevlinkManager() self._server_handler = server_handler def clear_dev_classes(self): self._device_classes = {} def add_device_class(self, name, cls): if name in self._device_classes: raise InterfaceManagerError("Device class name conflict %s" % name) self._device_classes[name] = cls return cls def reconnect_netlink(self): if self._nl_socket != None: self._nl_socket.close() self._nl_socket = None self._nl_socket = IPRSocket() self._nl_socket.bind(groups=NL_GROUPS) self.rescan_devices() def get_nl_socket(self): return self._nl_socket def pull_netlink_messages_into_queue(self): try: while True: rl, wl, xl = select.select([self._nl_socket], [], [], 0) if not len(rl): break self._msg_queue.extend(self._nl_socket.get()) except socket.error: self.reconnect_netlink() return [] def rescan_devices(self): self.request_netlink_dump() self.handle_netlink_msgs() def request_netlink_dump(self): self._nl_socket.put(None, RTM_GETLINK, msg_flags=NLM_F_REQUEST | NLM_F_DUMP) self._nl_socket.put(None, RTM_GETADDR, msg_flags=NLM_F_REQUEST | NLM_F_DUMP) def handle_netlink_msgs(self): self.pull_netlink_messages_into_queue() while len(self._msg_queue): msg = self._msg_queue.popleft() self._handle_netlink_msg(msg) # self._dl_manager.rescan_ports() # for device in self._devices.values(): # dl_port = self._dl_manager.get_port(device.name) # device._set_devlink(dl_port) def _handle_netlink_msg(self, msg): if msg['header']['type'] in [RTM_NEWLINK, RTM_NEWADDR, RTM_DELADDR]: if msg['index'] in self._devices: self._devices[msg['index']]._update_netlink(msg) elif msg['header']['type'] == RTM_NEWLINK: dev = self._device_classes["Device"](self) dev._init_netlink(msg) self._devices[msg['index']] = dev update_msg = { "type": "dev_created", "dev_data": dev._get_if_data() } self._server_handler.send_data_to_ctl(update_msg) dev._disable() elif msg['header']['type'] == RTM_DELLINK: if msg['family'] == PF_BRIDGE: return if msg['index'] in self._devices: dev = self._devices[msg['index']] dev._deleted = True del self._devices[msg['index']] del_msg = {"type": "dev_deleted", "ifindex": msg['index']} self._server_handler.send_data_to_ctl(del_msg) else: return def untrack_device(self, dev): if dev.ifindex in self._devices: del self._devices[dev.ifindex] def get_device(self, ifindex): self.rescan_devices() if ifindex in self._devices: return self._devices[ifindex] else: raise DeviceNotFound() def get_devices(self): self.rescan_devices() return list(self._devices.values()) def get_device_by_hwaddr(self, hwaddr): self.rescan_devices() for dev in list(self._devices.values()): if dev.hwaddr == hwaddr: return dev raise DeviceNotFound() def get_device_by_name(self, name): self.rescan_devices() for dev in list(self._devices.values()): if dev.name == name: return dev raise DeviceNotFound() def get_device_by_params(self, params): self.rescan_devices() matched = None for dev in list(self._devices.values()): matched = dev dev_data = dev.get_if_data() for key, value in params.items(): if key not in dev_data or dev_data[key] != value: matched = None break if matched: break return matched def deconfigure_all(self): for dev in self._devices.values(): pass # dev.clear_configuration() def create_device(self, clsname, args=[], kwargs={}): devcls = self._device_classes[clsname] try: device = devcls(self, *args, **kwargs) except KeyError as e: raise DeviceConfigError("%s is a mandatory argument" % e) device._create() device._bulk_enabled = False self.request_netlink_dump() self.pull_netlink_messages_into_queue() device_found = False while len(self._msg_queue): msg = self._msg_queue.popleft() if msg.get_attr("IFLA_IFNAME") == device.name: device_found = True device._init_netlink(msg) self._devices[msg['index']] = device else: self._handle_netlink_msg(msg) if device_found: return device else: raise DeviceError("Device creation failed") def replace_dev(self, if_id, dev): del self._devices[if_id] self._devices[if_id] = dev def _is_name_used(self, name): self.rescan_devices() for device in self._devices.values(): if name == device.name: return True out, _ = exec_cmd("ovs-vsctl --columns=name list Interface", log_outputs=False, die_on_err=False) for line in out.split("\n"): m = re.match(r'.*: \"(.*)\"', line) if m is not None: if name == m.group(1): return True return False def assign_name(self, prefix): index = 0 while (self._is_name_used(prefix + str(index))): index += 1 return prefix + str(index) def _assign_name_pair(self, prefix): index1 = 0 index2 = 0 while (self._is_name_used(prefix + str(index1))): index1 += 1 index2 = index1 + 1 while (self._is_name_used(prefix + str(index2))): index2 += 1 return prefix + str(index1), prefix + str(index2)
''' Simplest example to monitor Netlink events with a Python script. ''' from pyroute2 import IPRSocket from pprint import pprint ip = IPRSocket() ip.bind() pprint(ip.get()) ip.close()
class NetLinkListener(Plugin): """This plugin reads kernel messages and is able to react to certain network events""" states = {"UP": True, "DOWN": False} def __init__(self, api, config, *args, **kw): super(NetLinkListener, self).__init__(api, config, *args, **kw) self.listen = True self.socket = IPRSocket() def _init(self, *args, **kw): # super(NetLinkListener, self).__init__(*args, **kw) self.queue = Queue.Queue() self.listen = False # add handlers here self.event_handlers = { "RTM_NEWLINK": self.rtm_newlink_handler, "RTM_NEWADDR": self.rtm_newaddr_handler, "RTM_DELADDR": self.rtm_deladdr_handler } self.api.register_connectivity_handler(self.connectivity_request) self.logger.debug("Connectivity request handler registered") self._initialized() def _start(self): self.api.run_task(self.start_listening) self.api.run_task(self.start_queue_handler) self._started() def _stop(self): self.stop_listening() self._stopped() def start_listening(self): if self.socket is None: self.socket = IPRSocket() self.socket.bind() self.logger.info("Starting listener...") self.listen = True while self.listen: r, _, _ = select((self.socket, ), (), (), 0.5) if r: msg_array = self.socket.get() for event_msg in msg_array: try: self.queue.put(event_msg) except Queue.Full: self.logger.warn( "Message queue is full! it has %s elements", self.queue.qsize()) # stop after listening self.socket.close() self.logger.debug("Socket closed") def start_queue_handler(self): """Start reading the queue in a while loop until plugin stops listening to kernel messages""" self.logger.debug("Starting queue handler...") while self.listen or not self.queue.empty(): try: self.handle_event(self.queue.get(timeout=0.5)) except Queue.Empty: pass self.logger.debug("Queue handler finished") def stop_listening(self): """Stop listening to kernel messages -> shuts down queue handler""" self.logger.info("Stopping listener...") self.listen = False def handle_event(self, event_msg): """Find the handler matching the message event and run the handler with the message :param event_msg: event message from kernel message (kernel message is usually an array with 1 event message """ event = event_msg["event"] # self.logger.debug("new event:\r\n%s", json.dumps(event_msg, indent=2, sort_keys=True)) # self.logger.debug("new event: %s", event) handler = self.event_handlers.get(event) if handler is not None: self.logger.info("Handling event: %s", event) handler(event_msg) else: # self.logger.debug("No handler for event: %s", event) pass def rtm_newlink_handler(self, msg): """Handler parsing RTM_NEWLINK event messages :param msg: RTM_NEWLINK event message """ name = str() state = str() for attr in msg["attrs"]: if attr[0] == "IFLA_IFNAME": name = attr[1] elif attr[0] == "IFLA_OPERSTATE": state = attr[1] if name == str(): self.logger.debug( "No IFLA_IFNAME attribute found. Using index to assume interface" ) name = self.get_interface_name_from_index(msg) if name != str() and state != str(): try: interface = self.get_interface(name) if self.states[state]: self.api.events.interface_created.fire(interface) else: self.api.events.interface_removed.fire(interface) except InterfaceNotFoundException: self.logger.error( "RTM_NEWLINK: Unable to fire event for interface state change: %s is an unknown interface", name) self.logger.debug("Debug print of skipped message:\r\n%s", json.dumps(msg, indent=2, sort_keys=True)) else: self.logger.warn( "RTM_NEWLINK: interface not found or unknown state in msg!") self.logger.debug("Debug print of skipped message:\r\n%s", json.dumps(msg, indent=2, sort_keys=True)) def rtm_newaddr_handler(self, msg): """Handler parsing RTM_NEWADDR event messages :param msg: RTM_NEWADDR event message """ interface = str() address = str() for attr in msg["attrs"]: if attr[0] == "IFA_LABEL": interface = attr[1] elif attr[0] == "IFA_ADDRESS": address = attr[1] if interface == str(): self.logger.debug( "No IFA_LABEL attribute found. Using index to assume interface" ) interface = self.get_interface_name_from_index(msg) if interface != str() and address != str(): try: self.api.events.address_created.fire( self.get_interface(interface), Address(address=address, family=msg["family"])) except InterfaceNotFoundException: self.logger.error( "RTM_NEWADDR: Unable to fire address_created event: %s is an unknown interface", interface) self.logger.debug("Debug print of skipped message:\r\n%s", json.dumps(msg, indent=2, sort_keys=True)) else: self.logger.warn( "RTM_NEWADDR: interface and/or address not found in msg!") self.logger.debug("Debug print of skipped message:\r\n%s", json.dumps(msg, indent=2, sort_keys=True)) def rtm_deladdr_handler(self, msg): """Handler parsing RTM_DELADDR event messages :param msg: RTM_DELADDR event message """ interface = str() address = str() for attr in msg["attrs"]: if attr[0] == "IFA_LABEL": interface = attr[1] elif attr[0] == "IFA_ADDRESS": address = attr[1] if interface == str(): self.logger.debug( "No IFA_LABEL attribute found. Using index to assume interface" ) interface = self.get_interface_name_from_index(msg) if interface != str() and address != str(): try: self.api.events.address_removed.fire( self.get_interface(interface), Address(address=address, family=msg["family"])) except InterfaceNotFoundException: self.logger.error( "RTM_DELADDR: Unable to fire address_removed event: %s is an unknown interface", interface) self.logger.debug("Debug print of skipped message:\r\n%s", json.dumps(msg, indent=2, sort_keys=True)) else: self.logger.warn( "RTM_DELADDR: interface and/or address not found in msg!") self.logger.debug("Debug print of skipped message:\r\n%s", json.dumps(msg, indent=2, sort_keys=True)) def get_addresses(self, interface): """Get addresses of a given interface :param interface: name of interface :return: list of addresses """ n_addresses = netifaces.ifaddresses(interface) addresses = [] for family in n_addresses: for addr in n_addresses[family]: addresses.append(Address(address=addr["addr"], family=family)) return addresses def get_interface(self, name): """Returns an Interface object identified by name :param name: name of interface :return Interface: interface :raise UnknownInterface: if interface was not found """ if name not in netifaces.interfaces(): raise InterfaceNotFoundException("%s was not found" % name) else: addresses = self.get_addresses(name) hwaddress = [ addr for addr in addresses if addr[1] == netifaces.AF_LINK ][0] return Interface(name=name, addresses=addresses, hwaddress=hwaddress) def get_interface_name_from_index(self, msg): """Parse event message and try to assume interface from index attribute :param msg: event message :return: interface name corresponding to index value, or empty string if unsuccessful """ interface = str() try: interface = netifaces.interfaces()[msg["index"] - 1] self.logger.debug("Index: %s -> %s", msg["index"], interface) except IndexError: self.logger.debug("Unable to get interface from index") finally: return interface def connectivity_request(self, rcat=0): """Handles connectivity requests""" with Promise() as p: blacklist = ['lo'] interfaces = netifaces.interfaces() interface = next((x for x in interfaces if (x not in blacklist)), None) if interface is None: p.reject( InterfaceNotFoundException( "No interfaces found matching request")) else: p.fulfill((self.get_interface(interface), 0)) return p
class InterfaceManager(object): def __init__(self, server_handler): self._devices = {} #if_index to device self._id_mapping = {} #id from the ctl to if_index self._tmp_mapping = {} #id from the ctl to newly created device self._nl_socket = IPRSocket() self._nl_socket.bind() self.rescan_devices() self._server_handler = server_handler def map_if(self, if_id, if_index): if if_id in self._id_mapping: raise IfMgrError("Interface already mapped.") elif if_index not in self._devices: raise IfMgrError("No interface with index %s found." % if_index) self._id_mapping[if_id] = if_index return def unmap_if(self, if_id): if if_id in self._id_mapping: del self._id_mapping[if_id] elif if_id in self._tmp_mapping: del self._tmp_mapping[if_id] else: pass def clear_if_mapping(self): self._id_mapping = {} def get_id_by_if_index(self, if_index): for if_id, index in self._id_mapping.iteritems(): if if_index == index: return if_id return None def reconnect_netlink(self): if self._nl_socket != None: self._nl_socket.close() self._nl_socket = None self._nl_socket = IPRSocket() self._nl_socket.bind() def get_nl_socket(self): return self._nl_socket def rescan_devices(self): self._devices = {} devs = scan_netdevs() for dev in devs: if dev['index'] not in self._devices: device = Device(self) device.init_netlink(dev['netlink_msg']) self._devices[dev['index']] = device def handle_netlink_msgs(self, msgs): for msg in msgs: self._handle_netlink_msg(msg) def _handle_netlink_msg(self, msg): if msg['header']['type'] == RTM_NEWLINK: if msg['index'] in self._devices: update_msg = self._devices[msg['index']].update_netlink(msg) if update_msg != None: for if_id, if_index in self._id_mapping.iteritems(): if if_index == msg['index']: update_msg["if_id"] = if_id break if "if_id" in update_msg: self._server_handler.send_data_to_ctl(update_msg) else: dev = None for if_id, d in self._tmp_mapping.items(): d_cfg = d.get_conf_dict() if d_cfg["name"] == msg.get_attr("IFLA_IFNAME"): dev = d self._id_mapping[if_id] = msg['index'] del self._tmp_mapping[if_id] break if dev == None: dev = Device(self) dev.init_netlink(msg) self._devices[msg['index']] = dev elif msg['header']['type'] == RTM_DELLINK: if msg['index'] in self._devices: dev = self._devices[msg['index']] if dev.get_netns() == None and dev.get_conf_dict() == None: dev.del_link() del self._devices[msg['index']] else: return def get_mapped_device(self, if_id): if if_id in self._id_mapping: if_index = self._id_mapping[if_id] return self._devices[if_index] elif if_id in self._tmp_mapping: return self._tmp_mapping[if_id] else: return None def get_mapped_devices(self): ret = {} for if_id, if_index in self._id_mapping.iteritems(): ret[if_id] = self._devices[if_index] for if_id in self._tmp_mapping: ret[if_id] = self._tmp_mapping[if_id] return ret def get_device(self, if_index): if if_index in self._devices: return self._devices[if_index] else: return None def get_devices(self): return self._devices.values() def get_device_by_hwaddr(self, hwaddr): for dev in self._devices.values(): if dev.get_hwaddr() == hwaddr: return dev return None def get_device_by_params(self, params): matched = None for dev in self._devices.values(): matched = dev dev_data = dev.get_if_data() for key, value in params.iteritems(): if key not in dev_data or dev_data[key] != value: matched = None break if matched: break return matched def deconfigure_all(self): for dev in self._devices.itervalues(): dev.clear_configuration() def create_device_from_config(self, if_id, config): if config["type"] == "eth": raise IfMgrError("Ethernet devices can't be created.") config["name"] = self.assign_name(config) device = Device(self) device.set_configuration(config) device.create() self._tmp_mapping[if_id] = device return config["name"] def create_device_pair(self, if_id1, config1, if_id2, config2): name1, name2 = self.assign_name(config1) config1["name"] = name1 config2["name"] = name2 config1["peer_name"] = name2 config2["peer_name"] = name1 device1 = Device(self) device2 = Device(self) device1.set_configuration(config1) device2.set_configuration(config2) device1.create() device1.set_peer(device2) device2.set_peer(device1) self._tmp_mapping[if_id1] = device1 self._tmp_mapping[if_id2] = device2 return name1, name2 def _is_name_used(self, name): for device in self._devices.itervalues(): if name == device.get_name(): return True for device in self._tmp_mapping.itervalues(): if name == device.get_name(): return True return False def _assign_name_generic(self, prefix): index = 0 while (self._is_name_used(prefix + str(index))): index += 1 return prefix + str(index) def _assign_name_pair(self, prefix): index1 = 0 index2 = 0 while (self._is_name_used(prefix + str(index1))): index1 += 1 index2 = index1 + 1 while (self._is_name_used(prefix + str(index2))): index2 += 1 return prefix + str(index1), prefix + str(index2) def assign_name(self, config): if "name" in config: return config["name"] dev_type = config["type"] if dev_type == "eth": if (not "hwaddr" in config or "name" in config): return hwaddr = normalize_hwaddr(config["hwaddr"]) for dev in self._devices: if dev.get_hwaddr() == hwaddr: return dev.get_name() elif dev_type == "bond": return self._assign_name_generic("t_bond") elif dev_type == "bridge" or dev_type == "ovs_bridge": return self._assign_name_generic("t_br") elif dev_type == "macvlan": return self._assign_name_generic("t_macvlan") elif dev_type == "team": return self._assign_name_generic("t_team") elif dev_type == "vlan": netdev_name = self.get_mapped_device(config["slaves"][0]).get_name() vlan_tci = get_option(config, "vlan_tci") prefix = "%s.%s_" % (netdev_name, vlan_tci) return self._assign_name_generic(prefix) elif dev_type == "veth": return self._assign_name_pair("veth") elif dev_type == "vti": return self._assign_name_generic("vti") elif dev_type == "vti6": return self._assign_name_generic("t_ip6vti") else: return self._assign_name_generic("dev")
class InterfaceManager(object): def __init__(self, server_handler): self._devices = {} #if_index to device self._id_mapping = {} #id from the ctl to if_index self._tmp_mapping = {} #id from the ctl to newly created device self._nl_socket = IPRSocket() self._nl_socket.bind(groups=NL_GROUPS) self._dl_manager = DevlinkManager() self.rescan_devices() self._server_handler = server_handler def map_if(self, if_id, if_index): if if_id in self._id_mapping: raise IfMgrError("Interface already mapped.") elif if_index not in self._devices: raise IfMgrError("No interface with index %s found." % if_index) self._id_mapping[if_id] = if_index return def unmap_if(self, if_id): if if_id in self._id_mapping: del self._id_mapping[if_id] elif if_id in self._tmp_mapping: del self._tmp_mapping[if_id] else: pass def clear_if_mapping(self): self._id_mapping = {} def get_id_by_if_index(self, if_index): for if_id, index in self._id_mapping.iteritems(): if if_index == index: return if_id return None def reconnect_netlink(self): if self._nl_socket != None: self._nl_socket.close() self._nl_socket = None self._nl_socket = IPRSocket() self._nl_socket.bind(groups=NL_GROUPS) self.rescan_devices() def get_nl_socket(self): return self._nl_socket def rescan_devices(self): devices_to_remove = self._devices.keys() devs = scan_netdevs() for dev in devs: if dev['index'] not in self._devices: device = None for if_id, d in self._tmp_mapping.items(): d_cfg = d.get_conf_dict() if d_cfg["name"] == dev["name"]: device = d self._id_mapping[if_id] = dev['index'] del self._tmp_mapping[if_id] break if device == None: device = Device(self) device.init_netlink(dev['netlink_msg']) self._devices[dev['index']] = device else: self._devices[dev['index']].update_netlink(dev['netlink_msg']) devices_to_remove.remove(dev['index']) self._devices[dev['index']].clear_ips() for addr_msg in dev['ip_addrs']: self._devices[dev['index']].update_netlink(addr_msg) for i in devices_to_remove: if self._devices[i].get_netns() != None: continue dev_name = self._devices[i].get_name() logging.debug("Deleting Device with if_index %d, name %s because "\ "it doesn't exist anymore." % (i, dev_name)) del_msg = {"type": "if_deleted", "if_index": i} self._server_handler.send_data_to_ctl(del_msg) del self._devices[i] self._dl_manager.rescan_ports() for device in self._devices.values(): dl_port = self._dl_manager.get_port(device.get_name()) device.set_devlink(dl_port) def handle_netlink_msgs(self, msgs): for msg in msgs: self._handle_netlink_msg(msg) self._dl_manager.rescan_ports() for device in self._devices.values(): dl_port = self._dl_manager.get_port(device.get_name()) device.set_devlink(dl_port) def _handle_netlink_msg(self, msg): if msg['header']['type'] in [RTM_NEWLINK, RTM_NEWADDR, RTM_DELADDR]: if msg['index'] in self._devices: update_msg = self._devices[msg['index']].update_netlink(msg) if update_msg != None: for if_id, if_index in self._id_mapping.iteritems(): if if_index == msg['index']: update_msg["if_id"] = if_id break self._server_handler.send_data_to_ctl(update_msg) elif msg['header']['type'] == RTM_NEWLINK: dev = None for if_id, d in self._tmp_mapping.items(): d_cfg = d.get_conf_dict() if d_cfg["name"] == msg.get_attr("IFLA_IFNAME"): dev = d self._id_mapping[if_id] = msg['index'] del self._tmp_mapping[if_id] break if dev == None: dev = Device(self) update_msg = dev.init_netlink(msg) self._devices[msg['index']] = dev if update_msg != None: for if_id, if_index in self._id_mapping.iteritems(): if if_index == msg['index']: update_msg["if_id"] = if_id break self._server_handler.send_data_to_ctl(update_msg) elif msg['header']['type'] == RTM_DELLINK: if msg['index'] in self._devices: dev = self._devices[msg['index']] if dev.get_netns() == None and dev.get_conf_dict() == None: dev.del_link() del self._devices[msg['index']] del_msg = {"type": "if_deleted", "if_index": msg['index']} self._server_handler.send_data_to_ctl(del_msg) else: return def get_mapped_device(self, if_id): if if_id in self._id_mapping: if_index = self._id_mapping[if_id] return self._devices[if_index] elif if_id in self._tmp_mapping: return self._tmp_mapping[if_id] else: return None def get_mapped_devices(self): ret = {} for if_id, if_index in self._id_mapping.iteritems(): ret[if_id] = self._devices[if_index] for if_id in self._tmp_mapping: ret[if_id] = self._tmp_mapping[if_id] return ret def get_device(self, if_index): if if_index in self._devices: return self._devices[if_index] else: return None def get_devices(self): return self._devices.values() def get_device_by_hwaddr(self, hwaddr): for dev in self._devices.values(): if dev.get_hwaddr() == hwaddr: return dev return None def get_device_by_params(self, params): matched = None for dev in self._devices.values(): matched = dev dev_data = dev.get_if_data() for key, value in params.iteritems(): if key not in dev_data or dev_data[key] != value: matched = None break if matched: break return matched def deconfigure_all(self): for dev in self._devices.itervalues(): dev.clear_configuration() def create_device_from_config(self, if_id, config): if config["type"] == "eth": raise IfMgrError("Ethernet devices can't be created.") config["name"] = self.assign_name(config) device = Device(self) self._tmp_mapping[if_id] = device device.set_configuration(config) device.create() return config["name"] def create_device_pair(self, if_id1, config1, if_id2, config2): name1, name2 = self.assign_name(config1) config1["name"] = name1 config2["name"] = name2 config1["peer_name"] = name2 config2["peer_name"] = name1 device1 = Device(self) device2 = Device(self) self._tmp_mapping[if_id1] = device1 self._tmp_mapping[if_id2] = device2 device1.set_configuration(config1) device2.set_configuration(config2) device1.create() device1.set_peer(device2) device2.set_peer(device1) return name1, name2 def wait_interface_init(self): while len(self._tmp_mapping) > 0: rl, wl, xl = select.select([self._nl_socket], [], [], 1) if len(rl) == 0: continue msgs = recv_data(self._nl_socket)["data"] self.handle_netlink_msgs(msgs) def _is_name_used(self, name): self.rescan_devices() for device in self._devices.itervalues(): if name == device.get_name(): return True for device in self._tmp_mapping.itervalues(): if name == device.get_name(): return True return False def assign_name_generic(self, prefix): index = 0 while (self._is_name_used(prefix + str(index))): index += 1 return prefix + str(index) def _assign_name_pair(self, prefix): index1 = 0 index2 = 0 while (self._is_name_used(prefix + str(index1))): index1 += 1 index2 = index1 + 1 while (self._is_name_used(prefix + str(index2))): index2 += 1 return prefix + str(index1), prefix + str(index2) def assign_name(self, config): if "name" in config: return config["name"] dev_type = config["type"] if dev_type == "eth": if (not "hwaddr" in config or "name" in config): return hwaddr = normalize_hwaddr(config["hwaddr"]) for dev in self._devices: if dev.get_hwaddr() == hwaddr: return dev.get_name() elif dev_type == "bond": return self.assign_name_generic("t_bond") elif dev_type == "bridge" or dev_type == "ovs_bridge": return self.assign_name_generic("t_br") elif dev_type == "macvlan": return self.assign_name_generic("t_macvlan") elif dev_type == "team": return self.assign_name_generic("t_team") elif dev_type == "vlan": netdev_name = self.get_mapped_device(config["slaves"][0]).get_name() vlan_tci = get_option(config, "vlan_tci") prefix = "%s.%s_" % (netdev_name, vlan_tci) return self.assign_name_generic(prefix) elif dev_type == "veth": return self._assign_name_pair("veth") elif dev_type == "vti": return self.assign_name_generic("vti") elif dev_type == "vti6": return self.assign_name_generic("t_ip6vti") elif dev_type == "vxlan": return self.assign_name_generic("vxlan") elif dev_type == "gre": return self.assign_name_generic("gre_") elif dev_type == "ipip": return self.assign_name_generic("ipip_") elif dev_type == "dummy": return self.assign_name_generic("dummy_") else: return self.assign_name_generic("dev")
class InterfaceManager(object): def __init__(self, server_handler): self._device_classes = {} self._devices = {} #if_index to device self._nl_socket = IPRSocket() self._nl_socket.bind(groups=NL_GROUPS) self._dl_manager = DevlinkManager() self._server_handler = server_handler def clear_dev_classes(self): self._device_classes = {} def add_device_class(self, name, cls): if name in self._device_classes: raise InterfaceManagerError("Device class name conflict %s" % name) self._device_classes[name] = cls return cls def reconnect_netlink(self): if self._nl_socket != None: self._nl_socket.close() self._nl_socket = None self._nl_socket = IPRSocket() self._nl_socket.bind(groups=NL_GROUPS) self.rescan_devices() def get_nl_socket(self): return self._nl_socket def rescan_devices(self): devices_to_remove = self._devices.keys() devs = scan_netdevs() for dev in devs: if dev['index'] not in self._devices: device = self._device_classes["Device"](self) device._init_netlink(dev['netlink_msg']) self._devices[dev['index']] = device update_msg = {"type": "dev_created", "dev_data": device._get_if_data()} self._server_handler.send_data_to_ctl(update_msg) else: self._devices[dev['index']]._update_netlink(dev['netlink_msg']) try: devices_to_remove.remove(dev['index']) except ValueError: # we may have multiple updates for the same device, it's # okay not to find the device in devices_to_remove pass self._devices[dev['index']]._clear_ips() for addr_msg in dev['ip_addrs']: self._devices[dev['index']]._update_netlink(addr_msg) for i in devices_to_remove: dev_name = self._devices[i].name logging.debug("Deleting Device with if_index %d, name %s because "\ "it doesn't exist anymore." % (i, dev_name)) self._devices[i]._deleted = True del self._devices[i] del_msg = {"type": "dev_deleted", "if_index": i} self._server_handler.send_data_to_ctl(del_msg) self._dl_manager.rescan_ports() for device in self._devices.values(): dl_port = self._dl_manager.get_port(device.name) device._set_devlink(dl_port) def handle_netlink_msgs(self, msgs): for msg in msgs: self._handle_netlink_msg(msg) self._dl_manager.rescan_ports() for device in self._devices.values(): dl_port = self._dl_manager.get_port(device.name) device._set_devlink(dl_port) def _handle_netlink_msg(self, msg): if msg['header']['type'] in [RTM_NEWLINK, RTM_NEWADDR, RTM_DELADDR]: if msg['index'] in self._devices: self._devices[msg['index']]._update_netlink(msg) elif msg['header']['type'] == RTM_NEWLINK: dev = self._device_classes["Device"](self) dev._init_netlink(msg) self._devices[msg['index']] = dev update_msg = {"type": "dev_created", "dev_data": dev._get_if_data()} self._server_handler.send_data_to_ctl(update_msg) elif msg['header']['type'] == RTM_DELLINK: if msg['index'] in self._devices: dev = self._devices[msg['index']] dev._deleted = True del self._devices[msg['index']] del_msg = {"type": "dev_deleted", "if_index": msg['index']} self._server_handler.send_data_to_ctl(del_msg) else: return def get_device(self, if_index): self.rescan_devices() if if_index in self._devices: return self._devices[if_index] else: raise DeviceNotFound() def get_devices(self): self.rescan_devices() return self._devices.values() def get_device_by_hwaddr(self, hwaddr): self.rescan_devices() for dev in self._devices.values(): if dev.hwaddr == hwaddr: return dev raise DeviceNotFound() def get_device_by_name(self, name): self.rescan_devices() for dev in self._devices.values(): if dev.name == name: return dev raise DeviceNotFound() def get_device_by_params(self, params): self.rescan_devices() matched = None for dev in self._devices.values(): matched = dev dev_data = dev.get_if_data() for key, value in params.iteritems(): if key not in dev_data or dev_data[key] != value: matched = None break if matched: break return matched def deconfigure_all(self): for dev in self._devices.itervalues(): pass # dev.clear_configuration() def create_device(self, clsname, args=[], kwargs={}): devcls = self._device_classes[clsname] device = devcls(self, *args, **kwargs) device._create() devs = scan_netdevs() for dev in devs: if dev["name"] == device.name: device._init_netlink(dev['netlink_msg']) self._devices[dev['index']] = device return device return None def replace_dev(self, if_id, dev): del self._devices[if_id] self._devices[if_id] = dev def _is_name_used(self, name): self.rescan_devices() for device in self._devices.itervalues(): if name == device.name: return True out, _ = exec_cmd("ovs-vsctl --columns=name list Interface", log_outputs=False, die_on_err=False) for line in out.split("\n"): m = re.match(r'.*: \"(.*)\"', line) if m is not None: if name == m.group(1): return True return False def assign_name(self, prefix): index = 0 while (self._is_name_used(prefix + str(index))): index += 1 return prefix + str(index) def _assign_name_pair(self, prefix): index1 = 0 index2 = 0 while (self._is_name_used(prefix + str(index1))): index1 += 1 index2 = index1 + 1 while (self._is_name_used(prefix + str(index2))): index2 += 1 return prefix + str(index1), prefix + str(index2)