class SlaveMethods: ''' Exported xmlrpc methods ''' def __init__(self, command_context, netconfig, log_ctl): self._packet_captures = {} self._netconfig = netconfig self._command_context = command_context self._log_ctl = log_ctl self._capture_files = {} self._copy_targets = {} self._copy_sources = {} self._system_config = {} self._cache = ResourceCache( lnst_config.get_option("cache", "dir"), lnst_config.get_option("cache", "expiration_period")) self._resource_table = {} def hello(self, recipe_path): logging.info("Recieved a controller connection.") self.clear_resource_table() self._cache.del_old_entries() self.reset_file_transfers() date = datetime.datetime.now().strftime("%Y-%m-%d_%H:%M:%S") self._log_ctl.set_recipe(recipe_path, expand=date) sleep(1) if check_process_running("NetworkManager"): logging.warning("=============================================") logging.warning("NetworkManager is running on a slave machine!") if lnst_config.get_option("environment", "use_nm"): logging.warning("Support of NM is still experimental!") else: logging.warning("Usage of NM is disabled!") logging.warning("=============================================") return "hello" def bye(self): self.restore_system_config() self.clear_resource_table() self._cache.del_old_entries() self.reset_file_transfers() self._remove_capture_files() return "bye" def kill_cmds(self): logging.info("Killing all forked processes.") self._command_context.cleanup() return "Commands killed" def get_devices_by_hwaddr(self, hwaddr): name_scan = scan_netdevs() netdevs = [] for entry in name_scan: if entry["hwaddr"] == normalize_hwaddr(hwaddr): netdevs.append(entry) return netdevs def get_devices_by_devname(self, devname): name_scan = scan_netdevs() netdevs = [] for entry in name_scan: if entry["name"] == devname: netdevs.append(entry) return netdevs def set_device_down(self, hwaddr): devs = self.get_devices_by_hwaddr(hwaddr) for dev in devs: if is_nm_managed_by_name(dev["name"]): bus = dbus.SystemBus() nm_obj = bus.get_object("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager") nm_if = dbus.Interface(nm_obj, "org.freedesktop.NetworkManager") dev_obj_path = nm_if.GetDeviceByIpIface(dev["name"]) dev_obj = bus.get_object("org.freedesktop.NetworkManager", dev_obj_path) dev_props = dbus.Interface(dev_obj, "org.freedesktop.DBus.Properties") if dev_props.Get("org.freedesktop.NetworkManager.Device", "ActiveConnection") != "/": dev_if = dbus.Interface( dev_obj, "org.freedesktop.NetworkManager.Device") logging.debug("Disconnecting device %s: %s" % (dev["name"], dev_obj_path)) dev_if.Disconnect() else: exec_cmd("ip link set %s down" % dev["name"]) return True def get_interface_info(self, if_id): if_config = self._netconfig.get_interface_config(if_id) info = {} if "name" in if_config and if_config["name"] != None: info["name"] = if_config["name"] else: devs = self.get_devices_by_hwaddr(if_config["hwaddr"]) info["name"] = devs[0]["name"] if "hwaddr" in if_config and if_config["hwaddr"] != None: info["hwaddr"] = if_config["hwaddr"] else: devs = self.get_devices_by_devname(if_config["name"]) info["hwaddr"] = devs[0]["hwaddr"] return info def configure_interface(self, if_id, config): self._netconfig.add_interface_config(if_id, config) self._netconfig.configure(if_id) return True def deconfigure_interface(self, if_id): self._netconfig.deconfigure(if_id) self._netconfig.remove_interface_config(if_id) return True def netconfig_dump(self): return self._netconfig.dump_config().items() def start_packet_capture(self, filt): netconfig = self._netconfig.dump_config() files = {} for dev_id, dev_spec in netconfig.iteritems(): df_handle = NamedTemporaryFile(delete=False) dump_file = df_handle.name df_handle.close() files[dev_id] = dump_file pcap = PacketCapture() pcap.set_interface(dev_spec["name"]) pcap.set_output_file(dump_file) pcap.set_filter(filt) pcap.start() self._packet_captures[dev_id] = pcap self._capture_files = files return files def stop_packet_capture(self): netconfig = self._netconfig.dump_config() for dev_id in netconfig.keys(): pcap = self._packet_captures[dev_id] pcap.stop() return True def _remove_capture_files(self): for key, name in self._capture_files.iteritems(): logging.debug("Removing temporary packet capture file %s", name) os.unlink(name) self._capture_files.clear() def _update_system_config(self, options, persistent): system_config = self._system_config for option, values in options.iteritems(): if persistent: if option in system_config: del system_config[option] else: if not option in system_config: initial_val = {"initial_val": values["previous_val"]} system_config[option] = initial_val system_config[option]["current_val"] = values["current_val"] def restore_system_config(self): logging.info("Restoring system configuration") for option, values in self._system_config.iteritems(): try: cmd_str = "echo \"%s\" >%s" % (values["initial_val"], option) (stdout, stderr) = exec_cmd(cmd_str) except ExecCmdFail: logging.warn("Unable to restore '%s' config option!", option) return False self._system_config = {} return True def run_command(self, command): cmd = NetTestCommand(self._command_context, command, self._resource_table, self._log_ctl) self._command_context.add_cmd(cmd) res = cmd.run() if not cmd.forked(): self._command_context.del_cmd(cmd) if command["type"] == "system_config": if res["passed"]: self._update_system_config(res["res_data"]["options"], command["persistent"]) else: err = "Error occured while setting system "\ "configuration (%s)" % res["err_msg"] logging.error(err) return res def kill_command(self, id): cmd = self._command_context.get_cmd(id) cmd.kill() self._command_context.del_cmd(cmd) return True def machine_cleanup(self): NetConfigDeviceAllCleanup() self._netconfig.cleanup() self._command_context.cleanup() self._cache.del_old_entries() self.restore_system_config() return True def clear_resource_table(self): self._resource_table = {} return True def has_resource(self, res_hash): if self._cache.query(res_hash): return True return False def map_resource(self, res_hash, res_type, res_name): resource_location = self._cache.get_path(res_hash) if not res_type in self._resource_table: self._resource_table[res_type] = {} self._resource_table[res_type][res_name] = resource_location self._cache.renew_entry(res_hash) return True def add_resource_to_cache(self, file_hash, local_path, name, res_hash, res_type): self._cache.add_cache_entry(file_hash, local_path, name, res_type) return True def start_copy_to(self, filepath=None): if filepath in self._copy_targets: return "" if filepath: self._copy_targets[filepath] = open(filepath, "w+b") else: tmpfile = NamedTemporaryFile("w+b", delete=False) filepath = tmpfile.name self._copy_targets[filepath] = tmpfile return filepath def copy_part_to(self, filepath, binary_data): if self._copy_targets[filepath]: self._copy_targets[filepath].write(binary_data.data) return True return False def finish_copy_to(self, filepath): if self._copy_targets[filepath]: self._copy_targets[filepath].close() del self._copy_targets[filepath] return True return False def start_copy_from(self, filepath): if filepath in self._copy_sources or not os.path.exists(filepath): return False self._copy_sources[filepath] = open(filepath, "rb") return True def copy_part_from(self, filepath, buffsize): data = Binary(self._copy_sources[filepath].read(buffsize)) return data def finish_copy_from(self, filepath): if filepath in self._copy_sources: self._copy_sources[filepath].close() del self._copy_sources[filepath] return True return False def reset_file_transfers(self): for file_handle in self._copy_targets.itervalues(): file_handle.close() self._copy_targets = {} for file_handle in self._copy_sources.itervalues(): file_handle.close() self._copy_sources = {}
class SlaveMethods: ''' Exported xmlrpc methods ''' def __init__(self, command_context, log_ctl, if_manager): self._packet_captures = {} self._if_manager = if_manager self._command_context = command_context self._log_ctl = log_ctl self._capture_files = {} self._copy_targets = {} self._copy_sources = {} self._system_config = {} self._cache = ResourceCache(lnst_config.get_option("cache", "dir"), lnst_config.get_option("cache", "expiration_period")) self._resource_table = {'module': {}, 'tools': {}} def hello(self, recipe_path): self.machine_cleanup() logging.info("Recieved a controller connection.") self.clear_resource_table() self._cache.del_old_entries() self.reset_file_transfers() self._if_manager.rescan_devices() date = datetime.datetime.now().strftime("%Y-%m-%d_%H:%M:%S") self._log_ctl.set_recipe(recipe_path, expand=date) sleep(1) if check_process_running("NetworkManager"): logging.warning("=============================================") logging.warning("NetworkManager is running on a slave machine!") if lnst_config.get_option("environment", "use_nm"): logging.warning("Support of NM is still experimental!") else: logging.warning("Usage of NM is disabled!") logging.warning("=============================================") return "hello" def bye(self): self.restore_system_config() self.clear_resource_table() self._cache.del_old_entries() self.reset_file_transfers() self._remove_capture_files() return "bye" def kill_cmds(self): logging.info("Killing all forked processes.") self._command_context.cleanup() return "Commands killed" def map_if_by_hwaddr(self, if_id, hwaddr): devices = self.get_devices_by_hwaddr(hwaddr) if len(devices) == 1: dev = self._if_manager.get_device_by_hwaddr(hwaddr) self._if_manager.map_if(if_id, dev.get_if_index()) return devices def get_devices_by_devname(self, devname): name_scan = self._if_manager.get_devices() netdevs = [] for entry in name_scan: if entry["name"] == devname: netdevs.append(entry) return netdevs def get_devices_by_hwaddr(self, hwaddr): devices = self._if_manager.get_devices() matched = [] for dev in devices: if dev.get_hwaddr() == hwaddr: entry = {"name": dev.get_name(), "hwaddr": dev.get_hwaddr()} matched.append(entry) return matched def set_device_up(self, if_id): dev = self._if_manager.get_mapped_device(if_id) dev.up() return True def set_device_down(self, if_id): dev = self._if_manager.get_mapped_device(if_id) dev.down() return True def set_unmapped_device_down(self, hwaddr): dev = self._if_manager.get_device_by_hwaddr(hwaddr) dev.down() return True def configure_interface(self, if_id, config): device = self._if_manager.get_mapped_device(if_id) device.set_configuration(config) device.configure() return True def create_soft_interface(self, if_id, config): return self._if_manager.create_device_from_config(if_id, config) def deconfigure_interface(self, if_id): device = self._if_manager.get_mapped_device(if_id) device.clear_configuration() return True def start_packet_capture(self, filt): files = {} for if_id, dev in self._if_manager.get_mapped_devices().iteritems(): dev_name = dev.get_name() df_handle = NamedTemporaryFile(delete=False) dump_file = df_handle.name df_handle.close() files[if_id] = dump_file pcap = PacketCapture() pcap.set_interface(dev_name) pcap.set_output_file(dump_file) pcap.set_filter(filt) pcap.start() self._packet_captures[if_id] = pcap self._capture_files = files return files def stop_packet_capture(self): if self._packet_captures == None: return True for if_index, pcap in self._packet_captures.iteritems(): pcap.stop() self._packet_captures.clear() return True def _remove_capture_files(self): for key, name in self._capture_files.iteritems(): logging.debug("Removing temporary packet capture file %s", name) os.unlink(name) self._capture_files.clear() def _update_system_config(self, options, persistent): system_config = self._system_config for opt in options: option = opt["name"] prev = opt["previous_val"] curr = opt["current_val"] if persistent: if option in system_config: del system_config[option] else: if not option in system_config: system_config[option] = {"initial_val": prev} system_config[option]["current_val"] = curr def restore_system_config(self): logging.info("Restoring system configuration") for option, values in self._system_config.iteritems(): try: cmd_str = "echo \"%s\" >%s" % (values["initial_val"], option) (stdout, stderr) = exec_cmd(cmd_str) except ExecCmdFail: logging.warn("Unable to restore '%s' config option!", option) return False self._system_config = {} return True def run_command(self, command): cmd = NetTestCommand(self._command_context, command, self._resource_table, self._log_ctl) self._command_context.add_cmd(cmd) res = cmd.run() if not cmd.forked(): self._command_context.del_cmd(cmd) if command["type"] == "config": if res["passed"]: self._update_system_config(res["res_data"]["options"], command["persistent"]) else: err = "Error occured while setting system "\ "configuration (%s)" % res["res_data"]["err_msg"] logging.error(err) return res def kill_command(self, id): cmd = self._command_context.get_cmd(id) cmd.kill(None) self._command_context.del_cmd(cmd) return cmd.get_result() def machine_cleanup(self): logging.info("Performing machine cleanup.") self._command_context.cleanup() self._if_manager.deconfigure_all() self._if_manager.clear_if_mapping() self._cache.del_old_entries() self.restore_system_config() self._remove_capture_files() return True def clear_resource_table(self): self._resource_table = {'module': {}, 'tools': {}} return True def has_resource(self, res_hash): if self._cache.query(res_hash): return True return False def map_resource(self, res_hash, res_type, res_name): resource_location = self._cache.get_path(res_hash) if not res_type in self._resource_table: self._resource_table[res_type] = {} self._resource_table[res_type][res_name] = resource_location self._cache.renew_entry(res_hash) return True def add_resource_to_cache(self, file_hash, local_path, name, res_hash, res_type): self._cache.add_cache_entry(file_hash, local_path, name, res_type) return True def start_copy_to(self, filepath=None): if filepath in self._copy_targets: return "" if filepath: self._copy_targets[filepath] = open(filepath, "w+b") else: tmpfile = NamedTemporaryFile("w+b", delete=False) filepath = tmpfile.name self._copy_targets[filepath] = tmpfile return filepath def copy_part_to(self, filepath, binary_data): if self._copy_targets[filepath]: self._copy_targets[filepath].write(binary_data.data) return True return False def finish_copy_to(self, filepath): if self._copy_targets[filepath]: self._copy_targets[filepath].close() del self._copy_targets[filepath] return True return False def start_copy_from(self, filepath): if filepath in self._copy_sources or not os.path.exists(filepath): return False self._copy_sources[filepath] = open(filepath, "rb") return True def copy_part_from(self, filepath, buffsize): data = Binary(self._copy_sources[filepath].read(buffsize)) return data def finish_copy_from(self, filepath): if filepath in self._copy_sources: self._copy_sources[filepath].close() del self._copy_sources[filepath] return True return False def reset_file_transfers(self): for file_handle in self._copy_targets.itervalues(): file_handle.close() self._copy_targets = {} for file_handle in self._copy_sources.itervalues(): file_handle.close() self._copy_sources = {}
class SlaveMethods: ''' Exported xmlrpc methods ''' def __init__(self, job_context, log_ctl, net_namespaces, server_handler, slave_config, slave_server): self._packet_captures = {} self._if_manager = None self._job_context = job_context self._log_ctl = log_ctl self._net_namespaces = net_namespaces self._server_handler = server_handler self._slave_server = slave_server self._slave_config = slave_config self._capture_files = {} self._copy_targets = {} self._copy_sources = {} self._system_config = {} self._cache = ResourceCache(slave_config.get_option("cache", "dir"), slave_config.get_option("cache", "expiration_period")) self._dynamic_modules = {} self._dynamic_classes = {} self._dynamic_objects = {} def hello(self): logging.info("Recieved a controller connection.") slave_desc = {} if check_process_running("NetworkManager"): slave_desc["nm_running"] = True else: slave_desc["nm_running"] = False k_release, _ = exec_cmd("uname -r", False, False, False) r_release, _ = exec_cmd("cat /etc/redhat-release", False, False, False) slave_desc["kernel_release"] = k_release.strip() slave_desc["redhat_release"] = r_release.strip() slave_desc["lnst_version"] = lnst_version return ("hello", slave_desc) def prepare_machine(self): self.machine_cleanup() self._cache.del_old_entries() self.reset_file_transfers() return True def start_recipe(self, recipe_name): date = datetime.datetime.now().strftime("%Y-%m-%d_%H:%M:%S") self._log_ctl.set_recipe(recipe_name, expand=date) sleep(1) if check_process_running("NetworkManager"): logging.warning("=============================================") logging.warning("NetworkManager is running on a slave machine!") logging.warning("=============================================") for device in self._if_manager.get_devices(): try: device.store_cleanup_data() except DeviceDisabled: pass return True def bye(self): self.restore_system_config() self._cache.del_old_entries() self.reset_file_transfers() self._remove_capture_files() return "bye" def map_device_class(self, cls_name, module_name): if cls_name in self._dynamic_classes: return module = self._dynamic_modules[module_name] cls = getattr(module, cls_name) self._dynamic_classes["{}.{}".format(module_name, cls_name)] = cls setattr(Devices, cls_name, cls) def load_cached_module(self, module_name, res_hash): self._cache.renew_entry(res_hash) if module_name in self._dynamic_modules: return module_path = self._cache.get_path(res_hash) module = imp.load_source(module_name, module_path) self._dynamic_modules[module_name] = module def init_cls(self, cls_name, module_name, args, kwargs): module = self._dynamic_modules[module_name] cls = getattr(module, cls_name) self._dynamic_classes["{}.{}".format(module_name, cls_name)] = cls new_obj = cls(*args, **kwargs) self._dynamic_objects[id(new_obj)] = new_obj return id(new_obj) def init_if_manager(self): self._if_manager = InterfaceManager(self._server_handler) for cls_name in dir(Devices): cls = getattr(Devices, cls_name) if isclass(cls): self._if_manager.add_device_class(cls_name, cls) self._if_manager.rescan_devices() self._server_handler.set_if_manager(self._if_manager) return True def obj_method(self, obj_ref, name, args, kwargs): try: obj = self._dynamic_objects[obj_ref] method = getattr(obj, name) return method(*args, **kwargs) except LnstError: raise except Exception as exc: log_exc_traceback() raise LnstError(exc) def obj_getattr(self, obj_ref, name): try: obj = self._dynamic_objects[obj_ref] return getattr(obj, name) except LnstError: raise except Exception as exc: log_exc_traceback() raise LnstError(exc) def obj_setattr(self, obj_ref, name, value): try: obj = self._dynamic_objects[obj_ref] return setattr(obj, name, value) except LnstError: raise except Exception as exc: log_exc_traceback() raise LnstError(exc) def dev_method(self, ifindex, name, args, kwargs): dev = self._if_manager.get_device(ifindex) method = getattr(dev, name) return method(*args, **kwargs) def dev_getattr(self, ifindex, name): dev = self._if_manager.get_device(ifindex) return getattr(dev, name) def dev_setattr(self, ifindex, name, value): dev = self._if_manager.get_device(ifindex) return setattr(dev, name, value) def get_devices(self): devices = self._if_manager.get_devices() result = {} for device in devices: result[device.ifindex] = device._get_if_data() return result def get_device(self, ifindex): device = self._if_manager.get_device(ifindex) if device: return device._get_if_data() else: return None def get_devices_by_devname(self, devname): name_scan = self._if_manager.get_devices() netdevs = [] for entry in name_scan: if entry["name"] == devname: netdevs.append(entry) return netdevs def get_devices_by_hwaddr(self, hwaddr): devices = self._if_manager.get_devices() matched = [] for dev in devices: if dev.get_hwaddr() == hwaddr: entry = {"name": dev.get_name(), "hwaddr": dev.get_hwaddr()} matched.append(entry) return matched def get_devices_by_params(self, params): devices = self._if_manager.get_devices() matched = [] for dev in devices: dev_data = dev._get_if_data() entry = {"name": dev.get_name(), "hwaddr": dev.get_hwaddr()} for key, value in params.items(): if key not in dev_data or dev_data[key] != value: entry = None break if entry is not None: matched.append(entry) return matched def destroy_devices(self): if self._if_manager is None: return devices = self._if_manager.get_devices() for dev in devices: try: dev.destroy() except (DeviceDisabled, DeviceDeleted, DeviceConfigValueError): pass self._if_manager.rescan_devices() # def add_route(self, if_id, dest): # dev = self._if_manager.get_mapped_device(if_id) # if dev is None: # logging.error("Device with id '%s' not found." % if_id) # return False # dev.add_route(dest) # return True # def del_route(self, if_id, dest): # dev = self._if_manager.get_mapped_device(if_id) # if dev is None: # logging.error("Device with id '%s' not found." % if_id) # return False # dev.del_route(dest) # return True def create_device(self, clsname, args=[], kwargs={}): dev = self._if_manager.create_device(clsname, args, kwargs) return {"ifindex": dev.ifindex, "name": dev.name} def start_packet_capture(self, filt): if not is_installed("tcpdump"): raise Exception("Can't start packet capture, tcpdump not available") files = {} for if_id, dev in self._if_manager.get_mapped_devices().items(): if dev.get_netns() != None: continue dev_name = dev.get_name() df_handle = NamedTemporaryFile(delete=False) dump_file = df_handle.name df_handle.close() files[if_id] = dump_file pcap = PacketCapture() pcap.set_interface(dev_name) pcap.set_output_file(dump_file) pcap.set_filter(filt) pcap.start() self._packet_captures[if_id] = pcap self._capture_files = files return files def stop_packet_capture(self): if self._packet_captures == None: return True for ifindex, pcap in self._packet_captures.items(): pcap.stop() self._packet_captures.clear() return True def _remove_capture_files(self): for key, name in self._capture_files.items(): logging.debug("Removing temporary packet capture file %s", name) os.unlink(name) self._capture_files.clear() def _update_system_config(self, options, persistent): system_config = self._system_config for opt in options: option = opt["name"] prev = opt["previous_val"] curr = opt["current_val"] if persistent: if option in system_config: del system_config[option] else: if not option in system_config: system_config[option] = {"initial_val": prev} system_config[option]["current_val"] = curr def restore_system_config(self): logging.info("Restoring system configuration") for option, values in self._system_config.items(): try: cmd_str = "echo \"%s\" >%s" % (values["initial_val"], option) (stdout, stderr) = exec_cmd(cmd_str) except ExecCmdFail: logging.warn("Unable to restore '%s' config option!", option) return False self._system_config = {} return True def run_job(self, job): job_instance = Job(job, self._log_ctl) self._job_context.add_job(job_instance) res = job_instance.run() return res def kill_job(self, job_id, signal): job = self._job_context.get_job(job_id) if job is None: logging.error("No job %s found" % job_id) return False return job.kill(signal) def kill_jobs(self): logging.info("Killing all forked processes.") self._job_context.cleanup() return "Commands killed" def machine_cleanup(self): logging.info("Performing machine cleanup.") self._job_context.cleanup() self.restore_system_config() if self._if_manager is not None: self._if_manager.deconfigure_all() for netns in list(self._net_namespaces.keys()): self.del_namespace(netns) self._net_namespaces = {} for obj_id, obj in list(self._dynamic_objects.items()): del obj for cls_name in dir(Devices): cls = getattr(Devices, cls_name) if isclass(cls): delattr(Devices, cls_name) for module_name, module in list(self._dynamic_modules.items()): del sys.modules[module_name] self._dynamic_objects = {} self._dynamic_classes = {} self._dynamic_modules = {} self._if_manager = None self._server_handler.set_if_manager(None) self._cache.del_old_entries() self._remove_capture_files() return True def has_resource(self, res_hash): if self._cache.query(res_hash): return True return False def add_resource_to_cache(self, res_type, local_path, name): if res_type == "file": self._cache.add_file_entry(local_path, name) return True else: raise Exception("Unknown resource type") def start_copy_to(self, filepath=None): if filepath in self._copy_targets: return "" if filepath: self._copy_targets[filepath] = open(filepath, "w+b") else: tmpfile = NamedTemporaryFile("w+b", delete=False) filepath = tmpfile.name self._copy_targets[filepath] = tmpfile return filepath def copy_part_to(self, filepath, data): if self._copy_targets[filepath]: self._copy_targets[filepath].write(data) return True return False def finish_copy_to(self, filepath): if self._copy_targets[filepath]: self._copy_targets[filepath].close() del self._copy_targets[filepath] return True return False def start_copy_from(self, filepath): if filepath in self._copy_sources or not os.path.exists(filepath): return False self._copy_sources[filepath] = open(filepath, "rb") return True def copy_part_from(self, filepath, buffsize): data = self._copy_sources[filepath].read(buffsize) return data def finish_copy_from(self, filepath): if filepath in self._copy_sources: self._copy_sources[filepath].close() del self._copy_sources[filepath] return True return False def reset_file_transfers(self): for file_handle in self._copy_targets.values(): file_handle.close() self._copy_targets = {} for file_handle in self._copy_sources.values(): file_handle.close() self._copy_sources = {} def add_namespace(self, netns): if netns in self._net_namespaces: logging.debug("Network namespace %s already exists." % netns) else: logging.debug("Creating network namespace %s." % netns) read_pipe, write_pipe = multiprocessing.Pipe() pid = os.fork() if pid != 0: self._net_namespaces[netns] = {"pid": pid, "pipe": read_pipe} self._server_handler.add_netns(netns, read_pipe) result = self._slave_server.wait_for_result(netns) if result["result"] != True: raise Exception("Namespace creation failed") return True elif pid == 0: self._slave_server.set_netns_sighandlers() #create new network namespace libc_name = ctypes.util.find_library("c") #from sched.h CLONE_NEWNET = 0x40000000 CLONE_NEWNS = 0x00020000 #based on ipnetns.c from the iproute2 project MNT_DETACH = 0x00000002 MS_BIND = 4096 MS_SLAVE = 1<<19 MS_REC = 16384 libc = ctypes.CDLL(libc_name) #based on ipnetns.c from the iproute2 project #bind to named namespace netns_path = "/var/run/netns/" if not os.path.exists(netns_path): os.mkdir(netns_path, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) netns_path = netns_path + netns f = os.open(netns_path, os.O_RDONLY | os.O_CREAT | os.O_EXCL, 0) os.close(f) libc.unshare(CLONE_NEWNET) libc.mount("/proc/self/ns/net", netns_path, "none", MS_BIND, 0) #map network sysfs to new net libc.unshare(CLONE_NEWNS) libc.mount("", "/", "none", MS_SLAVE | MS_REC, 0) libc.umount2("/sys", MNT_DETACH) libc.mount(netns, "/sys", "sysfs", 0, 0) #set ctl socket to pipe to main netns self._server_handler.close_s_sock() self._server_handler.close_c_sock() self._server_handler.clear_connections() self._server_handler.clear_netns_connections() self._server_handler.set_netns(netns) self._server_handler.set_ctl_sock((write_pipe, "root_netns")) self._log_ctl.disable_logging() self._log_ctl.set_origin_name(netns) self._log_ctl.set_connection(write_pipe) self.init_if_manager() logging.debug("Created network namespace %s" % netns) return True else: raise Exception("Fork failed!") def del_namespace(self, netns): if netns not in self._net_namespaces: logging.debug("Network namespace %s doesn't exist." % netns) return False else: MNT_DETACH = 0x00000002 libc_name = ctypes.util.find_library("c") libc = ctypes.CDLL(libc_name) netns_path = "/var/run/netns/" + netns netns_pid = self._net_namespaces[netns]["pid"] os.kill(netns_pid, signal.SIGUSR1) os.waitpid(netns_pid, 0) # Remove named namespace try: libc.umount2(netns_path, MNT_DETACH) os.unlink(netns_path) except: logging.warning("Unable to remove named namespace %s." % netns_path) logging.debug("Network namespace %s removed." % netns) self._net_namespaces[netns]["pipe"].close() self._server_handler.del_netns(netns) del self._net_namespaces[netns] return True def set_dev_netns(self, dev, dst): exec_cmd("ip link set %s netns %s" % (dev.name, dst)) self._if_manager.untrack_device(dev) dev._deleted = True self._if_manager.rescan_devices() #TODO check if device appeared in the destination namespace return True # def return_if_netns(self, if_id): # device = self._if_manager.get_mapped_device(if_id) # if device.get_netns() == None: # dev_name = device.get_name() # ppid = os.getppid() # exec_cmd("ip link set %s netns %d" % (dev_name, ppid)) # self._if_manager.unmap_if(if_id) # return True # else: # netns = device.get_netns() # msg = {"type": "command", "method_name": "return_if_netns", # "args": [if_id]} # self._server_handler.send_data_to_netns(netns, msg) # result = self._slave_server.wait_for_result(netns) # if result["result"] != True: # raise Exception("Return from netns failed.") # device.set_netns(None) # return True def add_br_vlan(self, if_id, br_vlan_info): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) brt.add_vlan(br_vlan_info) return True def del_br_vlan(self, if_id, br_vlan_info): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) brt.del_vlan(br_vlan_info) return True def get_br_vlans(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) return brt.get_vlans() def add_br_fdb(self, if_id, br_fdb_info): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) brt.add_fdb(br_fdb_info) return True def del_br_fdb(self, if_id, br_fdb_info): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) brt.del_fdb(br_fdb_info) return True def get_br_fdbs(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) return brt.get_fdbs() def set_br_learning(self, if_id, br_learning_info): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) brt.set_learning(br_learning_info) return True def set_br_learning_sync(self, if_id, br_learning_sync_info): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) brt.set_learning_sync(br_learning_sync_info) return True def set_br_flooding(self, if_id, br_flooding_info): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) brt.set_flooding(br_flooding_info) return True def set_br_state(self, if_id, br_state_info): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) brt.set_state(br_state_info) return True
class SlaveMethods: ''' Exported xmlrpc methods ''' def __init__(self, command_context, log_ctl, if_manager, net_namespaces, server_handler, slave_server): self._packet_captures = {} self._if_manager = if_manager self._command_context = command_context self._log_ctl = log_ctl self._net_namespaces = net_namespaces self._server_handler = server_handler self._slave_server = slave_server self._capture_files = {} self._copy_targets = {} self._copy_sources = {} self._system_config = {} self._cache = ResourceCache( lnst_config.get_option("cache", "dir"), lnst_config.get_option("cache", "expiration_period")) self._resource_table = {'module': {}, 'tools': {}} self._bkp_nm_opt_val = lnst_config.get_option("environment", "use_nm") def hello(self, recipe_path): self.machine_cleanup() self.restore_nm_option() logging.info("Recieved a controller connection.") self.clear_resource_table() self._cache.del_old_entries() self.reset_file_transfers() self._if_manager.rescan_devices() date = datetime.datetime.now().strftime("%Y-%m-%d_%H:%M:%S") self._log_ctl.set_recipe(recipe_path, expand=date) sleep(1) slave_desc = {} if check_process_running("NetworkManager"): logging.warning("=============================================") logging.warning("NetworkManager is running on a slave machine!") if lnst_config.get_option("environment", "use_nm"): logging.warning("Support of NM is still experimental!") else: logging.warning("Usage of NM is disabled!") logging.warning("=============================================") slave_desc["nm_running"] = True else: slave_desc["nm_running"] = False k_release, _ = exec_cmd("uname -r", False, False, False) r_release, _ = exec_cmd("cat /etc/redhat-release", False, False, False) slave_desc["kernel_release"] = k_release.strip() slave_desc["redhat_release"] = r_release.strip() slave_desc["lnst_version"] = lnst_config.version return ("hello", slave_desc) def bye(self): self.restore_system_config() self.clear_resource_table() self._cache.del_old_entries() self.reset_file_transfers() self._remove_capture_files() return "bye" def kill_cmds(self): logging.info("Killing all forked processes.") self._command_context.cleanup() return "Commands killed" def map_if_by_hwaddr(self, if_id, hwaddr): devices = self.map_if_by_params(if_id, {'hwaddr': hwaddr}) return devices def map_if_by_params(self, if_id, params): devices = self.get_devices_by_params(params) if len(devices) == 1: dev = self._if_manager.get_device_by_params(params) self._if_manager.map_if(if_id, dev.get_if_index()) return devices def unmap_if(self, if_id): self._if_manager.unmap_if(if_id) return True def get_devices(self): self._if_manager.rescan_devices() devices = self._if_manager.get_devices() result = {} for device in devices: result[device._if_index] = device.get_if_data() return result def get_device(self, if_index): self._if_manager.rescan_devices() device = self._if_manager.get_device(if_index) if device: return device.get_if_data() else: return {} def get_devices_by_devname(self, devname): name_scan = self._if_manager.get_devices() netdevs = [] for entry in name_scan: if entry["name"] == devname: netdevs.append(entry) return netdevs def get_devices_by_hwaddr(self, hwaddr): devices = self._if_manager.get_devices() matched = [] for dev in devices: if dev.get_hwaddr() == hwaddr: entry = {"name": dev.get_name(), "hwaddr": dev.get_hwaddr()} matched.append(entry) return matched def get_devices_by_params(self, params): devices = self._if_manager.get_devices() matched = [] for dev in devices: dev_data = dev.get_if_data() entry = {"name": dev.get_name(), "hwaddr": dev.get_hwaddr()} for key, value in params.iteritems(): if key not in dev_data or dev_data[key] != value: entry = None break if entry is not None: matched.append(entry) return matched def get_if_data(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if dev is None: return {} return dev.get_if_data() def link_stats(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if dev is None: logging.error("Device with id '%s' not found." % if_id) return {} return dev.link_stats() def set_addresses(self, if_id, ips): dev = self._if_manager.get_mapped_device(if_id) if dev is None: logging.error("Device with id '%s' not found." % if_id) return False dev.set_addresses(ips) return True def add_route(self, if_id, dest, ipv6): dev = self._if_manager.get_mapped_device(if_id) if dev is None: logging.error("Device with id '%s' not found." % if_id) return False dev.add_route(dest, ipv6) return True def del_route(self, if_id, dest, ipv6): dev = self._if_manager.get_mapped_device(if_id) if dev is None: logging.error("Device with id '%s' not found." % if_id) return False dev.del_route(dest, ipv6) return True def add_nhs_route(self, if_id, dest, nhs, ipv6): dev = self._if_manager.get_mapped_device(if_id) if dev is None: logging.error("Device with id '%s' not found." % if_id) return False dev.add_nhs_route(dest, nhs, ipv6) return True def del_nhs_route(self, if_id, dest, nhs, ipv6): dev = self._if_manager.get_mapped_device(if_id) if dev is None: logging.error("Device with id '%s' not found." % if_id) return False dev.del_nhs_route(dest, nhs, ipv6) return True def set_device_up(self, if_id): dev = self._if_manager.get_mapped_device(if_id) dev.up() return True def set_device_down(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if dev is not None: dev.down() else: logging.error("Device with id '%s' not found." % if_id) return False return True def device_address_setup(self, if_id): dev = self._if_manager.get_mapped_device(if_id) dev.address_setup() return True def device_address_cleanup(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if dev is not None: dev.address_cleanup() else: logging.error("Device with id '%s' not found." % if_id) return False return True def set_link_up(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if dev is not None: dev.link_up() else: logging.error("Device with id '%s' not found." % if_id) return False return True def set_link_down(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if dev is not None: dev.link_down() else: logging.error("Device with id '%s' not found." % if_id) return False return True def set_unmapped_device_down(self, hwaddr): dev = self._if_manager.get_device_by_hwaddr(hwaddr) if dev is not None: dev.down() else: logging.warning("Device with hwaddr '%s' not found." % hwaddr) return True def configure_interface(self, if_id, config): device = self._if_manager.get_mapped_device(if_id) device.set_configuration(config) device.configure() return True def create_soft_interface(self, if_id, config): dev_name = self._if_manager.create_device_from_config(if_id, config) dev = self._if_manager.get_mapped_device(if_id) dev.configure() return dev_name def create_if_pair(self, if_id1, config1, if_id2, config2): dev_names = self._if_manager.create_device_pair( if_id1, config1, if_id2, config2) dev1 = self._if_manager.get_mapped_device(if_id1) dev2 = self._if_manager.get_mapped_device(if_id2) while dev1.get_if_index() == None and dev2.get_if_index() == None: msgs = self._server_handler.get_messages_from_con('netlink') for msg in msgs: self._if_manager.handle_netlink_msgs(msg[1]["data"]) if config1["netns"] != None: hwaddr = dev1.get_hwaddr() self.set_if_netns(if_id1, config1["netns"]) msg = { "type": "command", "method_name": "configure_interface", "args": [if_id1, config1] } self._server_handler.send_data_to_netns(config1["netns"], msg) result = self._slave_server.wait_for_result(config1["netns"]) if result["result"] != True: raise Exception("Configuration failed.") else: dev1.configure() if config2["netns"] != None: hwaddr = dev2.get_hwaddr() self.set_if_netns(if_id2, config2["netns"]) msg = { "type": "command", "method_name": "configure_interface", "args": [if_id2, config2] } self._server_handler.send_data_to_netns(config2["netns"], msg) result = self._slave_server.wait_for_result(config2["netns"]) if result["result"] != True: raise Exception("Configuration failed.") else: dev2.configure() return dev_names def deconfigure_if_pair(self, if_id1, if_id2): dev1 = self._if_manager.get_mapped_device(if_id1) dev2 = self._if_manager.get_mapped_device(if_id2) if dev1.get_netns() == None: dev1.deconfigure() else: netns = dev1.get_netns() msg = { "type": "command", "method_name": "deconfigure_interface", "args": [if_id1] } self._server_handler.send_data_to_netns(netns, msg) result = self._slave_server.wait_for_result(netns) if result["result"] != True: raise Exception("Deconfiguration failed.") self.return_if_netns(if_id1) if dev2.get_netns() == None: dev2.deconfigure() else: netns = dev2.get_netns() msg = { "type": "command", "method_name": "deconfigure_interface", "args": [if_id2] } self._server_handler.send_data_to_netns(netns, msg) result = self._slave_server.wait_for_result(netns) if result["result"] != True: raise Exception("Deconfiguration failed.") self.return_if_netns(if_id2) dev1.destroy() dev2.destroy() dev1.del_configuration() dev2.del_configuration() return True def deconfigure_interface(self, if_id): device = self._if_manager.get_mapped_device(if_id) if device is not None: device.clear_configuration() else: logging.error("No device with id '%s' to deconfigure." % if_id) return True def start_packet_capture(self, filt): if not is_installed("tcpdump"): raise Exception( "Can't start packet capture, tcpdump not available") files = {} for if_id, dev in self._if_manager.get_mapped_devices().iteritems(): if dev.get_netns() != None: continue dev_name = dev.get_name() df_handle = NamedTemporaryFile(delete=False) dump_file = df_handle.name df_handle.close() files[if_id] = dump_file pcap = PacketCapture() pcap.set_interface(dev_name) pcap.set_output_file(dump_file) pcap.set_filter(filt) pcap.start() self._packet_captures[if_id] = pcap self._capture_files = files return files def stop_packet_capture(self): if self._packet_captures == None: return True for if_index, pcap in self._packet_captures.iteritems(): pcap.stop() self._packet_captures.clear() return True def _remove_capture_files(self): for key, name in self._capture_files.iteritems(): logging.debug("Removing temporary packet capture file %s", name) os.unlink(name) self._capture_files.clear() def _update_system_config(self, options, persistent): system_config = self._system_config for opt in options: option = opt["name"] prev = opt["previous_val"] curr = opt["current_val"] if persistent: if option in system_config: del system_config[option] else: if not option in system_config: system_config[option] = {"initial_val": prev} system_config[option]["current_val"] = curr def restore_system_config(self): logging.info("Restoring system configuration") for option, values in self._system_config.iteritems(): try: cmd_str = "echo \"%s\" >%s" % (values["initial_val"], option) (stdout, stderr) = exec_cmd(cmd_str) except ExecCmdFail: logging.warn("Unable to restore '%s' config option!", option) return False self._system_config = {} return True def get_remaining_time(self, bg_id): cmd = self._command_context.get_cmd(bg_id) if "timeout" in cmd._command: cmd_timeout = cmd._command["timeout"] else: cmd_timeout = DEFAULT_TIMEOUT start_time = cmd._start_time current_time = time() remaining = cmd_timeout - (current_time - start_time) if remaining < 0: remaining = 0 return int(remaining) def run_command(self, command): cmd = NetTestCommand(self._command_context, command, self._resource_table, self._log_ctl) if self._command_context.get_cmd(cmd.get_id()) != None: prev_cmd = self._command_context.get_cmd(cmd.get_id()) if not prev_cmd.get_result_sent(): if cmd.get_id() is None: raise Exception("Previous foreground command still "\ "running!") else: raise Exception("Different command with id '%s' "\ "still running!" % cmd.get_id()) else: self._command_context.del_cmd(cmd) self._command_context.add_cmd(cmd) res = cmd.run() if not cmd.forked(): self._command_context.del_cmd(cmd) if command["type"] == "config": if res["passed"]: self._update_system_config(res["res_data"]["options"], command["persistent"]) else: err = "Error occured while setting system "\ "configuration (%s)" % res["res_data"]["err_msg"] logging.error(err) return res def kill_command(self, id): cmd = self._command_context.get_cmd(id) if cmd is not None: if not cmd.get_result_sent(): cmd.kill(None) result = cmd.get_result() cmd.set_result_sent() return result else: pass else: raise Exception("No command with id '%s'." % id) def machine_cleanup(self): logging.info("Performing machine cleanup.") self._command_context.cleanup() self.restore_system_config() devs = self._if_manager.get_mapped_devices() for if_id, dev in devs.iteritems(): peer = dev.get_peer() if peer == None: dev.clear_configuration() else: peer_if_index = peer.get_if_index() peer_id = self._if_manager.get_id_by_if_index(peer_if_index) self.deconfigure_if_pair(if_id, peer_id) self._if_manager.deconfigure_all() for netns in self._net_namespaces.keys(): self.del_namespace(netns) self._net_namespaces = {} self._if_manager.clear_if_mapping() self._cache.del_old_entries() self._remove_capture_files() return True def clear_resource_table(self): self._resource_table = {'module': {}, 'tools': {}} return True def has_resource(self, res_hash): if self._cache.query(res_hash): return True return False def map_resource(self, res_hash, res_type, res_name): resource_location = self._cache.get_path(res_hash) if not res_type in self._resource_table: self._resource_table[res_type] = {} self._resource_table[res_type][res_name] = resource_location self._cache.renew_entry(res_hash) return True def add_resource_to_cache(self, file_hash, local_path, name, res_hash, res_type): self._cache.add_cache_entry(file_hash, local_path, name, res_type) return True def start_copy_to(self, filepath=None): if filepath in self._copy_targets: return "" if filepath: self._copy_targets[filepath] = open(filepath, "w+b") else: tmpfile = NamedTemporaryFile("w+b", delete=False) filepath = tmpfile.name self._copy_targets[filepath] = tmpfile return filepath def copy_part_to(self, filepath, binary_data): if self._copy_targets[filepath]: self._copy_targets[filepath].write(binary_data.data) return True return False def finish_copy_to(self, filepath): if self._copy_targets[filepath]: self._copy_targets[filepath].close() del self._copy_targets[filepath] return True return False def start_copy_from(self, filepath): if filepath in self._copy_sources or not os.path.exists(filepath): return False self._copy_sources[filepath] = open(filepath, "rb") return True def copy_part_from(self, filepath, buffsize): data = Binary(self._copy_sources[filepath].read(buffsize)) return data def finish_copy_from(self, filepath): if filepath in self._copy_sources: self._copy_sources[filepath].close() del self._copy_sources[filepath] return True return False def reset_file_transfers(self): for file_handle in self._copy_targets.itervalues(): file_handle.close() self._copy_targets = {} for file_handle in self._copy_sources.itervalues(): file_handle.close() self._copy_sources = {} def enable_nm(self): logging.warning("====================================================") logging.warning("Enabling use of NetworkManager on controller request") logging.warning("====================================================") val = lnst_config.get_option("environment", "use_nm") lnst_config.set_option("environment", "use_nm", True) return val def disable_nm(self): logging.warning( "=====================================================") logging.warning( "Disabling use of NetworkManager on controller request") logging.warning( "=====================================================") val = lnst_config.get_option("environment", "use_nm") lnst_config.set_option("environment", "use_nm", False) return val def restore_nm_option(self): val = lnst_config.get_option("environment", "use_nm") if val == self._bkp_nm_opt_val: return val logging.warning("=========================================") logging.warning("Restoring use_nm option to original value") logging.warning("=========================================") lnst_config.set_option("environment", "use_nm", self._bkp_nm_opt_val) return val def add_namespace(self, netns): if netns in self._net_namespaces: logging.debug("Network namespace %s already exists." % netns) else: logging.debug("Creating network namespace %s." % netns) read_pipe, write_pipe = multiprocessing.Pipe() pid = os.fork() if pid != 0: self._net_namespaces[netns] = {"pid": pid, "pipe": read_pipe} self._server_handler.add_netns(netns, read_pipe) result = self._slave_server.wait_for_result(netns) return result["result"] elif pid == 0: self._slave_server.set_netns_sighandlers() #create new network namespace libc_name = ctypes.util.find_library("c") #from sched.h CLONE_NEWNET = 0x40000000 CLONE_NEWNS = 0x00020000 #based on ipnetns.c from the iproute2 project MNT_DETACH = 0x00000002 MS_BIND = 4096 MS_SLAVE = 1 << 19 MS_REC = 16384 libc = ctypes.CDLL(libc_name) #based on ipnetns.c from the iproute2 project #bind to named namespace netns_path = "/var/run/netns/" if not os.path.exists(netns_path): os.mkdir( netns_path, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) netns_path = netns_path + netns f = os.open(netns_path, os.O_RDONLY | os.O_CREAT | os.O_EXCL, 0) os.close(f) libc.unshare(CLONE_NEWNET) libc.mount("/proc/self/ns/net", netns_path, "none", MS_BIND, 0) #map network sysfs to new net libc.unshare(CLONE_NEWNS) libc.mount("", "/", "none", MS_SLAVE | MS_REC, 0) libc.umount2("/sys", MNT_DETACH) libc.mount(netns, "/sys", "sysfs", 0, 0) #set ctl socket to pipe to main netns self._server_handler.close_s_sock() self._server_handler.close_c_sock() self._server_handler.clear_connections() self._server_handler.clear_netns_connections() self._if_manager.reconnect_netlink() self._if_manager.clear_if_mapping() self._server_handler.add_connection( 'netlink', self._if_manager.get_nl_socket()) self._server_handler.set_netns(netns) self._server_handler.set_ctl_sock((write_pipe, "root_netns")) self._log_ctl.disable_logging() self._log_ctl.set_origin_name(netns) self._log_ctl.set_connection(write_pipe) logging.debug("Created network namespace %s" % netns) return True else: raise Exception("Fork failed!") def del_namespace(self, netns): if netns not in self._net_namespaces: logging.debug("Network namespace %s doesn't exist." % netns) return False else: MNT_DETACH = 0x00000002 libc_name = ctypes.util.find_library("c") libc = ctypes.CDLL(libc_name) netns_path = "/var/run/netns/" + netns netns_pid = self._net_namespaces[netns]["pid"] os.kill(netns_pid, signal.SIGUSR1) os.waitpid(netns_pid, 0) # Remove named namespace try: libc.umount2(netns_path, MNT_DETACH) os.unlink(netns_path) except: logging.warning("Unable to remove named namespace %s." % netns_path) logging.debug("Network namespace %s removed." % netns) self._net_namespaces[netns]["pipe"].close() self._server_handler.del_netns(netns) del self._net_namespaces[netns] return True def get_routes(self, route_filter): try: route_output, _ = exec_cmd("ip route show %s" % route_filter) except: return {} dc_routes = [] nh_routes = [] ip_re = "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" prefix_re = "^(" + ip_re + "(?:/\d{1,3})?)" # parse directly connected routes dc_route_re = prefix_re + " dev (\w+) (.*)" dc_matchs = re.findall(dc_route_re, route_output, re.M) for dc_match in dc_matchs: dc_route = { "prefix": dc_match[0], "dev": dc_match[1], "flags": dc_match[2] } dc_routes.append(dc_route) # parse nexthop routes nh_re = " via (" + ip_re + ").*dev (\w+) (.*)" nh_route_re = prefix_re + nh_re nh_route_matchs = re.findall(nh_route_re, route_output, re.M) for nh_route_match in nh_route_matchs: nexthop = { "ip": nh_route_match[1], "dev": nh_route_match[2], "flags": "" } nh_route = { "prefix": nh_route_match[0], "nexthops": [nexthop], "flags": nh_route_match[3] } nh_routes.append(nh_route) # parse ECMP routes ecmp_route_re = prefix_re + "(.*)\n((?:.*nexthop .*\n?)+)" ecmp_matchs = re.findall(ecmp_route_re, route_output, re.M) for ecmp_match in ecmp_matchs: # parse each nexthop nexthops = [] nh_matchs = re.findall(nh_re, ecmp_match[2]) for nh_match in nh_matchs: nexthop = { "ip": nh_match[0], "dev": nh_match[1], "flags": nh_match[2] } nexthops.append(nexthop) ecmp_route = { "prefix": ecmp_match[0], "nexthops": nexthops, "flags": ecmp_match[1] } nh_routes.append(ecmp_route) return dc_routes, nh_routes def set_if_netns(self, if_id, netns): netns_pid = self._net_namespaces[netns]["pid"] device = self._if_manager.get_mapped_device(if_id) dev_name = device.get_name() device.set_netns(netns) hwaddr = device.get_hwaddr() exec_cmd("ip link set %s netns %d" % (dev_name, netns_pid)) msg = { "type": "command", "method_name": "map_if_by_hwaddr", "args": [if_id, hwaddr] } self._server_handler.send_data_to_netns(netns, msg) result = self._slave_server.wait_for_result(netns) return result def return_if_netns(self, if_id): device = self._if_manager.get_mapped_device(if_id) if device.get_netns() == None: dev_name = device.get_name() ppid = os.getppid() exec_cmd("ip link set %s netns %d" % (dev_name, ppid)) self._if_manager.unmap_if(if_id) return True else: netns = device.get_netns() msg = { "type": "command", "method_name": "return_if_netns", "args": [if_id] } self._server_handler.send_data_to_netns(netns, msg) result = self._slave_server.wait_for_result(netns) if result["result"] != True: raise Exception("Return from netns failed.") device.set_netns(None) return True def add_br_vlan(self, if_id, br_vlan_info): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) brt.add_vlan(br_vlan_info) return True def del_br_vlan(self, if_id, br_vlan_info): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) brt.del_vlan(br_vlan_info) return True def get_br_vlans(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) return brt.get_vlans() def add_br_fdb(self, if_id, br_fdb_info): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) brt.add_fdb(br_fdb_info) return True def del_br_fdb(self, if_id, br_fdb_info): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) brt.del_fdb(br_fdb_info) return True def get_br_fdbs(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) return brt.get_fdbs() def set_br_learning(self, if_id, br_learning_info): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) brt.set_learning(br_learning_info) return True def set_br_learning_sync(self, if_id, br_learning_sync_info): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) brt.set_learning_sync(br_learning_sync_info) return True def set_br_flooding(self, if_id, br_flooding_info): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) brt.set_flooding(br_flooding_info) return True def set_br_state(self, if_id, br_state_info): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) brt.set_state(br_state_info) return True def set_speed(self, if_id, speed): dev = self._if_manager.get_mapped_device(if_id) if dev is not None: dev.set_speed(speed) else: logging.error("Device with id '%s' not found." % if_id) return False return True def set_autoneg(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if dev is not None: dev.set_autoneg() else: logging.error("Device with id '%s' not found." % if_id) return False return True def wait_interface_init(self): self._if_manager.wait_interface_init() return True def slave_add(self, if_id, slave_id): dev = self._if_manager.get_mapped_device(if_id) if dev is not None: dev.slave_add(slave_id) else: logging.error("Device with id '%s' not found." % if_id) return False return True def slave_del(self, if_id, slave_id): dev = self._if_manager.get_mapped_device(if_id) if dev is not None: dev.slave_del(slave_id) else: logging.error("Device with id '%s' not found." % if_id) return False return True def _is_systemd(self): stdout, _ = exec_cmd("pidof systemd", die_on_err=False) return len(stdout) != 0 def _configure_service(self, service, start=True): action = "start" if start else "stop" if self._is_systemd(): exec_cmd("systemctl {} {}".format(action, service)) else: exec_cmd("service {} {}".format(service, action)) return True def enable_service(self, service): return self._configure_service(service) def disable_service(self, service): return self._configure_service(service, start=False) def get_num_cpus(self): return int(os.sysconf('SC_NPROCESSORS_ONLN')) def get_ethtool_stats(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False return dev.get_ethtool_stats() def enable_lldp(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False dev.enable_lldp() return True def set_pause_on(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if dev is not None: dev.set_pause_on() else: logging.error("Device with id '%s' not found." % if_id) return False return True def set_pause_off(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if dev is not None: dev.set_pause_off() else: logging.error("Device with id '%s' not found." % if_id) return False return True
class SlaveMethods: ''' Exported xmlrpc methods ''' def __init__(self, command_context, log_ctl, if_manager, net_namespaces, server_handler, slave_server): self._packet_captures = {} self._if_manager = if_manager self._command_context = command_context self._log_ctl = log_ctl self._net_namespaces = net_namespaces self._server_handler = server_handler self._slave_server = slave_server self._capture_files = {} self._copy_targets = {} self._copy_sources = {} self._system_config = {} self._cache = ResourceCache(lnst_config.get_option("cache", "dir"), lnst_config.get_option("cache", "expiration_period")) self._resource_table = {'module': {}, 'tools': {}} self._bkp_nm_opt_val = lnst_config.get_option("environment", "use_nm") def hello(self, recipe_path): self.machine_cleanup() self.restore_nm_option() logging.info("Recieved a controller connection.") self.clear_resource_table() self._cache.del_old_entries() self.reset_file_transfers() self._if_manager.rescan_devices() date = datetime.datetime.now().strftime("%Y-%m-%d_%H:%M:%S") self._log_ctl.set_recipe(recipe_path, expand=date) sleep(1) if check_process_running("NetworkManager"): logging.warning("=============================================") logging.warning("NetworkManager is running on a slave machine!") if lnst_config.get_option("environment", "use_nm"): logging.warning("Support of NM is still experimental!") else: logging.warning("Usage of NM is disabled!") logging.warning("=============================================") return "hello" def bye(self): self.restore_system_config() self.clear_resource_table() self._cache.del_old_entries() self.reset_file_transfers() self._remove_capture_files() return "bye" def kill_cmds(self): logging.info("Killing all forked processes.") self._command_context.cleanup() return "Commands killed" def map_if_by_hwaddr(self, if_id, hwaddr): devices = self.get_devices_by_hwaddr(hwaddr) if len(devices) == 1: dev = self._if_manager.get_device_by_hwaddr(hwaddr) self._if_manager.map_if(if_id, dev.get_if_index()) return devices def unmap_if(self, if_id): self._if_manager.unmap_if(if_id) return True def get_devices(self): self._if_manager.rescan_devices() devices = self._if_manager.get_devices() result = {} for device in devices: if device._ifi_type == 1 and device._state == 'DOWN': result[device._if_index] = {'name' : device._name, 'hwaddr' : device._hwaddr} return result def get_devices_by_devname(self, devname): name_scan = self._if_manager.get_devices() netdevs = [] for entry in name_scan: if entry["name"] == devname: netdevs.append(entry) return netdevs def get_devices_by_hwaddr(self, hwaddr): devices = self._if_manager.get_devices() matched = [] for dev in devices: if dev.get_hwaddr() == hwaddr: entry = {"name": dev.get_name(), "hwaddr": dev.get_hwaddr()} matched.append(entry) return matched def set_device_up(self, if_id): dev = self._if_manager.get_mapped_device(if_id) dev.up() return True def set_device_down(self, if_id): dev = self._if_manager.get_mapped_device(if_id) dev.down() return True def set_unmapped_device_down(self, hwaddr): dev = self._if_manager.get_device_by_hwaddr(hwaddr) dev.down() return True def configure_interface(self, if_id, config): device = self._if_manager.get_mapped_device(if_id) device.set_configuration(config) device.configure() return True def create_soft_interface(self, if_id, config): dev_name = self._if_manager.create_device_from_config(if_id, config) dev = self._if_manager.get_mapped_device(if_id) dev.configure() return dev_name def create_if_pair(self, if_id1, config1, if_id2, config2): dev_names = self._if_manager.create_device_pair(if_id1, config1, if_id2, config2) dev1 = self._if_manager.get_mapped_device(if_id1) dev2 = self._if_manager.get_mapped_device(if_id2) while dev1.get_if_index() == None and dev2.get_if_index() == None: msgs = self._server_handler.get_messages_from_con('netlink') for msg in msgs: self._if_manager.handle_netlink_msgs(msg[1]["data"]) if config1["netns"] != None: hwaddr = dev1.get_hwaddr() self.set_if_netns(if_id1, config1["netns"]) msg = {"type": "command", "method_name": "configure_interface", "args": [if_id1, config1]} self._server_handler.send_data_to_netns(config1["netns"], msg) result = self._slave_server.wait_for_result(config1["netns"]) if result["result"] != True: raise Exception("Configuration failed.") else: dev1.configure() if config2["netns"] != None: hwaddr = dev2.get_hwaddr() self.set_if_netns(if_id2, config2["netns"]) msg = {"type": "command", "method_name": "configure_interface", "args": [if_id2, config2]} self._server_handler.send_data_to_netns(config2["netns"], msg) result = self._slave_server.wait_for_result(config2["netns"]) if result["result"] != True: raise Exception("Configuration failed.") else: dev2.configure() return dev_names def deconfigure_if_pair(self, if_id1, if_id2): dev1 = self._if_manager.get_mapped_device(if_id1) dev2 = self._if_manager.get_mapped_device(if_id2) if dev1.get_netns() == None: dev1.deconfigure() else: netns = dev1.get_netns() msg = {"type": "command", "method_name": "deconfigure_interface", "args": [if_id1]} self._server_handler.send_data_to_netns(netns, msg) result = self._slave_server.wait_for_result(netns) if result["result"] != True: raise Exception("Deconfiguration failed.") self.return_if_netns(if_id1) if dev2.get_netns() == None: dev2.deconfigure() else: netns = dev2.get_netns() msg = {"type": "command", "method_name": "deconfigure_interface", "args": [if_id2]} self._server_handler.send_data_to_netns(netns, msg) result = self._slave_server.wait_for_result(netns) if result["result"] != True: raise Exception("Deconfiguration failed.") self.return_if_netns(if_id2) dev1.destroy() dev2.destroy() dev1.del_configuration() dev2.del_configuration() return True def deconfigure_interface(self, if_id): device = self._if_manager.get_mapped_device(if_id) if device is not None: device.clear_configuration() else: logging.error("No device with id '%s' to deconfigure." % if_id) return True def start_packet_capture(self, filt): files = {} for if_id, dev in self._if_manager.get_mapped_devices().iteritems(): if dev.get_netns() != None: continue dev_name = dev.get_name() df_handle = NamedTemporaryFile(delete=False) dump_file = df_handle.name df_handle.close() files[if_id] = dump_file pcap = PacketCapture() pcap.set_interface(dev_name) pcap.set_output_file(dump_file) pcap.set_filter(filt) pcap.start() self._packet_captures[if_id] = pcap self._capture_files = files return files def stop_packet_capture(self): if self._packet_captures == None: return True for if_index, pcap in self._packet_captures.iteritems(): pcap.stop() self._packet_captures.clear() return True def _remove_capture_files(self): for key, name in self._capture_files.iteritems(): logging.debug("Removing temporary packet capture file %s", name) os.unlink(name) self._capture_files.clear() def _update_system_config(self, options, persistent): system_config = self._system_config for opt in options: option = opt["name"] prev = opt["previous_val"] curr = opt["current_val"] if persistent: if option in system_config: del system_config[option] else: if not option in system_config: system_config[option] = {"initial_val": prev} system_config[option]["current_val"] = curr def restore_system_config(self): logging.info("Restoring system configuration") for option, values in self._system_config.iteritems(): try: cmd_str = "echo \"%s\" >%s" % (values["initial_val"], option) (stdout, stderr) = exec_cmd(cmd_str) except ExecCmdFail: logging.warn("Unable to restore '%s' config option!", option) return False self._system_config = {} return True def get_remaining_time(self, bg_id): cmd = self._command_context.get_cmd(bg_id) if "timeout" in cmd._command: cmd_timeout = cmd._command["timeout"] else: cmd_timeout = DEFAULT_TIMEOUT start_time = cmd._start_time current_time = time() remaining = cmd_timeout - (current_time - start_time) if remaining < 0: remaining = 0 return int(remaining) def run_command(self, command): cmd = NetTestCommand(self._command_context, command, self._resource_table, self._log_ctl) self._command_context.add_cmd(cmd) res = cmd.run() if not cmd.forked(): self._command_context.del_cmd(cmd) if command["type"] == "config": if res["passed"]: self._update_system_config(res["res_data"]["options"], command["persistent"]) else: err = "Error occured while setting system "\ "configuration (%s)" % res["res_data"]["err_msg"] logging.error(err) return res def kill_command(self, id): cmd = self._command_context.get_cmd(id) cmd.kill(None) self._command_context.del_cmd(cmd) return cmd.get_result() def machine_cleanup(self): logging.info("Performing machine cleanup.") self._command_context.cleanup() devs = self._if_manager.get_mapped_devices() for if_id, dev in devs.iteritems(): peer = dev.get_peer() if peer == None: dev.clear_configuration() else: peer_if_index = peer.get_if_index() peer_id = self._if_manager.get_id_by_if_index(peer_if_index) self.deconfigure_if_pair(if_id, peer_id) self._if_manager.deconfigure_all() for netns in self._net_namespaces.keys(): self.del_namespace(netns) self._net_namespaces = {} self._if_manager.clear_if_mapping() self._cache.del_old_entries() self.restore_system_config() self._remove_capture_files() return True def clear_resource_table(self): self._resource_table = {'module': {}, 'tools': {}} return True def has_resource(self, res_hash): if self._cache.query(res_hash): return True return False def map_resource(self, res_hash, res_type, res_name): resource_location = self._cache.get_path(res_hash) if not res_type in self._resource_table: self._resource_table[res_type] = {} self._resource_table[res_type][res_name] = resource_location self._cache.renew_entry(res_hash) return True def add_resource_to_cache(self, file_hash, local_path, name, res_hash, res_type): self._cache.add_cache_entry(file_hash, local_path, name, res_type) return True def start_copy_to(self, filepath=None): if filepath in self._copy_targets: return "" if filepath: self._copy_targets[filepath] = open(filepath, "w+b") else: tmpfile = NamedTemporaryFile("w+b", delete=False) filepath = tmpfile.name self._copy_targets[filepath] = tmpfile return filepath def copy_part_to(self, filepath, binary_data): if self._copy_targets[filepath]: self._copy_targets[filepath].write(binary_data.data) return True return False def finish_copy_to(self, filepath): if self._copy_targets[filepath]: self._copy_targets[filepath].close() del self._copy_targets[filepath] return True return False def start_copy_from(self, filepath): if filepath in self._copy_sources or not os.path.exists(filepath): return False self._copy_sources[filepath] = open(filepath, "rb") return True def copy_part_from(self, filepath, buffsize): data = Binary(self._copy_sources[filepath].read(buffsize)) return data def finish_copy_from(self, filepath): if filepath in self._copy_sources: self._copy_sources[filepath].close() del self._copy_sources[filepath] return True return False def reset_file_transfers(self): for file_handle in self._copy_targets.itervalues(): file_handle.close() self._copy_targets = {} for file_handle in self._copy_sources.itervalues(): file_handle.close() self._copy_sources = {} def enable_nm(self): logging.warning("====================================================") logging.warning("Enabling use of NetworkManager on controller request") logging.warning("====================================================") val = lnst_config.get_option("environment", "use_nm") lnst_config.set_option("environment", "use_nm", True) return val def disable_nm(self): logging.warning("=====================================================") logging.warning("Disabling use of NetworkManager on controller request") logging.warning("=====================================================") val = lnst_config.get_option("environment", "use_nm") lnst_config.set_option("environment", "use_nm", False) return val def restore_nm_option(self): val = lnst_config.get_option("environment", "use_nm") if val == self._bkp_nm_opt_val: return val logging.warning("=========================================") logging.warning("Restoring use_nm option to original value") logging.warning("=========================================") lnst_config.set_option("environment", "use_nm", self._bkp_nm_opt_val) return val def add_namespace(self, netns): if netns in self._net_namespaces: logging.debug("Network namespace %s already exists." % netns) else: logging.debug("Creating network namespace %s." % netns) read_pipe, write_pipe = multiprocessing.Pipe() pid = os.fork() if pid != 0: self._net_namespaces[netns] = {"pid": pid, "pipe": read_pipe} self._server_handler.add_netns(netns, read_pipe) return None elif pid == 0: self._slave_server.set_netns_sighandlers() #create new network namespace libc_name = ctypes.util.find_library("c") #from sched.h CLONE_NEWNET = 0x40000000 CLONE_NEWNS = 0x00020000 #based on ipnetns.c from the iproute2 project MNT_DETACH = 0x00000002 MS_SLAVE = 1<<19 MS_REC = 16384 libc = ctypes.CDLL(libc_name) libc.unshare(CLONE_NEWNET) #based on ipnetns.c from the iproute2 project libc.unshare(CLONE_NEWNS) libc.mount("", "/", "none", MS_SLAVE | MS_REC, 0) libc.umount2("/sys", MNT_DETACH) libc.mount(netns, "/sys", "sysfs", 0, 0) #set ctl socket to pipe to main netns self._server_handler.close_s_sock() self._server_handler.close_c_sock() self._server_handler.clear_connections() self._server_handler.clear_netns_connections() self._if_manager.reconnect_netlink() self._if_manager.clear_if_mapping() self._server_handler.add_connection('netlink', self._if_manager.get_nl_socket()) self._server_handler.set_netns(netns) self._server_handler.set_ctl_sock((write_pipe, "root_netns")) self._log_ctl.disable_logging() self._log_ctl.set_origin_name(netns) self._log_ctl.set_connection(write_pipe) self._if_manager.rescan_devices() logging.debug("Created network namespace %s" % netns) return True else: raise Exception("Fork failed!") def del_namespace(self, netns): if netns not in self._net_namespaces: logging.debug("Network namespace %s doesn't exist." % netns) return False else: netns_pid = self._net_namespaces[netns]["pid"] os.kill(netns_pid, signal.SIGUSR1) os.waitpid(netns_pid, 0) logging.debug("Network namespace %s removed." % netns) self._net_namespaces[netns]["pipe"].close() self._server_handler.del_netns(netns) del self._net_namespaces[netns] return True def set_if_netns(self, if_id, netns): netns_pid = self._net_namespaces[netns]["pid"] device = self._if_manager.get_mapped_device(if_id) dev_name = device.get_name() device.set_netns(netns) hwaddr = device.get_hwaddr() exec_cmd("ip link set %s netns %d" % (dev_name, netns_pid)) msg = {"type": "command", "method_name": "map_if_by_hwaddr", "args": [if_id, hwaddr]} self._server_handler.send_data_to_netns(netns, msg) result = self._slave_server.wait_for_result(netns) return result def return_if_netns(self, if_id): device = self._if_manager.get_mapped_device(if_id) if device.get_netns() == None: dev_name = device.get_name() ppid = os.getppid() exec_cmd("ip link set %s netns %d" % (dev_name, ppid)) self._if_manager.unmap_if(if_id) return True else: netns = device.get_netns() msg = {"type": "command", "method_name": "return_if_netns", "args": [if_id]} self._server_handler.send_data_to_netns(netns, msg) result = self._slave_server.wait_for_result(netns) if result["result"] != True: raise Exception("Return from netns failed.") device.set_netns(None) return True
class SlaveMethods: ''' Exported xmlrpc methods ''' def __init__(self, command_context, log_ctl, if_manager, net_namespaces, server_handler, slave_server): self._packet_captures = {} self._if_manager = if_manager self._command_context = command_context self._log_ctl = log_ctl self._net_namespaces = net_namespaces self._server_handler = server_handler self._slave_server = slave_server self._capture_files = {} self._copy_targets = {} self._copy_sources = {} self._system_config = {} self._cache = ResourceCache( lnst_config.get_option("cache", "dir"), lnst_config.get_option("cache", "expiration_period")) self._resource_table = {'module': {}, 'tools': {}} self._bkp_nm_opt_val = lnst_config.get_option("environment", "use_nm") def hello(self, recipe_path): self.machine_cleanup() self.restore_nm_option() logging.info("Recieved a controller connection.") self.clear_resource_table() self._cache.del_old_entries() self.reset_file_transfers() self._if_manager.rescan_devices() date = datetime.datetime.now().strftime("%Y-%m-%d_%H:%M:%S") self._log_ctl.set_recipe(recipe_path, expand=date) sleep(1) slave_desc = {} if check_process_running("NetworkManager"): logging.warning("=============================================") logging.warning("NetworkManager is running on a slave machine!") if lnst_config.get_option("environment", "use_nm"): logging.warning("Support of NM is still experimental!") else: logging.warning("Usage of NM is disabled!") logging.warning("=============================================") slave_desc["nm_running"] = True else: slave_desc["nm_running"] = False k_release, _ = exec_cmd("uname -r", False, False, False) r_release, _ = exec_cmd("cat /etc/redhat-release", False, False, False) slave_desc["kernel_release"] = k_release.strip() slave_desc["redhat_release"] = r_release.strip() return ("hello", slave_desc) def bye(self): self.restore_system_config() self.clear_resource_table() self._cache.del_old_entries() self.reset_file_transfers() self._remove_capture_files() return "bye" def kill_cmds(self): logging.info("Killing all forked processes.") self._command_context.cleanup() return "Commands killed" def map_if_by_hwaddr(self, if_id, hwaddr): devices = self.map_if_by_params(if_id, {'hwaddr': hwaddr}) return devices def map_if_by_params(self, if_id, params): devices = self.get_devices_by_params(params) if len(devices) == 1: dev = self._if_manager.get_device_by_params(params) self._if_manager.map_if(if_id, dev.get_if_index()) return devices def unmap_if(self, if_id): self._if_manager.unmap_if(if_id) return True def get_devices(self): self._if_manager.rescan_devices() devices = self._if_manager.get_devices() result = {} for device in devices: if device._ifi_type == 1 and device._state == 'DOWN': result[device._if_index] = { 'name': device._name, 'hwaddr': device._hwaddr, 'driver': device._driver } return result def get_devices_by_devname(self, devname): name_scan = self._if_manager.get_devices() netdevs = [] for entry in name_scan: if entry["name"] == devname: netdevs.append(entry) return netdevs def get_devices_by_hwaddr(self, hwaddr): devices = self._if_manager.get_devices() matched = [] for dev in devices: if dev.get_hwaddr() == hwaddr: entry = {"name": dev.get_name(), "hwaddr": dev.get_hwaddr()} matched.append(entry) return matched def get_devices_by_params(self, params): devices = self._if_manager.get_devices() matched = [] for dev in devices: dev_data = dev.get_if_data() entry = {"name": dev.get_name(), "hwaddr": dev.get_hwaddr()} for key, value in params.iteritems(): if key not in dev_data or dev_data[key] != value: entry = None break if entry is not None: matched.append(entry) return matched def get_if_data(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if dev is None: return None return dev.get_if_data() def set_device_up(self, if_id): dev = self._if_manager.get_mapped_device(if_id) dev.up() return True def set_device_down(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if dev is not None: dev.down() else: logging.error("Device with id '%s' not found." % if_id) return False return True def set_link_up(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if dev is not None: dev.link_up() else: logging.error("Device with id '%s' not found." % if_id) return False return True def set_link_down(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if dev is not None: dev.link_down() else: logging.error("Device with id '%s' not found." % if_id) return False return True def set_unmapped_device_down(self, hwaddr): dev = self._if_manager.get_device_by_hwaddr(hwaddr) if dev is not None: dev.down() else: logging.warning("Device with hwaddr '%s' not found." % hwaddr) return True def configure_interface(self, if_id, config): device = self._if_manager.get_mapped_device(if_id) device.set_configuration(config) device.configure() return True def create_soft_interface(self, if_id, config): dev_name = self._if_manager.create_device_from_config(if_id, config) dev = self._if_manager.get_mapped_device(if_id) dev.configure() return dev_name def create_if_pair(self, if_id1, config1, if_id2, config2): dev_names = self._if_manager.create_device_pair( if_id1, config1, if_id2, config2) dev1 = self._if_manager.get_mapped_device(if_id1) dev2 = self._if_manager.get_mapped_device(if_id2) while dev1.get_if_index() == None and dev2.get_if_index() == None: msgs = self._server_handler.get_messages_from_con('netlink') for msg in msgs: self._if_manager.handle_netlink_msgs(msg[1]["data"]) if config1["netns"] != None: hwaddr = dev1.get_hwaddr() self.set_if_netns(if_id1, config1["netns"]) msg = { "type": "command", "method_name": "configure_interface", "args": [if_id1, config1] } self._server_handler.send_data_to_netns(config1["netns"], msg) result = self._slave_server.wait_for_result(config1["netns"]) if result["result"] != True: raise Exception("Configuration failed.") else: dev1.configure() if config2["netns"] != None: hwaddr = dev2.get_hwaddr() self.set_if_netns(if_id2, config2["netns"]) msg = { "type": "command", "method_name": "configure_interface", "args": [if_id2, config2] } self._server_handler.send_data_to_netns(config2["netns"], msg) result = self._slave_server.wait_for_result(config2["netns"]) if result["result"] != True: raise Exception("Configuration failed.") else: dev2.configure() return dev_names def deconfigure_if_pair(self, if_id1, if_id2): dev1 = self._if_manager.get_mapped_device(if_id1) dev2 = self._if_manager.get_mapped_device(if_id2) if dev1.get_netns() == None: dev1.deconfigure() else: netns = dev1.get_netns() msg = { "type": "command", "method_name": "deconfigure_interface", "args": [if_id1] } self._server_handler.send_data_to_netns(netns, msg) result = self._slave_server.wait_for_result(netns) if result["result"] != True: raise Exception("Deconfiguration failed.") self.return_if_netns(if_id1) if dev2.get_netns() == None: dev2.deconfigure() else: netns = dev2.get_netns() msg = { "type": "command", "method_name": "deconfigure_interface", "args": [if_id2] } self._server_handler.send_data_to_netns(netns, msg) result = self._slave_server.wait_for_result(netns) if result["result"] != True: raise Exception("Deconfiguration failed.") self.return_if_netns(if_id2) dev1.destroy() dev2.destroy() dev1.del_configuration() dev2.del_configuration() return True def deconfigure_interface(self, if_id): device = self._if_manager.get_mapped_device(if_id) if device is not None: device.clear_configuration() else: logging.error("No device with id '%s' to deconfigure." % if_id) return True def start_packet_capture(self, filt): files = {} for if_id, dev in self._if_manager.get_mapped_devices().iteritems(): if dev.get_netns() != None: continue dev_name = dev.get_name() df_handle = NamedTemporaryFile(delete=False) dump_file = df_handle.name df_handle.close() files[if_id] = dump_file pcap = PacketCapture() pcap.set_interface(dev_name) pcap.set_output_file(dump_file) pcap.set_filter(filt) pcap.start() self._packet_captures[if_id] = pcap self._capture_files = files return files def stop_packet_capture(self): if self._packet_captures == None: return True for if_index, pcap in self._packet_captures.iteritems(): pcap.stop() self._packet_captures.clear() return True def _remove_capture_files(self): for key, name in self._capture_files.iteritems(): logging.debug("Removing temporary packet capture file %s", name) os.unlink(name) self._capture_files.clear() def _update_system_config(self, options, persistent): system_config = self._system_config for opt in options: option = opt["name"] prev = opt["previous_val"] curr = opt["current_val"] if persistent: if option in system_config: del system_config[option] else: if not option in system_config: system_config[option] = {"initial_val": prev} system_config[option]["current_val"] = curr def restore_system_config(self): logging.info("Restoring system configuration") for option, values in self._system_config.iteritems(): try: cmd_str = "echo \"%s\" >%s" % (values["initial_val"], option) (stdout, stderr) = exec_cmd(cmd_str) except ExecCmdFail: logging.warn("Unable to restore '%s' config option!", option) return False self._system_config = {} return True def get_remaining_time(self, bg_id): cmd = self._command_context.get_cmd(bg_id) if "timeout" in cmd._command: cmd_timeout = cmd._command["timeout"] else: cmd_timeout = DEFAULT_TIMEOUT start_time = cmd._start_time current_time = time() remaining = cmd_timeout - (current_time - start_time) if remaining < 0: remaining = 0 return int(remaining) def run_command(self, command): cmd = NetTestCommand(self._command_context, command, self._resource_table, self._log_ctl) if self._command_context.get_cmd(cmd.get_id()) != None: prev_cmd = self._command_context.get_cmd(cmd.get_id()) if not prev_cmd.get_result_sent(): if cmd.get_id() is None: raise Exception("Previous foreground command still "\ "running!") else: raise Exception("Different command with id '%s' "\ "still running!" % cmd.get_id()) else: self._command_context.del_cmd(cmd) self._command_context.add_cmd(cmd) res = cmd.run() if not cmd.forked(): self._command_context.del_cmd(cmd) if command["type"] == "config": if res["passed"]: self._update_system_config(res["res_data"]["options"], command["persistent"]) else: err = "Error occured while setting system "\ "configuration (%s)" % res["res_data"]["err_msg"] logging.error(err) return res def kill_command(self, id): cmd = self._command_context.get_cmd(id) if cmd is not None: if not cmd.get_result_sent(): cmd.kill(None) result = cmd.get_result() cmd.set_result_sent() return result else: pass else: raise Exception("No command with id '%s'." % id) def machine_cleanup(self): logging.info("Performing machine cleanup.") self._command_context.cleanup() self.restore_system_config() devs = self._if_manager.get_mapped_devices() for if_id, dev in devs.iteritems(): peer = dev.get_peer() if peer == None: dev.clear_configuration() else: peer_if_index = peer.get_if_index() peer_id = self._if_manager.get_id_by_if_index(peer_if_index) self.deconfigure_if_pair(if_id, peer_id) self._if_manager.deconfigure_all() for netns in self._net_namespaces.keys(): self.del_namespace(netns) self._net_namespaces = {} self._if_manager.clear_if_mapping() self._cache.del_old_entries() self._remove_capture_files() return True def clear_resource_table(self): self._resource_table = {'module': {}, 'tools': {}} return True def has_resource(self, res_hash): if self._cache.query(res_hash): return True return False def map_resource(self, res_hash, res_type, res_name): resource_location = self._cache.get_path(res_hash) if not res_type in self._resource_table: self._resource_table[res_type] = {} self._resource_table[res_type][res_name] = resource_location self._cache.renew_entry(res_hash) return True def add_resource_to_cache(self, file_hash, local_path, name, res_hash, res_type): self._cache.add_cache_entry(file_hash, local_path, name, res_type) return True def start_copy_to(self, filepath=None): if filepath in self._copy_targets: return "" if filepath: self._copy_targets[filepath] = open(filepath, "w+b") else: tmpfile = NamedTemporaryFile("w+b", delete=False) filepath = tmpfile.name self._copy_targets[filepath] = tmpfile return filepath def copy_part_to(self, filepath, binary_data): if self._copy_targets[filepath]: self._copy_targets[filepath].write(binary_data.data) return True return False def finish_copy_to(self, filepath): if self._copy_targets[filepath]: self._copy_targets[filepath].close() del self._copy_targets[filepath] return True return False def start_copy_from(self, filepath): if filepath in self._copy_sources or not os.path.exists(filepath): return False self._copy_sources[filepath] = open(filepath, "rb") return True def copy_part_from(self, filepath, buffsize): data = Binary(self._copy_sources[filepath].read(buffsize)) return data def finish_copy_from(self, filepath): if filepath in self._copy_sources: self._copy_sources[filepath].close() del self._copy_sources[filepath] return True return False def reset_file_transfers(self): for file_handle in self._copy_targets.itervalues(): file_handle.close() self._copy_targets = {} for file_handle in self._copy_sources.itervalues(): file_handle.close() self._copy_sources = {} def enable_nm(self): logging.warning("====================================================") logging.warning("Enabling use of NetworkManager on controller request") logging.warning("====================================================") val = lnst_config.get_option("environment", "use_nm") lnst_config.set_option("environment", "use_nm", True) return val def disable_nm(self): logging.warning( "=====================================================") logging.warning( "Disabling use of NetworkManager on controller request") logging.warning( "=====================================================") val = lnst_config.get_option("environment", "use_nm") lnst_config.set_option("environment", "use_nm", False) return val def restore_nm_option(self): val = lnst_config.get_option("environment", "use_nm") if val == self._bkp_nm_opt_val: return val logging.warning("=========================================") logging.warning("Restoring use_nm option to original value") logging.warning("=========================================") lnst_config.set_option("environment", "use_nm", self._bkp_nm_opt_val) return val def add_namespace(self, netns): if netns in self._net_namespaces: logging.debug("Network namespace %s already exists." % netns) else: logging.debug("Creating network namespace %s." % netns) read_pipe, write_pipe = multiprocessing.Pipe() pid = os.fork() if pid != 0: self._net_namespaces[netns] = {"pid": pid, "pipe": read_pipe} self._server_handler.add_netns(netns, read_pipe) return None elif pid == 0: self._slave_server.set_netns_sighandlers() #create new network namespace libc_name = ctypes.util.find_library("c") #from sched.h CLONE_NEWNET = 0x40000000 CLONE_NEWNS = 0x00020000 #based on ipnetns.c from the iproute2 project MNT_DETACH = 0x00000002 MS_SLAVE = 1 << 19 MS_REC = 16384 libc = ctypes.CDLL(libc_name) libc.unshare(CLONE_NEWNET) #based on ipnetns.c from the iproute2 project libc.unshare(CLONE_NEWNS) libc.mount("", "/", "none", MS_SLAVE | MS_REC, 0) libc.umount2("/sys", MNT_DETACH) libc.mount(netns, "/sys", "sysfs", 0, 0) #set ctl socket to pipe to main netns self._server_handler.close_s_sock() self._server_handler.close_c_sock() self._server_handler.clear_connections() self._server_handler.clear_netns_connections() self._if_manager.reconnect_netlink() self._if_manager.clear_if_mapping() self._server_handler.add_connection( 'netlink', self._if_manager.get_nl_socket()) self._server_handler.set_netns(netns) self._server_handler.set_ctl_sock((write_pipe, "root_netns")) self._log_ctl.disable_logging() self._log_ctl.set_origin_name(netns) self._log_ctl.set_connection(write_pipe) self._if_manager.rescan_devices() logging.debug("Created network namespace %s" % netns) return True else: raise Exception("Fork failed!") def del_namespace(self, netns): if netns not in self._net_namespaces: logging.debug("Network namespace %s doesn't exist." % netns) return False else: netns_pid = self._net_namespaces[netns]["pid"] os.kill(netns_pid, signal.SIGUSR1) os.waitpid(netns_pid, 0) logging.debug("Network namespace %s removed." % netns) self._net_namespaces[netns]["pipe"].close() self._server_handler.del_netns(netns) del self._net_namespaces[netns] return True def set_if_netns(self, if_id, netns): netns_pid = self._net_namespaces[netns]["pid"] device = self._if_manager.get_mapped_device(if_id) dev_name = device.get_name() device.set_netns(netns) hwaddr = device.get_hwaddr() exec_cmd("ip link set %s netns %d" % (dev_name, netns_pid)) msg = { "type": "command", "method_name": "map_if_by_hwaddr", "args": [if_id, hwaddr] } self._server_handler.send_data_to_netns(netns, msg) result = self._slave_server.wait_for_result(netns) return result def return_if_netns(self, if_id): device = self._if_manager.get_mapped_device(if_id) if device.get_netns() == None: dev_name = device.get_name() ppid = os.getppid() exec_cmd("ip link set %s netns %d" % (dev_name, ppid)) self._if_manager.unmap_if(if_id) return True else: netns = device.get_netns() msg = { "type": "command", "method_name": "return_if_netns", "args": [if_id] } self._server_handler.send_data_to_netns(netns, msg) result = self._slave_server.wait_for_result(netns) if result["result"] != True: raise Exception("Return from netns failed.") device.set_netns(None) return True
class SlaveMethods: ''' Exported xmlrpc methods ''' def __init__(self, command_context, netconfig, log_ctl): self._packet_captures = {} self._netconfig = netconfig self._command_context = command_context self._log_ctl = log_ctl self._capture_files = {} self._copy_targets = {} self._copy_sources = {} self._system_config = {} self._cache = ResourceCache(lnst_config.get_option("cache", "dir"), lnst_config.get_option("cache", "expiration_period")) self._resource_table = {} def hello(self, recipe_path): logging.info("Recieved a controller connection.") self.clear_resource_table() self._cache.del_old_entries() self.reset_file_transfers() date = datetime.datetime.now().strftime("%Y-%m-%d_%H:%M:%S") self._log_ctl.set_recipe(recipe_path, expand=date) sleep(1) if check_process_running("NetworkManager"): logging.warning("=============================================") logging.warning("NetworkManager is running on a slave machine!") if lnst_config.get_option("environment", "use_nm"): logging.warning("Support of NM is still experimental!") else: logging.warning("Usage of NM is disabled!") logging.warning("=============================================") return "hello" def bye(self): self.restore_system_config() self.clear_resource_table() self._cache.del_old_entries() self.reset_file_transfers() self._remove_capture_files() return "bye" def kill_cmds(self): logging.info("Killing all forked processes.") self._command_context.cleanup() return "Commands killed" def get_devices_by_hwaddr(self, hwaddr): name_scan = scan_netdevs() netdevs = [] for entry in name_scan: if entry["hwaddr"] == normalize_hwaddr(hwaddr): netdevs.append(entry) return netdevs def get_devices_by_devname(self, devname): name_scan = scan_netdevs() netdevs = [] for entry in name_scan: if entry["name"] == devname: netdevs.append(entry) return netdevs def set_device_down(self, hwaddr): devs = self.get_devices_by_hwaddr(hwaddr) for dev in devs: if is_nm_managed_by_name(dev["name"]): bus = dbus.SystemBus() nm_obj = bus.get_object("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager") nm_if = dbus.Interface(nm_obj, "org.freedesktop.NetworkManager") dev_obj_path = nm_if.GetDeviceByIpIface(dev["name"]) dev_obj = bus.get_object("org.freedesktop.NetworkManager", dev_obj_path) dev_props = dbus.Interface(dev_obj, "org.freedesktop.DBus.Properties") if dev_props.Get("org.freedesktop.NetworkManager.Device", "ActiveConnection") != "/": dev_if = dbus.Interface(dev_obj, "org.freedesktop.NetworkManager.Device") logging.debug("Disconnecting device %s: %s" % (dev["name"], dev_obj_path)) dev_if.Disconnect() else: exec_cmd("ip link set %s down" % dev["name"]) return True def get_interface_info(self, if_id): if_config = self._netconfig.get_interface_config(if_id) info = {} if "name" in if_config and if_config["name"] != None: info["name"] = if_config["name"] else: devs = self.get_devices_by_hwaddr(if_config["hwaddr"]) info["name"] = devs[0]["name"] if "hwaddr" in if_config and if_config["hwaddr"] != None: info["hwaddr"] = if_config["hwaddr"] else: devs = self.get_devices_by_devname(if_config["name"]) info["hwaddr"] = devs[0]["hwaddr"] return info def configure_interface(self, if_id, config): self._netconfig.add_interface_config(if_id, config) self._netconfig.configure(if_id) return True def deconfigure_interface(self, if_id): self._netconfig.deconfigure(if_id) self._netconfig.remove_interface_config(if_id) return True def netconfig_dump(self): return self._netconfig.dump_config().items() def start_packet_capture(self, filt): netconfig = self._netconfig.dump_config() files = {} for dev_id, dev_spec in netconfig.iteritems(): df_handle = NamedTemporaryFile(delete=False) dump_file = df_handle.name df_handle.close() files[dev_id] = dump_file pcap = PacketCapture() pcap.set_interface(dev_spec["name"]) pcap.set_output_file(dump_file) pcap.set_filter(filt) pcap.start() self._packet_captures[dev_id] = pcap self._capture_files = files return files def stop_packet_capture(self): netconfig = self._netconfig.dump_config() for dev_id in netconfig.keys(): pcap = self._packet_captures[dev_id] pcap.stop() return True def _remove_capture_files(self): for key, name in self._capture_files.iteritems(): logging.debug("Removing temporary packet capture file %s", name) os.unlink(name) self._capture_files.clear() def _update_system_config(self, options, persistent): system_config = self._system_config for option, values in options.iteritems(): if persistent: if option in system_config: del system_config[option] else: if not option in system_config: initial_val = {"initial_val": values["previous_val"]} system_config[option] = initial_val system_config[option]["current_val"] = values["current_val"] def restore_system_config(self): logging.info("Restoring system configuration") for option, values in self._system_config.iteritems(): try: cmd_str = "echo \"%s\" >%s" % (values["initial_val"], option) (stdout, stderr) = exec_cmd(cmd_str) except ExecCmdFail: logging.warn("Unable to restore '%s' config option!", option) return False self._system_config = {} return True def run_command(self, command): cmd = NetTestCommand(self._command_context, command, self._resource_table, self._log_ctl) self._command_context.add_cmd(cmd) res = cmd.run() if not cmd.forked(): self._command_context.del_cmd(cmd) if command["type"] == "system_config": if res["passed"]: self._update_system_config(res["res_data"]["options"], command["persistent"]) else: err = "Error occured while setting system "\ "configuration (%s)" % res["err_msg"] logging.error(err) return res def kill_command(self, id): cmd = self._command_context.get_cmd(id) cmd.kill() self._command_context.del_cmd(cmd) return True def machine_cleanup(self): NetConfigDeviceAllCleanup() self._netconfig.cleanup() self._command_context.cleanup() self._cache.del_old_entries() self.restore_system_config() return True def clear_resource_table(self): self._resource_table = {} return True def has_resource(self, res_hash): if self._cache.query(res_hash): return True return False def map_resource(self, res_hash, res_type, res_name): resource_location = self._cache.get_path(res_hash) if not res_type in self._resource_table: self._resource_table[res_type] = {} self._resource_table[res_type][res_name] = resource_location self._cache.renew_entry(res_hash) return True def add_resource_to_cache(self, file_hash, local_path, name, res_hash, res_type): self._cache.add_cache_entry(file_hash, local_path, name, res_type) return True def start_copy_to(self, filepath=None): if filepath in self._copy_targets: return "" if filepath: self._copy_targets[filepath] = open(filepath, "w+b") else: tmpfile = NamedTemporaryFile("w+b", delete=False) filepath = tmpfile.name self._copy_targets[filepath] = tmpfile return filepath def copy_part_to(self, filepath, binary_data): if self._copy_targets[filepath]: self._copy_targets[filepath].write(binary_data.data) return True return False def finish_copy_to(self, filepath): if self._copy_targets[filepath]: self._copy_targets[filepath].close() del self._copy_targets[filepath] return True return False def start_copy_from(self, filepath): if filepath in self._copy_sources or not os.path.exists(filepath): return False self._copy_sources[filepath] = open(filepath, "rb") return True def copy_part_from(self, filepath, buffsize): data = Binary(self._copy_sources[filepath].read(buffsize)) return data def finish_copy_from(self, filepath): if filepath in self._copy_sources: self._copy_sources[filepath].close() del self._copy_sources[filepath] return True return False def reset_file_transfers(self): for file_handle in self._copy_targets.itervalues(): file_handle.close() self._copy_targets = {} for file_handle in self._copy_sources.itervalues(): file_handle.close() self._copy_sources = {}
class SlaveMethods: ''' Exported xmlrpc methods ''' def __init__(self, command_context, log_ctl, if_manager, net_namespaces, server_handler, slave_server): self._packet_captures = {} self._if_manager = if_manager self._command_context = command_context self._log_ctl = log_ctl self._net_namespaces = net_namespaces self._server_handler = server_handler self._slave_server = slave_server self._capture_files = {} self._copy_targets = {} self._copy_sources = {} self._system_config = {} self._cache = ResourceCache(lnst_config.get_option("cache", "dir"), lnst_config.get_option("cache", "expiration_period")) self._resource_table = {'module': {}, 'tools': {}} self._bkp_nm_opt_val = lnst_config.get_option("environment", "use_nm") def hello(self, recipe_path): self.machine_cleanup() self.restore_nm_option() logging.info("Recieved a controller connection.") self.clear_resource_table() self._cache.del_old_entries() self.reset_file_transfers() self._if_manager.rescan_devices() date = datetime.datetime.now().strftime("%Y-%m-%d_%H:%M:%S") self._log_ctl.set_recipe(recipe_path, expand=date) sleep(1) slave_desc = {} if check_process_running("NetworkManager"): logging.warning("=============================================") logging.warning("NetworkManager is running on a slave machine!") if lnst_config.get_option("environment", "use_nm"): logging.warning("Support of NM is still experimental!") else: logging.warning("Usage of NM is disabled!") logging.warning("=============================================") slave_desc["nm_running"] = True else: slave_desc["nm_running"] = False k_release, _ = exec_cmd("uname -r", False, False, False) r_release, _ = exec_cmd("cat /etc/redhat-release", False, False, False) slave_desc["kernel_release"] = k_release.strip() slave_desc["redhat_release"] = r_release.strip() slave_desc["lnst_version"] = lnst_config.version return ("hello", slave_desc) def bye(self): self.restore_system_config() self.clear_resource_table() self._cache.del_old_entries() self.reset_file_transfers() self._remove_capture_files() return "bye" def kill_cmds(self): logging.info("Killing all forked processes.") self._command_context.cleanup() return "Commands killed" def map_if_by_hwaddr(self, if_id, hwaddr): devices = self.map_if_by_params(if_id, {'hwaddr' : hwaddr}) return devices def map_if_by_params(self, if_id, params): devices = self.get_devices_by_params(params) if len(devices) == 1: dev = self._if_manager.get_device_by_params(params) self._if_manager.map_if(if_id, dev.get_if_index()) return devices def unmap_if(self, if_id): self._if_manager.unmap_if(if_id) return True def get_devices(self): self._if_manager.rescan_devices() devices = self._if_manager.get_devices() result = {} for device in devices: result[device._if_index] = device.get_if_data() return result def get_device(self, if_index): self._if_manager.rescan_devices() device = self._if_manager.get_device(if_index) if device: return device.get_if_data() else: return {} def get_devices_by_devname(self, devname): name_scan = self._if_manager.get_devices() netdevs = [] for entry in name_scan: if entry["name"] == devname: netdevs.append(entry) return netdevs def get_devices_by_hwaddr(self, hwaddr): devices = self._if_manager.get_devices() matched = [] for dev in devices: if dev.get_hwaddr() == hwaddr: entry = {"name": dev.get_name(), "hwaddr": dev.get_hwaddr()} matched.append(entry) return matched def get_devices_by_params(self, params): devices = self._if_manager.get_devices() matched = [] for dev in devices: dev_data = dev.get_if_data() entry = {"name": dev.get_name(), "hwaddr": dev.get_hwaddr()} for key, value in params.iteritems(): if key not in dev_data or dev_data[key] != value: entry = None break if entry is not None: matched.append(entry) return matched def get_if_data(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if dev is None: return {} return dev.get_if_data() def link_stats(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if dev is None: logging.error("Device with id '%s' not found." % if_id) return {} return dev.link_stats() def set_addresses(self, if_id, ips): dev = self._if_manager.get_mapped_device(if_id) if dev is None: logging.error("Device with id '%s' not found." % if_id) return False dev.set_addresses(ips) return True def add_route(self, if_id, dest, ipv6): dev = self._if_manager.get_mapped_device(if_id) if dev is None: logging.error("Device with id '%s' not found." % if_id) return False dev.add_route(dest, ipv6) return True def del_route(self, if_id, dest, ipv6): dev = self._if_manager.get_mapped_device(if_id) if dev is None: logging.error("Device with id '%s' not found." % if_id) return False dev.del_route(dest, ipv6) return True def add_nhs_route(self, if_id, dest, nhs, ipv6): dev = self._if_manager.get_mapped_device(if_id) if dev is None: logging.error("Device with id '%s' not found." % if_id) return False dev.add_nhs_route(dest, nhs, ipv6) return True def del_nhs_route(self, if_id, dest, nhs, ipv6): dev = self._if_manager.get_mapped_device(if_id) if dev is None: logging.error("Device with id '%s' not found." % if_id) return False dev.del_nhs_route(dest, nhs, ipv6) return True def set_device_up(self, if_id): dev = self._if_manager.get_mapped_device(if_id) dev.up() return True def set_device_down(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if dev is not None: dev.down() else: logging.error("Device with id '%s' not found." % if_id) return False return True def device_address_setup(self, if_id): dev = self._if_manager.get_mapped_device(if_id) dev.address_setup() return True def device_address_cleanup(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if dev is not None: dev.address_cleanup() else: logging.error("Device with id '%s' not found." % if_id) return False return True def set_link_up(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if dev is not None: dev.link_up() else: logging.error("Device with id '%s' not found." % if_id) return False return True def set_link_down(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if dev is not None: dev.link_down() else: logging.error("Device with id '%s' not found." % if_id) return False return True def set_unmapped_device_down(self, hwaddr): dev = self._if_manager.get_device_by_hwaddr(hwaddr) if dev is not None: dev.down() else: logging.warning("Device with hwaddr '%s' not found." % hwaddr) return True def configure_interface(self, if_id, config): device = self._if_manager.get_mapped_device(if_id) device.set_configuration(config) device.configure() return True def create_soft_interface(self, if_id, config): dev_name = self._if_manager.create_device_from_config(if_id, config) dev = self._if_manager.get_mapped_device(if_id) dev.configure() return dev_name def create_if_pair(self, if_id1, config1, if_id2, config2): dev_names = self._if_manager.create_device_pair(if_id1, config1, if_id2, config2) dev1 = self._if_manager.get_mapped_device(if_id1) dev2 = self._if_manager.get_mapped_device(if_id2) while dev1.get_if_index() == None and dev2.get_if_index() == None: msgs = self._server_handler.get_messages_from_con('netlink') for msg in msgs: self._if_manager.handle_netlink_msgs(msg[1]["data"]) if config1["netns"] != None: hwaddr = dev1.get_hwaddr() self.set_if_netns(if_id1, config1["netns"]) msg = {"type": "command", "method_name": "configure_interface", "args": [if_id1, config1]} self._server_handler.send_data_to_netns(config1["netns"], msg) result = self._slave_server.wait_for_result(config1["netns"]) if result["result"] != True: raise Exception("Configuration failed.") else: dev1.configure() if config2["netns"] != None: hwaddr = dev2.get_hwaddr() self.set_if_netns(if_id2, config2["netns"]) msg = {"type": "command", "method_name": "configure_interface", "args": [if_id2, config2]} self._server_handler.send_data_to_netns(config2["netns"], msg) result = self._slave_server.wait_for_result(config2["netns"]) if result["result"] != True: raise Exception("Configuration failed.") else: dev2.configure() return dev_names def deconfigure_if_pair(self, if_id1, if_id2): dev1 = self._if_manager.get_mapped_device(if_id1) dev2 = self._if_manager.get_mapped_device(if_id2) if dev1.get_netns() == None: dev1.deconfigure() else: netns = dev1.get_netns() msg = {"type": "command", "method_name": "deconfigure_interface", "args": [if_id1]} self._server_handler.send_data_to_netns(netns, msg) result = self._slave_server.wait_for_result(netns) if result["result"] != True: raise Exception("Deconfiguration failed.") self.return_if_netns(if_id1) if dev2.get_netns() == None: dev2.deconfigure() else: netns = dev2.get_netns() msg = {"type": "command", "method_name": "deconfigure_interface", "args": [if_id2]} self._server_handler.send_data_to_netns(netns, msg) result = self._slave_server.wait_for_result(netns) if result["result"] != True: raise Exception("Deconfiguration failed.") self.return_if_netns(if_id2) dev1.destroy() dev2.destroy() dev1.del_configuration() dev2.del_configuration() return True def deconfigure_interface(self, if_id): device = self._if_manager.get_mapped_device(if_id) if device is not None: device.clear_configuration() else: logging.error("No device with id '%s' to deconfigure." % if_id) return True def start_packet_capture(self, filt): if not is_installed("tcpdump"): raise Exception("Can't start packet capture, tcpdump not available") files = {} for if_id, dev in self._if_manager.get_mapped_devices().iteritems(): if dev.get_netns() != None: continue dev_name = dev.get_name() df_handle = NamedTemporaryFile(delete=False) dump_file = df_handle.name df_handle.close() files[if_id] = dump_file pcap = PacketCapture() pcap.set_interface(dev_name) pcap.set_output_file(dump_file) pcap.set_filter(filt) pcap.start() self._packet_captures[if_id] = pcap self._capture_files = files return files def stop_packet_capture(self): if self._packet_captures == None: return True for if_index, pcap in self._packet_captures.iteritems(): pcap.stop() self._packet_captures.clear() return True def _remove_capture_files(self): for key, name in self._capture_files.iteritems(): logging.debug("Removing temporary packet capture file %s", name) os.unlink(name) self._capture_files.clear() def _update_system_config(self, options, persistent): system_config = self._system_config for opt in options: option = opt["name"] prev = opt["previous_val"] curr = opt["current_val"] if persistent: if option in system_config: del system_config[option] else: if not option in system_config: system_config[option] = {"initial_val": prev} system_config[option]["current_val"] = curr def restore_system_config(self): logging.info("Restoring system configuration") for option, values in self._system_config.iteritems(): try: cmd_str = "echo \"%s\" >%s" % (values["initial_val"], option) (stdout, stderr) = exec_cmd(cmd_str) except ExecCmdFail: logging.warn("Unable to restore '%s' config option!", option) return False self._system_config = {} return True def get_remaining_time(self, bg_id): cmd = self._command_context.get_cmd(bg_id) if "timeout" in cmd._command: cmd_timeout = cmd._command["timeout"] else: cmd_timeout = DEFAULT_TIMEOUT start_time = cmd._start_time current_time = time() remaining = cmd_timeout - (current_time - start_time) if remaining < 0: remaining = 0 return int(remaining) def run_command(self, command): cmd = NetTestCommand(self._command_context, command, self._resource_table, self._log_ctl) if self._command_context.get_cmd(cmd.get_id()) != None: prev_cmd = self._command_context.get_cmd(cmd.get_id()) if not prev_cmd.get_result_sent(): if cmd.get_id() is None: raise Exception("Previous foreground command still "\ "running!") else: raise Exception("Different command with id '%s' "\ "still running!" % cmd.get_id()) else: self._command_context.del_cmd(cmd) self._command_context.add_cmd(cmd) res = cmd.run() if not cmd.forked(): self._command_context.del_cmd(cmd) if command["type"] == "config": if res["passed"]: self._update_system_config(res["res_data"]["options"], command["persistent"]) else: err = "Error occured while setting system "\ "configuration (%s)" % res["res_data"]["err_msg"] logging.error(err) return res def kill_command(self, id): cmd = self._command_context.get_cmd(id) if cmd is not None: if not cmd.get_result_sent(): cmd.kill(None) result = cmd.get_result() cmd.set_result_sent() return result else: pass else: raise Exception("No command with id '%s'." % id) def machine_cleanup(self): logging.info("Performing machine cleanup.") self._command_context.cleanup() self.restore_system_config() devs = self._if_manager.get_mapped_devices() for if_id, dev in devs.iteritems(): peer = dev.get_peer() if peer == None: dev.clear_configuration() else: peer_if_index = peer.get_if_index() peer_id = self._if_manager.get_id_by_if_index(peer_if_index) self.deconfigure_if_pair(if_id, peer_id) self._if_manager.deconfigure_all() for netns in self._net_namespaces.keys(): self.del_namespace(netns) self._net_namespaces = {} self._if_manager.clear_if_mapping() self._cache.del_old_entries() self._remove_capture_files() return True def clear_resource_table(self): self._resource_table = {'module': {}, 'tools': {}} return True def has_resource(self, res_hash): if self._cache.query(res_hash): return True return False def map_resource(self, res_hash, res_type, res_name): resource_location = self._cache.get_path(res_hash) if not res_type in self._resource_table: self._resource_table[res_type] = {} self._resource_table[res_type][res_name] = resource_location self._cache.renew_entry(res_hash) return True def add_resource_to_cache(self, file_hash, local_path, name, res_hash, res_type): self._cache.add_cache_entry(file_hash, local_path, name, res_type) return True def start_copy_to(self, filepath=None): if filepath in self._copy_targets: return "" if filepath: self._copy_targets[filepath] = open(filepath, "w+b") else: tmpfile = NamedTemporaryFile("w+b", delete=False) filepath = tmpfile.name self._copy_targets[filepath] = tmpfile return filepath def copy_part_to(self, filepath, binary_data): if self._copy_targets[filepath]: self._copy_targets[filepath].write(binary_data.data) return True return False def finish_copy_to(self, filepath): if self._copy_targets[filepath]: self._copy_targets[filepath].close() del self._copy_targets[filepath] return True return False def start_copy_from(self, filepath): if filepath in self._copy_sources or not os.path.exists(filepath): return False self._copy_sources[filepath] = open(filepath, "rb") return True def copy_part_from(self, filepath, buffsize): data = Binary(self._copy_sources[filepath].read(buffsize)) return data def finish_copy_from(self, filepath): if filepath in self._copy_sources: self._copy_sources[filepath].close() del self._copy_sources[filepath] return True return False def reset_file_transfers(self): for file_handle in self._copy_targets.itervalues(): file_handle.close() self._copy_targets = {} for file_handle in self._copy_sources.itervalues(): file_handle.close() self._copy_sources = {} def enable_nm(self): logging.warning("====================================================") logging.warning("Enabling use of NetworkManager on controller request") logging.warning("====================================================") val = lnst_config.get_option("environment", "use_nm") lnst_config.set_option("environment", "use_nm", True) return val def disable_nm(self): logging.warning("=====================================================") logging.warning("Disabling use of NetworkManager on controller request") logging.warning("=====================================================") val = lnst_config.get_option("environment", "use_nm") lnst_config.set_option("environment", "use_nm", False) return val def restore_nm_option(self): val = lnst_config.get_option("environment", "use_nm") if val == self._bkp_nm_opt_val: return val logging.warning("=========================================") logging.warning("Restoring use_nm option to original value") logging.warning("=========================================") lnst_config.set_option("environment", "use_nm", self._bkp_nm_opt_val) return val def add_namespace(self, netns): if netns in self._net_namespaces: logging.debug("Network namespace %s already exists." % netns) else: logging.debug("Creating network namespace %s." % netns) read_pipe, write_pipe = multiprocessing.Pipe() pid = os.fork() if pid != 0: self._net_namespaces[netns] = {"pid": pid, "pipe": read_pipe} self._server_handler.add_netns(netns, read_pipe) return False elif pid == 0: self._slave_server.set_netns_sighandlers() #create new network namespace libc_name = ctypes.util.find_library("c") #from sched.h CLONE_NEWNET = 0x40000000 CLONE_NEWNS = 0x00020000 #based on ipnetns.c from the iproute2 project MNT_DETACH = 0x00000002 MS_BIND = 4096 MS_SLAVE = 1<<19 MS_REC = 16384 libc = ctypes.CDLL(libc_name) #based on ipnetns.c from the iproute2 project #bind to named namespace netns_path = "/var/run/netns/" if not os.path.exists(netns_path): os.mkdir(netns_path, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) netns_path = netns_path + netns f = os.open(netns_path, os.O_RDONLY | os.O_CREAT | os.O_EXCL, 0) os.close(f) libc.unshare(CLONE_NEWNET) libc.mount("/proc/self/ns/net", netns_path, "none", MS_BIND, 0) #map network sysfs to new net libc.unshare(CLONE_NEWNS) libc.mount("", "/", "none", MS_SLAVE | MS_REC, 0) libc.umount2("/sys", MNT_DETACH) libc.mount(netns, "/sys", "sysfs", 0, 0) #set ctl socket to pipe to main netns self._server_handler.close_s_sock() self._server_handler.close_c_sock() self._server_handler.clear_connections() self._server_handler.clear_netns_connections() self._if_manager.reconnect_netlink() self._if_manager.clear_if_mapping() self._server_handler.add_connection('netlink', self._if_manager.get_nl_socket()) self._server_handler.set_netns(netns) self._server_handler.set_ctl_sock((write_pipe, "root_netns")) self._log_ctl.disable_logging() self._log_ctl.set_origin_name(netns) self._log_ctl.set_connection(write_pipe) logging.debug("Created network namespace %s" % netns) return True else: raise Exception("Fork failed!") def del_namespace(self, netns): if netns not in self._net_namespaces: logging.debug("Network namespace %s doesn't exist." % netns) return False else: MNT_DETACH = 0x00000002 libc_name = ctypes.util.find_library("c") libc = ctypes.CDLL(libc_name) netns_path = "/var/run/netns/" + netns netns_pid = self._net_namespaces[netns]["pid"] os.kill(netns_pid, signal.SIGUSR1) os.waitpid(netns_pid, 0) # Remove named namespace try: libc.umount2(netns_path, MNT_DETACH) os.unlink(netns_path) except: logging.warning("Unable to remove named namespace %s." % netns_path) logging.debug("Network namespace %s removed." % netns) self._net_namespaces[netns]["pipe"].close() self._server_handler.del_netns(netns) del self._net_namespaces[netns] return True def set_if_netns(self, if_id, netns): netns_pid = self._net_namespaces[netns]["pid"] device = self._if_manager.get_mapped_device(if_id) dev_name = device.get_name() device.set_netns(netns) hwaddr = device.get_hwaddr() exec_cmd("ip link set %s netns %d" % (dev_name, netns_pid)) msg = {"type": "command", "method_name": "map_if_by_hwaddr", "args": [if_id, hwaddr]} self._server_handler.send_data_to_netns(netns, msg) result = self._slave_server.wait_for_result(netns) return result def return_if_netns(self, if_id): device = self._if_manager.get_mapped_device(if_id) if device.get_netns() == None: dev_name = device.get_name() ppid = os.getppid() exec_cmd("ip link set %s netns %d" % (dev_name, ppid)) self._if_manager.unmap_if(if_id) return True else: netns = device.get_netns() msg = {"type": "command", "method_name": "return_if_netns", "args": [if_id]} self._server_handler.send_data_to_netns(netns, msg) result = self._slave_server.wait_for_result(netns) if result["result"] != True: raise Exception("Return from netns failed.") device.set_netns(None) return True def add_br_vlan(self, if_id, br_vlan_info): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) brt.add_vlan(br_vlan_info) return True def del_br_vlan(self, if_id, br_vlan_info): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) brt.del_vlan(br_vlan_info) return True def get_br_vlans(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) return brt.get_vlans() def add_br_fdb(self, if_id, br_fdb_info): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) brt.add_fdb(br_fdb_info) return True def del_br_fdb(self, if_id, br_fdb_info): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) brt.del_fdb(br_fdb_info) return True def get_br_fdbs(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) return brt.get_fdbs() def set_br_learning(self, if_id, br_learning_info): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) brt.set_learning(br_learning_info) return True def set_br_learning_sync(self, if_id, br_learning_sync_info): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) brt.set_learning_sync(br_learning_sync_info) return True def set_br_flooding(self, if_id, br_flooding_info): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) brt.set_flooding(br_flooding_info) return True def set_br_state(self, if_id, br_state_info): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False brt = BridgeTool(dev.get_name()) brt.set_state(br_state_info) return True def set_speed(self, if_id, speed): dev = self._if_manager.get_mapped_device(if_id) if dev is not None: dev.set_speed(speed) else: logging.error("Device with id '%s' not found." % if_id) return False return True def set_autoneg(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if dev is not None: dev.set_autoneg() else: logging.error("Device with id '%s' not found." % if_id) return False return True def wait_interface_init(self): self._if_manager.wait_interface_init() return True def slave_add(self, if_id, slave_id): dev = self._if_manager.get_mapped_device(if_id) if dev is not None: dev.slave_add(slave_id) else: logging.error("Device with id '%s' not found." % if_id) return False return True def slave_del(self, if_id, slave_id): dev = self._if_manager.get_mapped_device(if_id) if dev is not None: dev.slave_del(slave_id) else: logging.error("Device with id '%s' not found." % if_id) return False return True def _is_systemd(self): stdout, _ = exec_cmd("pidof systemd", die_on_err=False) return len(stdout) != 0 def _configure_service(self, service, start=True): action = "start" if start else "stop" if self._is_systemd(): exec_cmd("systemctl {} {}".format(action, service)) else: exec_cmd("service {} {}".format(service, action)) return True def enable_service(self, service): return self._configure_service(service) def disable_service(self, service): return self._configure_service(service, start=False) def get_num_cpus(self): return int(os.sysconf('SC_NPROCESSORS_ONLN')) def get_ethtool_stats(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False return dev.get_ethtool_stats() def enable_lldp(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if not dev: logging.error("Device with id '%s' not found." % if_id) return False dev.enable_lldp() return True def set_pause_on(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if dev is not None: dev.set_pause_on() else: logging.error("Device with id '%s' not found." % if_id) return False return True def set_pause_off(self, if_id): dev = self._if_manager.get_mapped_device(if_id) if dev is not None: dev.set_pause_off() else: logging.error("Device with id '%s' not found." % if_id) return False return True