Ejemplo n.º 1
0
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 = {}
Ejemplo n.º 2
0
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 = {}
Ejemplo n.º 3
0
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
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
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
Ejemplo n.º 6
0
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
Ejemplo n.º 7
0
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 = {}
Ejemplo n.º 8
0
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