ansible/test/runner/lib/manage_ci.py
Matt Clay 1059dee51f [stable-2.5] Backport test infra updates and test fixes. (#49204)
* Switch tests from RHEL 7.5 to 7.6.

(cherry picked from commit 6745ee7cc8)

* Remove CI platform: freebsd/10.4

(cherry picked from commit e6ffc4f89a)

* Add `--raw` option to ansible-test shell command.

It is currently supported only with the `--remote` option.

This makes it easier to troubleshoot new instances which are not
yet supported by the setup scripts used by ansible-test.

(cherry picked from commit 0826a00803)

* Support skip of platforms by version in tests. (#48826)

* Support skip of platforms by version in tests.

Previously a remote platform could be skipped completely using the alias:

`skip/{platform}` such as `skip/rhel`

Now a specific platform version can be skipped using the alias:

`skip/{platform}{version}` such as `skip/rhel7.6`

This feature is available for platforms specified with the `--remote` option.

* Add skip by version to the docs.

(cherry picked from commit 8066acc90c)

* Fix ansible-test skip warning message.

(cherry picked from commit 3b705efc93)

* Fix lookup_passwordstore test skipping. (#49178)

* Fix lookup_passwordstore test skipping.

Skip all of RHEL instead of specific versions.
Skip all of CentOS < 7 instead of specific versions.

This makes the test more robust when testing newer versions.

Tests could be executed on RHEL if EPEL was installed during the test.
(cherry picked from commit 704dae2cda)
2018-12-04 13:35:00 -08:00

293 lines
9.5 KiB
Python

"""Access Ansible Core CI remote services."""
from __future__ import absolute_import, print_function
import os
import pipes
import tempfile
import time
import lib.pytar
from lib.util import (
SubprocessError,
ApplicationError,
run_command,
intercept_command,
)
from lib.core_ci import (
AnsibleCoreCI,
)
from lib.ansible_util import (
ansible_environment,
)
from lib.config import (
ShellConfig,
)
class ManageWindowsCI(object):
"""Manage access to a Windows instance provided by Ansible Core CI."""
def __init__(self, core_ci):
"""
:type core_ci: AnsibleCoreCI
"""
self.core_ci = core_ci
self.ssh_args = ['-i', self.core_ci.ssh_key.key]
ssh_options = dict(
BatchMode='yes',
StrictHostKeyChecking='no',
UserKnownHostsFile='/dev/null',
ServerAliveInterval=15,
ServerAliveCountMax=4,
)
for ssh_option in sorted(ssh_options):
self.ssh_args += ['-o', '%s=%s' % (ssh_option, ssh_options[ssh_option])]
def setup(self):
"""Used in delegate_remote to setup the host, no action is required for Windows."""
pass
def wait(self):
"""Wait for instance to respond to ansible ping."""
extra_vars = [
'ansible_connection=winrm',
'ansible_host=%s' % self.core_ci.connection.hostname,
'ansible_user=%s' % self.core_ci.connection.username,
'ansible_password=%s' % self.core_ci.connection.password,
'ansible_port=%s' % self.core_ci.connection.port,
'ansible_winrm_server_cert_validation=ignore',
]
name = 'windows_%s' % self.core_ci.version
env = ansible_environment(self.core_ci.args)
cmd = ['ansible', '-m', 'win_ping', '-i', '%s,' % name, name, '-e', ' '.join(extra_vars)]
for dummy in range(1, 120):
try:
intercept_command(self.core_ci.args, cmd, 'ping', env=env)
return
except SubprocessError:
time.sleep(10)
raise ApplicationError('Timeout waiting for %s/%s instance %s.' %
(self.core_ci.platform, self.core_ci.version, self.core_ci.instance_id))
def download(self, remote, local):
"""
:type remote: str
:type local: str
"""
self.scp('%s@%s:%s' % (self.core_ci.connection.username, self.core_ci.connection.hostname, remote), local)
def upload(self, local, remote):
"""
:type local: str
:type remote: str
"""
self.scp(local, '%s@%s:%s' % (self.core_ci.connection.username, self.core_ci.connection.hostname, remote))
def ssh(self, command, options=None, force_pty=True):
"""
:type command: str | list[str]
:type options: list[str] | None
:type force_pty: bool
"""
if not options:
options = []
if force_pty:
options.append('-tt')
if isinstance(command, list):
command = ' '.join(pipes.quote(c) for c in command)
run_command(self.core_ci.args,
['ssh', '-q'] + self.ssh_args +
options +
['-p', '22',
'%s@%s' % (self.core_ci.connection.username, self.core_ci.connection.hostname)] +
[command])
def scp(self, src, dst):
"""
:type src: str
:type dst: str
"""
for dummy in range(1, 10):
try:
run_command(self.core_ci.args,
['scp'] + self.ssh_args +
['-P', '22', '-q', '-r', src, dst])
return
except SubprocessError:
time.sleep(10)
raise ApplicationError('Failed transfer: %s -> %s' % (src, dst))
class ManageNetworkCI(object):
"""Manage access to a network instance provided by Ansible Core CI."""
def __init__(self, core_ci):
"""
:type core_ci: AnsibleCoreCI
"""
self.core_ci = core_ci
def wait(self):
"""Wait for instance to respond to ansible ping."""
extra_vars = [
'ansible_host=%s' % self.core_ci.connection.hostname,
'ansible_port=%s' % self.core_ci.connection.port,
'ansible_connection=local',
'ansible_ssh_private_key_file=%s' % self.core_ci.ssh_key.key,
]
name = '%s-%s' % (self.core_ci.platform, self.core_ci.version.replace('.', '-'))
env = ansible_environment(self.core_ci.args)
cmd = [
'ansible',
'-m', '%s_command' % self.core_ci.platform,
'-a', 'commands=?',
'-u', self.core_ci.connection.username,
'-i', '%s,' % name,
'-e', ' '.join(extra_vars),
name,
]
for dummy in range(1, 90):
try:
intercept_command(self.core_ci.args, cmd, 'ping', env=env)
return
except SubprocessError:
time.sleep(10)
raise ApplicationError('Timeout waiting for %s/%s instance %s.' %
(self.core_ci.platform, self.core_ci.version, self.core_ci.instance_id))
class ManagePosixCI(object):
"""Manage access to a POSIX instance provided by Ansible Core CI."""
def __init__(self, core_ci):
"""
:type core_ci: AnsibleCoreCI
"""
self.core_ci = core_ci
self.ssh_args = ['-i', self.core_ci.ssh_key.key]
ssh_options = dict(
BatchMode='yes',
StrictHostKeyChecking='no',
UserKnownHostsFile='/dev/null',
ServerAliveInterval=15,
ServerAliveCountMax=4,
)
for ssh_option in sorted(ssh_options):
self.ssh_args += ['-o', '%s=%s' % (ssh_option, ssh_options[ssh_option])]
if self.core_ci.platform == 'freebsd':
if self.core_ci.provider == 'aws':
self.become = ['su', '-l', 'root', '-c']
elif self.core_ci.provider == 'azure':
self.become = ['sudo', '-in', 'sh', '-c']
else:
raise NotImplementedError('provider %s has not been implemented' % self.core_ci.provider)
elif self.core_ci.platform == 'osx':
self.become = ['sudo', '-in', 'PATH=/usr/local/bin:$PATH']
elif self.core_ci.platform == 'rhel':
self.become = ['sudo', '-in', 'bash', '-c']
def setup(self):
"""Start instance and wait for it to become ready and respond to an ansible ping."""
self.wait()
if isinstance(self.core_ci.args, ShellConfig):
if self.core_ci.args.raw:
return
self.configure()
self.upload_source()
def wait(self):
"""Wait for instance to respond to SSH."""
for dummy in range(1, 90):
try:
self.ssh('id')
return
except SubprocessError:
time.sleep(10)
raise ApplicationError('Timeout waiting for %s/%s instance %s.' %
(self.core_ci.platform, self.core_ci.version, self.core_ci.instance_id))
def configure(self):
"""Configure remote host for testing."""
self.upload('test/runner/setup/remote.sh', '/tmp')
self.ssh('chmod +x /tmp/remote.sh && /tmp/remote.sh %s' % self.core_ci.platform)
def upload_source(self):
"""Upload and extract source."""
with tempfile.NamedTemporaryFile(prefix='ansible-source-', suffix='.tgz') as local_source_fd:
remote_source_dir = '/tmp'
remote_source_path = os.path.join(remote_source_dir, os.path.basename(local_source_fd.name))
if not self.core_ci.args.explain:
lib.pytar.create_tarfile(local_source_fd.name, '.', lib.pytar.DefaultTarFilter())
self.upload(local_source_fd.name, remote_source_dir)
self.ssh('rm -rf ~/ansible && mkdir ~/ansible && cd ~/ansible && tar oxzf %s' % remote_source_path)
def download(self, remote, local):
"""
:type remote: str
:type local: str
"""
self.scp('%s@%s:%s' % (self.core_ci.connection.username, self.core_ci.connection.hostname, remote), local)
def upload(self, local, remote):
"""
:type local: str
:type remote: str
"""
self.scp(local, '%s@%s:%s' % (self.core_ci.connection.username, self.core_ci.connection.hostname, remote))
def ssh(self, command, options=None):
"""
:type command: str | list[str]
:type options: list[str] | None
"""
if not options:
options = []
if isinstance(command, list):
command = ' '.join(pipes.quote(c) for c in command)
run_command(self.core_ci.args,
['ssh', '-tt', '-q'] + self.ssh_args +
options +
['-p', str(self.core_ci.connection.port),
'%s@%s' % (self.core_ci.connection.username, self.core_ci.connection.hostname)] +
self.become + [pipes.quote(command)])
def scp(self, src, dst):
"""
:type src: str
:type dst: str
"""
for dummy in range(1, 10):
try:
run_command(self.core_ci.args,
['scp'] + self.ssh_args +
['-P', str(self.core_ci.connection.port), '-q', '-r', src, dst])
return
except SubprocessError:
time.sleep(10)
raise ApplicationError('Failed transfer: %s -> %s' % (src, dst))