Skip to content

Commit

Permalink
Fix network setup for collections in ansible-test.
Browse files Browse the repository at this point in the history
  • Loading branch information
mattclay committed Feb 22, 2020
1 parent 00b70bf commit 482885e
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 10 deletions.
2 changes: 2 additions & 0 deletions changelogs/fragments/ansible-test-network-collections.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
minor_changes:
- ansible-test now supports provisioning of network resources when testing network collections
6 changes: 2 additions & 4 deletions test/lib/ansible_test/_data/completion/network.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
ios/csr1000v
junos/vmx
junos/vsrx
vyos/1.1.8
ios/csr1000v connection=local
vyos/1.1.8 connection=local
53 changes: 52 additions & 1 deletion test/lib/ansible_test/_internal/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
display,
raw_command,
get_docker_completion,
get_network_completion,
get_remote_completion,
generate_pip_command,
read_lines_without_comments,
Expand Down Expand Up @@ -209,6 +210,10 @@ def parse_args():
else:
epilog = 'Install the "argcomplete" python package to enable tab completion.'

def key_value_type(value): # type: (str) -> t.Tuple[str, str]
"""Wrapper around key_value."""
return key_value(argparse, value)

parser = argparse.ArgumentParser(epilog=epilog)

common = argparse.ArgumentParser(add_help=False)
Expand Down Expand Up @@ -417,6 +422,18 @@ def parse_args():
action='append',
help='network platform/version').completer = complete_network_platform

network_integration.add_argument('--platform-collection',
type=key_value_type,
metavar='PLATFORM=COLLECTION',
action='append',
help='collection used to test platform').completer = complete_network_platform_collection

network_integration.add_argument('--platform-connection',
type=key_value_type,
metavar='PLATFORM=CONNECTION',
action='append',
help='connection used to test platform').completer = complete_network_platform_connection

network_integration.add_argument('--inventory',
metavar='PATH',
help='path to inventory used for tests')
Expand Down Expand Up @@ -639,6 +656,16 @@ def parse_args():
return args


def key_value(argparse, value): # type: (argparse_module, str) -> t.Tuple[str, str]
"""Type parsing and validation for argparse key/value pairs separated by an '=' character."""
parts = value.split('=')

if len(parts) != 2:
raise argparse.ArgumentTypeError('"%s" must be in the format "key=value"' % value)

return tuple(parts)


# noinspection PyProtectedMember
def add_coverage_analyze(coverage_subparsers, coverage_common): # type: (argparse_module._SubParsersAction, argparse_module.ArgumentParser) -> None
"""Add the `coverage analyze` subcommand."""
Expand Down Expand Up @@ -1039,11 +1066,35 @@ def complete_network_platform(prefix, parsed_args, **_):
:type parsed_args: any
:rtype: list[str]
"""
images = read_lines_without_comments(os.path.join(ANSIBLE_TEST_DATA_ROOT, 'completion', 'network.txt'), remove_blank_lines=True)
images = sorted(get_network_completion())

return [i for i in images if i.startswith(prefix) and (not parsed_args.platform or i not in parsed_args.platform)]


def complete_network_platform_collection(prefix, parsed_args, **_):
"""
:type prefix: unicode
:type parsed_args: any
:rtype: list[str]
"""
left = prefix.split('=')[0]
images = sorted(set(image.split('/')[0] for image in get_network_completion()))

return [i + '=' for i in images if i.startswith(left) and (not parsed_args.platform_collection or i not in [x[0] for x in parsed_args.platform_collection])]


def complete_network_platform_connection(prefix, parsed_args, **_):
"""
:type prefix: unicode
:type parsed_args: any
:rtype: list[str]
"""
left = prefix.split('=')[0]
images = sorted(set(image.split('/')[0] for image in get_network_completion()))

return [i + '=' for i in images if i.startswith(left) and (not parsed_args.platform_connection or i not in [x[0] for x in parsed_args.platform_connection])]


def complete_network_testcase(prefix, parsed_args, **_):
"""
:type prefix: unicode
Expand Down
2 changes: 2 additions & 0 deletions test/lib/ansible_test/_internal/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,8 @@ def __init__(self, args):
super(NetworkIntegrationConfig, self).__init__(args, 'network-integration')

self.platform = args.platform # type: t.List[str]
self.platform_collection = dict(args.platform_collection or []) # type: t.Dict[str, str]
self.platform_connection = dict(args.platform_connection or []) # type: t.Dict[str, str]
self.inventory = args.inventory # type: str
self.testcase = args.testcase # type: str

