from bootstrapvz.base import Task from bootstrapvz.common.tasks import host from bootstrapvz.common import phases from bootstrapvz.common.tools import rel_path from bootstrapvz.common.tools import log_check_call import os import json class AddRequiredCommands(Task): description = 'Adding commands required for provisioning with ansible' phase = phases.validation successors = [host.CheckExternalCommands] @classmethod def run(cls, info): info.host_dependencies['ansible'] = 'ansible' class CheckPlaybookPath(Task): description = 'Checking whether the playbook path exist' phase = phases.validation @classmethod def run(cls, info): from bootstrapvz.common.exceptions import TaskError playbook = rel_path(info.manifest.path, info.manifest.plugins['ansible']['playbook']) if not os.path.exists(playbook): msg = 'The playbook file {playbook} does not exist.'.format(playbook=playbook) raise TaskError(msg) if not os.path.isfile(playbook): msg = 'The playbook path {playbook} does not point to a file.'.format(playbook=playbook) raise TaskError(msg) class AddPackages(Task): description = 'Making sure python is installed' phase = phases.preparation @classmethod def run(cls, info): info.packages.add('python') class RunAnsiblePlaybook(Task): description = 'Running ansible playbook' phase = phases.user_modification @classmethod def run(cls, info): # Extract playbook and directory playbook = rel_path(info.manifest.path, info.manifest.plugins['ansible']['playbook']) # build the inventory file inventory = os.path.join(info.root, 'tmp/bootstrap-inventory') with open(inventory, 'w') as handle: conn = '{} ansible_connection=chroot'.format(info.root) content = "" if 'groups' in info.manifest.plugins['ansible']: for group in info.manifest.plugins['ansible']['groups']: content += '[{}]\n{}\n'.format(group, conn) else: content = conn handle.write(content) # build the ansible command cmd = ['ansible-playbook', '-i', inventory, playbook] if 'extra_vars' in info.manifest.plugins['ansible']: cmd.extend(['--extra-vars', json.dumps(info.manifest.plugins['ansible']['extra_vars'])]) if 'tags' in info.manifest.plugins['ansible']: cmd.extend(['--tags', ','.join(info.manifest.plugins['ansible']['tags'])]) if 'skip_tags' in info.manifest.plugins['ansible']: cmd.extend(['--skip-tags', ','.join(info.manifest.plugins['ansible']['skip_tags'])]) if 'opt_flags' in info.manifest.plugins['ansible']: # Should probably do proper validation on these, but I don't think it should be used very often. cmd.extend(info.manifest.plugins['ansible']['opt_flags']) # Run and remove the inventory file log_check_call(cmd) os.remove(inventory) class RemoveAnsibleSSHUserDir(Task): description = 'Removing .ansible directory' phase = phases.user_modification predecessors = [RunAnsiblePlaybook] @classmethod def run(cls, info): ssh_user = info.manifest.plugins['ansible']['extra_vars']['ansible_ssh_user'] # os.path.expanduser does not work in a chroot, # so we use sh instead [ssh_user_home] = log_check_call(['chroot', info.root, 'sh', '-c', 'echo ~' + ssh_user]) from shutil import rmtree # [1:] to remove the leading slash from e.g. /home/ansible ansible_dir_path = os.path.join(info.root, ssh_user_home[1:], '.ansible') rmtree(ansible_dir_path)