diff --git a/automation/check-patch.packages b/automation/check-patch.packages index 8da18dad..eaaa820b 100644 --- a/automation/check-patch.packages +++ b/automation/check-patch.packages @@ -1,3 +1,4 @@ +ansible bats dnf dnf-command(builddep) diff --git a/automation/check-patch.packages.el7 b/automation/check-patch.packages.el7 index e9407f92..3d018e3b 100644 --- a/automation/check-patch.packages.el7 +++ b/automation/check-patch.packages.el7 @@ -1,3 +1,4 @@ +ansible bats git libffi-devel diff --git a/lago/plugins/vm.py b/lago/plugins/vm.py index 9efcfc06..890137db 100644 --- a/lago/plugins/vm.py +++ b/lago/plugins/vm.py @@ -235,7 +235,7 @@ def extract_paths(self, paths, ignore_nopath): :exc:`~lago.plugins.vm.ExtractPathError`: on all other failures. """ if self.vm.alive() and self.vm.ssh_reachable( - tries=1, propagate_fail=False + tries=5, propagate_fail=False ): self._extract_paths_scp(paths=paths, ignore_nopath=ignore_nopath) else: diff --git a/lago/sdk.py b/lago/sdk.py index de21c6c6..b11bb9e9 100644 --- a/lago/sdk.py +++ b/lago/sdk.py @@ -1,6 +1,7 @@ from __future__ import print_function from lago import cmd from lago.config import config as lago_config +from lago.lago_ansible import LagoAnsible from lago import workdir as lago_workdir from lago.log_utils import get_default_log_formatter from sdk_utils import SDKWrapper, setup_sdk_logging @@ -136,3 +137,62 @@ def destroy(self): entirely the Lago working directory. """ self._workdir.destroy() + + def ansible_inventory_temp_file( + self, keys=['vm-type', 'groups', 'vm-provider'] + ): + """ + Context manager which returns Ansible inventory written on a tempfile. + This is the same as :func:`~ansible_inventory`, only the inventory file + is written to a tempfile. + + Args: + keys (list of str): Path to the keys that will be used to + create groups. + + Yields: + tempfile.NamedTemporaryFile: Temp file containing the inventory + """ + lansible = LagoAnsible(self._prefix) + return lansible.get_inventory_temp_file(keys=keys) + + def ansible_inventory( + self, + keys=['vm-type', 'groups', 'vm-provider'], + ): + """ + Get an Ansible inventory as a string, ``keys`` should be list on which + to group the hosts by. You can use any key defined in LagoInitFile. + + Examples of possible `keys`: + + `keys=['disks/0/metadata/arch']`, would group the hosts by the + architecture. + + `keys=['/disks/0/metadata/distro', 'disks/0/metadata/arch']`, + would create groups by architecture and also by distro. + + `keys=['groups']` - would group hosts by the groups defined for + each VM in the LagoInitFile, i.e.: + + domains: + + vm-01: + ... + groups: web-server + .. + vm-02: + .. + groups: db-server + + + Args: + keys (list of str): Path to the keys that will be used to + create groups. + + Returns: + str: INI-like Ansible inventory + """ + + lansible = LagoAnsible(self._prefix) + return lansible.get_inventory_str(keys=keys) diff --git a/lago/ssh.py b/lago/ssh.py index bccf4cd3..41daaa36 100644 --- a/lago/ssh.py +++ b/lago/ssh.py @@ -366,6 +366,8 @@ def get_ssh_client( host_name, err, ) + except EOFError as err: + LOGGER.debug('EOFError connecting to %s: %s', host_name, err) ssh_tries -= 1 time.sleep(1) else: diff --git a/tests/functional-sdk/test_sdk_sanity.py b/tests/functional-sdk/test_sdk_sanity.py index c9c2170b..f5c73624 100644 --- a/tests/functional-sdk/test_sdk_sanity.py +++ b/tests/functional-sdk/test_sdk_sanity.py @@ -8,6 +8,7 @@ import uuid from jinja2 import Environment, BaseLoader from lago import sdk +from lago.utils import run_command @pytest.fixture(scope='module') @@ -15,7 +16,7 @@ def init_str(images): init_template = textwrap.dedent( """ domains: - {% for vm_name, template in images.iteritems() %} + {% for vm_name, template in images.viewitems() %} {{ vm_name }}: memory: 1024 nics: @@ -33,6 +34,7 @@ def init_str(images): - /etc/resolv.conf - /etc/sysconfig - /etc/NetworkManager + groups: group{{ loop.index % 2 }} {% endfor %} nets: @@ -205,3 +207,37 @@ def test_load_env_up(env, vms, tmp_workdir): assert vm.state() == 'running' for vm in loaded_env.get_vms().values(): assert vm.ssh_reachable(tries=200) + + +@pytest.mark.check_merged +def test_ansible_inventory(monkeypatch, env, test_results, vms): + + # ansible returns the results in a bulk to stdout. Ideally we would test + # forthe hostname of each machine, but that is broken on debian. + # Instead, we let it compute something and count the unique occurences. + + cmd = 'echo __abcd$(( 24 + 12 ))efgh___' + expected = '__abcd36efgh__' + results = [] + + with env.ansible_inventory_temp_file(keys=['groups']) as inv: + for group in ['group0', 'group1']: + logfile = os.path.join( + test_results, 'ansible-{0}.log'.format(group) + ) + monkeypatch.setenv('ANSIBLE_LOG_PATH', logfile) + monkeypatch.setenv('ANSIBLE_HOST_KEY_CHECKING', 'False') + res = run_command( + [ + 'ansible', 'groups={0}'.format(group), '-v', '-u', 'root', + '-i', inv.name, '-m', 'raw', '-a', cmd + ] + ) + + assert res.code == 0 + assert res.out is not None + results.append(res) + + occurences = sum([result.out.count(expected) for result in results]) + + assert occurences == len(vms.keys())