From 190f2a6988d61134225bebdc4260ec14d686fbf4 Mon Sep 17 00:00:00 2001 From: Valentin Schneider Date: Wed, 18 Sep 2019 12:04:36 +0100 Subject: [PATCH 1/3] wlgen/workload: Move taskset/cgroup wrapping into its own method While at it, remove the local variable "_command" into "command", because it's a local variable... --- lisa/wlgen/workload.py | 54 +++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/lisa/wlgen/workload.py b/lisa/wlgen/workload.py index 307a2f60c..46cd0fdd0 100644 --- a/lisa/wlgen/workload.py +++ b/lisa/wlgen/workload.py @@ -111,6 +111,37 @@ 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(self, cpus=None, cgroup=None, background=False, as_root=False, timeout=None): """ Execute the workload on the configured target. @@ -135,30 +166,15 @@ 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) + logger.info("Execution start: %s", command) if background: - target.background(_command, as_root=as_root) + target.background(command, as_root=as_root) else: - self.output = target.execute(_command, as_root=as_root, timeout=timeout) + self.output = target.execute(command, as_root=as_root, timeout=timeout) logger.info("Execution complete") logfile = ArtifactPath.join(self.res_dir, 'output.log') -- GitLab From a7d66e44acb23f55685d75151ade3fea32c4448f Mon Sep 17 00:00:00 2001 From: Valentin Schneider Date: Wed, 18 Sep 2019 12:09:54 +0100 Subject: [PATCH 2/3] wlgen/workload: Split background execution into a separate method This also deprecates the "background" parameter of Workload.run(). --- lisa/wlgen/workload.py | 59 +++++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/lisa/wlgen/workload.py b/lisa/wlgen/workload.py index 46cd0fdd0..bcab4a0ac 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): """ @@ -141,7 +139,42 @@ class Workload(Loggable): 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. @@ -170,17 +203,13 @@ class Workload(Loggable): command = self.wrap_command(cpus, cgroup) logger.info("Execution start: %s", command) + self.output = target.execute(command, as_root=as_root, timeout=timeout) + logger.info("Execution complete") - 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") - - 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 -- GitLab From a9631a3e90dbf893290e528ea2886e29ef3c0f10 Mon Sep 17 00:00:00 2001 From: Valentin Schneider Date: Wed, 18 Sep 2019 12:20:01 +0100 Subject: [PATCH 3/3] wlgen: Make Workload name an optionnal __init__() parameter We only really use it to name the (host-side) results directory, and this already gets appended a guaranteed-unique timestamp so we don't really need any special name. Make it possible to specify a name, but default to just using the class name. --- lisa/wlgen/rta.py | 2 +- lisa/wlgen/sysbench.py | 2 +- lisa/wlgen/workload.py | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lisa/wlgen/rta.py b/lisa/wlgen/rta.py index 59c169402..e46fbe5ae 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 567f008c5..210d49bff 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 bcab4a0ac..2105fc9f2 100644 --- a/lisa/wlgen/workload.py +++ b/lisa/wlgen/workload.py @@ -52,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" @@ -76,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 = "" @@ -95,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 -- GitLab