提交 b9cc7abe 编写于 作者: C Cleber Rosa

Job Script Plugin: allow user scripts to be run pre/post job

This script introduces a pair of directories that will be searched
for executable files, to be run before and after the actual execution
of a job.
Signed-off-by: NCleber Rosa <crosa@redhat.com>
上级 6ea85516
......@@ -7,7 +7,7 @@
Summary: Avocado Test Framework
Name: avocado
Version: 0.34.0
Release: 0%{?dist}
Release: 1%{?dist}
License: GPLv2
Group: Development/Tools
URL: http://avocado-framework.github.io/
......@@ -61,12 +61,16 @@ selftests/run
%dir /etc/avocado
%dir /etc/avocado/conf.d
%dir /etc/avocado/sysinfo
%dir /etc/avocado/scripts/job/pre.d
%dir /etc/avocado/scripts/job/post.d
%config(noreplace)/etc/avocado/avocado.conf
%config(noreplace)/etc/avocado/conf.d/README
%config(noreplace)/etc/avocado/conf.d/gdb.conf
%config(noreplace)/etc/avocado/sysinfo/commands
%config(noreplace)/etc/avocado/sysinfo/files
%config(noreplace)/etc/avocado/sysinfo/profilers
%config(noreplace)/etc/avocado/scripts/job/pre.d/README
%config(noreplace)/etc/avocado/scripts/job/post.d/README
%{python_sitelib}/avocado*
%{_bindir}/avocado
%{_bindir}/avocado-rest-client
......@@ -110,6 +114,9 @@ examples of how to write tests on your own.
%{_datadir}/avocado/wrappers
%changelog
* Thu Apr 14 2016 Cleber Rosa <cleber@redhat.com> - 0.34.0-1
- Added job pre/post scripts directories
* Mon Mar 21 2016 Cleber Rosa <cleber@redhat.com> - 0.34.0-0
- New upstream release 0.34.0
......
import os
import logging
from avocado.utils import process
from avocado.core.settings import settings
from avocado.plugins.base import JobPre, JobPost
CONFIG_SECTION = 'plugins.jobscripts'
class JobScripts(JobPre, JobPost):
name = 'jobscripts'
description = 'Runs scripts before/after the job is run'
def __init__(self):
self.log = logging.getLogger("avocado.app")
self.warn_non_existing_dir = settings.get_value(section=CONFIG_SECTION,
key="warn_non_existing_dir",
key_type=bool,
default=False)
self.warn_non_zero_status = settings.get_value(section=CONFIG_SECTION,
key="warn_non_zero_status",
key_type=bool,
default=True)
def _run_scripts(self, kind, scripts_dir, job):
if not os.path.isdir(scripts_dir):
if self.warn_non_existing_dir:
self.log.error("Directory configured to hold %s-job scripts "
"has not been found: %s", kind, scripts_dir)
return
dir_list = os.listdir(scripts_dir)
scripts = [os.path.join(scripts_dir, f) for f in dir_list]
scripts = [f for f in scripts
if os.access(f, os.R_OK | os.X_OK)]
scripts.sort()
if not scripts:
return
env = self._job_to_environment_variables(job)
for script in scripts:
result = process.run(script, ignore_status=True, env=env)
if (result.exit_status != 0) and self.warn_non_zero_status:
self.log.error('%s job script "%s" exited with status "%i"',
kind.capitalize(), script, result.exit_status)
@staticmethod
def _job_to_environment_variables(job):
env = {}
env['AVOCADO_JOB_UNIQUE_ID'] = job.unique_id
env['AVOCADO_JOB_STATUS'] = job.status
if job.logdir is not None:
env['AVOCADO_JOB_LOGDIR'] = job.logdir
return env
def pre(self, job):
path = settings.get_value(section=CONFIG_SECTION,
key="pre", key_type=str,
default="/etc/avocado/scripts/job/pre.d/")
self._run_scripts('pre', path, job)
def post(self, job):
path = settings.get_value(section=CONFIG_SECTION,
key="post", key_type=str,
default="/etc/avocado/scripts/job/post.d/")
self._run_scripts('post', path, job)
......@@ -258,6 +258,72 @@ The instances should have:
.. [#f1] Avocado plugins can introduce additional test types.
Job Pre and Post Scripts
========================
Avocado ships with a plugin (installed by default) that allows running
scripts before and after the actual execution of Jobs. A user can be
sure that, when a given "pre" script is run, no test in that job has
been run, and when the "post" scripts are run, all the tests in a
given job have already finished running.
Configuration
-------------
By default, the script directory location is::
/etc/avocado/scripts/job
Inside that directory, that is a directory for pre-job scripts::
/etc/avocado/scripts/job/pre.d
And for post-job scripts::
/etc/avocado/scripts/job/post.d
All the configuration about the Pre/Post Job Scripts are placed under
the ``avocado.plugins.jobscripts`` config section. To change the
location for the pre-job scripts, your configuration should look
something like this::
[plugins.jobscripts]
pre = /my/custom/directory/for/pre/job/scripts/
Accordingly, to change the location for the post-job scripts, your
configuration should look something like this::
[plugins.jobscripts]
post = /my/custom/directory/for/post/scripts/
A couple of other configuration options are available under the same
section:
* ``warn_non_existing_dir``: gives warnings if the configured (or
default) directory set for either pre or post scripts do not exist
* ``warn_non_zero_status``: gives warnings if a given script (either
pre or post) exits with non-zero status
Script Execution Environment
----------------------------
All scripts are run in separate process with some environment
variables set. These can be used in your scripts in any way you wish:
* ``AVOCADO_JOB_UNIQUE_ID``: the unique `job-id`_.
* ``AVOCADO_JOB_STATUS``: the current status of the job.
* ``AVOCADO_JOB_LOGDIR``: the filesystem location that holds the logs
and various other files for a given job run.
Note: Even though these variables should all be set, it's a good
practice for scripts to check if they're set before using their
values. This may prevent unintended actions such as writing to the
current working directory instead of to the ``AVOCADO_JOB_LOGDIR`` if
this is not set.
Finally, any failures in the Pre/Post scripts will not alter the
status of the corresponding jobs.
Job Cleanup
===========
......
[plugins.jobscripts]
# Directory with scripts to be executed before a job is run
pre = /etc/avocado/scripts/job/pre.d/
# Directory with scripts to be executed after a job is run
post = /etc/avocado/scripts/job/post.d/
# Warn if configured (or default) directory does not exist
warn_non_existing_dir = False
# Warn if any script run return non-zero status
warn_non_zero_status = True
Put your post-job scripts here. They need to be readable and executable by
the Avocado user running the jobs. The order of execution is based on their
file names. If order is important, use a prefix, such as 001-myscript,
002-otherscript, etc.
Put your pre-job scripts here. They need to be readable and executable by
the Avocado user running the jobs. The order of execution is based on their
file names. If order is important, use a prefix, such as 001-myscript,
002-otherscript, etc.
import os
import shutil
import sys
import tempfile
if sys.version_info[:2] == (2, 6):
import unittest2 as unittest
else:
import unittest
from avocado.core import exit_codes
from avocado.utils import process
from avocado.utils import script
SCRIPT_PRE_TOUCH = """#!/bin/sh -e
touch %s"""
TEST_CHECK_TOUCH = """#!/bin/sh -e
test -f %s"""
SCRIPT_POST_RM = """#!/bin/sh -e
rm %s"""
SCRIPT_PRE_POST_CFG = """[plugins.jobscripts]
pre = %s
post = %s
warn_non_existing_dir = True
warn_non_zero_status = True"""
SCRIPT_NON_EXISTING_DIR_CFG = """[plugins.jobscripts]
pre = %s
warn_non_existing_dir = True
warn_non_zero_status = False"""
SCRIPT_NON_ZERO_STATUS = """#!/bin/sh
exit 1"""
SCRIPT_NON_ZERO_CFG = """[plugins.jobscripts]
pre = %s
warn_non_existing_dir = False
warn_non_zero_status = True"""
class JobScriptsTest(unittest.TestCase):
def setUp(self):
self.tmpdir = tempfile.mkdtemp(prefix='avocado_' + __name__)
self.pre_dir = os.path.join(self.tmpdir, 'pre.d')
os.mkdir(self.pre_dir)
self.post_dir = os.path.join(self.tmpdir, 'post.d')
os.mkdir(self.post_dir)
def test_pre_post(self):
"""
Runs both pre and post scripts and makes sure both execute properly
"""
touch_script = script.Script(os.path.join(self.pre_dir,
'touch.sh'),
SCRIPT_PRE_TOUCH)
touch_script.save()
test_check_touch = script.Script(os.path.join(self.tmpdir,
'check_touch.sh'),
TEST_CHECK_TOUCH)
test_check_touch.save()
rm_script = script.Script(os.path.join(self.post_dir,
'rm.sh'),
SCRIPT_POST_RM)
rm_script.save()
config = script.TemporaryScript("pre_post.conf",
SCRIPT_PRE_POST_CFG % (self.pre_dir,
self.post_dir))
with config:
cmd = './scripts/avocado --config %s run %s' % (config,
test_check_touch)
result = process.run(cmd)
# Pre/Post scripts failures do not (currently?) alter the exit status
self.assertEqual(result.exit_status, exit_codes.AVOCADO_ALL_OK)
self.assertNotIn('Pre job script "%s" exited with status "1"' % touch_script,
result.stderr)
self.assertNotIn('Post job script "%s" exited with status "1"' % rm_script,
result.stderr)
def test_status_non_zero(self):
"""
Checks warning when script returns non-zero status
"""
non_zero_script = script.Script(os.path.join(self.pre_dir,
'non_zero.sh'),
SCRIPT_NON_ZERO_STATUS)
non_zero_script.save()
config = script.TemporaryScript("non_zero.conf",
SCRIPT_NON_ZERO_CFG % self.pre_dir)
with config:
cmd = './scripts/avocado --config %s run passtest' % config
result = process.run(cmd)
# Pre/Post scripts failures do not (currently?) alter the exit status
self.assertEqual(result.exit_status, exit_codes.AVOCADO_ALL_OK)
self.assertEqual('Pre job script "%s" exited with status "1"\n' % non_zero_script,
result.stderr)
def test_non_existing_dir(self):
"""
Checks warning with non existing pre dir
"""
non_zero_script = script.Script(os.path.join(self.pre_dir,
'non_zero.sh'),
SCRIPT_NON_ZERO_STATUS)
non_zero_script.save()
self.pre_dir = '/non/existing/dir'
config = script.TemporaryScript("non_existing_dir.conf",
SCRIPT_NON_EXISTING_DIR_CFG % self.pre_dir)
with config:
cmd = './scripts/avocado --config %s run passtest' % config
result = process.run(cmd)
# Pre/Post scripts failures do not (currently?) alter the exit status
self.assertEqual(result.exit_status, exit_codes.AVOCADO_ALL_OK)
self.assertIn('-job scripts has not been found', result.stderr)
self.assertNotIn('Pre job script "%s" exited with status "1"' % non_zero_script,
result.stderr)
def tearDown(self):
shutil.rmtree(self.tmpdir)
if __name__ == '__main__':
unittest.main()
......@@ -64,6 +64,10 @@ def get_data_files():
data_files += [(get_dir(['etc', 'avocado', 'sysinfo']),
['etc/avocado/sysinfo/commands', 'etc/avocado/sysinfo/files',
'etc/avocado/sysinfo/profilers'])]
data_files += [(get_dir(['etc', 'avocado', 'scripts', 'job', 'pre.d']),
['etc/avocado/scripts/job/pre.d/README'])]
data_files += [(get_dir(['etc', 'avocado', 'scripts', 'job', 'post.d']),
['etc/avocado/scripts/job/post.d/README'])]
data_files += [(get_tests_dir(), glob.glob('examples/tests/*.py'))]
for data_dir in glob.glob('examples/tests/*.data'):
fmt_str = '%s/*' % data_dir
......@@ -146,7 +150,10 @@ if __name__ == '__main__':
'run = avocado.plugins.run:Run',
'sysinfo = avocado.plugins.sysinfo:SysInfo',
'plugins = avocado.plugins.plugins:Plugins',
]
],
'avocado.plugins.job.prepost': [
'jobscripts = avocado.plugins.jobscripts:JobScripts',
],
},
zip_safe=False,
test_suite='selftests')
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册