diff --git a/lisa/wlgen/rta.py b/lisa/wlgen/rta.py index 59c1694025f4ae37d54bb97137ff6148d2b49b26..e46fbe5ae381cd9e4fec95ddc131c3aaf0b3c7d5 100644 --- a/lisa/wlgen/rta.py +++ b/lisa/wlgen/rta.py @@ -46,7 +46,7 @@ class RTA(Workload): sched_policies = ['OTHER', 'FIFO', 'RR', 'DEADLINE'] - def __init__(self, target, name, res_dir=None, json_file=None): + def __init__(self, target, name=None, res_dir=None, json_file=None): # Don't add code here, use the early/late init methods instead. # This lets us factorize some code for the class methods that serve as # alternate constructors. diff --git a/lisa/wlgen/sysbench.py b/lisa/wlgen/sysbench.py index 567f008c5210eb44ab71604c7011bfd80d98b919..210d49bffe1bc5ee5c4d150ce456e82c395f095b 100644 --- a/lisa/wlgen/sysbench.py +++ b/lisa/wlgen/sysbench.py @@ -51,7 +51,7 @@ class Sysbench(Workload): required_tools = Workload.required_tools + ['sysbench'] - def __init__(self, target, name, res_dir=None): + def __init__(self, target, name=None, res_dir=None): super().__init__(target, name, res_dir) sysbench_bin = self.target.which('sysbench') diff --git a/lisa/wlgen/workload.py b/lisa/wlgen/workload.py index 307a2f60cea3d9726d404e47728c8a57290e4780..2105fc9f2a21ac4c50a5d4877c042e64bcc5fe25 100644 --- a/lisa/wlgen/workload.py +++ b/lisa/wlgen/workload.py @@ -15,15 +15,13 @@ # limitations under the License. # -import logging -import os -from shlex import quote - from shlex import quote from datetime import datetime +from subprocess import PIPE + from devlib.utils.misc import list_to_mask -from lisa.utils import Loggable, ArtifactPath +from lisa.utils import Loggable, ArtifactPath, deprecate class Workload(Loggable): """ @@ -54,7 +52,7 @@ class Workload(Loggable): **Implementation example**:: class Printer(Workload): - def __init__(self, target, name, res_dir=None): + def __init__(self, target, name=None, res_dir=None): super().__init__(target, name, res_dir) self.command = "echo" @@ -78,9 +76,9 @@ class Workload(Loggable): :meth:`lisa.target.Target.install_tools`. """ - def __init__(self, target, name, res_dir=None): + def __init__(self, target, name=None, res_dir=None): self.target = target - self.name = name + self.name = name or self.__class__.__qualname__ self.command = None self.output = "" @@ -97,7 +95,7 @@ class Workload(Loggable): logger.info("Creating target's run_dir: %s", self.run_dir) res_dir = res_dir if res_dir else target.get_res_dir( - name='{}-{}'.format(self.__class__.__qualname__, name) + name=name, append_time=True ) self.res_dir = res_dir @@ -111,6 +109,72 @@ class Workload(Loggable): logger.info("Wiping run_dir [%s]", self.run_dir) self.target.execute("rm -rf {}".format(quote(self.run_dir))) + def wrap_command(self, cpus=None, cgroup=None): + """ + Wrap the workload's command with taskset and cgroup constraints + + :param cpus: CPUs on which to restrict the workload execution (taskset) + :type cpus: list(int) + + :param cgroup: cgroup in which to run the workload + :type cgroup: str + + :returns: The command augmented with relevant taskset/cgroup invocations + """ + command = self.command + + if not command: + raise RuntimeError("Workload does not specify any command to execute") + + if cpus: + taskset_bin = self.target.which('taskset') + if not taskset_bin: + raise RuntimeError("Could not find 'taskset' executable on the target") + + cpumask = list_to_mask(cpus) + taskset_cmd = '{} {}'.format(quote(taskset_bin), quote('0x{:x}'.format(cpumask))) + command = '{} {}'.format(taskset_cmd, command) + + if cgroup: + command = self.target.cgroups.run_into_cmd(cgroup, command) + + return command + def run_background(self, cpus=None, cgroup=None, as_root=False, + stdout=PIPE, stderr=PIPE): + """ + Execute the workload on the configured target asynchronously. + + :param cpus: CPUs on which to restrict the workload execution (taskset) + :type cpus: list(int) + + :param cgroup: cgroup in which to run the workload + :type cgroup: str + + :param as_root: Whether to run the workload as root or not + :type as_root: bool + + :returns: the Popen handle of the host subprocess executing the remote + command + + .. note:: Popen handles can be used as context managers, so you could + (and probably should) use this like so:: + + with run_background() as handle: + do_stuff_with(handle) + handle.communicate() + """ + logger = self.get_logger() + if not self.command: + raise RuntimeError("Workload does not specify any command to execute") + + command = self.wrap_command(cpus, cgroup) + target = self.target + + logger.info("Background execution start: %s", command) + return target.background(command, stdout=stdout, stderr=stderr, as_root=as_root) + + @deprecate(parameter="background", replaced_by=run_background, + deprecated_in='2.0', removed_in='2.1') def run(self, cpus=None, cgroup=None, background=False, as_root=False, timeout=None): """ Execute the workload on the configured target. @@ -135,36 +199,17 @@ class Workload(Loggable): The standard output will be saved into a file in ``self.res_dir`` """ logger = self.get_logger() - if not self.command: - raise RuntimeError("Workload does not specify any command to execute") - - _command = self.command target = self.target + command = self.wrap_command(cpus, cgroup) - if cpus: - taskset_bin = target.which('taskset') - if not taskset_bin: - raise RuntimeError("Could not find 'taskset' executable on the target") - - cpumask = list_to_mask(cpus) - taskset_cmd = '{} {}'.format(quote(taskset_bin), quote('0x{:x}'.format(cpumask))) - _command = '{} {}'.format(taskset_cmd, _command) - - if cgroup: - _command = target.cgroups.run_into_cmd(cgroup, _command) - - logger.info("Execution start: %s", _command) - - if background: - target.background(_command, as_root=as_root) - else: - self.output = target.execute(_command, as_root=as_root, timeout=timeout) - logger.info("Execution complete") + logger.info("Execution start: %s", command) + self.output = target.execute(command, as_root=as_root, timeout=timeout) + logger.info("Execution complete") - logfile = ArtifactPath.join(self.res_dir, 'output.log') - logger.debug('Saving stdout to %s...', logfile) + logfile = ArtifactPath.join(self.res_dir, 'output.log') + logger.debug('Saving stdout to %s...', logfile) - with open(logfile, 'w') as ofile: - ofile.write(self.output) + with open(logfile, 'w') as ofile: + ofile.write(self.output) # vim :set tabstop=4 shiftwidth=4 textwidth=80 expandtab