Removed tasklib directory
Looks like we don't need it any more. Besides there is a separate repository https://github.com/stackforge/fuel-tasklib Change-Id: I25b7f6d4ab9d6e3ff1d77d262913f4697557528e
This commit is contained in:
parent
38d0e7db78
commit
53801c201b
27
run_tests.sh
27
run_tests.sh
|
@ -21,8 +21,6 @@ function usage {
|
||||||
echo "Run Fuel-Web test suite(s)"
|
echo "Run Fuel-Web test suite(s)"
|
||||||
echo ""
|
echo ""
|
||||||
echo " -h, --help Print this usage message"
|
echo " -h, --help Print this usage message"
|
||||||
echo " -k, --tasklib Run tasklib unit and functional tests"
|
|
||||||
echo " -K, --no-tasklib Don't run tasklib unit and functional tests"
|
|
||||||
echo " -n, --nailgun Run NAILGUN unit/integration tests"
|
echo " -n, --nailgun Run NAILGUN unit/integration tests"
|
||||||
echo " -N, --no-nailgun Don't run NAILGUN unit/integration tests"
|
echo " -N, --no-nailgun Don't run NAILGUN unit/integration tests"
|
||||||
echo " -x, --performance Run NAILGUN performance tests"
|
echo " -x, --performance Run NAILGUN performance tests"
|
||||||
|
@ -56,8 +54,6 @@ function process_options {
|
||||||
-n|--nailgun) nailgun_tests=1;;
|
-n|--nailgun) nailgun_tests=1;;
|
||||||
-N|--no-nailgun) no_nailgun_tests=1;;
|
-N|--no-nailgun) no_nailgun_tests=1;;
|
||||||
-x|--performance) performance_tests=1;;
|
-x|--performance) performance_tests=1;;
|
||||||
-k|--tasklib) tasklib_tests=1;;
|
|
||||||
-K|--no-tasklib) no_tasklib_tests=1;;
|
|
||||||
-u|--upgrade) upgrade_system=1;;
|
-u|--upgrade) upgrade_system=1;;
|
||||||
-U|--no-upgrade) no_upgrade_system=1;;
|
-U|--no-upgrade) no_upgrade_system=1;;
|
||||||
-s|--shotgun) shotgun_tests=1;;
|
-s|--shotgun) shotgun_tests=1;;
|
||||||
|
@ -126,8 +122,6 @@ no_ui_unit_tests=0
|
||||||
ui_func_tests=0
|
ui_func_tests=0
|
||||||
no_ui_func_tests=0
|
no_ui_func_tests=0
|
||||||
certain_tests=0
|
certain_tests=0
|
||||||
tasklib_tests=0
|
|
||||||
no_tasklib_tests=0
|
|
||||||
ui_func_selenium_tests=0
|
ui_func_selenium_tests=0
|
||||||
|
|
||||||
function run_tests {
|
function run_tests {
|
||||||
|
@ -154,7 +148,6 @@ function run_tests {
|
||||||
# Enable all tests if none was specified skipping all explicitly disabled tests.
|
# Enable all tests if none was specified skipping all explicitly disabled tests.
|
||||||
if [[ $nailgun_tests -eq 0 && \
|
if [[ $nailgun_tests -eq 0 && \
|
||||||
$performance_tests -eq 0 && \
|
$performance_tests -eq 0 && \
|
||||||
$tasklib_tests -eq 0 && \
|
|
||||||
$ui_lint_checks -eq 0 && \
|
$ui_lint_checks -eq 0 && \
|
||||||
$ui_unit_tests -eq 0 && \
|
$ui_unit_tests -eq 0 && \
|
||||||
$ui_func_tests -eq 0 && \
|
$ui_func_tests -eq 0 && \
|
||||||
|
@ -164,7 +157,6 @@ function run_tests {
|
||||||
$flake8_checks -eq 0 ]]; then
|
$flake8_checks -eq 0 ]]; then
|
||||||
|
|
||||||
if [ $no_nailgun_tests -ne 1 ]; then nailgun_tests=1; fi
|
if [ $no_nailgun_tests -ne 1 ]; then nailgun_tests=1; fi
|
||||||
if [ $no_tasklib_tests -ne 1 ]; then tasklib_tests=1; fi
|
|
||||||
if [ $no_ui_lint_checks -ne 1 ]; then ui_lint_checks=1; fi
|
if [ $no_ui_lint_checks -ne 1 ]; then ui_lint_checks=1; fi
|
||||||
if [ $no_ui_unit_tests -ne 1 ]; then ui_unit_tests=1; fi
|
if [ $no_ui_unit_tests -ne 1 ]; then ui_unit_tests=1; fi
|
||||||
if [ $no_ui_func_tests -ne 1 ]; then ui_func_tests=1; fi
|
if [ $no_ui_func_tests -ne 1 ]; then ui_func_tests=1; fi
|
||||||
|
@ -184,11 +176,6 @@ function run_tests {
|
||||||
run_nailgun_tests || errors+=" nailgun_tests"
|
run_nailgun_tests || errors+=" nailgun_tests"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $tasklib_tests -eq 1 ]; then
|
|
||||||
echo "Starting Tasklib tests"
|
|
||||||
run_tasklib_tests || errors+=" tasklib tests"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ $ui_lint_checks -eq 1 ]; then
|
if [ $ui_lint_checks -eq 1 ]; then
|
||||||
echo "Starting UI lint checks..."
|
echo "Starting UI lint checks..."
|
||||||
run_lint_ui || errors+=" ui_lint_checks"
|
run_lint_ui || errors+=" ui_lint_checks"
|
||||||
|
@ -444,19 +431,6 @@ function run_shotgun_tests {
|
||||||
return $result
|
return $result
|
||||||
}
|
}
|
||||||
|
|
||||||
function run_tasklib_tests {
|
|
||||||
local result=0
|
|
||||||
|
|
||||||
pushd $ROOT/tasklib >> /dev/null
|
|
||||||
|
|
||||||
# run tests
|
|
||||||
tox -epy26 || result=1
|
|
||||||
|
|
||||||
popd >> /dev/null
|
|
||||||
|
|
||||||
return $result
|
|
||||||
}
|
|
||||||
|
|
||||||
function run_flake8_subproject {
|
function run_flake8_subproject {
|
||||||
local DIRECTORY=$1
|
local DIRECTORY=$1
|
||||||
local result=0
|
local result=0
|
||||||
|
@ -477,7 +451,6 @@ function run_flake8_subproject {
|
||||||
function run_flake8 {
|
function run_flake8 {
|
||||||
local result=0
|
local result=0
|
||||||
run_flake8_subproject nailgun && \
|
run_flake8_subproject nailgun && \
|
||||||
run_flake8_subproject tasklib && \
|
|
||||||
run_flake8_subproject fuelmenu && \
|
run_flake8_subproject fuelmenu && \
|
||||||
run_flake8_subproject network_checker && \
|
run_flake8_subproject network_checker && \
|
||||||
run_flake8_subproject fuel_upgrade_system/fuel_upgrade && \
|
run_flake8_subproject fuel_upgrade_system/fuel_upgrade && \
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
tmp/*
|
|
||||||
*.egg-info
|
|
||||||
*.pyc
|
|
||||||
tasklib/tests/functional/tmp/
|
|
||||||
tasklib.log
|
|
|
@ -1 +0,0 @@
|
||||||
include *requirements.txt
|
|
|
@ -1,72 +0,0 @@
|
||||||
Tasklib
|
|
||||||
=======
|
|
||||||
|
|
||||||
Tasklib is mediator between different configuration management providers
|
|
||||||
and orchestration mechanism in Fuel.
|
|
||||||
|
|
||||||
It will try to cover next areas:
|
|
||||||
- part of the plugable fuel architecture
|
|
||||||
See tasklib/tasklib/tests/functional for detailed descriptionof
|
|
||||||
how tasklib plugability will work
|
|
||||||
- Control mechanism for tasks in fuel
|
|
||||||
To support different types of workflow we will provide
|
|
||||||
ability to terminate, list all running, stop/pause task
|
|
||||||
- Abstraction layer between tasks and orchestration, which will allow
|
|
||||||
easier development and debuging of tasks
|
|
||||||
- General reporting solution for tasks
|
|
||||||
|
|
||||||
Executions drivers
|
|
||||||
==================
|
|
||||||
- puppet
|
|
||||||
- exec
|
|
||||||
|
|
||||||
Puppet
|
|
||||||
--------
|
|
||||||
Puppet executor supports general metadata for running puppet manifests.
|
|
||||||
Example of such metadata (task.yaml):
|
|
||||||
|
|
||||||
type: puppet - required
|
|
||||||
puppet_manifest: file.pp - default is site.pp
|
|
||||||
puppet_moduels: /etc/puppet/modules
|
|
||||||
puppet_options: --debug
|
|
||||||
|
|
||||||
All defaults you can find in:
|
|
||||||
>> taskcmd conf
|
|
||||||
|
|
||||||
It works next way:
|
|
||||||
After task.yaml is found - executor will look for puppet_manifest
|
|
||||||
and run:
|
|
||||||
puppet apply --modulepath=/etc/puppet/modules file.pp
|
|
||||||
with additional options you will provide
|
|
||||||
|
|
||||||
Exec
|
|
||||||
-----
|
|
||||||
|
|
||||||
type: exec - required
|
|
||||||
cmd: echo 12 - required
|
|
||||||
|
|
||||||
will execute any cmd provided as subprocess
|
|
||||||
|
|
||||||
EXAMPLES:
|
|
||||||
=========
|
|
||||||
|
|
||||||
taskcmd -c tasklib/tests/functional/conf.yaml conf
|
|
||||||
|
|
||||||
taskcmd -c tasklib/tests/functional/conf.yaml list
|
|
||||||
|
|
||||||
taskcmd -c tasklib/tests/functional/conf.yaml daemon puppet/sleep
|
|
||||||
taskcmd -c tasklib/tests/functional/conf.yaml status puppet/sleep
|
|
||||||
|
|
||||||
taskcmd -c tasklib/tests/functional/conf.yaml run puppet/cmd
|
|
||||||
|
|
||||||
taskcmd -c tasklib/tests/functional/conf.yaml run puppet/invalid
|
|
||||||
|
|
||||||
HOW TO RUN TESTS:
|
|
||||||
==================
|
|
||||||
python setup.py develop
|
|
||||||
pip install -r test-requires.txt
|
|
||||||
|
|
||||||
nosetests tasklib/tests
|
|
||||||
|
|
||||||
For some functional tests installed puppet is required,
|
|
||||||
so if you dont have puppet - they will be skipped
|
|
|
@ -1,3 +0,0 @@
|
||||||
argparse
|
|
||||||
daemonize
|
|
||||||
pyyaml
|
|
|
@ -1,55 +0,0 @@
|
||||||
# Copyright 2014 Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# 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 os
|
|
||||||
import setuptools
|
|
||||||
|
|
||||||
|
|
||||||
def requirements():
|
|
||||||
dir_path = os.path.dirname(os.path.realpath(__file__))
|
|
||||||
requirements = []
|
|
||||||
with open('{0}/requirements.txt'.format(dir_path), 'r') as reqs:
|
|
||||||
requirements = reqs.readlines()
|
|
||||||
return requirements
|
|
||||||
|
|
||||||
|
|
||||||
name = 'tasklib'
|
|
||||||
version = '7.0.0'
|
|
||||||
|
|
||||||
|
|
||||||
setuptools.setup(
|
|
||||||
name=name,
|
|
||||||
version=version,
|
|
||||||
description='Tasklib package',
|
|
||||||
long_description="""Tasklib is intended to be mediator between different
|
|
||||||
configuration management solutions and orchestrator.
|
|
||||||
This is required to support plugable tasks/actions with good
|
|
||||||
amount of control, such as stop/pause/poll state.
|
|
||||||
""",
|
|
||||||
classifiers=[
|
|
||||||
"Development Status :: 4 - Beta",
|
|
||||||
"Programming Language :: Python",
|
|
||||||
],
|
|
||||||
author='Mirantis Inc.',
|
|
||||||
author_email='product@mirantis.com',
|
|
||||||
url='http://mirantis.com',
|
|
||||||
keywords='tasklib mirantis',
|
|
||||||
packages=setuptools.find_packages(),
|
|
||||||
zip_safe=False,
|
|
||||||
install_requires=requirements(),
|
|
||||||
include_package_data=True,
|
|
||||||
entry_points={
|
|
||||||
'console_scripts': [
|
|
||||||
'taskcmd = tasklib.cli:main',
|
|
||||||
]})
|
|
|
@ -1,13 +0,0 @@
|
||||||
# Copyright 2014 Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# 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.
|
|
|
@ -1,13 +0,0 @@
|
||||||
# Copyright 2014 Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# 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.
|
|
|
@ -1,35 +0,0 @@
|
||||||
# Copyright 2014 Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# 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
|
|
||||||
|
|
||||||
from tasklib import exceptions
|
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class Action(object):
|
|
||||||
|
|
||||||
def __init__(self, task, config):
|
|
||||||
self.task = task
|
|
||||||
self.config = config
|
|
||||||
log.debug('Init action with task %s', self.task.name)
|
|
||||||
|
|
||||||
def verify(self):
|
|
||||||
if 'type' not in self.task.metadata:
|
|
||||||
raise exceptions.NotValidMetadata()
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
raise NotImplementedError('Should be implemented by action driver.')
|
|
|
@ -1,44 +0,0 @@
|
||||||
# Copyright 2014 Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# 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
|
|
||||||
|
|
||||||
from tasklib.actions import action
|
|
||||||
from tasklib import exceptions
|
|
||||||
from tasklib import utils
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class ExecAction(action.Action):
|
|
||||||
|
|
||||||
def verify(self):
|
|
||||||
super(ExecAction, self).verify()
|
|
||||||
if 'cmd' not in self.task.metadata:
|
|
||||||
raise exceptions.NotValidMetadata()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def command(self):
|
|
||||||
return self.task.metadata['cmd']
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
log.debug('Running task %s with command %s',
|
|
||||||
self.task.name, self.command)
|
|
||||||
exit_code, stdout, stderr = utils.execute(self.command)
|
|
||||||
log.debug(
|
|
||||||
'Task %s with command %s\n returned code %s\n out %s err%s',
|
|
||||||
self.task.name, self.command, exit_code, stdout, stderr)
|
|
||||||
if exit_code != 0:
|
|
||||||
raise exceptions.Failed()
|
|
||||||
return exit_code
|
|
|
@ -1,68 +0,0 @@
|
||||||
# Copyright 2014 Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# 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 os
|
|
||||||
|
|
||||||
from tasklib.actions import action
|
|
||||||
from tasklib import exceptions
|
|
||||||
from tasklib import utils
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class PuppetAction(action.Action):
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
log.debug('Running puppet task %s with command %s',
|
|
||||||
self.task.name, self.command)
|
|
||||||
exit_code, stdout, stderr = utils.execute(self.command)
|
|
||||||
log.debug(
|
|
||||||
'Task %s with command %s\n returned code %s\n out %s err%s',
|
|
||||||
self.task.name, self.command, exit_code, stdout, stderr)
|
|
||||||
# 0 - no changes
|
|
||||||
# 2 - was some changes but successfull
|
|
||||||
# 4 - failures during transaction
|
|
||||||
# 6 - changes and failures
|
|
||||||
if exit_code not in [0, 2]:
|
|
||||||
raise exceptions.Failed()
|
|
||||||
return exit_code
|
|
||||||
|
|
||||||
@property
|
|
||||||
def manifest(self):
|
|
||||||
return (self.task.metadata.get('puppet_manifest') or
|
|
||||||
self.config['puppet_manifest'])
|
|
||||||
|
|
||||||
@property
|
|
||||||
def puppet_options(self):
|
|
||||||
if 'puppet_options' in self.task.metadata:
|
|
||||||
return self.task.metadata['puppet_options']
|
|
||||||
return self.config['puppet_options']
|
|
||||||
|
|
||||||
@property
|
|
||||||
def puppet_modules(self):
|
|
||||||
return (self.task.metadata.get('puppet_modules') or
|
|
||||||
self.config['puppet_modules'])
|
|
||||||
|
|
||||||
@property
|
|
||||||
def command(self):
|
|
||||||
cmd = ['puppet', 'apply', '--detailed-exitcodes']
|
|
||||||
if self.puppet_modules:
|
|
||||||
cmd.append('--modulepath={0}'.format(self.puppet_modules))
|
|
||||||
if self.puppet_options:
|
|
||||||
cmd.append(self.puppet_options)
|
|
||||||
if self.config['debug']:
|
|
||||||
cmd.append('--debug --verbose --evaltrace')
|
|
||||||
cmd.append(os.path.join(self.task.dir, self.manifest))
|
|
||||||
return ' '.join(cmd)
|
|
|
@ -1,94 +0,0 @@
|
||||||
# Copyright 2014 Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# 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 os
|
|
||||||
|
|
||||||
import daemonize
|
|
||||||
|
|
||||||
from tasklib import exceptions
|
|
||||||
from tasklib import task
|
|
||||||
from tasklib import utils
|
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class TaskAgent(object):
|
|
||||||
|
|
||||||
def __init__(self, task_name, config):
|
|
||||||
log.debug('Initializing task agent for task %s', task_name)
|
|
||||||
self.config = config
|
|
||||||
self.task = task.Task(task_name, self.config)
|
|
||||||
self.init_directories()
|
|
||||||
|
|
||||||
def init_directories(self):
|
|
||||||
utils.ensure_dir_created(self.config['pid_dir'])
|
|
||||||
utils.ensure_dir_created(self.config['report_dir'])
|
|
||||||
utils.ensure_dir_created(self.task.pid_dir)
|
|
||||||
utils.ensure_dir_created(self.task.report_dir)
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
try:
|
|
||||||
self.set_status(utils.STATUS.running.name)
|
|
||||||
result = self.task.run()
|
|
||||||
self.set_status(utils.STATUS.end.name)
|
|
||||||
return result
|
|
||||||
except exceptions.NotFound as exc:
|
|
||||||
log.warning('Cant find task %s with path %s',
|
|
||||||
self.task.name, self.task.file)
|
|
||||||
self.set_status(utils.STATUS.notfound.name)
|
|
||||||
except exceptions.Failed as exc:
|
|
||||||
log.error('Task %s failed with msg %s', self.task.name, exc.msg)
|
|
||||||
self.set_status(utils.STATUS.failed.name)
|
|
||||||
except Exception:
|
|
||||||
log.exception('Task %s erred', self.task.name)
|
|
||||||
self.set_status(utils.STATUS.error.name)
|
|
||||||
finally:
|
|
||||||
self.clean()
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return 'tasklib agent - {0}'.format(self.task.name)
|
|
||||||
|
|
||||||
def report(self):
|
|
||||||
return 'placeholder'
|
|
||||||
|
|
||||||
def status(self):
|
|
||||||
if not os.path.exists(self.task.status_file):
|
|
||||||
return utils.STATUS.notfound.name
|
|
||||||
with open(self.task.status_file) as f:
|
|
||||||
return f.read()
|
|
||||||
|
|
||||||
def set_status(self, status):
|
|
||||||
with open(self.task.status_file, 'w') as f:
|
|
||||||
f.write(status)
|
|
||||||
|
|
||||||
def code(self):
|
|
||||||
status = self.status()
|
|
||||||
return getattr(utils.STATUS, status).code
|
|
||||||
|
|
||||||
@property
|
|
||||||
def pid(self):
|
|
||||||
return os.path.join(self.task.pid_dir, 'run.pid')
|
|
||||||
|
|
||||||
def daemon(self):
|
|
||||||
log.debug('Daemonizing task %s with pidfile %s',
|
|
||||||
self.task.name, self.pid)
|
|
||||||
daemon = daemonize.Daemonize(
|
|
||||||
app=str(self), pid=self.pid, action=self.run)
|
|
||||||
daemon.start()
|
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
if os.path.exists(self.pid):
|
|
||||||
os.unlink(self.pid)
|
|
|
@ -1,115 +0,0 @@
|
||||||
# Copyright 2014 Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
"""Tasklib cmd interface
|
|
||||||
Exit Codes:
|
|
||||||
ended successfully - 0
|
|
||||||
running - 1
|
|
||||||
valid but failed - 2
|
|
||||||
unexpected error - 3
|
|
||||||
notfound such task - 4
|
|
||||||
"""
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import sys
|
|
||||||
import textwrap
|
|
||||||
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
from tasklib import agent
|
|
||||||
from tasklib import config
|
|
||||||
from tasklib import logger
|
|
||||||
from tasklib import task
|
|
||||||
from tasklib import utils
|
|
||||||
|
|
||||||
|
|
||||||
class CmdApi(object):
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.parser = argparse.ArgumentParser(
|
|
||||||
description=textwrap.dedent(__doc__),
|
|
||||||
formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
||||||
self.subparser = self.parser.add_subparsers(
|
|
||||||
title='actions',
|
|
||||||
description='Supported actions',
|
|
||||||
help='Provide of one valid actions')
|
|
||||||
self.config = config.Config()
|
|
||||||
self.register_options()
|
|
||||||
self.register_actions()
|
|
||||||
|
|
||||||
def register_options(self):
|
|
||||||
self.parser.add_argument(
|
|
||||||
'--config', '-c', dest='config', default=None,
|
|
||||||
help='Path to configuration file')
|
|
||||||
self.parser.add_argument(
|
|
||||||
'--debug', '-d', dest='debug', action='store_true', default=None)
|
|
||||||
|
|
||||||
def register_actions(self):
|
|
||||||
task_arg = [(('task',), {'type': str})]
|
|
||||||
self.register_parser('list')
|
|
||||||
self.register_parser('conf')
|
|
||||||
for name in ('run', 'daemon', 'report', 'status', 'show'):
|
|
||||||
self.register_parser(name, task_arg)
|
|
||||||
|
|
||||||
def register_parser(self, func_name, arguments=()):
|
|
||||||
parser = self.subparser.add_parser(func_name)
|
|
||||||
parser.set_defaults(func=getattr(self, func_name))
|
|
||||||
for args, kwargs in arguments:
|
|
||||||
parser.add_argument(*args, **kwargs)
|
|
||||||
|
|
||||||
def parse(self, args):
|
|
||||||
parsed = self.parser.parse_args(args)
|
|
||||||
if parsed.config:
|
|
||||||
self.config.update_from_file(parsed.config)
|
|
||||||
if parsed.debug is not None:
|
|
||||||
self.config['debug'] = parsed.debug
|
|
||||||
logger.setup_logging(self.config)
|
|
||||||
return parsed.func(parsed)
|
|
||||||
|
|
||||||
def list(self, args):
|
|
||||||
for task_dir in utils.find_all_tasks(self.config):
|
|
||||||
print(task.Task.task_from_dir(task_dir, self.config))
|
|
||||||
|
|
||||||
def show(self, args):
|
|
||||||
meta = task.Task(args.task, self.config).metadata
|
|
||||||
print(yaml.dump(meta, default_flow_style=False))
|
|
||||||
|
|
||||||
def run(self, args):
|
|
||||||
task_agent = agent.TaskAgent(args.task, self.config)
|
|
||||||
task_agent.run()
|
|
||||||
status = task_agent.status()
|
|
||||||
print(status)
|
|
||||||
return task_agent.code()
|
|
||||||
|
|
||||||
def daemon(self, args):
|
|
||||||
task_agent = agent.TaskAgent(args.task, self.config)
|
|
||||||
task_agent.daemon()
|
|
||||||
|
|
||||||
def report(self, args):
|
|
||||||
task_agent = agent.TaskAgent(args.task, self.config)
|
|
||||||
print(task_agent.report())
|
|
||||||
|
|
||||||
def status(self, args):
|
|
||||||
task_agent = agent.TaskAgent(args.task, self.config)
|
|
||||||
exit_code = task_agent.code()
|
|
||||||
print(task_agent.status())
|
|
||||||
return exit_code
|
|
||||||
|
|
||||||
def conf(self, args):
|
|
||||||
print(self.config)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
api = CmdApi()
|
|
||||||
exit_code = api.parse(sys.argv[1:])
|
|
||||||
exit(exit_code)
|
|
|
@ -1,57 +0,0 @@
|
||||||
# Copyright 2014 Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# 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 os
|
|
||||||
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
|
|
||||||
class Config(object):
|
|
||||||
|
|
||||||
def __init__(self, config_file=None):
|
|
||||||
self.curdir = os.getcwd()
|
|
||||||
self.config = self.default_config
|
|
||||||
if config_file:
|
|
||||||
self.update_from_file(config_file)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def default_config(self):
|
|
||||||
return {
|
|
||||||
'library_dir': '/etc/puppet/tasks',
|
|
||||||
'puppet_modules': '/etc/puppet/modules',
|
|
||||||
'puppet_options': ('--logdest syslog '
|
|
||||||
'--logdest /var/log/puppet.log'
|
|
||||||
'--trace --no-report'),
|
|
||||||
'report_dir': '/var/tmp/task_report',
|
|
||||||
'pid_dir': '/var/tmp/task_run',
|
|
||||||
'puppet_manifest': 'site.pp',
|
|
||||||
'status_file': 'status',
|
|
||||||
'debug': True,
|
|
||||||
'task_file': 'task.yaml',
|
|
||||||
'log_file': '/var/log/tasklib.log'}
|
|
||||||
|
|
||||||
def update_from_file(self, config_file):
|
|
||||||
if os.path.exists(config_file):
|
|
||||||
with open(config_file) as f:
|
|
||||||
loaded = yaml.load(f.read())
|
|
||||||
self.config.update(loaded)
|
|
||||||
|
|
||||||
def __getitem__(self, key):
|
|
||||||
return self.config.get(key, None)
|
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
|
||||||
self.config[key] = value
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return yaml.dump(self.config, default_flow_style=False)
|
|
|
@ -1,33 +0,0 @@
|
||||||
# Copyright 2014 Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
|
|
||||||
class TasklibException(Exception):
|
|
||||||
|
|
||||||
msg = 'Tasklib generic error'
|
|
||||||
|
|
||||||
|
|
||||||
class NotFound(TasklibException):
|
|
||||||
|
|
||||||
msg = 'No task with provided name'
|
|
||||||
|
|
||||||
|
|
||||||
class NotValidMetadata(TasklibException):
|
|
||||||
|
|
||||||
msg = 'Missing critical items in metadata'
|
|
||||||
|
|
||||||
|
|
||||||
class Failed(TasklibException):
|
|
||||||
|
|
||||||
msg = 'Task failed'
|
|
|
@ -1,37 +0,0 @@
|
||||||
# Copyright 2014 Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# 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 sys
|
|
||||||
|
|
||||||
|
|
||||||
def setup_logging(config):
|
|
||||||
logger = logging.getLogger()
|
|
||||||
logger.setLevel(logging.DEBUG)
|
|
||||||
formatter = logging.Formatter(
|
|
||||||
'%(asctime)s %(levelname)s %(process)d (%(module)s) %(message)s',
|
|
||||||
"%Y-%m-%d %H:%M:%S")
|
|
||||||
|
|
||||||
if sys.stdout.isatty():
|
|
||||||
stream_handler = logging.StreamHandler()
|
|
||||||
stream_handler.setLevel(logging.DEBUG)
|
|
||||||
stream_handler.setFormatter(formatter)
|
|
||||||
logger.addHandler(stream_handler)
|
|
||||||
|
|
||||||
if config['log_file']:
|
|
||||||
file_handler = logging.FileHandler(config['log_file'])
|
|
||||||
file_handler.setLevel(logging.DEBUG)
|
|
||||||
file_handler.setFormatter(formatter)
|
|
||||||
logger.addHandler(file_handler)
|
|
||||||
return logger
|
|
|
@ -1,79 +0,0 @@
|
||||||
# Copyright 2014 Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# 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 os
|
|
||||||
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
from tasklib.actions import exec_action
|
|
||||||
from tasklib.actions import puppet
|
|
||||||
from tasklib import exceptions
|
|
||||||
|
|
||||||
# use stevedore here
|
|
||||||
type_mapping = {'exec': exec_action.ExecAction,
|
|
||||||
'puppet': puppet.PuppetAction}
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class Task(object):
|
|
||||||
"""Unit of execution. Contains pre/post/run subtasks."""
|
|
||||||
|
|
||||||
def __init__(self, task_name, config):
|
|
||||||
self.config = config
|
|
||||||
self.name = task_name
|
|
||||||
self.dir = os.path.abspath(
|
|
||||||
os.path.join(config['library_dir'], self.name))
|
|
||||||
self.file = os.path.abspath(
|
|
||||||
os.path.join(self.dir, config['task_file']))
|
|
||||||
self.pid_dir = os.path.abspath(
|
|
||||||
os.path.join(self.config['pid_dir'], self.name))
|
|
||||||
self.report_dir = os.path.abspath(
|
|
||||||
os.path.join(self.config['report_dir'], self.name))
|
|
||||||
self.status_file = os.path.abspath(os.path.join(
|
|
||||||
self.report_dir, self.config['status_file']))
|
|
||||||
self._metadata = {}
|
|
||||||
log.debug('Init task %s with task file %s', self.name, self.file)
|
|
||||||
|
|
||||||
def verify(self):
|
|
||||||
if not os.path.exists(self.file):
|
|
||||||
raise exceptions.NotFound()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def metadata(self):
|
|
||||||
if self._metadata:
|
|
||||||
return self._metadata
|
|
||||||
with open(self.file) as f:
|
|
||||||
self._metadata = yaml.load(f.read())
|
|
||||||
return self._metadata
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def task_from_dir(cls, task_dir, config):
|
|
||||||
path = task_dir.replace(config['library_dir'], '').split('/')
|
|
||||||
task_name = '/'.join((p for p in path if p))
|
|
||||||
return cls(task_name, config)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "{0:10} | {1:15}".format(self.name, self.dir)
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
"""Will be used to run a task."""
|
|
||||||
self.verify()
|
|
||||||
action_class = type_mapping.get(self.metadata.get('type'))
|
|
||||||
if action_class is None:
|
|
||||||
raise exceptions.NotValidMetadata()
|
|
||||||
action = action_class(self, self.config)
|
|
||||||
action.verify()
|
|
||||||
return action.run()
|
|
|
@ -1,13 +0,0 @@
|
||||||
# Copyright 2014 Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# 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.
|
|
|
@ -1,52 +0,0 @@
|
||||||
# Copyright 2014 Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# 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 os
|
|
||||||
import time
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
from tasklib import utils
|
|
||||||
from tasklib.utils import STATUS
|
|
||||||
|
|
||||||
|
|
||||||
class BaseUnitTest(unittest.TestCase):
|
|
||||||
"""Tasklib base unittest."""
|
|
||||||
|
|
||||||
|
|
||||||
class BaseFunctionalTest(BaseUnitTest):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.dir_path = os.path.dirname(os.path.realpath(__file__))
|
|
||||||
self.conf_path = os.path.join(self.dir_path, 'functional', 'conf.yaml')
|
|
||||||
self.base_command = ['taskcmd', '-c', self.conf_path]
|
|
||||||
|
|
||||||
def check_puppet_installed(self):
|
|
||||||
exit_code, out, err = utils.execute('which puppet')
|
|
||||||
if exit_code == 1:
|
|
||||||
self.skipTest('Puppet is not installed')
|
|
||||||
|
|
||||||
def execute(self, add_command):
|
|
||||||
command = self.base_command + add_command
|
|
||||||
cmd = ' '.join(command)
|
|
||||||
return utils.execute(cmd)
|
|
||||||
|
|
||||||
def wait_ready(self, cmd, timeout):
|
|
||||||
started = time.time()
|
|
||||||
while time.time() - started < timeout:
|
|
||||||
exit_code, out, err = self.execute(cmd)
|
|
||||||
if out.strip('\n') != STATUS.running.name:
|
|
||||||
return exit_code, out, err
|
|
||||||
self.fail('Command {0} failed to finish with timeout {1}'.format(
|
|
||||||
cmd, timeout))
|
|
|
@ -1,13 +0,0 @@
|
||||||
# Copyright 2014 Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# 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.
|
|
|
@ -1,5 +0,0 @@
|
||||||
library_dir: tasklib/tests/functional
|
|
||||||
pid_dir: tmp/task_run
|
|
||||||
report_dir: tmp/task_report
|
|
||||||
debug: true
|
|
||||||
log_file: tasklib.log
|
|
|
@ -1,2 +0,0 @@
|
||||||
type: exec
|
|
||||||
description: "Should fail on validation"
|
|
|
@ -1,3 +0,0 @@
|
||||||
type: exec
|
|
||||||
cmd: 'echo 42 | grep 100'
|
|
||||||
description: "grep will return 1, if nothing will be found"
|
|
|
@ -1,3 +0,0 @@
|
||||||
type: exec
|
|
||||||
cmd: sleep 1.5
|
|
||||||
description: "Will be used for testing stop action"
|
|
|
@ -1,3 +0,0 @@
|
||||||
type: exec
|
|
||||||
cmd: echo 1
|
|
||||||
description: "Most primitive task available"
|
|
|
@ -1,3 +0,0 @@
|
||||||
exec { "which which":
|
|
||||||
path => ["/usr/bin", "/usr/sbin"]
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
type: puppet
|
|
|
@ -1,6 +0,0 @@
|
||||||
file {"tasklibtest":
|
|
||||||
path => "/tmp/tasklibtest",
|
|
||||||
ensure => present,
|
|
||||||
mode => 0640,
|
|
||||||
content => "I'm a file created created by tasklib test",
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
type: puppet
|
|
||||||
puppet_manifest: file.pp
|
|
|
@ -1 +0,0 @@
|
||||||
file {"invalid":
|
|
|
@ -1,2 +0,0 @@
|
||||||
type: puppet
|
|
||||||
description: "site.pp is default manifest provided by config"
|
|
|
@ -1,3 +0,0 @@
|
||||||
exec { "echo 15":
|
|
||||||
path => ["/bin", "sbin"]
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
type: puppet
|
|
||||||
description: 'Just sleep fot 1 seconds'
|
|
|
@ -1,26 +0,0 @@
|
||||||
# Copyright 2014 Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
from tasklib.tests import base
|
|
||||||
from tasklib.utils import STATUS
|
|
||||||
|
|
||||||
|
|
||||||
class TestDaemon(base.BaseFunctionalTest):
|
|
||||||
|
|
||||||
def test_exec_long_task(self):
|
|
||||||
exit_code, out, err = self.execute(['daemon', 'exec/long'])
|
|
||||||
self.assertEqual(exit_code, 0)
|
|
||||||
exit_code, out, err = self.wait_ready(['status', 'exec/long'], 2)
|
|
||||||
self.assertEqual(exit_code, 0)
|
|
||||||
self.assertEqual(out.strip('\n'), STATUS.end.name)
|
|
|
@ -1,51 +0,0 @@
|
||||||
# Copyright 2014 Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
from tasklib.tests import base
|
|
||||||
from tasklib.utils import STATUS
|
|
||||||
|
|
||||||
|
|
||||||
class TestFunctionalExecTasks(base.BaseFunctionalTest):
|
|
||||||
"""Each test will follow next pattern:
|
|
||||||
1. Run test with provided name - taskcmd -c conf.yaml run test/test
|
|
||||||
2. check status of task
|
|
||||||
"""
|
|
||||||
|
|
||||||
def test_simple_run(self):
|
|
||||||
exit_code, out, err = self.execute(['run', 'exec/simple'])
|
|
||||||
self.assertEqual(exit_code, 0)
|
|
||||||
exit_code, out, err = self.execute(['status', 'exec/simple'])
|
|
||||||
self.assertEqual(out.strip('\n'), STATUS.end.name)
|
|
||||||
self.assertEqual(exit_code, 0)
|
|
||||||
|
|
||||||
def test_failed_run(self):
|
|
||||||
exit_code, out, err = self.execute(['run', 'exec/fail'])
|
|
||||||
self.assertEqual(exit_code, 2)
|
|
||||||
exit_code, out, err = self.execute(['status', 'exec/fail'])
|
|
||||||
self.assertEqual(out.strip('\n'), STATUS.failed.name)
|
|
||||||
self.assertEqual(exit_code, 2)
|
|
||||||
|
|
||||||
def test_error(self):
|
|
||||||
exit_code, out, err = self.execute(['run', 'exec/error'])
|
|
||||||
self.assertEqual(exit_code, 3)
|
|
||||||
exit_code, out, err = self.execute(['status', 'exec/error'])
|
|
||||||
self.assertEqual(out.strip('\n'), STATUS.error.name)
|
|
||||||
self.assertEqual(exit_code, 3)
|
|
||||||
|
|
||||||
def test_notfound(self):
|
|
||||||
exit_code, out, err = self.execute(['run', 'exec/notfound'])
|
|
||||||
self.assertEqual(exit_code, 4)
|
|
||||||
exit_code, out, err = self.execute(['status', 'exec/notfound'])
|
|
||||||
self.assertEqual(out.strip('\n'), STATUS.notfound.name)
|
|
||||||
self.assertEqual(exit_code, 4)
|
|
|
@ -1,39 +0,0 @@
|
||||||
# Copyright 2014 Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# 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 os
|
|
||||||
|
|
||||||
from tasklib.tests import base
|
|
||||||
|
|
||||||
|
|
||||||
class TestFunctionalExecTasks(base.BaseFunctionalTest):
|
|
||||||
"""Each test will follow next pattern:
|
|
||||||
1. Run test with provided name - taskcmd -c conf.yaml run test/test
|
|
||||||
2. check status of task
|
|
||||||
"""
|
|
||||||
|
|
||||||
def test_puppet_file(self):
|
|
||||||
test_file = '/tmp/tasklibtest'
|
|
||||||
if os.path.exists(test_file):
|
|
||||||
os.unlink(test_file)
|
|
||||||
exit_code, out, err = self.execute(['run', 'puppet/file'])
|
|
||||||
self.assertEqual(exit_code, 0)
|
|
||||||
|
|
||||||
def test_puppet_invalid(self):
|
|
||||||
exit_code, out, err = self.execute(['run', 'puppet/invalid'])
|
|
||||||
self.assertEqual(exit_code, 2)
|
|
||||||
|
|
||||||
def test_puppet_cmd(self):
|
|
||||||
exit_code, out, err = self.execute(['run', 'puppet/cmd'])
|
|
||||||
self.assertEqual(exit_code, 0)
|
|
|
@ -1,37 +0,0 @@
|
||||||
# Copyright 2014 Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# 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 mock
|
|
||||||
|
|
||||||
from tasklib import cli
|
|
||||||
from tasklib.tests import base
|
|
||||||
|
|
||||||
|
|
||||||
class TestCmdApi(base.BaseUnitTest):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.api = cli.CmdApi()
|
|
||||||
self.api.config['log_file'] = None
|
|
||||||
|
|
||||||
@mock.patch('tasklib.cli.task.Task.task_from_dir')
|
|
||||||
@mock.patch('tasklib.cli.utils.find_all_tasks')
|
|
||||||
def test_list(self, mfind, mtask):
|
|
||||||
tasks = ['/etc/library/test/deploy', '/etc/library/test/rollback']
|
|
||||||
mfind.return_value = tasks
|
|
||||||
self.api.parse(['list'])
|
|
||||||
mfind.assert_called_once_with(self.api.config)
|
|
||||||
expected_calls = []
|
|
||||||
for t in tasks:
|
|
||||||
expected_calls.append(mock.call(t, self.api.config))
|
|
||||||
self.assertEqual(expected_calls, mtask.call_args_list)
|
|
|
@ -1,49 +0,0 @@
|
||||||
# Copyright 2014 Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# 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 mock
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
from tasklib import config
|
|
||||||
from tasklib.tests import base
|
|
||||||
|
|
||||||
|
|
||||||
@mock.patch('tasklib.task.os.path.exists')
|
|
||||||
class TestConfig(base.BaseUnitTest):
|
|
||||||
|
|
||||||
def test_default_config_when_no_file_exists(self, mexists):
|
|
||||||
mexists.return_value = False
|
|
||||||
conf = config.Config(config_file='/etc/tasklib/test.yaml')
|
|
||||||
self.assertEqual(conf.default_config, conf.config)
|
|
||||||
|
|
||||||
def test_default_when_no_file_provided(self, mexists):
|
|
||||||
conf = config.Config()
|
|
||||||
self.assertEqual(conf.default_config, conf.config)
|
|
||||||
|
|
||||||
def test_non_default_config_from_valid_yaml(self, mexists):
|
|
||||||
mexists.return_value = True
|
|
||||||
provided = {'library_dir': '/var/run/tasklib',
|
|
||||||
'puppet_manifest': 'init.pp'}
|
|
||||||
mopen = mock.mock_open(read_data=yaml.dump(provided))
|
|
||||||
with mock.patch('tasklib.config.open', mopen, create=True):
|
|
||||||
conf = config.Config(config_file='/etc/tasklib/test.yaml')
|
|
||||||
self.assertNotEqual(
|
|
||||||
conf.config['library_dir'], conf.default_config['library_dir'])
|
|
||||||
self.assertEqual(
|
|
||||||
conf.config['library_dir'], provided['library_dir'])
|
|
||||||
self.assertNotEqual(
|
|
||||||
conf.config['puppet_manifest'],
|
|
||||||
conf.default_config['puppet_manifest'])
|
|
||||||
self.assertEqual(
|
|
||||||
conf.config['puppet_manifest'], provided['puppet_manifest'])
|
|
|
@ -1,41 +0,0 @@
|
||||||
# Copyright 2014 Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# 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 mock
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
from tasklib import config
|
|
||||||
from tasklib import task
|
|
||||||
from tasklib.tests import base
|
|
||||||
|
|
||||||
|
|
||||||
@mock.patch('tasklib.task.os.path.exists')
|
|
||||||
@mock.patch('tasklib.utils.execute')
|
|
||||||
class TestExecTask(base.BaseUnitTest):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.meta = {'cmd': 'echo 1',
|
|
||||||
'type': 'exec'}
|
|
||||||
self.only_required = {'type': 'puppet'}
|
|
||||||
self.config = config.Config()
|
|
||||||
|
|
||||||
def test_base_cmd_task(self, mexecute, mexists):
|
|
||||||
mexists.return_value = True
|
|
||||||
mexecute.return_value = (0, '', '')
|
|
||||||
mopen = mock.mock_open(read_data=yaml.dump(self.meta))
|
|
||||||
puppet_task = task.Task('test/cmd', self.config)
|
|
||||||
with mock.patch('tasklib.task.open', mopen, create=True):
|
|
||||||
puppet_task.run()
|
|
||||||
expected_cmd = 'echo 1'
|
|
||||||
mexecute.assert_called_once_with(expected_cmd)
|
|
|
@ -1,63 +0,0 @@
|
||||||
# Copyright 2014 Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# 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 mock
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
from tasklib import config
|
|
||||||
from tasklib import task
|
|
||||||
from tasklib.tests import base
|
|
||||||
|
|
||||||
|
|
||||||
@mock.patch('tasklib.task.os.path.exists')
|
|
||||||
@mock.patch('tasklib.utils.execute')
|
|
||||||
class TestPuppetTask(base.BaseUnitTest):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.meta = {'puppet_manifest': 'init.pp',
|
|
||||||
'puppet_options': '--debug',
|
|
||||||
'puppet_modules': '/etc/my_puppet_modules',
|
|
||||||
'type': 'puppet'}
|
|
||||||
self.only_required = {'type': 'puppet'}
|
|
||||||
self.config = config.Config()
|
|
||||||
|
|
||||||
def test_basic_puppet_action(self, mexecute, mexists):
|
|
||||||
mexists.return_value = True
|
|
||||||
mexecute.return_value = (0, '', '')
|
|
||||||
mopen = mock.mock_open(read_data=yaml.dump(self.meta))
|
|
||||||
puppet_task = task.Task('test/puppet', self.config)
|
|
||||||
with mock.patch('tasklib.task.open', mopen, create=True):
|
|
||||||
puppet_task.run()
|
|
||||||
expected_cmd = ['puppet', 'apply', '--detailed-exitcodes',
|
|
||||||
'--modulepath=/etc/my_puppet_modules',
|
|
||||||
'--debug']
|
|
||||||
expected = ' '.join(expected_cmd)
|
|
||||||
self.assertEqual(mexecute.call_count, 1)
|
|
||||||
received = mexecute.call_args[0][0]
|
|
||||||
self.assertTrue(expected in received)
|
|
||||||
|
|
||||||
def test_default_puppet_action(self, mexecute, mexists):
|
|
||||||
mexists.return_value = True
|
|
||||||
mexecute.return_value = (0, '', '')
|
|
||||||
mopen = mock.mock_open(read_data=yaml.dump(self.only_required))
|
|
||||||
puppet_task = task.Task('test/puppet/only_required', self.config)
|
|
||||||
with mock.patch('tasklib.task.open', mopen, create=True):
|
|
||||||
puppet_task.run()
|
|
||||||
expected_cmd = [
|
|
||||||
'puppet', 'apply', '--detailed-exitcodes',
|
|
||||||
'--modulepath={0}'.format(self.config['puppet_modules'])]
|
|
||||||
expected = ' '.join(expected_cmd)
|
|
||||||
self.assertEqual(mexecute.call_count, 1)
|
|
||||||
received = mexecute.call_args[0][0]
|
|
||||||
self.assertTrue(expected in received)
|
|
|
@ -1,57 +0,0 @@
|
||||||
# Copyright 2014 Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# 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 os
|
|
||||||
|
|
||||||
import mock
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
from tasklib import config
|
|
||||||
from tasklib import exceptions
|
|
||||||
from tasklib import task
|
|
||||||
from tasklib.tests import base
|
|
||||||
|
|
||||||
|
|
||||||
@mock.patch('tasklib.task.os.path.exists')
|
|
||||||
class TestBaseTask(base.BaseUnitTest):
|
|
||||||
"""Basic task tests."""
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.conf = config.Config()
|
|
||||||
|
|
||||||
def test_create_task_from_path(self, mexists):
|
|
||||||
name = 'ceph/deploy'
|
|
||||||
task_dir = os.path.join(self.conf['library_dir'], name)
|
|
||||||
test_task = task.Task.task_from_dir(task_dir, self.conf)
|
|
||||||
self.assertEqual(test_task.name, name)
|
|
||||||
self.assertEqual(test_task.dir, task_dir)
|
|
||||||
|
|
||||||
def test_verify_raises_not_found(self, mexists):
|
|
||||||
mexists.return_value = False
|
|
||||||
test_task = task.Task('ceph/deploy', self.conf)
|
|
||||||
self.assertRaises(exceptions.NotFound, test_task.verify)
|
|
||||||
|
|
||||||
def test_verify_nothing_happens_if_file_exists(self, mexists):
|
|
||||||
mexists.return_value = True
|
|
||||||
test_task = task.Task('ceph/deploy', self.conf)
|
|
||||||
test_task.verify()
|
|
||||||
|
|
||||||
def test_read_metadata_from_valid_yaml(self, mexists):
|
|
||||||
mexists.return_value = True
|
|
||||||
meta = {'report_dir': '/tmp/report_dir',
|
|
||||||
'pid_dir': '/tmp/pid_dir'}
|
|
||||||
mopen = mock.mock_open(read_data=yaml.dump(meta))
|
|
||||||
test_task = task.Task('ceph/deploy', self.conf)
|
|
||||||
with mock.patch('tasklib.task.open', mopen, create=True):
|
|
||||||
self.assertEqual(meta, test_task.metadata)
|
|
|
@ -1,51 +0,0 @@
|
||||||
# Copyright 2014 Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
from collections import namedtuple
|
|
||||||
import fnmatch
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
|
|
||||||
Status = namedtuple('Status', ['name', 'code'])
|
|
||||||
|
|
||||||
|
|
||||||
def key_value_enum(enums):
|
|
||||||
enums = dict([(k, Status(k, v)) for k, v in enums.iteritems()])
|
|
||||||
return type('Enum', (), enums)
|
|
||||||
|
|
||||||
|
|
||||||
STATUS = key_value_enum({'running': 1,
|
|
||||||
'end': 0,
|
|
||||||
'error': 3,
|
|
||||||
'notfound': 4,
|
|
||||||
'failed': 2})
|
|
||||||
|
|
||||||
|
|
||||||
def find_all_tasks(config):
|
|
||||||
for root, dirnames, filenames in os.walk(config['library_dir']):
|
|
||||||
for filename in fnmatch.filter(filenames, config['task_file']):
|
|
||||||
yield os.path.dirname(os.path.join(root, filename))
|
|
||||||
|
|
||||||
|
|
||||||
def ensure_dir_created(path):
|
|
||||||
if not os.path.exists(path):
|
|
||||||
os.makedirs(path)
|
|
||||||
|
|
||||||
|
|
||||||
def execute(cmd):
|
|
||||||
command = subprocess.Popen(
|
|
||||||
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
|
|
||||||
stdout, stderr = command.communicate()
|
|
||||||
return command.returncode, stdout, stderr
|
|
|
@ -1,4 +0,0 @@
|
||||||
-r requirements.txt
|
|
||||||
mock==1.0.1
|
|
||||||
nose
|
|
||||||
tox
|
|
|
@ -1,38 +0,0 @@
|
||||||
[tox]
|
|
||||||
minversion = 1.6
|
|
||||||
skipsdist = True
|
|
||||||
envlist = py26,py27,pep8
|
|
||||||
|
|
||||||
[testenv]
|
|
||||||
usedevelop = True
|
|
||||||
install_command = pip install {packages}
|
|
||||||
setenv = VIRTUAL_ENV={envdir}
|
|
||||||
deps = -r{toxinidir}/test-requirements.txt
|
|
||||||
commands =
|
|
||||||
nosetests {posargs:tasklib}
|
|
||||||
|
|
||||||
[tox:jenkins]
|
|
||||||
downloadcache = ~/cache/pip
|
|
||||||
|
|
||||||
[testenv:pep8]
|
|
||||||
deps = hacking==0.7
|
|
||||||
usedevelop = False
|
|
||||||
commands =
|
|
||||||
flake8 {posargs:tasklib}
|
|
||||||
|
|
||||||
[testenv:venv]
|
|
||||||
commands = {posargs:}
|
|
||||||
|
|
||||||
[testenv:devenv]
|
|
||||||
envdir = devenv
|
|
||||||
usedevelop = True
|
|
||||||
|
|
||||||
[flake8]
|
|
||||||
ignore = H234,H302,H802
|
|
||||||
exclude = .venv,.git,.tox,dist,doc,*lib/python*,*egg,build,tools,__init__.py,docs
|
|
||||||
show-pep8 = True
|
|
||||||
show-source = True
|
|
||||||
count = True
|
|
||||||
|
|
||||||
[hacking]
|
|
||||||
import_exceptions = testtools.matchers
|
|
Loading…
Reference in New Issue