From d60cb70768aee6f635f45a3678dd4e08b1898dac Mon Sep 17 00:00:00 2001 From: Nadav Goldin Date: Thu, 1 Jun 2017 17:19:28 +0300 Subject: [PATCH 1/3] ssh: catch EOFError paramiko.Transport.start_client can raise EOFError directly, in that case we should still normally retry getting a connection. This was apparent in debian, where this exception might get thrown on the first attempts. Signed-off-by: Nadav Goldin --- lago/ssh.py | 2 ++ 1 file changed, 2 insertions(+) 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: From 4f3c43e07c6581279db27f9c1b21f8bfded4c78f Mon Sep 17 00:00:00 2001 From: Nadav Goldin Date: Thu, 1 Jun 2017 18:27:38 +0300 Subject: [PATCH 2/3] ssh: increase SSH tries on collect to 5 It is apparent in the logs that we fallback too early to libguestfs, giving only one try for SSH. Extracting the logs with libguestfs should be the last resort, so for now increasing number of SSH tries to 5. Signed-off-by: Nadav Goldin --- lago/plugins/vm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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: From e06f5022dfd6d10f26b9e2ba50abc3268b86174b Mon Sep 17 00:00:00 2001 From: Nadav Goldin Date: Thu, 1 Jun 2017 18:22:38 +0300 Subject: [PATCH 3/3] sdk: added ansible inventory methods These functions are delegated to lago_ansible Also added functional tests on 'check-merged' stage. Signed-off-by: Nadav Goldin --- automation/check-patch.packages | 1 + automation/check-patch.packages.el7 | 1 + lago/sdk.py | 60 +++++++++++++++++++++++++ tests/functional-sdk/test_sdk_sanity.py | 38 +++++++++++++++- 4 files changed, 99 insertions(+), 1 deletion(-) 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/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/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())