Allow to run custom ansible modules on NodeCollection
Added run_task method to NodeCollection Added add_module_paths Change-Id: I9956787945525b95523d96f3975f580fa6ff2803
This commit is contained in:
parent
3b40beabd8
commit
9f5966194a
@ -17,6 +17,7 @@ import jsonschema
|
||||
import pbr.version
|
||||
import yaml
|
||||
|
||||
from os_faults.ansible import executor
|
||||
from os_faults.api import error
|
||||
from os_faults.api import human
|
||||
from os_faults import registry
|
||||
@ -120,3 +121,13 @@ def human_api(distractor, command):
|
||||
:param command: text command
|
||||
"""
|
||||
human.execute(distractor, command)
|
||||
|
||||
|
||||
def register_ansible_modules(paths):
|
||||
"""Registers ansible modules by provided paths
|
||||
|
||||
Allows to use custom ansible modules in NodeCollection.run_task method
|
||||
|
||||
:param path: list of paths to folders with ansible modules
|
||||
"""
|
||||
executor.add_module_paths(paths)
|
||||
|
@ -23,6 +23,8 @@ from ansible.plugins import callback as callback_pkg
|
||||
from ansible.vars import VariableManager
|
||||
from oslo_log import log as logging
|
||||
|
||||
from os_faults.api import error
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
STATUS_OK = 'OK'
|
||||
@ -89,6 +91,26 @@ def resolve_relative_path(file_name):
|
||||
return path
|
||||
|
||||
|
||||
MODULE_PATHS = {
|
||||
resolve_relative_path('os_faults/ansible/modules'),
|
||||
}
|
||||
|
||||
|
||||
def get_module_paths():
|
||||
global MODULE_PATHS
|
||||
return MODULE_PATHS
|
||||
|
||||
|
||||
def add_module_paths(paths):
|
||||
global MODULE_PATHS
|
||||
for path in paths:
|
||||
if not os.path.exists(path):
|
||||
raise error.OSFError('{!r} does not exist'.format(path))
|
||||
# find all subfolders
|
||||
dirs = [x[0] for x in os.walk(path)]
|
||||
MODULE_PATHS.update(dirs)
|
||||
|
||||
|
||||
Options = namedtuple('Options',
|
||||
['connection', 'password', 'module_path', 'forks',
|
||||
'remote_user', 'private_key_file',
|
||||
@ -102,8 +124,6 @@ class AnsibleRunner(object):
|
||||
jump_host=None, private_key_file=None, become=None):
|
||||
super(AnsibleRunner, self).__init__()
|
||||
|
||||
module_path = resolve_relative_path('os_faults/ansible/modules')
|
||||
|
||||
ssh_common_args = SSH_COMMON_ARGS
|
||||
if jump_host:
|
||||
ssh_common_args += (
|
||||
@ -112,7 +132,8 @@ class AnsibleRunner(object):
|
||||
% dict(key=private_key_file, user=remote_user, host=jump_host))
|
||||
|
||||
self.options = Options(
|
||||
connection='smart', password=password, module_path=module_path,
|
||||
connection='smart', password=password,
|
||||
module_path=os.pathsep.join(get_module_paths()),
|
||||
forks=forks, remote_user=remote_user,
|
||||
private_key_file=private_key_file,
|
||||
ssh_common_args=ssh_common_args, ssh_extra_args=None,
|
||||
|
@ -28,6 +28,15 @@ class NodeCollection(object):
|
||||
:return: NodeCollection consisting just one node
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def run_task(self, task, raise_on_error=True):
|
||||
"""Run ansible task on node colection
|
||||
|
||||
:param task: ansible task as dict
|
||||
:param raise_on_error: throw exception in case of error
|
||||
:return: AnsibleExecutionRecord with results of task
|
||||
"""
|
||||
|
||||
@public
|
||||
def reboot(self):
|
||||
"""Reboot all nodes gracefully
|
||||
|
@ -43,6 +43,11 @@ class DevStackNode(node_collection.NodeCollection):
|
||||
def pick(self):
|
||||
return self
|
||||
|
||||
def run_task(self, task, raise_on_error=True):
|
||||
# TODO(astudenov): refactor DevStackManagement.execute
|
||||
# to be consistent with api
|
||||
self.cloud_management.execute(self.host.ip, task)
|
||||
|
||||
def reboot(self):
|
||||
task = {'command': 'reboot'}
|
||||
self.cloud_management.execute(self.host.ip, task)
|
||||
|
@ -59,6 +59,11 @@ class FuelNodeCollection(node_collection.NodeCollection):
|
||||
power_management=self.power_management,
|
||||
hosts=random.sample(self.hosts, count))
|
||||
|
||||
def run_task(self, task, raise_on_error=True):
|
||||
logging.info('Run task: %s on nodes: %s', task, self)
|
||||
self.cloud_management.execute_on_cloud(self.get_ips(), task,
|
||||
raise_on_error=raise_on_error)
|
||||
|
||||
def reboot(self):
|
||||
logging.info('Reboot nodes: %s', self)
|
||||
task = {'command': 'reboot now'}
|
||||
@ -352,6 +357,7 @@ class FuelManagement(cloud_management.CloudManagement):
|
||||
|
||||
:param hosts: List of host FQDNs
|
||||
:param task: Ansible task
|
||||
:param raise_on_error: throw exception in case of error
|
||||
:return: Ansible execution result (list of records)
|
||||
"""
|
||||
if raise_on_error:
|
||||
|
@ -43,6 +43,11 @@ class DevStackNodeTestCase(test.TestCase):
|
||||
def test_pick(self):
|
||||
self.assertIs(self.node_collection, self.node_collection.pick())
|
||||
|
||||
def test_run_task(self):
|
||||
self.node_collection.run_task({'foo': 'bar'})
|
||||
self.mock_cloud_management.execute.assert_called_once_with(
|
||||
'10.0.0.2', {'foo': 'bar'})
|
||||
|
||||
def test_reboot(self):
|
||||
self.node_collection.reboot()
|
||||
self.mock_cloud_management.execute.assert_called_once_with(
|
||||
|
@ -63,6 +63,12 @@ class FuelNodeCollectionTestCase(test.TestCase):
|
||||
self.assertEqual(1, len(one))
|
||||
self.assertIn(one.hosts[0], self.hosts)
|
||||
|
||||
def test_run_task(self):
|
||||
self.node_collection.run_task({'foo': 'bar'}, raise_on_error=False)
|
||||
self.mock_cloud_management.execute_on_cloud.assert_called_once_with(
|
||||
['10.0.0.2', '10.0.0.3', '10.0.0.4', '10.0.0.5'], {'foo': 'bar'},
|
||||
raise_on_error=False)
|
||||
|
||||
def test_pick_count(self):
|
||||
two = self.node_collection.pick(count=2)
|
||||
self.assertEqual(2, len(two))
|
||||
|
@ -17,6 +17,7 @@ import mock
|
||||
import yaml
|
||||
|
||||
import os_faults
|
||||
from os_faults.ansible import executor
|
||||
from os_faults.api import error
|
||||
from os_faults.drivers import devstack
|
||||
from os_faults.drivers import fuel
|
||||
@ -132,3 +133,16 @@ class OSFaultsTestCase(test.TestCase):
|
||||
@mock.patch('os.path.exists', return_value=False)
|
||||
def test_connect_no_config_files(self, mock_os_path_exists):
|
||||
self.assertRaises(error.OSFError, os_faults.connect)
|
||||
|
||||
@mock.patch('os.path.exists', side_effect=lambda x: 'bad' not in x)
|
||||
@mock.patch('os.walk', side_effect=lambda x: ([x, [], []],
|
||||
[x + 'subdir', [], []]))
|
||||
@mock.patch.object(executor, 'MODULE_PATHS', set())
|
||||
def test_register_ansible_modules(self, mock_os_walk, mock_os_path_exists):
|
||||
os_faults.register_ansible_modules(['/my/path/', '/other/path/'])
|
||||
self.assertEqual(executor.get_module_paths(),
|
||||
{'/my/path/', '/my/path/subdir',
|
||||
'/other/path/', '/other/path/subdir'})
|
||||
|
||||
self.assertRaises(error.OSFError, os_faults.register_ansible_modules,
|
||||
['/my/bad/path/'])
|
||||
|
Loading…
x
Reference in New Issue
Block a user