From 3c11ad0e16a5e4546a720aaa1c154b57096466ef Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Mon, 19 Jun 2017 16:41:00 +0100 Subject: [PATCH 1/8] env: Don't use root on local targets This can allow us to use local targets for exercising LISA code in self-tests. --- libs/utils/env.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/utils/env.py b/libs/utils/env.py index 2857f35a2..fef587c9d 100644 --- a/libs/utils/env.py +++ b/libs/utils/env.py @@ -520,7 +520,8 @@ class TestEnv(ShareState): self.target = devlib.LocalLinuxTarget( platform = platform, load_default_modules = False, - modules = self.__modules) + modules = self.__modules, + connection_settings = {'unrooted': True}) else: raise ValueError('Config error: not supported [platform] type {}'\ .format(platform_type)) -- GitLab From a1662a08d4e99cf65daa92d32775d8980c879683 Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Fri, 27 Jan 2017 19:10:23 +0000 Subject: [PATCH 2/8] executor: Don't mount tmpfs for run dir if we don't have root --- libs/utils/executor.py | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/libs/utils/executor.py b/libs/utils/executor.py index 7c7060b22..7eb27dd62 100644 --- a/libs/utils/executor.py +++ b/libs/utils/executor.py @@ -188,6 +188,7 @@ class Executor(): # Initialize globals self._default_cgroup = None self._cgroup = None + self._old_selinux_mode = None # Setup logging self._log = logging.getLogger('Executor') @@ -328,26 +329,30 @@ class Executor(): self._log.debug('Setup RT-App run folder [%s]...', self.te.run_dir) self.target.execute('[ -d {0} ] || mkdir {0}'\ .format(self.te.run_dir)) - self.target.execute( + + if self.target.is_rooted: + self.target.execute( 'grep schedtest /proc/mounts || '\ ' mount -t tmpfs -o size=1024m {} {}'\ .format('schedtest', self.te.run_dir), as_root=True) - # tmpfs mounts have an SELinux context with "tmpfs" as the type (while - # other files we create have "shell_data_file"). That prevents non-root - # users from creating files in tmpfs mounts. For now, just put SELinux - # in permissive mode to get around that. - try: - # First, save the old SELinux mode - self._old_selinux_mode = self.target.execute('getenforce') - self._log.warning('Setting target SELinux in permissive mode') - self.target.execute('setenforce 0', as_root=True) - except TargetError: - # Probably the target doesn't have SELinux, or there are no - # contexts set up. No problem. - self._log.warning("Couldn't set SELinux in permissive mode. " - "This is probably fine.") - self._old_selinux_mode = None + + # tmpfs mounts have an SELinux context with "tmpfs" as the type + # (while other files we create have "shell_data_file"). That + # prevents non-root users from creating files in tmpfs mounts. For + # now, just put SELinux in permissive mode to get around that. + try: + # First, save the old SELinux mode + self._old_selinux_mode = self.target.execute('getenforce') + except TargetError: + # Probably the target doesn't have SELinux. No problem. + pass + else: + + self._log.warning('Setting target SELinux in permissive mode') + self.target.execute('setenforce 0', as_root=True) + else: + self._log.warning('Not mounting tempfs because no root') def _setup_cpufreq(self, tc): if 'cpufreq' not in tc: -- GitLab From 12a6542429c4c40c4ce1ef846f064f892762f41a Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Mon, 19 Jun 2017 16:49:06 +0100 Subject: [PATCH 3/8] test_executor: Add self-tests for Executor class --- tests/lisa/test_executor.py | 82 +++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 tests/lisa/test_executor.py diff --git a/tests/lisa/test_executor.py b/tests/lisa/test_executor.py new file mode 100644 index 000000000..58f2f9e73 --- /dev/null +++ b/tests/lisa/test_executor.py @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (C) 2017, ARM Limited and contributors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import logging +import shutil +import os +from unittest import TestCase + +from env import TestEnv +from executor import Executor + +class SetUpTarget(TestCase): + @classmethod + def setUpClass(cls): + cls._log = logging.getLogger('TestExecutor') + + def setUp(self): + self.res_dir='test_{}'.format(self.__class__.__name__) + self.te = TestEnv( + target_conf={'platform': 'host'}, + test_conf={'results_dir': self.res_dir}, + force_new=True) + +class TestMagicSmoke(SetUpTarget): + def test_files_created(self): + """Test that we can run experiments and get output files""" + conf_name = 'myconf' + wl_name = 'mywl' + + results_dir = os.path.join(self.te.LISA_HOME, 'results', self.res_dir, + 'rtapp:{}:{}'.format(conf_name, wl_name)) + if os.path.isdir(results_dir): + shutil.rmtree(results_dir) + + experiments_conf = { + 'confs': [{ + 'tag': conf_name + }], + "wloads" : { + wl_name : { + "type" : "rt-app", + "conf" : { + "class" : "profile", + "params" : { + "mytask" : { + "kind" : "Periodic", + "params" : { + "duty_cycle_pct": 10, + "duration_s": 0.2, + }, + }, + }, + }, + }, + }, + } + + executor = Executor(self.te, experiments_conf) + executor.run() + + self.assertTrue( + os.path.isdir(results_dir), + 'Expected to find a directory at {}'.format(results_dir)) + + result_1_dir = os.path.join(results_dir, '1') + self.assertTrue( + os.path.isdir(result_1_dir), + 'Expected to find a directory at {}'.format(result_1_dir)) -- GitLab From 9b93142b7b2256bad19e3020d4831440125815bc Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Thu, 20 Apr 2017 19:15:17 +0100 Subject: [PATCH 4/8] executor: Allow providing Workload object directly The Executor class currently requires you to use a dictionary grammar to configure the workloads you want to run. This is not necessary when you are using Python interactively and have direct access to the wlgen API. With this commit, Executor gains the ability for the "wloads" field in the experiments_conf to map names directly to workload objects. A very simple test case is added to exercise at least one code path using this new feature. --- libs/utils/executor.py | 11 ++++++----- tests/lisa/test_executor.py | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/libs/utils/executor.py b/libs/utils/executor.py index 7eb27dd62..529ce87bf 100644 --- a/libs/utils/executor.py +++ b/libs/utils/executor.py @@ -243,10 +243,6 @@ class Executor(): self._log.info('Results will be collected under:') self._log.info(' %s', self.te.res_dir) - if any(wl['type'] == 'rt-app' - for wl in self._experiments_conf['wloads'].values()): - self._log.info('rt-app workloads found, installing tool on target') - self.te.install_tools(['rt-app']) def run(self): self._print_section('Experiments execution') @@ -641,7 +637,12 @@ class Executor(): # Configure the test workload wlspec = self._experiments_conf['wloads'][wl_idx] - wload = self._wload_conf(wl_idx, wlspec) + if type(wlspec) == dict: + wload = self._wload_conf(wl_idx, wlspec) + else: + wload = wlspec + + self.te.install_tools([wload.executor]) # Keep track of platform configuration test_dir = '{}/{}:{}:{}'\ diff --git a/tests/lisa/test_executor.py b/tests/lisa/test_executor.py index 58f2f9e73..c675c89d7 100644 --- a/tests/lisa/test_executor.py +++ b/tests/lisa/test_executor.py @@ -22,6 +22,7 @@ from unittest import TestCase from env import TestEnv from executor import Executor +from wlgen import RTA, Periodic class SetUpTarget(TestCase): @classmethod @@ -80,3 +81,39 @@ class TestMagicSmoke(SetUpTarget): self.assertTrue( os.path.isdir(result_1_dir), 'Expected to find a directory at {}'.format(result_1_dir)) + +class TestDirectObject(SetUpTarget): + """Test that Executor can be configured directly using Workload objects""" + def test_files_created(self): + """Test that we can run experiments and get output files""" + conf_name = 'myconf' + wl_name = 'mywl' + + results_dir = os.path.join(self.te.LISA_HOME, 'results', self.res_dir, + 'rtapp:{}:{}'.format(conf_name, wl_name)) + if os.path.isdir(results_dir): + shutil.rmtree(results_dir) + + wload = RTA(self.te.target, wl_name) + wload.conf('profile', {'mytask': Periodic().get()}) + + experiments_conf = { + 'confs': [{ + 'tag': conf_name + }], + "wloads" : { + wl_name : wload + } + } + + executor = Executor(self.te, experiments_conf) + executor.run() + + self.assertTrue( + os.path.isdir(results_dir), + 'Expected to find a directory at {}'.format(results_dir)) + + result_1_dir = os.path.join(results_dir, '1') + self.assertTrue( + os.path.isdir(result_1_dir), + 'Expected to find a directory at {}'.format(result_1_dir)) -- GitLab From 1e87ba88bd3a3ce7d4d2f4143fab663019c1c1ac Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Mon, 19 Jun 2017 19:21:50 +0100 Subject: [PATCH 5/8] env: return rtapp-calib from configuration even if rt-app not listed in tools This means that if the user forgets to specify "rt-app" in the "tools" field of the test_conf, they can still later construct rt-app workloads and use the TestEnv.calibration method to access the rtapp-calib they listed in their target config. --- libs/utils/env.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/libs/utils/env.py b/libs/utils/env.py index fef587c9d..9697e8a8c 100644 --- a/libs/utils/env.py +++ b/libs/utils/env.py @@ -764,7 +764,13 @@ class TestEnv(ShareState): force=False and we have not installed rt-app. """ - if not force and self._calib: + if not force: + if not self._calib and 'rtapp-calib' in self.conf: + self._log.warning('Using configuration provided RTApp calibration') + self._calib = { + int(key): int(value) + for key, value in self.conf['rtapp-calib'].items() + } return self._calib required = force or 'rt-app' in self.__installed_tools @@ -773,15 +779,8 @@ class TestEnv(ShareState): self._log.debug('No RT-App workloads, skipping calibration') return - if not force and 'rtapp-calib' in self.conf: - self._log.warning('Using configuration provided RTApp calibration') - self._calib = { - int(key): int(value) - for key, value in self.conf['rtapp-calib'].items() - } - else: - self._log.info('Calibrating RTApp...') - self._calib = RTA.calibrate(self.target) + self._log.info('Calibrating RTApp...') + self._calib = RTA.calibrate(self.target) self._log.info('Using RT-App calibration values:') self._log.info(' %s', -- GitLab From 0ad234188dbc281e2b70158febcfba80e0d650ff Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Wed, 19 Jul 2017 12:20:45 +0100 Subject: [PATCH 6/8] rta: Allow providing task objects directly in profile conf You currently have to call the `get` method on all the tasks you provide in an RTA profile configuration: rta_wload.conf('profile', {'my_task': Profile().get()}) Have RTA automatically do this for you, so you can provide the task objects directly: rta_wload.conf('profile', {'my_task': Profile()}) --- libs/wlgen/wlgen/rta.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libs/wlgen/wlgen/rta.py b/libs/wlgen/wlgen/rta.py index 496435e0f..8e9c9277f 100644 --- a/libs/wlgen/wlgen/rta.py +++ b/libs/wlgen/wlgen/rta.py @@ -385,6 +385,9 @@ class RTA(Workload): for tid in sorted(self.params['profile'].keys()): task = self.params['profile'][tid] + if isinstance(task, RTATask): + task = task.get() + # Initialize task configuration task_conf = {} -- GitLab From cb290c8473b1c39b99be23c5a0fdef0fcec3b761 Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Wed, 19 Jul 2017 12:23:15 +0100 Subject: [PATCH 7/8] wlgen: Allow specifying workload configurations in constructor You currently have to construct objects and then call `conf` on them, like this: wl = RTA(target, 'my_wl') wl.conf(kind='profile', ) In order to make client code more declarative and hopefully more readable, add parameters to the constructors, so that the above can be done with: wl = RTA(target, 'my_wl', profile=) --- libs/wlgen/wlgen/rta.py | 7 ++++--- libs/wlgen/wlgen/workload.py | 13 ++++++++++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/libs/wlgen/wlgen/rta.py b/libs/wlgen/wlgen/rta.py index 8e9c9277f..04ce176b0 100644 --- a/libs/wlgen/wlgen/rta.py +++ b/libs/wlgen/wlgen/rta.py @@ -50,7 +50,8 @@ class RTA(Workload): def __init__(self, target, name, - calibration=None): + calibration=None, + *args, **kwargs): """ :param target: Devlib target to run workload on. :param name: Human-readable name for the workload. @@ -67,8 +68,6 @@ class RTA(Workload): # TODO: Assume rt-app is pre-installed on target # self.target.setup('rt-app') - super(RTA, self).__init__(target, name) - # rt-app executor self.wtype = 'rtapp' self.executor = 'rt-app' @@ -81,6 +80,8 @@ class RTA(Workload): self.rta_conf = None self.test_label = None + super(RTA, self).__init__(target, name, *args, **kwargs) + # Setup RTA callbacks self.setCallback('postrun', self.__postrun) diff --git a/libs/wlgen/wlgen/workload.py b/libs/wlgen/wlgen/workload.py index 5e3cb6c04..33330b8c1 100644 --- a/libs/wlgen/wlgen/workload.py +++ b/libs/wlgen/wlgen/workload.py @@ -41,7 +41,13 @@ class Workload(object): def __init__(self, target, - name): + name, + profile=None, + custom_conf=None, + *args, **kwargs): + + if custom_conf and profile_conf: + raise ValueError('Provide only one of custom_conf and profile_conf') # Target device confguration self.target = target @@ -96,6 +102,11 @@ class Workload(object): self._log.info('Setup new workload %s', self.name) + if profile: + self.conf('profile', profile, *args, **kwargs) + elif custom_conf: + self.conf('custom', custom_conf, *args, **kwargs) + def __callback(self, step, **kwords): if step not in self.steps.keys(): raise ValueError('Callbacks for [%s] step not supported', step) -- GitLab From 186f0dee7ca46f1a75d0fc0ad6e526fdb64367eb Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Thu, 20 Jul 2017 17:00:12 +0100 Subject: [PATCH 8/8] env: Fix rt-app calibration when force=False This currently requires force to be set to True to enable actually triggering rt-app calibration. Fix that be re-ordering the condions. --- libs/utils/env.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/libs/utils/env.py b/libs/utils/env.py index 9697e8a8c..19b1bec6e 100644 --- a/libs/utils/env.py +++ b/libs/utils/env.py @@ -764,7 +764,7 @@ class TestEnv(ShareState): force=False and we have not installed rt-app. """ - if not force: + if not force and 'rt-app' not in self.__installed_tools: if not self._calib and 'rtapp-calib' in self.conf: self._log.warning('Using configuration provided RTApp calibration') self._calib = { @@ -773,12 +773,6 @@ class TestEnv(ShareState): } return self._calib - required = force or 'rt-app' in self.__installed_tools - - if not required: - self._log.debug('No RT-App workloads, skipping calibration') - return - self._log.info('Calibrating RTApp...') self._calib = RTA.calibrate(self.target) -- GitLab