Expand Down
9 changes: 6 additions & 3 deletions test/lib/ansible_test/_internal/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
generate_pip_command,
find_python,
get_docker_completion,
get_network_settings,
get_remote_completion,
cmd_quote,
ANSIBLE_LIB_ROOT,
Expand Down Expand Up @@ -560,10 +561,12 @@ def network_inventory(remotes):
ansible_host=remote.connection.hostname,
ansible_user=remote.connection.username,
ansible_ssh_private_key_file=os.path.abspath(remote.ssh_key.key),
ansible_network_os=remote.platform,
ansible_connection='local'
)

settings = get_network_settings(remote.args, remote.platform, remote.version)

options.update(settings.inventory_vars)

groups[remote.platform].append(
'%s %s' % (
remote.name.replace('.', '-'),
Expand Down Expand Up @@ -1401,7 +1404,7 @@ def command_integration_role(args, target, start_at_task, test_dir, inventory_pa
win_output_dir=r'C:\ansible_testing',
))
elif isinstance(args, NetworkIntegrationConfig):
hosts = target.name[:target.name.find('_')]
hosts = target.network_platform
gather_facts = False
else:
hosts = 'testhost'
Expand Down
8 changes: 6 additions & 2 deletions test/lib/ansible_test/_internal/manage_ci.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
cmd_quote,
display,
ANSIBLE_TEST_DATA_ROOT,
get_network_settings,
)

from .util_common import (
Expand Down Expand Up @@ -149,19 +150,22 @@ def __init__(self, core_ci):

def wait(self):
"""Wait for instance to respond to ansible ping."""
settings = get_network_settings(self.core_ci.args, self.core_ci.platform, self.core_ci.version)

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,
] + [
'%s=%s' % (key, value) for key, value in settings.inventory_vars.items()
]

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,
'-m', '%s%s_command' % (settings.collection + '.' if settings.collection else '', self.core_ci.platform),
'-a', 'commands=?',
'-u', self.core_ci.connection.username,
'-i', '%s,' % name,
Expand Down
4 changes: 4 additions & 0 deletions test/lib/ansible_test/_internal/target.py
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,10 @@ def __init__(self, path, modules, prefixes):
self.needs_file = tuple(sorted(set('/'.join(g.split('/')[2:]) for g in groups if
g.startswith('needs/file/') and not g.startswith('needs/file/%s/' % targets_relative_path))))

# network platform
networks = [g.split('/')[1] for g in groups if g.startswith('network/')]
self.network_platform = networks[0] if networks else None

for group in itertools.islice(groups, 0, len(groups)):
if '/' in group:
parts = group.split('/')
Expand Down
32 changes: 32 additions & 0 deletions test/lib/ansible_test/_internal/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@

DOCKER_COMPLETION = {} # type: t.Dict[str, t.Dict[str, str]]
REMOTE_COMPLETION = {} # type: t.Dict[str, t.Dict[str, str]]
NETWORK_COMPLETION = {} # type: t.Dict[str, t.Dict[str, str]]
PYTHON_PATHS = {} # type: t.Dict[str, str]

try:
Expand Down Expand Up @@ -134,6 +135,13 @@ def get_remote_completion():
return get_parameterized_completion(REMOTE_COMPLETION, 'remote')


def get_network_completion():
"""
:rtype: dict[str, dict[str, str]]
"""
return get_parameterized_completion(NETWORK_COMPLETION, 'network')


def get_parameterized_completion(cache, name):
"""
:type cache: dict[str, dict[str, str]]
Expand Down Expand Up @@ -743,6 +751,30 @@ def __init__(self, name):
self.name = name


class NetworkPlatformSettings:
"""Settings required for provisioning a network platform."""
def __init__(self, collection, inventory_vars): # type: (str, t.Type[str, str]) -> None
self.collection = collection
self.inventory_vars = inventory_vars


def get_network_settings(args, platform, version): # type: (NetworkIntegrationConfig, str, str) -> NetworkPlatformSettings
"""Returns settings for the given network platform and version."""
platform_version = '%s/%s' % (platform, version)
completion = get_network_completion().get(platform_version, {})
collection = args.platform_collection.get(platform, completion.get('collection'))

settings = NetworkPlatformSettings(
collection,
dict(
ansible_connection=args.platform_connection.get(platform, completion.get('connection')),
ansible_network_os='%s.%s' % (collection, platform) if collection else platform,
)
)

return settings


def docker_qualify_image(name):
"""
:type name: str
Expand Down

0 comments on commit 482885e

Please sign in to comment.