diff --git a/bootstrap/playbooks/files/ubuntu-ansible.sh b/bootstrap/playbooks/files/ubuntu-ansible.sh old mode 100644 new mode 100755 index ad8414cb..3bf33a4d --- a/bootstrap/playbooks/files/ubuntu-ansible.sh +++ b/bootstrap/playbooks/files/ubuntu-ansible.sh @@ -8,4 +8,4 @@ sudo apt-get update sudo apt-get install -y python-setuptools python-dev autoconf g++ sudo easy_install pip sudo pip install -U pip -sudo pip install "ansible<2.0" +sudo pip install "ansible" diff --git a/requirements.txt b/requirements.txt index 324663b2..438763ca 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ tabulate==0.7.5 gevent>=1.0.2 # we need callbacks for now -ansible<2.0 +ansible mock multipledispatch==0.4.8 diff --git a/solar/core/handlers/ansible_playbook.py b/solar/core/handlers/ansible_playbook.py index fb7ec89c..10be0c78 100644 --- a/solar/core/handlers/ansible_playbook.py +++ b/solar/core/handlers/ansible_playbook.py @@ -19,7 +19,6 @@ import json import shutil import tempfile -from solar.core.handlers import base from solar.core.handlers.base import SOLAR_TEMP_LOCAL_LOCATION from solar.core.handlers.base import TempFileHandler from solar.core.log import log @@ -29,7 +28,7 @@ from solar.core.provider import SVNProvider ROLES_PATH = '/etc/ansible/roles' -class AnsiblePlaybookBase(base.BaseHandler): +class AnsiblePlaybookBase(TempFileHandler): def download_roles(self, urls): if not os.path.exists(ROLES_PATH): @@ -39,8 +38,43 @@ class AnsiblePlaybookBase(base.BaseHandler): provider.run() shutil.copytree(provider.directory, ROLES_PATH) + def make_ansible_command(self, remote_playbook_file, + remote_inventory_file, remote_extra_vars_file, + ansible_library_path): + if ansible_library_path: + remote_ansible_library_path = ansible_library_path.replace( + SOLAR_TEMP_LOCAL_LOCATION, '/tmp/') + call_args = [ + 'ansible-playbook', + '--module-path', + remote_ansible_library_path, + '-i', + remote_inventory_file, + '--extra-vars', + '@%s' % remote_extra_vars_file, + remote_playbook_file + ] + else: + call_args = [ + 'ansible-playbook', + '-i', + remote_inventory_file, + '--extra-vars', + '@%s' % remote_extra_vars_file, + remote_playbook_file + ] + return call_args -class AnsiblePlaybook(AnsiblePlaybookBase, TempFileHandler): + def _copy_ansible_library(self, resource): + base_path = resource.db_obj.base_path + src_ansible_library_dir = os.path.join(base_path, 'ansible_library') + trg_ansible_library_dir = None + if os.path.exists(src_ansible_library_dir): + log.debug("Adding ansible_library for %s", resource.name) + trg_ansible_library_dir = os.path.join( + self.dirs[resource.name], 'ansible_library') + shutil.copytree(src_ansible_library_dir, trg_ansible_library_dir) + return trg_ansible_library_dir def _make_playbook(self, resource, action, action_path): dir_path = self.dirs[resource.name] @@ -68,6 +102,9 @@ class AnsiblePlaybook(AnsiblePlaybookBase, TempFileHandler): r_args = resource.args return json.dumps(r_args) + +class AnsiblePlaybook(AnsiblePlaybookBase): + def action(self, resource, action): action_file = os.path.join( resource.db_obj.actions_path, @@ -95,28 +132,11 @@ class AnsiblePlaybook(AnsiblePlaybookBase, TempFileHandler): remote_extra_vars_file = extra_vars_file.replace( SOLAR_TEMP_LOCAL_LOCATION, '/tmp/') - if ansible_library_path: - remote_ansible_library_path = ansible_library_path.replace( - SOLAR_TEMP_LOCAL_LOCATION, '/tmp/') - call_args = [ - 'ansible-playbook', - '--module-path', - remote_ansible_library_path, - '-i', - remote_inventory_file, - '--extra-vars', - '@%s' % remote_extra_vars_file, - remote_playbook_file - ] - else: - call_args = [ - 'ansible-playbook', - '-i', - remote_inventory_file, - '--extra-vars', - '@%s' % remote_extra_vars_file, - remote_playbook_file - ] + call_args = self.make_ansible_command(remote_playbook_file, + remote_inventory_file, + remote_extra_vars_file, + ansible_library_path) + log.debug('EXECUTING: %s', ' '.join(call_args)) rst = self.transport_run.run(resource, *call_args) diff --git a/solar/core/handlers/ansible_playbook_local.py b/solar/core/handlers/ansible_playbook_local.py index 15530cbc..f7e0b9a8 100644 --- a/solar/core/handlers/ansible_playbook_local.py +++ b/solar/core/handlers/ansible_playbook_local.py @@ -15,73 +15,60 @@ import os -# this has to be before callbacks, otherwise ansible circural import problem -from ansible import utils - -# intentional line, otherwise H306 -from ansible import callbacks - -import ansible.constants as C -from ansible.playbook import PlayBook - -from solar.core.transports.base import find_named_transport from solar import errors from solar.core.handlers.ansible_playbook import AnsiblePlaybookBase +from solar.core.log import log +from solar.core.transports.base import find_named_transport +from solar.utils import execute class AnsiblePlaybookLocal(AnsiblePlaybookBase): + def _make_inventory(self, resource): + ssh_transport = find_named_transport(resource, 'ssh') + ssh_key = ssh_transport.get('key') + ssh_password = ssh_transport.get('password') + + if ssh_key: + inventory = '{0} ansible_ssh_host={1} ansible_connection=ssh \ + ansible_ssh_user={2} ansible_ssh_private_key_file={3}' + ssh_auth_data = ssh_key + elif ssh_password: + inventory = '{0} ansible_ssh_host={1} \ + ansible_ssh_user={2} ansible_ssh_pass={3}' + ssh_auth_data = ssh_password + else: + raise Exception("No key and no password given") + user = ssh_transport['user'] + host = resource.ip() + return inventory.format(host, host, user, ssh_auth_data) + def action(self, resource, action): - # This would require to put this file to remote and execute it (mostly) - - ssh_props = find_named_transport(resource, 'ssh') - - remote_user = ssh_props['user'] - private_key_file = ssh_props.get('key') - ssh_password = ssh_props.get('password') action_file = os.path.join( resource.db_obj.actions_path, resource.actions[action]) + files = self._make_playbook(resource, + action, + action_file) + playbook_file, inventory_file, extra_vars_file = files + variables = resource.args if 'roles' in variables: self.download_roles(variables['roles']) - host = resource.ip() - transport = C.DEFAULT_TRANSPORT + ansible_library_path = self._copy_ansible_library(resource) + call_args = self.make_ansible_command(playbook_file, + inventory_file, + extra_vars_file, + ansible_library_path) - C.HOST_KEY_CHECKING = False + log.debug('EXECUTING: %s', ' '.join(call_args)) - stats = callbacks.AggregateStats() - playbook_cb = callbacks.PlaybookCallbacks(verbose=utils.VERBOSITY) - runner_cb = callbacks.PlaybookRunnerCallbacks( - stats, verbose=utils.VERBOSITY) - - opts = dict( - playbook=action_file, - remote_user=remote_user, - host_list=[host], - extra_vars=variables, - callbacks=playbook_cb, - runner_callbacks=runner_cb, - stats=stats, - transport=transport) - - if ssh_password: - opts['remote_pass'] = ssh_password - elif private_key_file: - opts['private_key_file'] = private_key_file + ret, out, err = execute(call_args) + if ret == 0: + return else: - raise Exception("No key and no password given") - - play = PlayBook(**opts) - - play.run() - summary = stats.summarize(host) - - if summary.get('unreachable') or summary.get('failures'): - raise errors.SolarError( - 'Ansible playbook %s failed with next summary %s', - action_file, summary) + raise errors.SolarError(out) diff --git a/solar/core/handlers/ansible_template_local.py b/solar/core/handlers/ansible_template_local.py index 56be7530..83fba152 100644 --- a/solar/core/handlers/ansible_template_local.py +++ b/solar/core/handlers/ansible_template_local.py @@ -57,9 +57,13 @@ class AnsibleTemplateLocal(AnsibleTemplateBase): log.debug('inventory_file: %s', inventory_file) log.debug('playbook_file: %s', playbook_file) - lib_path = self.get_library_path() - call_args = ['ansible-playbook', '--module-path', lib_path, - '-i', inventory_file, playbook_file] + lib_path = self._copy_ansible_library(resource) + if lib_path: + call_args = ['ansible-playbook', '--module-path', lib_path, + '-i', inventory_file, playbook_file] + else: + call_args = ['ansible-playbook', '-i', inventory_file, + playbook_file] log.debug('EXECUTING: %s', ' '.join(call_args)) ret, out, err = execute(call_args)