def _copy_raw_data(self): """Copy raw test results to the output directory.""" # Paths to the raw statistics. raw_stats_paths = {} # Paths to test reports' description files. descr_paths = {} for res in self.rsts: srcpath = res.dirpath resrootdir = "raw-" + res.reportid dstpath = self.outdir.joinpath(resrootdir) if self.relocatable: action = "copy" else: action = "symlink" FSHelpers.move_copy_link(srcpath, dstpath, action=action, exist_ok=True) if res.stats_path.is_dir(): hlink = f"<a href=\"{resrootdir}/{res.stats_path.name}\">Raw statistics</a>" raw_stats_paths[res.reportid] = hlink if res.descr_path.is_file(): hlink = f"<a href=\"{resrootdir}/{res.descr_path.name}\">Test description</a>" descr_paths[res.reportid] = hlink return raw_stats_paths, descr_paths
def __init__(self, proc, timeout=30): """ Class constructor. The arguments are as follows. * proc - the 'Proc' or 'SSH' object that defines the host to operate on. This object will keep a 'proc' reference and use it in various methods. * timeout - longest time in seconts to wait for data in the trace buffer. """ self._reader = None self._proc = proc self.timeout = timeout self.raw_line = None mntpoint = FSHelpers.mount_debugfs(proc=proc) self.ftpath = mntpoint.joinpath("tracing/trace") self.ftpipe_path = mntpoint.joinpath("tracing/trace_pipe") for path in (self.ftpath, self.ftpipe_path): if not FSHelpers.isfile(path, proc=proc): raise ErrorNotSupported( f"linux kernel function trace file was not found at " f"'{path}'{proc.hostmsg}") cmd = f"cat {self.ftpipe_path}" name = "stale wult function trace reader process" ProcHelpers.kill_processes(cmd, log=True, name=name, proc=self._proc) self._clear() self._reader = self._proc.run_async(cmd, shell=True)
def _generate_report(self): """Put together the final HTML report.""" _LOG.info("Generating the HTML report.") # Make sure the output directory exists. try: self.outdir.mkdir(parents=True, exist_ok=True) except OSError as err: raise Error(f"failed to create directory '{self.outdir}': {err}") raw_stats_paths, descr_paths = self._copy_raw_data() # Find the styles and templates paths. templdir = FSHelpers.search_for_app_data( "wult", Path("templates"), pathdescr="HTML report Jinja2 templates") csspath = FSHelpers.search_for_app_data( "wult", Path("css/style.css"), pathdescr="HTML report CSS file") # Copy the styles file to the output directory. dstpath = self.outdir.joinpath("style.css") try: shutil.copyfile(csspath, dstpath) except OSError as err: raise Error( f"failed to copy CSS file from '{csspath}' to '{dstpath}':\n{err}" ) # The summary table is only included into the main HTML page. sum_tbl = self._prepare_summary_table(raw_stats_paths, descr_paths) links_tbl = self._prepare_links_table() # Each column name gets its own HTML page. for colname, pinfos in self._pinfos.items(): stats_tbl = self._prepare_stats_table(pinfos) # Render the template. jenv = Jinja2.build_jenv(templdir, trim_blocks=True, lstrip_blocks=True) jenv.globals["stats_tbl"] = stats_tbl jenv.globals["pinfos"] = pinfos jenv.globals["colname"] = colname jenv.globals["title_descr"] = self.title_descr jenv.globals["toolname"] = self._refinfo["toolname"] if sum_tbl: jenv.globals["sum_tbl"] = sum_tbl jenv.globals["links_tbl"] = links_tbl templfile = outfile = "index.html" sum_tbl = None else: templfile = "metric.html" outfile = links_tbl[colname]["fname"] Jinja2.render_template(jenv, Path(templfile), outfile=self.outdir.joinpath(outfile))
def __init__(self, devid, cpunum, proc): """The class constructor. The arguments are the same as in '_WultDeviceBase.__init__()'.""" super().__init__(devid, cpunum, proc) self._pci_info = None self._devpath = None self._captured = None path = Path(f"/sys/bus/pci/devices/{self._devid}") if not FSHelpers.exists(path, proc=proc): raise ErrorNotFound(f"cannot find device '{self._devid}'{self._proc.hostmsg}:\n" f"path {path} does not exist") self._devpath = FSHelpers.abspath(path, proc=self._proc) self._pci_info = LsPCI.LsPCI(proc).get_info(Path(self._devpath).name) if self.supported_devices and self._pci_info['devid'] not in self.supported_devices: supported = ["%s - %s" % (key, val) for key, val in self.supported_devices.items()] supported = "\n * ".join(supported) raise ErrorNotSupported(f"PCI device '{self._pci_info['pciaddr']}' (PCI ID " f"{self._pci_info['devid']}) is not supported by wult driver " f"{self.drvname}.\nHere is the list of supported PCI IDs:\n* " f"{supported}") self.info["name"] = "Intel I210" self.info["devid"] = self._pci_info["pciaddr"] if self.supported_devices: self.info["descr"] = self.supported_devices[self._pci_info['devid']] else: self.info["name"] = self._pci_info["name"] self.info["descr"] = self.info['name'].capitalize() self.info["descr"] += f". PCI address {self._pci_info['pciaddr']}, Vendor ID " \ f"{self._pci_info['vendorid']}, Device ID {self._pci_info['devid']}."
def _get_driver(self): """ Find out whether the PCI device is bound to any driver. If it is not, returns the '(None, None)' tuple. Otherwise returns a tuple of: * driver name * driver sysfs path """ drvpath = Path(f"{self._devpath}/driver") if not FSHelpers.exists(drvpath, proc=self._proc): return (None, None) drvpath = FSHelpers.abspath(drvpath, proc=self._proc) drvname = Path(drvpath).name return (drvname, drvpath)
def _get_hw_addr(self): """ Return the hardware address for the NIC corresponding to the network interface. Typically the hardware address is a PCI address, such as '0000:04:00.0'. """ # The "device" symlink leads to the sysfs subdirectory corresponding to the underlying NIC. path = self._sysfsbase / "device" if not FSHelpers.exists(path, proc=self._proc): raise ErrorNotFound( f"cannot find network interface '{self.ifname}':\n" f"path '{path}' does not exist{self._proc.hostmsg}'") # The name of the subdirectory is the hardware address. path = FSHelpers.abspath(path, proc=self._proc) return path.name
def get_path_pairs(proc, toolname, helperpath): """ Yield paths for 'toolname' driver and helpertool source code and deployables. Arguments are same as in 'is_deploy_needed()'. """ lproc = Procs.Proc() for path, is_drv in [(_DRV_SRC_SUBPATH, True), (_HELPERS_SRC_SUBPATH, False)]: srcpath = FSHelpers.search_for_app_data(toolname, path / toolname, default=None) # Some tools may not have helpers. if not srcpath: continue for deployable in _get_deployables(srcpath, lproc): deploypath = None # Deployable can be driver module or helpertool. if is_drv: deploypath = _get_module_path(proc, deployable) else: if helperpath and helperpath.name == deployable: deploypath = helperpath else: deploypath = get_helpers_deploy_path(proc, toolname) deploypath = Path(deploypath, "bin", deployable) yield srcpath, deploypath
def _set_launch_distance(self): """Set launch distance limits to driver.""" try: limit_path = self._basedir / "ldist_max_nsec" with self._proc.open(limit_path, "r") as fobj: ldist_max = fobj.read().strip() limit_path = self._basedir / "ldist_min_nsec" with self._proc.open(limit_path, "r") as fobj: ldist_min = fobj.read().strip() except Error as err: raise Error( f"failed to read launch distance limit from '{limit_path}'" f"{self._proc.hostmsg}:\n{err}") ldist_min = Trivial.str_to_num(ldist_min) ldist_max = Trivial.str_to_num(ldist_max) from_path = self._basedir / "ldist_from_nsec" to_path = self._basedir / "ldist_to_nsec" for ldist, ldist_path in zip_longest(self._ldist, [from_path, to_path], fillvalue=self._ldist[-1]): if ldist < ldist_min or ldist > ldist_max: raise Error( f"launch distance '{ldist}' is out of range, it should be in range of " f"[{ldist_min},{ldist_max}]") if not FSHelpers.exists(ldist_path, proc=self._proc): raise Error( f"path i'{ldist_path}' does not exist{self._proc.hostmsg}") # open()/write() doesn't work for this file when done over SSH. self._proc.run_verify(f"echo {ldist} > {ldist_path}", shell=True)
def disable_ipv6(self): """Disable IPv6 for the network interface.""" path = Path("/proc/sys/net/ipv6/conf/{self.ifname}/disable_ipv6") if FSHelpers.isfile(path, proc=self._proc): with self._proc.open(path, "w") as fobj: fobj.write("1")
def get_helpers_deploy_path(proc, toolname): """ Get helpers deployment path for 'toolname' on the system associated with the 'proc' object. """ helpers_path = os.environ.get(f"{toolname.upper()}_HELPERSPATH") if not helpers_path: helpers_path = FSHelpers.get_homedir(proc=proc) / HELPERS_LOCAL_DIR return helpers_path
def start(self): """Start the latency measurements.""" # Sanity check. if not FSHelpers.exists(self._enable_path, proc=self._proc): raise Error( f"path {self._enable_path} does not exist{self._proc.hostmsg}") self._proc.run_verify(f"echo 1 > {self._enable_path}", shell=True)
def bind(self, drvname): """Bind the PCI device to driver 'drvname'.""" _LOG.debug("binding device '%s' to driver '%s'%s", self._pci_info["pciaddr"], drvname, self._proc.hostmsg) failmsg = f"failed to bind device '{self._pci_info['pciaddr']}' to driver '{drvname}'" \ f"{self._proc.hostmsg}" self._dmesg_capture() drvpath = Path(f"/sys/bus/pci/drivers/{drvname}") if not FSHelpers.exists(drvpath, proc=self._proc): raise Error(f"{failmsg}':\npath '{drvpath}' does not exist{self._proc.hostmsg}") cur_drvname = self.get_driver_name() if cur_drvname == drvname: _LOG.debug("device '%s' is already bound to driver '%s'%s", self._pci_info["pciaddr"], drvname, self._proc.hostmsg) return if cur_drvname: raise Error(f"{failmsg}:\nit is already bound to driver '{cur_drvname}'") # At this point we do not know if the driver supports this PCI ID. So start with the # assumption that it does not, in which case writing to the 'new_id' file should do both: # * make the driver aware of the PCI ID # * bind the device path = f"{drvpath}/new_id" val = f"{self._pci_info['vendorid']} {self._pci_info['devid']}" bound = True try: with self._proc.open(path, "wt") as fobj: _LOG.debug("writing '%s' to file '%s'", val, path) fobj.write(val) except Error as err: bound = False if not bound: # Probably the driver already knows about this PCI ID. Use the 'bind' file in this case. path = f"{drvpath}/bind" val = self._pci_info["pciaddr"] with self._proc.open(path, "wt") as fobj: _LOG.debug("writing '%s' to file '%s'", val, path) try: fobj.write(val) except Error as err: raise Error(f"{failmsg}:\n{err}\n{self._get_new_dmesg()}") # Verify that the device is bound to the driver. if not self._get_driver()[1]: raise Error(f"{failmsg}\n{self._get_new_dmesg()}") _LOG.debug("binded device '%s' to driver '%s'%s\n%s", self._pci_info["pciaddr"], drvname, self._proc.hostmsg, self._get_new_dmesg())
def __init__(self, ifname, tc_bin="tc", handover_delta=500000, phc2sys_bin="phc2sys", proc=None): """ Class constructor. The arguments are as follows. * ifname - name of the interface to use for scheduling network packets in the future. * tc_bin - path to the 'tc' tool that should be used for setting up the ETF qdisc. * handover_delta - the qdisc delta - the time offset in microseconds when the qdisc hands the packet over to the network driver. * phc2sys_bin - path to the 'phc2sys' tool that will be run in background and periodically synchronize the host and NIC clock. * proc - the 'Proc' or 'SSH' object that defines the host to configure the ETF qdisc on (default is the local host). This object will keep a 'proc' reference and use it in various methods. Note about phc2sys. When ETF qdisc offloads packets to the NIC, it is important to keep host and NIC times in sync, because Linux kernel API accept absolute time values to send the packets at, and these values are passed down to the NIC as-is. If NIC's time is different, the packets will be sent at incorrect time or just dropped causing errors like "missing deadline". """ self._netif = None self._old_tc_err_msg = None self._phc2sys_proc = None self._ifname = ifname self._tc_bin = FSHelpers.which(tc_bin, proc=proc) self._handover_delta = int(handover_delta * 1000) self._phc2sys_bin = FSHelpers.which(phc2sys_bin, proc=proc) self._proc = proc self._netif = _NetIface.NetIface(ifname, proc=proc) self._old_tc_err_msg = f"the 'tc' tool installed{self._proc.hostmsg} is not new enough " \ f"and does not support the ETF qdisc.\nPlease, install 'tc' " \ f"version '181023' or greater.\nThe 'tc' tool is part of the " \ f"'iproute2' project. Run 'tc -V' to check its version."
def _get_ifinfos(proc): """ For every network interfaces backed by a real device on the system defined by 'proc', yield the following tuples: * interface name * device HW address """ for ifname, path, typ in FSHelpers.lsdir(_SYSFSBASE, proc=proc): if typ != "@": # We expect a symlink. continue devpath = None with contextlib.suppress(Error): devpath = FSHelpers.abspath(path / "device", proc=proc) if not devpath: continue yield ifname, devpath.name
def _check_ip_tool_present(self): """Verifies that the "ip" tool is available.""" if self._ip_tool_present: return if not FSHelpers.which("ip", default=None, proc=self._proc): raise ErrorNotSupported( f"the 'ip' tool is not installed{self._proc.hostmsg}.\nThis " f"tool is part of the 'iproute2' project, please install it.") self._ip_tool_present = True
def __init__(self, proc=None): """Class constructor.""" if not proc: proc = Procs.Proc() self._proc = proc self._lspci_bin = "lspci" if not FSHelpers.which(self._lspci_bin, default=None, proc=proc): raise ErrorNotSupported( f"the '{self._lspci_bin}' tool is not installed{proc.hostmsg}")
def get_newest_mtime(path): """Scan items in 'path' and return newest modification time among entries found.""" newest = 0 for _, fpath, _ in FSHelpers.lsdir(path, must_exist=False): mtime = os.path.getmtime(fpath) if mtime > newest: newest = mtime if not newest: raise Error(f"no files found in '{path}'") return newest
def __init__(self, proc, ifname, res, ndlrunner_bin, cstats=None, ts_bin=None, ldist=None): """ The class constructor. The arguments are as follows. * proc - the 'Proc' or 'SSH' object that defines the host to run the measurements on. * ifname - the network interface name to use for measuring the latency. * res - the 'WORawResult' object to store the results at. * ndlrunner_bin - path to the 'ndlrunner' helper. * cstats - True: collect C-state statistics, fail if 'turbostat' not found. None: collect C-state statistics, only if 'turbostat' is found. False: do not collect C-state statistics. * ts_bin - path to the 'turbostat' tool, default is just "turbostat". * ldist - for how far in the future the delayed network packets should be scheduled in microseconds. Default is [5000, 10000] microseconds. """ self._proc = proc self._ifname = ifname self._res = res self._ndlrunner_bin = ndlrunner_bin self._run_ts = cstats self._ts_bin = ts_bin self._ldist = ldist self._ts_heading = "" self._ndl_lines = None self._drv = None self._rtd_path = None self._ndlrunner = None self._colmap = None self._progress = None self._max_rtd = 0 self._post_trigger = None self._post_trigger_thresh = None self._etfqdisc = None self._nmcli = None self._netif = None self._verify_input_args() self._cstate_stats_init() self._progress = _ProgressLine.ProgressLine(period=1) self._drv = KernelModule.KernelModule(proc, "ndl") mntpath = FSHelpers.mount_debugfs(proc=proc) self._rtd_path = mntpath.joinpath(f"{self._drv.name}/rtd") self._etfqdisc = _ETFQdisc.ETFQdisc(ifname, proc=proc)
def _get_module_path(proc, name): """Return path to installed module. Return 'None', if module not found.""" cmd = f"modinfo -n {name}" stdout, _, exitcode = proc.run(cmd) if exitcode != 0: return None modpath = Path(stdout.strip()) if FSHelpers.isfile(modpath, proc): return modpath return None
def _deploy(args): """Deploy helpers and drivers.""" with contextlib.closing(get_proc(args, args.ihost)) as iproc, \ contextlib.closing(get_proc(args, args.bhost)) as bproc: remotesrc = args.bhost != "localhost" remotedst = args.ihost != "localhost" if hasattr(args, "helpersrc"): helpersdst = args.tmpdir / "helpers_deployed" _LOG.debug("Deploying helpers to '%s'%s", helpersdst, bproc.hostmsg) cmd = f"make -C '{args.helpersrc}' install PREFIX='{helpersdst}'" stdout, stderr = bproc.run_verify(cmd) _log_cmd_output(args, stdout, stderr) iproc.rsync(str(helpersdst) + "/", args.helpers_path, remotesrc=remotesrc, remotedst=remotedst) if hasattr(args, "drvsrc"): dstdir = args.kmodpath.joinpath(_DRV_SRC_SUBPATH) FSHelpers.mkdir(dstdir, parents=True, exist_ok=True, proc=iproc) for name in _get_deployables(args.drvsrc, bproc): installed_module = _get_module_path(iproc, name) srcpath = args.drvsrc.joinpath(f"{name}.ko") dstpath = dstdir.joinpath(f"{name}.ko") _LOG.info("Deploying driver '%s' to '%s'%s", name, dstpath, iproc.hostmsg) iproc.rsync(srcpath, dstpath, remotesrc=remotesrc, remotedst=remotedst) if installed_module and installed_module.resolve() != dstpath.resolve(): _LOG.debug("removing old module '%s'%s", installed_module, iproc.hostmsg) iproc.run_verify(f"rm -f '{installed_module}'") stdout, stderr = iproc.run_verify(f"depmod -a -- '{args.kver}'") _log_cmd_output(args, stdout, stderr) # Potentially the deployed driver may crash the system before it gets to write-back data # to the file-system (e.g., what 'depmod' modified). This may lead to subsequent boot # problems. So sync the file-system now. iproc.run_verify("sync")
def __init__(self, name): """ The class constructor. The arguments are as follows. * name - name of the tool to load the definitions for (e.g., 'wult'). """ self.name = name self.info = None self.vanilla_info = None path = FSHelpers.search_for_app_data("wult", Path(f"defs/{name}.yml"), pathdescr=f"{name} datapoints definitions file") self.info = self.vanilla_info = YAML.load(path)
def _verify_input_args(self): """Verify and adjust the constructor input arguments.""" if not self._ldist: self._ldist = "5000, 10000" self._ldist = _Common.validate_ldist(self._ldist) # Validate the 'ndlrunner' helper path. if not FSHelpers.isexe(self._ndlrunner_bin, proc=self._proc): raise Error( f"bad 'ndlrunner' helper path '{self._ndlrunner_bin}' - does not exist" f"{self._proc.hostmsg} or not an executable file")
def _get_cstate_indexes(self, cpu): """Yield tuples of of C-state indexes and sysfs paths for cpu number 'cpu'.""" basedir = self._sysfs_base / f"cpu{cpu}" / "cpuidle" name = None for name, path, typ in FSHelpers.lsdir(basedir, proc=self._proc): errmsg = f"unexpected entry '{name}' in '{basedir}'{self._proc.hostmsg}" if typ != "/" or not name.startswith("state"): raise Error(errmsg) index = name[len("state"):] if not Trivial.is_int(index): raise Error(errmsg) yield int(index), Path(path) if name is None: raise Error(f"C-states are not supported{self._proc.hostmsg}")
def __init__(self, proc=None): """ Initialize a class instance for the host associated with the 'proc' object. By default it is is going to be the local host, but 'proc' can be used to pass a connected 'SSH' object, in which case all operation will be done on the remote host. This object will keep a 'proc' reference and use it in various methods. """ if not proc: proc = Procs.Proc() self._proc = proc self._saved_managed = OrderedDict() if not FSHelpers.which("nmcli", default=None, proc=proc): raise ErrorNotSupported( f"the 'nmcli' tool is not installed{proc.hostmsg}")
def set_post_trigger(self, path, trange=None): """ Configure the post-trigger - a program that has to be executed after a datapoint is collected. The arguments are as follows. * path - path to the executable program to run. The program will be executed with the '--latency <value>' option, where '<value>' is the observed wake latency value in nanoseconds. * trange - the post-trigger range. By default, the trigger program is executed on every datapoint. But if the trigger range is provided, the trigger program will be executed only when wake latency is in trigger range. """ if not FSHelpers.isexe(path, proc=self._proc): raise Error( f"post-trigger program '{path}' does not exist{self._proc.hostmsg} or it " f"is not an executable file") self._post_trigger = path if trange is not None: vals = Trivial.split_csv_line(trange) for idx, val in enumerate(vals): if not Trivial.is_int(val): raise Error( f"bad post-trigger range value '{val}', should be an integer " f"amount of nanoseconds") vals[idx] = Trivial.str_to_num(val, default=None) if vals[idx] < 0: raise Error( f"bad post trigger range value '{vals[idx]}', should be greater or " f"equal to zero") if len(vals) != 2: raise Error( f"bad post trigger range '{trange}', it should include 2 numbers" ) if vals[1] - vals[0] < 0: raise Error( f"bad post trigger range '{trange}', first number cannot be greater " f"than the second number") self._post_trigger_range = vals
def _cstate_stats_init(self): """Initialize information related to C-state statistics and the 'turbostat' tool.""" if self._run_ts is False: if not self._ts_bin: return raise Error( "path to 'turbostat' tool given, but C-state statistics collection is " "disabled") if not self._ts_bin: self._ts_bin = FSHelpers.which("turbostat", default=None, proc=self._proc) if not self._ts_bin: msg = f"cannot collect C-state statistics: the 'turbostat' tool was not found" \ f"{self._proc.hostmsg}" if self._run_ts is True: raise ErrorNotSupported(msg) self._run_ts = False _LOG.info(msg) return # Make sure turbostat version is '19.08.31' or newer. Turbostat versions are dates (e.g., # version '19.03.20' is Mar 20 2019), so we can use the 'datetime' module to compare the # versions. version = self._get_turbostat_version() vdate = (int(val) for val in version.split(".")) if datetime.datetime(*vdate) < datetime.datetime(19, 8, 31): msg = f"cannot collect C-state statistics: too old turbostat version '{version}', " \ f"use turbostat version '19.08.31' or newer\n(checked {self._ts_bin}" \ f"{self._proc.hostmsg}" if self._run_ts: raise ErrorNotSupported(msg) self._run_ts = False _LOG.info(msg) else: self._run_ts = True self._build_colmap()
def __init__(self, devid, cpunum, proc, ldist=None, force=False): """ Initialize a class instance for a PCI device 'devid'. The arguments are as follows. * devid - the device "ID", which can be a PCI address or a network interface name. * cpunum - the measured CPU number. * proc - the host to operate on. This object will keep a 'proc' reference and use it in various methods * ldist - how far in future should the delayed event be scheduled. * force - initialize measurement device, even if it is already in use. """ self._cpunum = cpunum self._proc = proc self._ldist = ldist self._drv = None self._saved_drvname = None self.dev = None self._basedir = None self._enable_path = None self._main_drv = None # This is a debugging option that allows to disable automatic wult modules unloading on # 'close()'. self.unload = True # Whether kernel messages should be monitored. They are very useful if something goes wrong. self.dmesg = True self.dev = Devices.WultDevice(devid, cpunum, proc, force=force) self._main_drv = KernelModule.KernelModule(proc, "wult") self._drv = KernelModule.KernelModule(proc, self.dev.drvname) mntpoint = FSHelpers.mount_debugfs(proc=proc) self._basedir = mntpoint / "wult" self._enable_path = self._basedir / "enabled" msg = f"Compatible device '{self.dev.info['name']}'{proc.hostmsg}:\n" \ f" * Device ID: {self.dev.info['devid']}\n" \ f" - {self.dev.info['descr']}" _LOG.info(msg)
def set_post_trigger(self, path, thresh=None): """ Configure the post-trigger - a program that has to be executed after a datapoint is collected. The arguments are as follows. * path - path to the executable program to run. The program will be executed with the '--latency <value>' option, where '<value>' is the observed latency value in nanoseconds. * thresh - the post-trigger threshold. By default, the trigger program is executed on evey datapoint. But if a threshold is provided, the trigger program will be executed only when latency exceeds the threshold. """ if not FSHelpers.isexe(path, proc=self._proc): raise Error( f"file '{path}' does not exist or it is not an executalbe file" ) self._post_trigger = path if thresh is not None: if not Trivial.is_int(thresh): raise Error( f"bad post-trigger threshold value '{thresh}', should be an integer " f"amount of nanoseconds") self._post_trigger_thresh = int(thresh)
def __init__(self, ifid, proc=None): """ Initialize a class instance network interface corresponding to 'ifid' on the host associated with the 'proc' object. The 'ifid' argumen can be either the network interface name or its hardware address (e.g., the PCI address of the network card corresponding to the network interface). By default this class is intialized for the local host, but 'proc' can be used to pass a connected 'SSH' object, in which case all operation will be done on the remote host. This object will keep a 'proc' reference and use it in various methods. """ if not proc: proc = Procs.Proc() self._ifid = ifid self._proc = proc self.ifname = None self.hwaddr = None self._sysfsbase = None self._saved_ip_info = {} self._ip_tool_present = None sysfsbase = _SYSFSBASE.joinpath(ifid) if FSHelpers.isdir(sysfsbase, proc=proc): # 'ifid' is a network interface name. self.ifname = ifid self._sysfsbase = sysfsbase self.hwaddr = self._get_hw_addr() else: # 'ifid' is probably a HW address (e.g., PCI address). self.ifname = self._hw_addr_to_ifname() if not self.ifname: self._raise_iface_not_found() self.hwaddr = ifid self._sysfsbase = _SYSFSBASE.joinpath(self.ifname)
def _deploy_prepare(args, toolname, minkver): """ Validate command-line arguments of the "deploy" command and prepare for builing the helpers and drivers. The arguments are as follows. o args - the command line arguments. o toolname - name of the tool being deployed (e.g., 'ndl'). o minkver - the minimum required version number. """ args.tmpdir = None args.kver = None if not args.ihost: args.ihost = "localhost" if not args.bhost: args.bhost = args.ihost if args.ihost != args.bhost and not args.bhost == "localhost": raise Error("build host (--build-host) must be the local host or the same as deploy host " "(--host)") if args.ihost == "localhost" and args.bhost == "localhost": for attr in ("username", "privkey", "timeout"): if getattr(args, attr) is not None: _LOG.warning("ignoring the '--%s' option as it not useful for a local host", attr) if not args.timeout: args.timeout = 8 else: args.timeout = Trivial.str_to_num(args.timeout) if not args.username: args.username = "******" if args.privkey and not args.privkey.is_dir(): raise Error(f"path '{args.privkey}' does not exist or it is not a directory") if hasattr(args, "drvsrc"): if not args.drvsrc: args.drvsrc = FSHelpers.search_for_app_data("wult", _DRV_SRC_SUBPATH/f"{toolname}", pathdescr=f"{toolname} drivers sources") if not args.drvsrc.is_dir(): raise Error(f"path '{args.drvsrc}' does not exist or it is not a directory") if hasattr(args, "helpersrc"): if not args.helpersrc: args.helpersrc = FSHelpers.search_for_app_data("wult", _HELPERS_SRC_SUBPATH/f"{toolname}", pathdescr=f"{toolname} helper sources") if not args.helpersrc.is_dir(): raise Error(f"path '{args.helpersrc}' does not exist or it is not a directory") with contextlib.closing(get_proc(args, args.bhost)) as proc: if not FSHelpers.which("make", default=None, proc=proc): raise Error(f"please, install the 'make' tool{proc.hostmsg}") if not args.ksrc: args.kver = KernelVersion.get_kver(proc=proc) if not args.ksrc: args.ksrc = Path(f"/lib/modules/{args.kver}/build") else: args.ksrc = FSHelpers.abspath(args.ksrc, proc=proc) if not FSHelpers.isdir(args.ksrc, proc=proc): raise Error(f"kernel sources directory '{args.ksrc}' does not exist{proc.hostmsg}") if not args.kver: args.kver = KernelVersion.get_kver_ktree(args.ksrc, proc=proc) _LOG.info("Kernel sources path: %s", args.ksrc) _LOG.info("Kernel version: %s", args.kver) if KernelVersion.kver_lt(args.kver, minkver): raise Error(f"version of the kernel{proc.hostmsg} is {args.kver}, and it is not new " f"enough.\nPlease, use kernel version {minkver} or newer.") args.tmpdir = FSHelpers.mktemp(prefix=f"{toolname}-", proc=proc) if hasattr(args, "drvsrc"): _LOG.debug("copying the drivers to %s:\n '%s' -> '%s'", proc.hostname, args.drvsrc, args.tmpdir) proc.rsync(f"{args.drvsrc}/", args.tmpdir / "drivers", remotesrc=False, remotedst=True) args.drvsrc = args.tmpdir / "drivers" _LOG.info("Drivers will be compiled on host '%s'", proc.hostname) if hasattr(args, "helpersrc"): _LOG.debug("copying the helpers to %s:\n '%s' -> '%s'", proc.hostname, args.helpersrc, args.tmpdir) proc.rsync(f"{args.helpersrc}/", args.tmpdir / "helpers", remotesrc=False, remotedst=True) args.helpersrc = args.tmpdir / "helpers" _LOG.info("Helpers will be compiled on host '%s'", proc.hostname) with contextlib.closing(get_proc(args, args.ihost)) as proc: if not args.kmodpath: args.kmodpath = Path(f"/lib/modules/{args.kver}") if not FSHelpers.isdir(args.kmodpath, proc=proc): raise Error(f"kernel modules directory '{args.kmodpath}' does not exist{proc.hostmsg}") _LOG.info("Drivers will be deployed to '%s'%s", args.kmodpath, proc.hostmsg) _LOG.info("Kernel modules path%s: %s", proc.hostmsg, args.kmodpath) if hasattr(args, "helpersrc"): if not args.helpers_path: args.helpers_path = get_helpers_deploy_path(proc, toolname) _LOG.info("Helpers will be deployed to '%s'%s", args.helpers_path, proc.hostmsg)