risotto/ansible/action_plugins/rougail.py

204 lines
8.8 KiB
Python

#!/usr/bin/python3
from asyncio import run
from os import readlink, walk, chdir, getcwd, makedirs
from os.path import join, islink, isdir
from risotto.machine import load, templates, INSTALL_DIR, INSTALL_CONFIG_DIR, INSTALL_TMPL_DIR, INSTALL_IMAGES_DIR, INSTALL_TESTS_DIR
from rougail.utils import normalize_family
from shutil import rmtree
import tarfile
try:
from ansible.plugins.action import ActionBase
from ansible.module_utils.basic import AnsibleModule
class FakeModule(AnsibleModule):
def __init__(self):
pass
except:
class ActionBase():
def __init__(self, *args, **kwargs):
raise Exception('works only with ansible')
ARCHIVES_DIR = '/tmp/new_configurations'
async def build_files(hostname: str,
only_machine: str,
just_copy: bool,
copy_tests: bool,
) -> None:
config = await load(copy_tests=copy_tests)
if only_machine:
machines = [only_machine]
else:
machines = [await subconfig.option.description() for subconfig in await config.option.list(type='optiondescription')]
# shasums = {}
directories = {}
for machine in machines:
if just_copy and hostname == machine:
continue
await templates(machine,
config,
just_copy=just_copy,
copy_manuals=True,
)
#FIXME dest_dir?
is_host = machine == hostname
# shasums[machine] = {'shasums': get_shasums(dest_dir, is_host)}
if is_host:
# shasums[machine]['config_dir'] = '/usr/local/lib'
directories[machine] = '/usr/local/lib'
else:
# shasums[machine]['config_dir'] = await config.option(normalize_family(machine)).option('general.config_dir').value.get()
directories[machine] = await config.option(normalize_family(machine)).option('general.config_dir').value.get()
return directories
def is_diff(server_name, remote_directories):
ret = {}
module = FakeModule()
current_path = getcwd()
root = join(INSTALL_DIR, INSTALL_CONFIG_DIR, server_name)
chdir(root)
search_paths = [join(directory[2:], f) for directory, subdirectories, files in walk('.') for f in files]
chdir(current_path)
for path in search_paths:
if path not in remote_directories:
return True
full_path = join(root, path)
if not islink(full_path):
if remote_directories[path] != module.digest_from_file(full_path, 'sha256'):
return True
elif remote_directories[path] != readlink(full_path):
return True
remote_directories.pop(path)
if remote_directories:
return True
return False
class ActionModule(ActionBase):
def run(self, tmp=None, task_vars=None):
super(ActionModule, self).run(tmp, task_vars)
module_args = self._task.args.copy()
hostname = module_args.pop('hostname')
only_machine = module_args.pop('only_machine')
configure_host = module_args.pop('configure_host')
copy_tests = module_args.pop('copy_tests')
if 'copy_templates' in module_args:
copy_templates = module_args.pop('copy_templates')
else:
copy_templates = False
directories = run(build_files(hostname,
only_machine,
False,
copy_tests,
))
module_args['directories'] = list(directories.values())
module_args['directories'].append('/var/lib/risotto/images_files')
remote = self._execute_module(module_name='compare',
module_args=module_args,
task_vars=task_vars,
)
if remote.get('failed'):
if 'module_stdout' in remote:
msg = remote['module_stdout']
else:
msg = remote['msg']
raise Exception(f'error in remote action: {msg}')
if copy_templates:
run(build_files(hostname,
only_machine,
True,
copy_tests,
))
machines_changed = []
for machine, directory in directories.items():
if directory not in remote['directories']:
machines_changed.append(machine)
continue
if is_diff(machine, remote['directories'][directory]):
machines_changed.append(machine)
current_path = getcwd()
if isdir(ARCHIVES_DIR):
rmtree(ARCHIVES_DIR)
makedirs(ARCHIVES_DIR)
if machines_changed:
self._execute_module(module_name='file',
module_args={'path': ARCHIVES_DIR,
'state': 'absent',
},
task_vars=task_vars,
)
self._execute_module(module_name='file',
module_args={'path': ARCHIVES_DIR,
'state': 'directory',
},
task_vars=task_vars,
)
machines = machines_changed.copy()
if self._task.args['hostname'] in machines_changed:
machine = self._task.args['hostname']
machines.remove(machine)
chdir(f'{task_vars["host_install_dir"]}/{INSTALL_CONFIG_DIR}/{machine}')
tar_filename = f'{ARCHIVES_DIR}/host.tar'
with tarfile.open(tar_filename, 'w') as archive:
archive.add('.')
chdir(current_path)
self._transfer_file(tar_filename, tar_filename)
# archive and send
if machines:
chdir(f'{task_vars["host_install_dir"]}/{INSTALL_CONFIG_DIR}')
tar_filename = f'{ARCHIVES_DIR}/machines.tar'
with tarfile.open(tar_filename, 'w') as archive:
for machine in machines:
if machine == self._task.args['hostname']:
continue
archive.add(f'{machine}')
self._transfer_file(tar_filename, tar_filename)
else:
machines = []
# archive and send
chdir(f'{task_vars["host_install_dir"]}/{INSTALL_IMAGES_DIR}/')
tar_filename = f'{ARCHIVES_DIR}/{INSTALL_IMAGES_DIR}.tar'
with tarfile.open(tar_filename, 'w') as archive:
archive.add('.')
self._transfer_file(tar_filename, tar_filename)
# tests
self._execute_module(module_name='file',
module_args={'path': '/var/lib/risotto/tests',
'state': 'absent',
},
task_vars=task_vars,
)
if copy_tests:
chdir(f'{task_vars["host_install_dir"]}/{INSTALL_TESTS_DIR}/')
tar_filename = f'{ARCHIVES_DIR}/{INSTALL_TESTS_DIR}.tar'
with tarfile.open(tar_filename, 'w') as archive:
archive.add('.')
self._transfer_file(tar_filename, tar_filename)
# templates
self._execute_module(module_name='file',
module_args={'path': '/var/lib/risotto/templates',
'state': 'absent',
},
task_vars=task_vars,
)
if copy_templates:
chdir(f'{task_vars["host_install_dir"]}/')
tar_filename = f'{ARCHIVES_DIR}/{INSTALL_TMPL_DIR}.tar'
with tarfile.open(tar_filename, 'w') as archive:
archive.add(INSTALL_TMPL_DIR)
self._transfer_file(tar_filename, tar_filename)
remote = self._execute_module(module_name='unarchive',
module_args={'remote_src': True,
'src': '/tmp/new_configurations/templates.tar',
'dest': '/var/lib/risotto',
},
task_vars=task_vars,
)
chdir(current_path)
changed = machines_changed != []
return dict(ansible_facts=dict({}), changed=changed, machines_changed=machines, host_changed=self._task.args['hostname'] in machines_changed)