Version in base suite: 2.14.16-0+deb12u1 Base version: ansible-core_2.14.16-0+deb12u1 Target version: ansible-core_2.14.18-0+deb12u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/a/ansible-core/ansible-core_2.14.16-0+deb12u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/a/ansible-core/ansible-core_2.14.18-0+deb12u1.dsc PKG-INFO | 2 changelogs/CHANGELOG-v2.14.rst | 46 changelogs/changelog.yaml | 75 + debian/changelog | 29 debian/patches/0022-CVE-2024-11079.patch | 333 ++++++ debian/patches/series | 1 debian/salsa-ci.yml | 6 debian/tests/ansible-test-integration.py | 422 ++++++++ debian/tests/control | 83 + debian/tests/testbed-setup.sh | 42 lib/ansible/executor/task_executor.py | 4 lib/ansible/galaxy/role.py | 22 lib/ansible/module_utils/ansible_release.py | 2 lib/ansible/modules/user.py | 17 lib/ansible/plugins/action/include_vars.py | 3 lib/ansible/release.py | 2 lib/ansible_core.egg-info/PKG-INFO | 2 lib/ansible_core.egg-info/SOURCES.txt | 80 - packaging/release.py | 102 + pyproject.toml | 2 test/integration/targets/ansible-galaxy-role/tasks/valid-role-symlinks.yml | 116 -- test/integration/targets/ansible-vault/runme.sh | 23 test/integration/targets/apt_repository/tasks/apt.yml | 25 test/integration/targets/collections_runtime_pythonpath/ansible-collection-python-dist-boo/pyproject.toml | 1 test/integration/targets/collections_runtime_pythonpath/runme.sh | 6 test/integration/targets/dnf/filter_plugins/dnf_module_list.py | 40 test/integration/targets/dnf/tasks/main.yml | 26 test/integration/targets/dnf/tasks/modularity.yml | 9 test/integration/targets/dnf/vars/CentOS.yml | 2 test/integration/targets/dnf/vars/Fedora.yml | 6 test/integration/targets/dnf/vars/RedHat-9.yml | 2 test/integration/targets/dnf/vars/RedHat.yml | 2 test/integration/targets/incidental_vyos_config/aliases | 2 test/integration/targets/incidental_vyos_config/defaults/main.yaml | 3 test/integration/targets/incidental_vyos_config/tasks/cli.yaml | 26 test/integration/targets/incidental_vyos_config/tasks/cli_config.yaml | 18 test/integration/targets/incidental_vyos_config/tasks/main.yaml | 3 test/integration/targets/incidental_vyos_config/tests/cli/backup.yaml | 113 -- test/integration/targets/incidental_vyos_config/tests/cli/check_config.yaml | 63 - test/integration/targets/incidental_vyos_config/tests/cli/comment.yaml | 34 test/integration/targets/incidental_vyos_config/tests/cli/config.cfg | 3 test/integration/targets/incidental_vyos_config/tests/cli/save.yaml | 54 - test/integration/targets/incidental_vyos_config/tests/cli/simple.yaml | 53 - test/integration/targets/incidental_vyos_config/tests/cli_config/cli_backup.yaml | 114 -- test/integration/targets/incidental_vyos_config/tests/cli_config/cli_basic.yaml | 28 test/integration/targets/incidental_vyos_config/tests/cli_config/cli_comment.yaml | 30 test/integration/targets/incidental_vyos_lldp_interfaces/aliases | 2 test/integration/targets/incidental_vyos_lldp_interfaces/defaults/main.yaml | 3 test/integration/targets/incidental_vyos_lldp_interfaces/meta/main.yaml | 3 test/integration/targets/incidental_vyos_lldp_interfaces/tasks/cli.yaml | 19 test/integration/targets/incidental_vyos_lldp_interfaces/tasks/main.yaml | 2 test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/_populate.yaml | 14 test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/_populate_intf.yaml | 10 test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/_remove_config.yaml | 8 test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/deleted.yaml | 46 test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/empty_config.yaml | 36 test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/merged.yaml | 58 - test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/overridden.yaml | 49 test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/replaced.yaml | 63 - test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/rtt.yaml | 57 - test/integration/targets/incidental_vyos_lldp_interfaces/vars/main.yaml | 130 -- test/integration/targets/incidental_vyos_prepare_tests/aliases | 1 test/integration/targets/incidental_vyos_prepare_tests/tasks/main.yaml | 13 test/integration/targets/include_vars-ad-hoc/dir/encrypted.yml | 6 test/integration/targets/include_vars-ad-hoc/runme.sh | 22 test/integration/targets/include_vars-ad-hoc/vaultpass | 3 test/integration/targets/module_utils_facts.system.selinux/tasks/main.yml | 2 test/integration/targets/network_cli/aliases | 3 test/integration/targets/network_cli/passworded_user.yml | 14 test/integration/targets/network_cli/runme.sh | 27 test/integration/targets/network_cli/setup.yml | 14 test/integration/targets/network_cli/teardown.yml | 14 test/integration/targets/no_log/action_plugins/action_sets_no_log.py | 8 test/integration/targets/no_log/ansible_no_log_in_result.yml | 13 test/integration/targets/no_log/dynamic.yml | 29 test/integration/targets/no_log/no_log_config.yml | 2 test/integration/targets/no_log/no_log_local.yml | 15 test/integration/targets/no_log/no_log_suboptions.yml | 14 test/integration/targets/no_log/no_log_suboptions_invalid.yml | 29 test/integration/targets/no_log/runme.sh | 16 test/integration/targets/no_log/secretvars.yml | 32 test/integration/targets/user/tasks/main.yml | 5 test/integration/targets/user/tasks/ssh_keygen.yml | 100 + test/integration/targets/user/tasks/test_local.yml | 9 test/lib/ansible_test/_data/completion/network.txt | 1 test/lib/ansible_test/_data/completion/windows.txt | 2 test/lib/ansible_test/_internal/docker_util.py | 10 test/lib/ansible_test/_internal/pypi_proxy.py | 11 test/lib/ansible_test/_internal/util.py | 24 test/lib/ansible_test/_util/target/setup/bootstrap.sh | 9 test/sanity/code-smell/package-data.py | 2 test/sanity/ignore.txt | 3 test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/action/vyos.py | 129 -- test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/cliconf/vyos.py | 343 ------ test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/doc_fragments/vyos.py | 63 - test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/facts/facts.py | 22 test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py | 263 ----- test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/interfaces/interfaces.py | 69 - test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/l3_interfaces/l3_interfaces.py | 81 - test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/lag_interfaces/lag_interfaces.py | 80 - test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/lldp_global/lldp_global.py | 56 - test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/lldp_interfaces/lldp_interfaces.py | 89 - test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/static_routes/static_routes.py | 99 - test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/config/lldp_interfaces/lldp_interfaces.py | 438 -------- test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/facts.py | 83 - test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py | 380 ------- test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/interfaces/interfaces.py | 134 -- test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py | 143 -- test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py | 152 -- test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/legacy/base.py | 162 --- test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/lldp_global/lldp_global.py | 116 -- test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/lldp_interfaces/lldp_interfaces.py | 155 --- test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py | 181 --- test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/utils/utils.py | 231 ---- test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/vyos.py | 124 -- test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_command.py | 223 ---- test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_config.py | 354 ------ test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_facts.py | 174 --- test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_lldp_interfaces.py | 513 ---------- test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/terminal/vyos.py | 53 - 120 files changed, 1606 insertions(+), 6334 deletions(-) diff -Nru ansible-core-2.14.16/PKG-INFO ansible-core-2.14.18/PKG-INFO --- ansible-core-2.14.16/PKG-INFO 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/PKG-INFO 2024-11-04 18:35:24.000000000 +0000 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: ansible-core -Version: 2.14.16 +Version: 2.14.18 Summary: Radically simple IT automation Home-page: https://ansible.com/ Author: Ansible, Inc. diff -Nru ansible-core-2.14.16/changelogs/CHANGELOG-v2.14.rst ansible-core-2.14.18/changelogs/CHANGELOG-v2.14.rst --- ansible-core-2.14.16/changelogs/CHANGELOG-v2.14.rst 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/changelogs/CHANGELOG-v2.14.rst 2024-11-04 18:35:24.000000000 +0000 @@ -5,6 +5,51 @@ .. contents:: Topics +v2.14.18 +======== + +Release Summary +--------------- + +| Release Date: 2024-11-04 +| `Porting Guide `__ + + +Minor Changes +------------- + +- ansible-test - Improve container runtime probe error handling. When unexpected probe output is encountered, an error with more useful debugging information is provided. +- ansible-test - Removed support for provisioning remote Windows 2012 and 2012-R2 instances. +- ansible-test - Removed the ``vyos/1.1.8`` network remote as it is no longer functional. +- ansible-test - Update ``pypi-test-container`` to version 3.1.0. + +Security Fixes +-------------- + +- include_vars action - Ensure that result masking is correctly requested when vault-encrypted files are read. (CVE-2024-8775) +- task result processing - Ensure that action-sourced result masking (``_ansible_no_log=True``) is preserved. (CVE-2024-8775) +- user action won't allow ssh-keygen, chown and chmod to run on existing ssh public key file, avoiding traversal on existing symlinks (CVE-2024-9902). + +Bugfixes +-------- + +- user action will now require O(force) to overwrite the public part of an ssh key when generating ssh keys, as was already the case for the private part. + +v2.14.17 +======== + +Release Summary +--------------- + +| Release Date: 2024-05-20 +| `Porting Guide `__ + + +Bugfixes +-------- + +- ansible-test - Automatically enable the PyPI proxy for the ``centos7`` container to restore the ability to use ``pip`` in that container. + v2.14.16 ======== @@ -66,6 +111,7 @@ -------- - ``ansible-test sanity --test runtime-metadata`` - add ``action_plugin`` as a valid field for modules in the schema (https://github.com/ansible/ansible/pull/82562). +- ansible-galaxy role install - fix symlinks (https://github.com/ansible/ansible/issues/82702, https://github.com/ansible/ansible/issues/81965). - ansible-galaxy role install - normalize tarfile paths and symlinks using ``ansible.utils.path.unfrackpath`` and consider them valid as long as the realpath is in the tarfile's role directory (https://github.com/ansible/ansible/issues/81965). - unsafe data - Enable directly using ``AnsibleUnsafeText`` with Python ``pathlib`` (https://github.com/ansible/ansible/issues/82414) diff -Nru ansible-core-2.14.16/changelogs/changelog.yaml ansible-core-2.14.18/changelogs/changelog.yaml --- ansible-core-2.14.16/changelogs/changelog.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/changelogs/changelog.yaml 2024-11-04 18:35:24.000000000 +0000 @@ -1016,6 +1016,8 @@ bugfixes: - '``ansible-test sanity --test runtime-metadata`` - add ``action_plugin`` as a valid field for modules in the schema (https://github.com/ansible/ansible/pull/82562).' + - ansible-galaxy role install - fix symlinks (https://github.com/ansible/ansible/issues/82702, + https://github.com/ansible/ansible/issues/81965). - ansible-galaxy role install - normalize tarfile paths and symlinks using ``ansible.utils.path.unfrackpath`` and consider them valid as long as the realpath is in the tarfile's role directory (https://github.com/ansible/ansible/issues/81965). @@ -1102,6 +1104,79 @@ - 82574-ansible-test-ansible-doc-underscore.yml - winrm-task-timeout.yml release_date: '2024-04-16' + 2.14.17: + changes: + release_summary: '| Release Date: 2024-05-20 + + | `Porting Guide `__ + + ' + codename: C'mon Everybody + fragments: + - 2.14.17_summary.yaml + release_date: '2024-05-20' + 2.14.17rc1: + changes: + bugfixes: + - ansible-test - Automatically enable the PyPI proxy for the ``centos7`` container + to restore the ability to use ``pip`` in that container. + release_summary: '| Release Date: 2024-05-13 + + | `Porting Guide `__ + + ' + codename: C'mon Everybody + fragments: + - 2.14.17rc1_summary.yaml + - ansible-test-centos7-pypi-proxy.yml + release_date: '2024-05-13' + 2.14.18: + changes: + release_summary: '| Release Date: 2024-11-04 + + | `Porting Guide `__ + + ' + codename: C'mon Everybody + fragments: + - 2.14.18_summary.yaml + release_date: '2024-11-04' + 2.14.18rc1: + changes: + bugfixes: + - user action will now require O(force) to overwrite the public part of an ssh + key when generating ssh keys, as was already the case for the private part. + minor_changes: + - ansible-test - Improve container runtime probe error handling. When unexpected + probe output is encountered, an error with more useful debugging information + is provided. + - ansible-test - Removed support for provisioning remote Windows 2012 and 2012-R2 + instances. + - ansible-test - Removed the ``vyos/1.1.8`` network remote as it is no longer + functional. + - ansible-test - Update ``pypi-test-container`` to version 3.1.0. + release_summary: '| Release Date: 2024-10-29 + + | `Porting Guide `__ + + ' + security_fixes: + - include_vars action - Ensure that result masking is correctly requested when + vault-encrypted files are read. (CVE-2024-8775) + - task result processing - Ensure that action-sourced result masking (``_ansible_no_log=True``) + is preserved. (CVE-2024-8775) + - user action won't allow ssh-keygen, chown and chmod to run on existing ssh + public key file, avoiding traversal on existing symlinks (CVE-2024-9902). + codename: C'mon Everybody + fragments: + - 2.14.18rc1_summary.yaml + - ansible-test-probe-error-handling.yml + - ansible-test-pypi-test-container-update.yml + - ansible-test-vyos.yml + - ansible-test-windows-2012.yml + - cve-2024-8775.yml + - user_ssh_fix.yml + release_date: '2024-10-29' 2.14.1rc1: changes: bugfixes: diff -Nru ansible-core-2.14.16/debian/changelog ansible-core-2.14.18/debian/changelog --- ansible-core-2.14.16/debian/changelog 2024-06-25 14:52:07.000000000 +0000 +++ ansible-core-2.14.18/debian/changelog 2024-12-04 17:12:49.000000000 +0000 @@ -1,3 +1,32 @@ +ansible-core (2.14.18-0+deb12u1) bookworm; urgency=medium + + [ Lee Garrett ] + * New stable bugfix release + * Add integration tests to autopkgtest + * Fix CVE-2024-11079: This vulnerability allows attackers to bypass unsafe + content protections using the hostvars object to reference and execute + templated content. This issue can lead to arbitrary code execution if remote + data or module outputs are improperly templated within playbooks. + + [ Bastien Roucariès ] + * Fix CVE-2024-8775: A flaw was found in Ansible, + where sensitive information stored in Ansible Vault files can be exposed in + plaintext during the execution of a playbook. This occurs when using tasks + such as include_vars to load vaulted variables without setting the no_log: + true parameter, resulting in sensitive data being printed in the playbook + output or logs. This can lead to the unintentional disclosure of secrets + like passwords or API keys, compromising security and potentially allowing + unauthorized access or actions. (Closes: #1082851) + * Fix CVE-2024-9902: A flaw was found in Ansible. + The ansible-core `user` module can allow an unprivileged user to silently + create or replace the contents of any file on any system path and take + ownership of it when a privileged user executes the `user` module against + the unprivileged user's home directory. If the unprivileged user has + traversal permissions on the directory containing the exploited target file, + they retain full control over the contents of the file as its owner. + + -- Lee Garrett Wed, 04 Dec 2024 18:12:49 +0100 + ansible-core (2.14.16-0+deb12u1) bookworm; urgency=medium * New stable release (Closes: #1070193) diff -Nru ansible-core-2.14.16/debian/patches/0022-CVE-2024-11079.patch ansible-core-2.14.18/debian/patches/0022-CVE-2024-11079.patch --- ansible-core-2.14.16/debian/patches/0022-CVE-2024-11079.patch 1970-01-01 00:00:00.000000000 +0000 +++ ansible-core-2.14.18/debian/patches/0022-CVE-2024-11079.patch 2024-12-04 17:12:49.000000000 +0000 @@ -0,0 +1,333 @@ +Description: Fix CVE-2024-11079 hostvars unsafe context + This vulnerability allows attackers to bypass unsafe content protections using + the hostvars object to reference and execute templated content. This issue can + lead to arbitrary code execution if remote data or module outputs are + improperly templated within playbooks. + . + This patch is based on the backport of the patch to ansible-core 2.16 +Origin: backport, https://github.com/ansible/ansible/pull/84353 +Bug: https://github.com/ansible/ansible/pull/84339 +Bug-Debian: https://bugs.debian.org/1088106 +Reviewed-by: Lee Garrett +Last-Update: 2024-12-04 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- /dev/null ++++ b/changelogs/fragments/unsafe_hostvars_fix.yml +@@ -0,0 +1,2 @@ ++security_fixes: ++ - Templating will not prefer AnsibleUnsafe when a variable is referenced via hostvars - CVE-2024-11079 +--- a/lib/ansible/template/__init__.py ++++ b/lib/ansible/template/__init__.py +@@ -49,7 +49,7 @@ + from ansible.module_utils._text import to_native, to_text, to_bytes + from ansible.module_utils.common.collections import is_sequence + from ansible.plugins.loader import filter_loader, lookup_loader, test_loader +-from ansible.template.native_helpers import ansible_native_concat, ansible_eval_concat, ansible_concat ++from ansible.template.native_helpers import AnsibleUndefined, ansible_native_concat, ansible_eval_concat, ansible_concat + from ansible.template.template import AnsibleJ2Template + from ansible.template.vars import AnsibleJ2Vars + from ansible.utils.display import Display +@@ -296,35 +296,6 @@ + return _update_wrapper(wrapper, func) + + +-class AnsibleUndefined(StrictUndefined): +- ''' +- A custom Undefined class, which returns further Undefined objects on access, +- rather than throwing an exception. +- ''' +- def __getattr__(self, name): +- if name == '__UNSAFE__': +- # AnsibleUndefined should never be assumed to be unsafe +- # This prevents ``hasattr(val, '__UNSAFE__')`` from evaluating to ``True`` +- raise AttributeError(name) +- # Return original Undefined object to preserve the first failure context +- return self +- +- def __getitem__(self, key): +- # Return original Undefined object to preserve the first failure context +- return self +- +- def __repr__(self): +- return 'AnsibleUndefined(hint={0!r}, obj={1!r}, name={2!r})'.format( +- self._undefined_hint, +- self._undefined_obj, +- self._undefined_name +- ) +- +- def __contains__(self, item): +- # Return original Undefined object to preserve the first failure context +- return self +- +- + class AnsibleContext(Context): + ''' + A custom context, which intercepts resolve_or_missing() calls and sets a flag +--- a/lib/ansible/template/native_helpers.py ++++ b/lib/ansible/template/native_helpers.py +@@ -7,13 +7,19 @@ + + + import ast ++from collections.abc import Mapping + from itertools import islice, chain + from types import GeneratorType + ++from ansible.module_utils.common.collections import is_sequence + from ansible.module_utils._text import to_text + from ansible.module_utils.six import string_types + from ansible.parsing.yaml.objects import AnsibleVaultEncryptedUnicode + from ansible.utils.native_jinja import NativeJinjaText ++from ansible.utils.unsafe_proxy import wrap_var ++import ansible.module_utils.compat.typing as t ++ ++from jinja2.runtime import StrictUndefined + + + _JSON_MAP = { +@@ -30,6 +36,40 @@ + return ast.Constant(value=_JSON_MAP[node.id]) + + ++def _is_unsafe(value: t.Any) -> bool: ++ """ ++ Our helper function, which will also recursively check dict and ++ list entries due to the fact that they may be repr'd and contain ++ a key or value which contains jinja2 syntax and would otherwise ++ lose the AnsibleUnsafe value. ++ """ ++ to_check = [value] ++ seen = set() ++ ++ while True: ++ if not to_check: ++ break ++ ++ val = to_check.pop(0) ++ val_id = id(val) ++ ++ if val_id in seen: ++ continue ++ seen.add(val_id) ++ ++ if isinstance(val, AnsibleUndefined): ++ continue ++ if isinstance(val, Mapping): ++ to_check.extend(val.keys()) ++ to_check.extend(val.values()) ++ elif is_sequence(val): ++ to_check.extend(val) ++ elif getattr(val, '__UNSAFE__', False): ++ return True ++ ++ return False ++ ++ + def ansible_eval_concat(nodes): + """Return a string of concatenated compiled nodes. Throw an undefined error + if any of the nodes is undefined. +@@ -45,17 +85,28 @@ + if not head: + return '' + ++ unsafe = False ++ + if len(head) == 1: + out = head[0] + + if isinstance(out, NativeJinjaText): + return out + ++ unsafe = _is_unsafe(out) + out = to_text(out) + else: + if isinstance(nodes, GeneratorType): + nodes = chain(head, nodes) +- out = ''.join([to_text(v) for v in nodes]) ++ ++ out_values = [] ++ for v in nodes: ++ if not unsafe and _is_unsafe(v): ++ unsafe = True ++ ++ out_values.append(to_text(v)) ++ ++ out = ''.join(out_values) + + # if this looks like a dictionary, list or bool, convert it to such + if out.startswith(('{', '[')) or out in ('True', 'False'): +@@ -70,6 +121,9 @@ + except (ValueError, SyntaxError, MemoryError): + pass + ++ if unsafe: ++ out = wrap_var(out) ++ + return out + + +@@ -80,7 +134,19 @@ + + Used in Templar.template() when jinja2_native=False and convert_data=False. + """ +- return ''.join([to_text(v) for v in nodes]) ++ unsafe = False ++ values = [] ++ for v in nodes: ++ if not unsafe and _is_unsafe(v): ++ unsafe = True ++ ++ values.append(to_text(v)) ++ ++ out = ''.join(values) ++ if unsafe: ++ out = wrap_var(out) ++ ++ return out + + + def ansible_native_concat(nodes): +@@ -97,6 +163,8 @@ + if not head: + return None + ++ unsafe = False ++ + if len(head) == 1: + out = head[0] + +@@ -117,10 +185,21 @@ + # short-circuit literal_eval for anything other than strings + if not isinstance(out, string_types): + return out ++ ++ unsafe = _is_unsafe(out) ++ + else: + if isinstance(nodes, GeneratorType): + nodes = chain(head, nodes) +- out = ''.join([to_text(v) for v in nodes]) ++ ++ out_values = [] ++ for v in nodes: ++ if not unsafe and _is_unsafe(v): ++ unsafe = True ++ ++ out_values.append(to_text(v)) ++ ++ out = ''.join(out_values) + + try: + evaled = ast.literal_eval( +@@ -130,10 +209,45 @@ + ast.parse(out, mode='eval') + ) + except (ValueError, SyntaxError, MemoryError): ++ if unsafe: ++ out = wrap_var(out) ++ + return out + + if isinstance(evaled, string_types): + quote = out[0] +- return f'{quote}{evaled}{quote}' ++ evaled = f'{quote}{evaled}{quote}' ++ ++ if unsafe: ++ evaled = wrap_var(evaled) + + return evaled ++ ++ ++class AnsibleUndefined(StrictUndefined): ++ """ ++ A custom Undefined class, which returns further Undefined objects on access, ++ rather than throwing an exception. ++ """ ++ def __getattr__(self, name): ++ if name == '__UNSAFE__': ++ # AnsibleUndefined should never be assumed to be unsafe ++ # This prevents ``hasattr(val, '__UNSAFE__')`` from evaluating to ``True`` ++ raise AttributeError(name) ++ # Return original Undefined object to preserve the first failure context ++ return self ++ ++ def __getitem__(self, key): ++ # Return original Undefined object to preserve the first failure context ++ return self ++ ++ def __repr__(self): ++ return 'AnsibleUndefined(hint={0!r}, obj={1!r}, name={2!r})'.format( ++ self._undefined_hint, ++ self._undefined_obj, ++ self._undefined_name ++ ) ++ ++ def __contains__(self, item): ++ # Return original Undefined object to preserve the first failure context ++ return self +--- a/lib/ansible/vars/hostvars.py ++++ b/lib/ansible/vars/hostvars.py +@@ -110,11 +110,12 @@ + return self._find_host(host_name) is not None + + def __iter__(self): +- for host in self._inventory.hosts: +- yield host ++ # include implicit localhost only if it has variables set ++ yield from self._inventory.hosts | {'localhost': self._inventory.localhost} if self._inventory.localhost else {} + + def __len__(self): +- return len(self._inventory.hosts) ++ # include implicit localhost only if it has variables set ++ return len(self._inventory.hosts) + (1 if self._inventory.localhost else 0) + + def __repr__(self): + out = {} +--- /dev/null ++++ b/test/integration/targets/template/cve-2024-11079.yml +@@ -0,0 +1,30 @@ ++- name: test CVE-2024-11079 loop variables preserve unsafe hostvars ++ hosts: localhost ++ gather_facts: false ++ tasks: ++ - set_fact: ++ foo: ++ safe: ++ prop: '{{ "{{" }} unsafe_var {{ "}}" }}' ++ unsafe: ++ prop: !unsafe '{{ unsafe_var }}' ++ ++ - name: safe var through hostvars loop is templated ++ assert: ++ that: ++ - item.prop == expected ++ loop: ++ - "{{ hostvars['localhost']['foo']['safe'] }}" ++ vars: ++ unsafe_var: bar ++ expected: bar ++ ++ - name: unsafe var through hostvars loop is not templated ++ assert: ++ that: ++ - item.prop == expected ++ loop: ++ - "{{ hostvars['localhost']['foo']['unsafe'] }}" ++ vars: ++ unsafe_var: bar ++ expected: !unsafe '{{ unsafe_var }}' +--- a/test/integration/targets/template/runme.sh ++++ b/test/integration/targets/template/runme.sh +@@ -38,6 +38,10 @@ + # ensure unsafe is preserved, even with extra newlines + ansible-playbook unsafe.yml -v "$@" + ++# CVE 2024-11079 ++ANSIBLE_JINJA2_NATIVE=true ansible-playbook cve-2024-11079.yml -v "$@" ++ANSIBLE_JINJA2_NATIVE=false ansible-playbook cve-2024-11079.yml -v "$@" ++ + # ensure Jinja2 overrides from a template are used + ansible-playbook in_template_overrides.yml -v "$@" + diff -Nru ansible-core-2.14.16/debian/patches/series ansible-core-2.14.18/debian/patches/series --- ansible-core-2.14.16/debian/patches/series 2024-06-25 13:35:56.000000000 +0000 +++ ansible-core-2.14.18/debian/patches/series 2024-12-04 17:12:49.000000000 +0000 @@ -1,3 +1,4 @@ 0005-use-py3.patch 0009-resolvelib_compat.patch 0010-fix-facter.patch +0022-CVE-2024-11079.patch diff -Nru ansible-core-2.14.16/debian/salsa-ci.yml ansible-core-2.14.18/debian/salsa-ci.yml --- ansible-core-2.14.16/debian/salsa-ci.yml 1970-01-01 00:00:00.000000000 +0000 +++ ansible-core-2.14.18/debian/salsa-ci.yml 2024-12-04 17:12:49.000000000 +0000 @@ -0,0 +1,6 @@ +include: + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/recipes/debian.yml + +variables: + RELEASE: 'bookworm' + SALSA_CI_IMAGES_LINTIAN: "${SALSA_CI_IMAGES}/lintian:bookworm" diff -Nru ansible-core-2.14.16/debian/tests/ansible-test-integration.py ansible-core-2.14.18/debian/tests/ansible-test-integration.py --- ansible-core-2.14.16/debian/tests/ansible-test-integration.py 1970-01-01 00:00:00.000000000 +0000 +++ ansible-core-2.14.18/debian/tests/ansible-test-integration.py 2024-12-04 17:12:49.000000000 +0000 @@ -0,0 +1,422 @@ +#!/usr/bin/python3 + +import argparse +import os +import subprocess +import sys +import time + +# helper function to print log messages depending on verbosity +def log (level, *msg): + if args.verbose >= level: + print(' '.join([str(x) for x in msg]), flush=True) + +sys.dont_write_bytecode = True + +# helper function to run and log processes +def runprog (name, progargs, env=None, exit_on_failure=True): + log(1, 'Running', name) + proc = subprocess.run( + progargs, + timeout = 300, + capture_output = True, + text = True, + env = env, + ) + if proc.returncode != 0: + log(0,"#"*72) + log(0,'Running', name, 'failed!') + log(0,"Returncode:", proc.returncode) + log(1,"#### STDOUT ####") + log(1, proc.stdout) + log(1, "#### STDERR ####") + log(1, proc.stderr) + log(0,"#"*72) + if exit_on_failure: + sys.exit(proc.returncode) + return proc + +def locale_debug(name): + if args.verbose >= 3: + log(2, name) + log(2, 'output of /usr/bin/locale:') + subprocess.run(['/usr/bin/locale']) + prog = runprog('cat /etc/default/locale', ['cat', '/etc/default/locale']) + log(2, prog.stdout, prog.stderr) + # grep will exit 1 when it doesn't return anything, so don't fail on it. + prog = runprog( + 'grep -v -P \'^#|^$\' /etc/locale.gen', + ['grep', '-v', '-P', '^#|^$', '/etc/locale.gen'], + exit_on_failure=False, + ) + log(2, prog.stdout, prog.stderr) + +parser = argparse.ArgumentParser( + prog='ansible-test-integration.py', + description='python script to run ansible-test integration against the Debian source package', +) + +# Whether to run the default tests not in any other list +parser.add_argument( + '--default-tests', + action=argparse.BooleanOptionalAction, + default=False, + help='Run the default tests not listed anywhere else. (default: no)', +) + +parser.add_argument( + '--requires-root', + action=argparse.BooleanOptionalAction, + default=False, + help='Run tests that require root. (default: no)', +) + +parser.add_argument( + '--requires-ssh', + action=argparse.BooleanOptionalAction, + default=False, + help='Run tests that require a specially configured SSH server. (default: no)' +) + +parser.add_argument( + '--requires-apt-mark-manual', + action=argparse.BooleanOptionalAction, + default=False, + help='Run tests that do "apt-mark manual" on certain packages. (default: no)', +) + +parser.add_argument( + '--fails-on-pip', + action=argparse.BooleanOptionalAction, + default=False, + help='Run tests that run "pip3 install" on certain modules. (default: no)', +) + +parser.add_argument( + '--failing', + action=argparse.BooleanOptionalAction, + default=False, + help='Run tests that fail on other reasons. (default: no)', +) + +parser.add_argument( + '--setup', + action=argparse.BooleanOptionalAction, + default=True, + help='Setup testbed via sudo. (default: yes)', +) + +parser.add_argument( + '--dry-run', + action=argparse.BooleanOptionalAction, + default=False, + help='Print the list of targets without actually running them', +) + +parser.add_argument( + '--check-test-lists', + action=argparse.BooleanOptionalAction, + default=False, + help='Check if all the hardcoded integration tests from the other \ + flags (e.g. --requires-root, --failing, ,etc.) still exist \ + in the ansible-core test suite. Defaults to no. When set, \ + skips everything else.', +) +parser.add_argument( + '--verbose', '-v', + action='count', + default=1, + help='verbosity between 0 and 5. 0 will only emit errors. 1=INFO. \ + More for increasing levels of debug info. Defaults to 1.', +) + +args = parser.parse_args() + +# Don't set up test env when checking test lists +if args.check_test_lists: + args.setup = False + +locale_debug('locale before setting os.environ:') +os.environ['LANG'] = 'en_US.UTF-8' +locale_debug('locale after setting os.environ:') + +if args.setup is True: + proc = runprog('testbed-setup.sh', ['sudo', './debian/tests/testbed-setup.sh']) + log(2,"#### STDOUT ####") + log(2, proc.stdout) + log(2, "#### STDERR ####") + log(2, proc.stderr) + locale_debug('locale after running testbed-setup.sh:') + +# integration tests requiring root in some form +integration_requires_root = { + 'ansible-test-container', # has needs/root in aliases file + 'ansible-vault', # https://github.com/ansible/ansible/issues/83837 + 'any_errors_fatal', # PermissionError: [Errno 13] Permission denied: '/tmp/autopkgtest.EkgNeu/build.sJ3/real-tree/test/results/.tmp/output_dir/venv' + 'apt_key', # add/removes apt keys + 'become', # calls setup_test_user + 'blockinfile', # setup_remote_tmp_dir handler fails (hidden output) + 'callback_default', # checks for an error that has root's homedir + 'changed_when', # PermissionError: [Errno 13] Permission denied: b'/tmp/autopkgtest.EkgNeu/build.sJ3/real-tree/test/results/data/integration-2024-08-14-23-14-30.json' + 'copy', # has needs/root in aliases file + 'cron', # installs packages via setup_cron + 'debconf', # Writes to debconf database + 'dpkg_selections', # needs root to use dpkg + 'facts_linux_network', # has needs/root in aliases file + 'file', # has needs/root in aliases file + 'filter_core', # calls setup_test_user + 'find', # calls setup_test_user, has needs/root in aliases file + 'gathering_facts', # has needs/root in aliases file + 'gathering', # writes to /etc/ansible/facts.d/ + 'group', # wants to add/remove systemd groups + 'hardware_facts', # creates and destroys logical volumes + 'keyword_inheritance', # calls setup_test_user + 'lookup_password', # calls setup_test_user + 'lookup_unvault', # has needs/root in aliases file + #'module_defaults', # requires sudo + 'module_utils', # calls setup_test_user, has needs/root in aliases file + 'noexec', # calls mount + 'old_style_cache_plugins', # has needs/root in aliases file + 'omit', # calls setup_test_user + 'package', # touches /usr/bin/ + 'pip', # has needs/root in aliases file + 'raw', # uses 'su' as become method and doesn't pass a password + 'reboot', # has needs/root in aliases file + 'service', # requires root to create/start/stop services + 'slurp', # calls setup_test_user + 'systemd', # disables/enables services + 'template', # has needs/root in aliases file + 'unsafe_writes', # has needs/root in aliases file + 'user', # has needs/root in aliases file +} + +# integration tests requiring a running ssh server +integration_requires_ssh = { + 'become_unprivileged', + 'cli', + 'connection_paramiko_ssh', + 'connection_ssh', + 'delegate_to', + 'fetch', + 'module_tracebacks', +} + +# integration tests requiring root because the apt module is used to +# install missing packages, or to mark packages as manually installed +integration_requires_apt_mark_manual = { + #'ansible-galaxy-collection-scm', # apt-mark manual git + 'ansible-pull', # apt-mark manual git + #'debconf', # apt-mark manual debconf-utils + 'iptables', # apt-mark manual iptables + 'git', # apt-mark manual git +} + +integration_fails_on_pip = { + 'ansible-galaxy-collection-cli', # fails on pip + 'ansible-inventory', # pip error: externally-managed-environment ## upstream fix + 'builtin_vars_prompt', # passlib: pip error: externally-managed-environment + 'debugger', # pip installs pexpect + 'pause', # pip installs pexpect +} + +integration_failing = { + 'ansible-galaxy-collection-scm': "ansible-core on PyPI depends on resolvelib<1.1.0", + 'ansible-galaxy-role': 'dict object has no attribute lnk_source', ## needs upstream fix? + 'ansible-test-docker': "pwsh doesn't exist in Debian yet", + 'ansible-test': 'installs and runs python libs from remote', + 'ansible-test-installed': 'checks only valid if source tree has bin/ directory', + 'ansible-test-sanity': 'checks are only valid for the source tree', + 'ansible-test-units-forked': '?????', + 'ansible-test-vendoring': 'Should test with ./bin/ansible-test which was removed in v2.18.0', ## needs upstream fix + 'become_su': 'This looks like a false positive, needs upstream confirmation', + 'copy': 'requires /root/.ssh/authorized_keys to exist', + 'facts_d': 'seems to read an unreadable problem without error', ## needs upstream fix + 'infra': 'requires hacking/test-module.py not present', + 'interpreter_discovery_python': 'detects /usr/bin/python3.11, expect python3, detects os_version 12.6, expects it to compare > 10', + 'packaging_cli-doc': 'checks only valid if source tree has bin/ directory', + 'remote_tmp': 'Will often show false positive on: "Test tempdir is removed", needs upstream fixing', + 'service_facts': "Version comparison failed: '<' not supported between instances of 'str' and 'int'", # writes to /usr/sbin/ + 'template_jinja2_non_native': 'no need to test against latest jinja2', + + # tests failing in bookworm, not going to fix them here unless future patches touch those code paths. + 'ansible-galaxy': "hardcoded root; filed MR with upstream", + 'apt': "requires deb-src lines; filed MR with upstream", + 'hostname': "requires upstream fixes; filed MR with upstream", + 'subversion': "sets up a svn repo via apache2 incorrectly; needs upstream fix", + 'unarchive': "tries to install non-existing package language-pack-fr; needs upstream fix", +} + +if subprocess.check_output(['dpkg', '--print-architecture'], text=True).rstrip('\n') not in {'amd64', 'ppc64el'}: + integration_failing['binary_modules_posix'] = 'needs statically-built helloworld_linux_* from ci-files.testing.ansible.com' + +# work around autopkgtest providing the source tree owned by a different user +if args.setup: + runprog('git config hack for normal user', ['git', 'config', '--global', '--add', 'safe.directory', '*']) + runprog('git config hack for root', ['sudo', 'git', 'config', '--global', '--add', 'safe.directory', '*']) + +pyver = str(sys.version_info.major) + '.' + str(sys.version_info.minor) + +overall_test_rc = 0 +failed_tests = [] +succeeded_tests = [] + +# retrieve a list of all integration tests +all_targets_cmd = runprog( + 'ansible-test to retrieve list of targets', + ['ansible-test', 'integration', '--list-targets', '--allow-root', '--allow-destructive'], + env={**os.environ, 'PYTHONPATH': os.path.join(os.getcwd(), 'test', 'lib')}, +) + +# Compile list of all targets +all_targets = set(all_targets_cmd.stdout.splitlines()) + +default_targets = list( + all_targets + - integration_requires_root + - integration_requires_ssh + - integration_requires_apt_mark_manual + - integration_fails_on_pip + - set(integration_failing.keys()) +) + +def check_if_list_in_all_targets( l1, name): + l = l1 - all_targets + global overall_test_rc # hack to change it out of func + if len(l) !=0: + log(0, name, 'has elements not in all_targets list!') + for i in l: + log(0, ' ', i) + overall_test_rc = 1 + log(0, ' ^^^^ consider removing those.') + else: + log(0, name, 'looks good.') + +if args.check_test_lists: + check_if_list_in_all_targets(integration_requires_root, 'integration_requires_root') + check_if_list_in_all_targets(integration_requires_ssh, 'integration_requires_ssh') + check_if_list_in_all_targets(integration_requires_apt_mark_manual, 'integration_requires_apt_mark_manual') + check_if_list_in_all_targets(integration_fails_on_pip, 'integration_fails_on_pip') + check_if_list_in_all_targets(integration_failing.keys(), 'integration_failing') + sys.exit(overall_test_rc) + +# compile a list of targets to run, depending on CLI parameters +targets = [] +skipped = [] + +if args.default_tests is True: + targets.extend(default_targets) +else: + skipped.extend(default_targets) + +if args.requires_root is True: + targets.extend(integration_requires_root) +else: + skipped.extend(integration_requires_root) + +if args.requires_ssh is True: + targets.extend(integration_requires_ssh) +else: + skipped.extend(integration_requires_ssh) + +if args.requires_apt_mark_manual is True: + targets.extend(integration_requires_apt_mark_manual) +else: + skipped.extend(integration_requires_apt_mark_manual) + +if args.fails_on_pip is True: + targets.extend(integration_fails_on_pip) +else: + skipped.extend(integration_fails_on_pip) + +if args.failing is True: + targets.extend(integration_failing) +else: + skipped.extend(integration_failing) + +targets.sort() +skipped.sort() + +for i in targets: + + if args.dry_run is True: + log(1, 'Would run ansible-test in', i) + skipped.append(i) + continue + + print ("\n" + "#"*72, flush=True) + print ("#### Running integration tests in", i, flush=True) + print ("#"*72, flush=True) + + cmdlist = [ + 'ansible-test', + 'integration', + '--allow-root', + '--allow-destructive', + '--venv-system-site-packages', + '--python-interpreter', + '/usr/bin/python3', + '--python', + pyver, + '--local', + '--color', 'yes', + i + ] + + env={ + **os.environ, + 'PYTHONPATH': os.path.join(os.getcwd(), 'test', 'lib'), + } + + + if i in integration_requires_root: + cmdlist = ['sudo', '--preserve-env=PYTHONPATH'] + cmdlist + + # For debugging permission errors on writing results output + if args.verbose >= 3: + subprocess.run(['ls', '-ld', './test/']) + subprocess.run(['ls', '-ld', './test/results/']) + subprocess.run(['ls', '-ld', './test/results/.tmp/']) + subprocess.run(['ls', '-ld', './test/results/.tmp/output_dir/']) + + # fix any permission errors due to some root tests not cleaning up correctly + subprocess.run(['sudo', 'chown', '-R', 'user:user', './test']) + + start = time.time() + proc = subprocess.run(cmdlist, env=env) + end = time.time() + + if proc.returncode != 0: + failed_tests.append({ + 'name': i, + 'time': int( end - start) + }) + overall_test_rc = proc.returncode + else: + succeeded_tests.append({ + 'name': i, + 'time': int( end - start) + }) + +if overall_test_rc != 0: + print ("#"*72, flush=True) + print ("#### failed tests are:", flush=True) + for i in failed_tests: + print ("####", i['name'], flush=True) + print ("#"*72, flush=True) + +log(1, '### TEST SUMMARY ###') +log(1, 'succeeded tests:', len(succeeded_tests)) +log(1, 'failed tests:', len(failed_tests)) +log(1, 'skipped tests:', len(skipped)) + +log(2, '### succeeded test list:') +for i in succeeded_tests: + log(2, '{:<30} (elapsed time: {:>3}s)'.format( i['name'], i['time'] ) ) +log(2, '### failed test list:') +for i in failed_tests: + log(2, '{:<30} (elapsed time: {:>3}s)'.format( i['name'], i['time'] ) ) +log(2, '### skipped test list:') +for i in skipped: + log(2, i) + +sys.exit(overall_test_rc) diff -Nru ansible-core-2.14.16/debian/tests/control ansible-core-2.14.18/debian/tests/control --- ansible-core-2.14.16/debian/tests/control 2024-06-25 13:35:56.000000000 +0000 +++ ansible-core-2.14.18/debian/tests/control 2024-12-04 17:12:49.000000000 +0000 @@ -17,3 +17,86 @@ python3-tz, python3-winrm, python3-yaml + +# integration tests that can run as unprivileged user + with sudo +Test-Command: ./debian/tests/ansible-test-integration.py --default-tests --requires-apt-mark-manual --requires-root +Features: test-name=ansible-test-integration-default +Depends: @, + acl, # lineinfile integration test + apache2-utils, # used by subversion integration test + bash, # Many scripts have bash shebangs + cargo, # needed to build cryptography on some architectures, which some tests install from PyPI + debconf-utils, # debconf + equivs, # integration test "apt" + file, # stat + gcc, # needed to build cffi on some architectures, which some tests install from PyPI + git, # used by most tests + gnupg, # integration test apt_key + gzip, # integration test git + iptables, # integration test iptables + libffi-dev, # needed to build cffi on some architectures, which some tests install from PyPI + libfile-fcntllock-perl, # integration test "apt" + libssl-dev, # needed to build cryptography on some architectures, which some tests install from PyPI + libyaml-dev, # needed to build PyYAML on some architectures, which some tests install from PyPI + locales, + lvm2, # integration test hardware_facts + openssl, # integration test ansible-galaxy + pkg-config, # needed to build cryptography on some architectures, which some tests install from PyPI + python3-apt, # needed for checking if packages are installed + python3-dev, # needed to build cffi on some architectures, which some tests install from PyPI + python3-distlib, # ansible-galaxy-collection-cli + python3-passlib, # grep 'setup/always/setup_passlib' test/integration/targets/*/aliases + python3-pexpect, # grep 'setup/always/setup_pexpect' test/integration/targets/*/aliases + python3-pip, # ansible-test-config, builtin_vars_prompt, remove again and fix tests + python3-pytest, # ansible-test-units-assertions + python3-pytest-mock, # ansible-test-units-assertions + python3-setuptools, # integration test "pip" + python3-venv, # ansible-inventory; ./test/lib/ansible_test/_util/target/injector/virtualenv.sh + python3-virtualenv, # ansible-inventory, ansible-test-installed; ./test/lib/ansible_test/_util/target/injector/virtualenv.sh + python3-yaml, # ansible-test-units-forked + sudo, # needed for testbed-setup.sh + tar, # integration test git + unzip, # integration test git + zip, # integration test git +Restrictions: + allow-stderr, # lots of STDERR output + breaks-testbed, # many tests add/delete files, or change the system in fundamental ways + needs-internet, # used for ansible-galaxy and pip tests + needs-sudo, # used to call testbed-setup.sh + +# integration tests that fail on some variation of running pip install +Test-Command: ./debian/tests/ansible-test-integration.py --fails-on-pip +Features: test-name=ansible-test-integration-fails-on-pip +Depends: @, + git, + locales, + python3-passlib, # grep 'setup/always/setup_passlib' test/integration/targets/*/aliases + python3-pexpect, # grep 'setup/always/setup_pexpect' test/integration/targets/*/aliases + python3-pip, # ansible-test-config, builtin_vars_prompt, remove again and fix tests +Restrictions: + allow-stderr, # lots of STDERR output + flaky, # known to be broken + needs-internet, # used for ansible-galaxy and pip tests + needs-sudo, # used to call testbed-setup.sh + # don't run broken tests on CI, as that makes autopkgtest time out + # Run `autopkgtest --ignore-restrictions disabled-on-ci [...]` to run locally. + # Thanks elbrus for the tip! + disabled-on-ci, + +# integration tests that fail for other reasons +Test-Command: ./debian/tests/ansible-test-integration.py --failing +Features: test-name=ansible-test-integration-failing +Depends: @, + git, + locales, + python3-passlib, # grep 'setup/always/setup_passlib' test/integration/targets/*/aliases + python3-pexpect, # grep 'setup/always/setup_pexpect' test/integration/targets/*/aliases + python3-pip, # ansible-test-config, builtin_vars_prompt, remove again and fix tests + shellcheck, # ansible-test-docker +Restrictions: + allow-stderr, # lots of STDERR output + flaky, # known to be broken + needs-internet, # used for ansible-galaxy and pip tests + needs-sudo, # used to call testbed-setup.sh + # don't run broken tests on CI + disabled-on-ci, diff -Nru ansible-core-2.14.16/debian/tests/testbed-setup.sh ansible-core-2.14.18/debian/tests/testbed-setup.sh --- ansible-core-2.14.16/debian/tests/testbed-setup.sh 1970-01-01 00:00:00.000000000 +0000 +++ ansible-core-2.14.18/debian/tests/testbed-setup.sh 2024-12-04 17:12:49.000000000 +0000 @@ -0,0 +1,42 @@ +#!/usr/bin/sh + +# helper script called from within ansible-test-integration.py to + +set -eu + +# workaround for various integration tests trying to do this and failing +# due to missing root permission +echo 'apt-mark changes' +/usr/bin/apt-mark manual debconf-utils || true +/usr/bin/apt-mark manual equivs || true +/usr/bin/apt-mark manual git || true +/usr/bin/apt-mark manual iptables || true +/usr/bin/apt-mark manual libfile-fcntllock-perl || true +/usr/bin/apt-mark manual openssl || true +/usr/bin/apt-mark manual python3-distlib || true + +# Set default locale to en_US.UTF-8 to remove lots of warnings when +# running the tests +echo 'debconf selection changes' +# These values get overwritten by what is in the config files when +# running dpkg-reconfigure, see https://bugs.debian.org/684134 +cat << EOF | debconf-set-selections +locales locales/default_environment_locale select en_US.UTF-8 +locales locales/locales_to_be_generated multiselect en_US.UTF-8 UTF-8 +EOF + +# So set these values manually in the config files. +echo 'en_US.UTF-8 UTF-8' > /etc/locale.gen +echo 'LANG=en_US.UTF-8' > /etc/locale.conf +echo 'LANG=en_US.UTF-8' > /etc/default/locale + +echo 'dpkg-reconfigure changes' +dpkg-reconfigure --frontend=noninteractive locales + +# create empty authorized_keys for integration test 'copy' +[ -d /root/.ssh ] || mkdir /root/.ssh/ + +touch /root/.ssh/authorized_keys + +# allow pip to install system packages for the tests +rm -f /usr/lib/python3*/EXTERNALLY-MANAGED diff -Nru ansible-core-2.14.16/lib/ansible/executor/task_executor.py ansible-core-2.14.18/lib/ansible/executor/task_executor.py --- ansible-core-2.14.16/lib/ansible/executor/task_executor.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/lib/ansible/executor/task_executor.py 2024-11-04 18:35:24.000000000 +0000 @@ -645,8 +645,8 @@ self._handler.cleanup() display.debug("handler run complete") - # preserve no log - result["_ansible_no_log"] = no_log + # propagate no log to result- the action can set this, so only overwrite it with the task's value if missing or falsey + result["_ansible_no_log"] = bool(no_log or result.get('_ansible_no_log', False)) if self._task.action not in C._ACTION_WITH_CLEAN_FACTS: result = wrap_var(result) diff -Nru ansible-core-2.14.16/lib/ansible/galaxy/role.py ansible-core-2.14.18/lib/ansible/galaxy/role.py --- ansible-core-2.14.16/lib/ansible/galaxy/role.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/lib/ansible/galaxy/role.py 2024-11-04 18:35:24.000000000 +0000 @@ -387,6 +387,8 @@ else: os.makedirs(self.path) + resolved_archive = unfrackpath(archive_parent_dir, follow=False) + # We strip off any higher-level directories for all of the files # contained within the tar file here. The default is 'github_repo-target'. # Gerrit instances, on the other hand, does not have a parent directory at all. @@ -401,33 +403,29 @@ if not (attr_value := getattr(member, attr, None)): continue - if attr_value.startswith(os.sep) and not is_subpath(attr_value, archive_parent_dir): - err = f"Invalid {attr} for tarfile member: path {attr_value} is not a subpath of the role {archive_parent_dir}" - raise AnsibleError(err) - if attr == 'linkname': # Symlinks are relative to the link - relative_to_archive_dir = os.path.dirname(getattr(member, 'name', '')) - archive_dir_path = os.path.join(archive_parent_dir, relative_to_archive_dir, attr_value) + relative_to = os.path.dirname(getattr(member, 'name', '')) else: # Normalize paths that start with the archive dir attr_value = attr_value.replace(archive_parent_dir, "", 1) attr_value = os.path.join(*attr_value.split(os.sep)) # remove leading os.sep - archive_dir_path = os.path.join(archive_parent_dir, attr_value) + relative_to = '' - resolved_archive = unfrackpath(archive_parent_dir) - resolved_path = unfrackpath(archive_dir_path) - if not is_subpath(resolved_path, resolved_archive): - err = f"Invalid {attr} for tarfile member: path {resolved_path} is not a subpath of the role {resolved_archive}" + full_path = os.path.join(resolved_archive, relative_to, attr_value) + if not is_subpath(full_path, resolved_archive, real=True): + err = f"Invalid {attr} for tarfile member: path {full_path} is not a subpath of the role {resolved_archive}" raise AnsibleError(err) - relative_path = os.path.join(*resolved_path.replace(resolved_archive, "", 1).split(os.sep)) or '.' + relative_path_dir = os.path.join(resolved_archive, relative_to) + relative_path = os.path.join(*full_path.replace(relative_path_dir, "", 1).split(os.sep)) setattr(member, attr, relative_path) if _check_working_data_filter(): # deprecated: description='extract fallback without filter' python_version='3.11' role_tar_file.extract(member, to_native(self.path), filter='data') # type: ignore[call-arg] else: + # Remove along with manual path filter once Python 3.12 is minimum supported version role_tar_file.extract(member, to_native(self.path)) # write out the install info file for later use diff -Nru ansible-core-2.14.16/lib/ansible/module_utils/ansible_release.py ansible-core-2.14.18/lib/ansible/module_utils/ansible_release.py --- ansible-core-2.14.16/lib/ansible/module_utils/ansible_release.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/lib/ansible/module_utils/ansible_release.py 2024-11-04 18:35:24.000000000 +0000 @@ -19,6 +19,6 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -__version__ = '2.14.16' +__version__ = '2.14.18' __author__ = 'Ansible, Inc.' __codename__ = "C'mon Everybody" diff -Nru ansible-core-2.14.16/lib/ansible/modules/user.py ansible-core-2.14.18/lib/ansible/modules/user.py --- ansible-core-2.14.16/lib/ansible/modules/user.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/lib/ansible/modules/user.py 2024-11-04 18:35:24.000000000 +0000 @@ -1152,9 +1152,11 @@ overwrite = None try: ssh_key_file = self.get_ssh_key_path() + pub_file = '%s.pub' % ssh_key_file except Exception as e: return (1, '', to_native(e)) ssh_dir = os.path.dirname(ssh_key_file) + if not os.path.exists(ssh_dir): if self.module.check_mode: return (0, '', '') @@ -1163,12 +1165,23 @@ os.chown(ssh_dir, info[2], info[3]) except OSError as e: return (1, '', 'Failed to create %s: %s' % (ssh_dir, to_native(e))) + if os.path.exists(ssh_key_file): if self.force: - # ssh-keygen doesn't support overwriting the key interactively, so send 'y' to confirm + self.module.warn('Overwriting existing ssh key private file "%s"' % ssh_key_file) overwrite = 'y' else: + self.module.warn('Found existing ssh key private file "%s", no force, so skipping ssh-keygen generation' % ssh_key_file) return (None, 'Key already exists, use "force: yes" to overwrite', '') + + if os.path.exists(pub_file): + if self.force: + self.module.warn('Overwriting existing ssh key public file "%s"' % pub_file) + os.unlink(pub_file) + else: + self.module.warn('Found existing ssh key public file "%s", no force, so skipping ssh-keygen generation' % pub_file) + return (None, 'Public key already exists, use "force: yes" to overwrite', '') + cmd = [self.module.get_bin_path('ssh-keygen', True)] cmd.append('-t') cmd.append(self.ssh_type) @@ -1235,7 +1248,7 @@ # If the keys were successfully created, we should be able # to tweak ownership. os.chown(ssh_key_file, info[2], info[3]) - os.chown('%s.pub' % ssh_key_file, info[2], info[3]) + os.chown(pub_file, info[2], info[3]) return (rc, out, err) def ssh_key_fingerprint(self): diff -Nru ansible-core-2.14.16/lib/ansible/plugins/action/include_vars.py ansible-core-2.14.18/lib/ansible/plugins/action/include_vars.py --- ansible-core-2.14.16/lib/ansible/plugins/action/include_vars.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/lib/ansible/plugins/action/include_vars.py 2024-11-04 18:35:24.000000000 +0000 @@ -237,7 +237,8 @@ b_data, show_content = self._loader._get_file_contents(filename) data = to_text(b_data, errors='surrogate_or_strict') - self.show_content = show_content + self.show_content &= show_content # mask all results if any file was encrypted + data = self._loader.load(data, file_name=filename, show_content=show_content) if not data: data = dict() diff -Nru ansible-core-2.14.16/lib/ansible/release.py ansible-core-2.14.18/lib/ansible/release.py --- ansible-core-2.14.16/lib/ansible/release.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/lib/ansible/release.py 2024-11-04 18:35:24.000000000 +0000 @@ -19,6 +19,6 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -__version__ = '2.14.16' +__version__ = '2.14.18' __author__ = 'Ansible, Inc.' __codename__ = "C'mon Everybody" diff -Nru ansible-core-2.14.16/lib/ansible_core.egg-info/PKG-INFO ansible-core-2.14.18/lib/ansible_core.egg-info/PKG-INFO --- ansible-core-2.14.16/lib/ansible_core.egg-info/PKG-INFO 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/lib/ansible_core.egg-info/PKG-INFO 2024-11-04 18:35:24.000000000 +0000 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: ansible-core -Version: 2.14.16 +Version: 2.14.18 Summary: Radically simple IT automation Home-page: https://ansible.com/ Author: Ansible, Inc. diff -Nru ansible-core-2.14.16/lib/ansible_core.egg-info/SOURCES.txt ansible-core-2.14.18/lib/ansible_core.egg-info/SOURCES.txt --- ansible-core-2.14.16/lib/ansible_core.egg-info/SOURCES.txt 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/lib/ansible_core.egg-info/SOURCES.txt 2024-11-04 18:35:24.000000000 +0000 @@ -875,6 +875,11 @@ test/integration/targets/ansible-galaxy-collection/vars/main.yml test/integration/targets/ansible-galaxy-role/aliases test/integration/targets/ansible-galaxy-role/files/create-role-archive.py +test/integration/targets/ansible-galaxy-role/files/safe-symlinks/defaults/main.yml +test/integration/targets/ansible-galaxy-role/files/safe-symlinks/defaults/common_vars/subdir/group0/main.yml +test/integration/targets/ansible-galaxy-role/files/safe-symlinks/handlers/utils.yml +test/integration/targets/ansible-galaxy-role/files/safe-symlinks/meta/main.yml +test/integration/targets/ansible-galaxy-role/files/safe-symlinks/tasks/utils/suite.yml test/integration/targets/ansible-galaxy-role/meta/main.yml test/integration/targets/ansible-galaxy-role/tasks/dir-traversal.yml test/integration/targets/ansible-galaxy-role/tasks/main.yml @@ -1716,6 +1721,7 @@ test/integration/targets/dict_transformations/tasks/test_convert_camelCase.yml test/integration/targets/dict_transformations/tasks/test_convert_snake_case.yml test/integration/targets/dnf/aliases +test/integration/targets/dnf/filter_plugins/dnf_module_list.py test/integration/targets/dnf/meta/main.yml test/integration/targets/dnf/tasks/cacheonly.yml test/integration/targets/dnf/tasks/dnf.yml @@ -1730,10 +1736,6 @@ test/integration/targets/dnf/tasks/repo.yml test/integration/targets/dnf/tasks/skip_broken_and_nobest.yml test/integration/targets/dnf/tasks/test_sos_removal.yml -test/integration/targets/dnf/vars/CentOS.yml -test/integration/targets/dnf/vars/Fedora.yml -test/integration/targets/dnf/vars/RedHat-9.yml -test/integration/targets/dnf/vars/RedHat.yml test/integration/targets/dnf/vars/main.yml test/integration/targets/dpkg_selections/aliases test/integration/targets/dpkg_selections/defaults/main.yaml @@ -2058,37 +2060,6 @@ test/integration/targets/incidental_ios_file/tasks/main.yaml test/integration/targets/incidental_ios_file/tests/cli/net_get.yaml test/integration/targets/incidental_ios_file/tests/cli/net_put.yaml -test/integration/targets/incidental_vyos_config/aliases -test/integration/targets/incidental_vyos_config/defaults/main.yaml -test/integration/targets/incidental_vyos_config/tasks/cli.yaml -test/integration/targets/incidental_vyos_config/tasks/cli_config.yaml -test/integration/targets/incidental_vyos_config/tasks/main.yaml -test/integration/targets/incidental_vyos_config/tests/cli/backup.yaml -test/integration/targets/incidental_vyos_config/tests/cli/check_config.yaml -test/integration/targets/incidental_vyos_config/tests/cli/comment.yaml -test/integration/targets/incidental_vyos_config/tests/cli/config.cfg -test/integration/targets/incidental_vyos_config/tests/cli/save.yaml -test/integration/targets/incidental_vyos_config/tests/cli/simple.yaml -test/integration/targets/incidental_vyos_config/tests/cli_config/cli_backup.yaml -test/integration/targets/incidental_vyos_config/tests/cli_config/cli_basic.yaml -test/integration/targets/incidental_vyos_config/tests/cli_config/cli_comment.yaml -test/integration/targets/incidental_vyos_lldp_interfaces/aliases -test/integration/targets/incidental_vyos_lldp_interfaces/defaults/main.yaml -test/integration/targets/incidental_vyos_lldp_interfaces/meta/main.yaml -test/integration/targets/incidental_vyos_lldp_interfaces/tasks/cli.yaml -test/integration/targets/incidental_vyos_lldp_interfaces/tasks/main.yaml -test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/_populate.yaml -test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/_populate_intf.yaml -test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/_remove_config.yaml -test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/deleted.yaml -test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/empty_config.yaml -test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/merged.yaml -test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/overridden.yaml -test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/replaced.yaml -test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/rtt.yaml -test/integration/targets/incidental_vyos_lldp_interfaces/vars/main.yaml -test/integration/targets/incidental_vyos_prepare_tests/aliases -test/integration/targets/incidental_vyos_prepare_tests/tasks/main.yaml test/integration/targets/incidental_win_reboot/aliases test/integration/targets/incidental_win_reboot/tasks/main.yml test/integration/targets/incidental_win_reboot/templates/post_reboot.ps1 @@ -2277,6 +2248,8 @@ test/integration/targets/include_vars/aliases test/integration/targets/include_vars-ad-hoc/aliases test/integration/targets/include_vars-ad-hoc/runme.sh +test/integration/targets/include_vars-ad-hoc/vaultpass +test/integration/targets/include_vars-ad-hoc/dir/encrypted.yml test/integration/targets/include_vars-ad-hoc/dir/inc.yml test/integration/targets/include_vars/defaults/main.yml test/integration/targets/include_vars/tasks/main.yml @@ -2835,18 +2808,16 @@ test/integration/targets/module_utils_urls/library/test_peercert.py test/integration/targets/module_utils_urls/meta/main.yml test/integration/targets/module_utils_urls/tasks/main.yml -test/integration/targets/network_cli/aliases -test/integration/targets/network_cli/passworded_user.yml -test/integration/targets/network_cli/runme.sh -test/integration/targets/network_cli/setup.yml -test/integration/targets/network_cli/teardown.yml test/integration/targets/no_log/aliases +test/integration/targets/no_log/ansible_no_log_in_result.yml test/integration/targets/no_log/dynamic.yml test/integration/targets/no_log/no_log_config.yml test/integration/targets/no_log/no_log_local.yml test/integration/targets/no_log/no_log_suboptions.yml test/integration/targets/no_log/no_log_suboptions_invalid.yml test/integration/targets/no_log/runme.sh +test/integration/targets/no_log/secretvars.yml +test/integration/targets/no_log/action_plugins/action_sets_no_log.py test/integration/targets/no_log/library/module.py test/integration/targets/noexec/aliases test/integration/targets/noexec/inventory @@ -3588,6 +3559,7 @@ test/integration/targets/user/files/userlist.sh test/integration/targets/user/meta/main.yml test/integration/targets/user/tasks/main.yml +test/integration/targets/user/tasks/ssh_keygen.yml test/integration/targets/user/tasks/test_create_system_user.yml test/integration/targets/user/tasks/test_create_user.yml test/integration/targets/user/tasks/test_create_user_home.yml @@ -4155,34 +4127,6 @@ test/support/network-integration/collections/ansible_collections/cisco/ios/plugins/modules/ios_command.py test/support/network-integration/collections/ansible_collections/cisco/ios/plugins/modules/ios_config.py test/support/network-integration/collections/ansible_collections/cisco/ios/plugins/terminal/ios.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/action/vyos.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/cliconf/vyos.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/doc_fragments/vyos.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/vyos.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/facts/facts.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/interfaces/interfaces.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/l3_interfaces/l3_interfaces.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/lag_interfaces/lag_interfaces.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/lldp_global/lldp_global.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/lldp_interfaces/lldp_interfaces.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/static_routes/static_routes.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/config/lldp_interfaces/lldp_interfaces.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/facts.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/interfaces/interfaces.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/legacy/base.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/lldp_global/lldp_global.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/lldp_interfaces/lldp_interfaces.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/utils/utils.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_command.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_config.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_facts.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_lldp_interfaces.py -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/terminal/vyos.py test/support/windows-integration/collections/ansible_collections/ansible/windows/plugins/action/win_copy.py test/support/windows-integration/collections/ansible_collections/ansible/windows/plugins/module_utils/WebRequest.psm1 test/support/windows-integration/collections/ansible_collections/ansible/windows/plugins/modules/async_status.ps1 diff -Nru ansible-core-2.14.16/packaging/release.py ansible-core-2.14.18/packaging/release.py --- ansible-core-2.14.16/packaging/release.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/packaging/release.py 2024-11-04 18:35:24.000000000 +0000 @@ -369,6 +369,7 @@ ANSIBLE_BIN_DIR = CHECKOUT_DIR / "bin" ANSIBLE_RELEASE_FILE = ANSIBLE_DIR / "release.py" ANSIBLE_REQUIREMENTS_FILE = CHECKOUT_DIR / "requirements.txt" +ANSIBLE_PYPROJECT_TOML_FILE = CHECKOUT_DIR / "pyproject.toml" DIST_DIR = CHECKOUT_DIR / "dist" VENV_DIR = DIST_DIR / ".venv" / "release" @@ -708,6 +709,35 @@ return env +def get_pypi_project(repository: str, project: str, version: Version | None = None) -> dict[str, t.Any]: + """Return the project JSON from PyPI for the specified repository, project and version (optional).""" + endpoint = PYPI_ENDPOINTS[repository] + + if version: + url = f"{endpoint}/{project}/{version}/json" + else: + url = f"{endpoint}/{project}/json" + + opener = urllib.request.build_opener() + response: http.client.HTTPResponse + + try: + with opener.open(url) as response: + data = json.load(response) + except urllib.error.HTTPError as ex: + if version: + target = f'{project!r} version {version}' + else: + target = f'{project!r}' + + if ex.status == http.HTTPStatus.NOT_FOUND: + raise ApplicationError(f"Could not find {target} on PyPI.") from None + + raise RuntimeError(f"Failed to get {target} from PyPI.") from ex + + return data + + def get_ansible_version(version: str | None = None, /, commit: str | None = None, mode: VersionMode = VersionMode.DEFAULT) -> Version: """Parse and return the current ansible-core version, the provided version or the version from the provided commit.""" if version and commit: @@ -751,12 +781,17 @@ pre = "" elif not pre and version.pre is not None: pre = f"{version.pre[0]}{version.pre[1]}" + elif not pre: + pre = "b1" # when there is no existing pre and none specified, advance to b1 + elif version.is_postrelease: # The next version of a post release is the next pre-release *or* micro release component. if final: pre = "" elif not pre and version.pre is not None: pre = f"{version.pre[0]}{version.pre[1] + 1}" + elif not pre: + pre = "rc1" # when there is no existing pre and none specified, advance to rc1 if version.pre is None: micro = version.micro + 1 @@ -797,6 +832,38 @@ ANSIBLE_RELEASE_FILE.write_text(updated) +def get_latest_setuptools_version() -> Version: + """Return the latest setuptools version found on PyPI.""" + data = get_pypi_project('pypi', 'setuptools') + version = Version(data['info']['version']) + + return version + + +def set_setuptools_upper_bound(requested_version: Version) -> None: + """Set the upper bound on setuptools in pyproject.toml.""" + current = ANSIBLE_PYPROJECT_TOML_FILE.read_text() + pattern = re.compile(r'^(?Prequires = \["setuptools >= )(?P[^,]+)(?P, <= )(?P[^"]+)(?P".*)$', re.MULTILINE) + match = pattern.search(current) + + if not match: + raise ApplicationError(f"Unable to find the 'requires' entry in: {ANSIBLE_PYPROJECT_TOML_FILE.relative_to(CHECKOUT_DIR)}") + + current_version = Version(match.group('upper')) + + if requested_version == current_version: + return + + display.show(f"Updating setuptools upper bound from {current_version} to {requested_version} ...") + + updated = pattern.sub(fr'\g\g\g{requested_version}\g', current) + + if current == updated: + raise RuntimeError("Failed to set the setuptools upper bound.") + + ANSIBLE_PYPROJECT_TOML_FILE.write_text(updated) + + def create_reproducible_sdist(original_path: pathlib.Path, output_path: pathlib.Path, mtime: int) -> None: """Read the specified sdist and write out a new copy with uniform file metadata at the specified location.""" with tarfile.open(original_path) as original_archive: @@ -873,21 +940,7 @@ @functools.cache def get_release_artifact_details(repository: str, version: Version, validate: bool) -> list[ReleaseArtifact]: """Return information about the release artifacts hosted on PyPI.""" - endpoint = PYPI_ENDPOINTS[repository] - url = f"{endpoint}/ansible-core/{version}/json" - - opener = urllib.request.build_opener() - response: http.client.HTTPResponse - - try: - with opener.open(url) as response: - data = json.load(response) - except urllib.error.HTTPError as ex: - if ex.status == http.HTTPStatus.NOT_FOUND: - raise ApplicationError(f"Version {version} not found on PyPI.") from None - - raise RuntimeError(f"Failed to get {version} from PyPI: {ex}") from ex - + data = get_pypi_project(repository, 'ansible-core', version) artifacts = [describe_release_artifact(version, item, validate) for item in data["urls"]] expected_artifact_types = {"bdist_wheel", "sdist"} @@ -1041,7 +1094,7 @@ # Release Artifacts {%- for release in releases %} -* {{ release.package_label }}: [{{ release.url|basename }}]({{ release.url }}) - {{ release.size }} bytes +* {{ release.package_label }}: [{{ release.url|basename }}]({{ release.url }}) - ‌{{ release.size }} bytes * {{ release.digest }} ({{ release.digest_algorithm }}) {%- endfor %} """ @@ -1130,9 +1183,10 @@ pre=dict(exclusive="version", help="increment version to the specified pre-release (aN, bN, rcN)"), final=dict(exclusive="version", action="store_true", help="increment version to the next final release"), commit=dict(help="commit to tag"), - mailto=dict(name="--no-mailto", action="store_false", help="write announcement to console instead of using a mailto: link"), + mailto=dict(name="--mailto", action="store_true", help="write announcement to mailto link instead of console"), validate=dict(name="--no-validate", action="store_false", help="disable validation of PyPI artifacts against local ones"), prompt=dict(name="--no-prompt", action="store_false", help="disable interactive prompt before publishing with twine"), + setuptools=dict(name='--no-setuptools', action="store_false", help="disable updating setuptools upper bound"), allow_tag=dict(action="store_true", help="allow an existing release tag (for testing)"), allow_stale=dict(action="store_true", help="allow a stale checkout (for testing)"), allow_dirty=dict(action="store_true", help="allow untracked files and files with changes (for testing)"), @@ -1189,10 +1243,11 @@ # noinspection PyUnusedLocal @command -def prepare(final: bool = False, pre: str | None = None, version: str | None = None) -> None: +def prepare(final: bool = False, pre: str | None = None, version: str | None = None, setuptools: bool | None = None) -> None: """Prepare a release.""" command.run( update_version, + update_setuptools, check_state, generate_summary, generate_changelog, @@ -1214,6 +1269,16 @@ @command +def update_setuptools(setuptools: bool) -> None: + """Update the setuptools upper bound in pyproject.toml.""" + if not setuptools: + return + + requested_version = get_latest_setuptools_version() + set_setuptools_upper_bound(requested_version) + + +@command def generate_summary() -> None: """Generate a summary changelog fragment for this release.""" version = get_ansible_version() @@ -1259,6 +1324,7 @@ add=( CHANGELOGS_DIR, ANSIBLE_RELEASE_FILE, + ANSIBLE_PYPROJECT_TOML_FILE, ), allow_stale=allow_stale, ) diff -Nru ansible-core-2.14.16/pyproject.toml ansible-core-2.14.18/pyproject.toml --- ansible-core-2.14.16/pyproject.toml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/pyproject.toml 2024-11-04 18:35:24.000000000 +0000 @@ -1,3 +1,3 @@ [build-system] -requires = ["setuptools >= 45.2.0"] +requires = ["setuptools >= 45.2.0, <= 75.3.0"] # lower bound to support controller Python versions, upper bound for latest version tested at release build-backend = "setuptools.build_meta" diff -Nru ansible-core-2.14.16/test/integration/targets/ansible-galaxy-role/tasks/valid-role-symlinks.yml ansible-core-2.14.18/test/integration/targets/ansible-galaxy-role/tasks/valid-role-symlinks.yml --- ansible-core-2.14.16/test/integration/targets/ansible-galaxy-role/tasks/valid-role-symlinks.yml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/ansible-galaxy-role/tasks/valid-role-symlinks.yml 2024-11-04 18:35:24.000000000 +0000 @@ -1,78 +1,38 @@ -- name: create test directories - file: - path: '{{ remote_tmp_dir }}/dir-traversal/{{ item }}' - state: directory - loop: - - source - - target - - roles - -- name: create subdir in the role content to test relative symlinks - file: - dest: '{{ remote_tmp_dir }}/dir-traversal/source/role_subdir' - state: directory - -- copy: - dest: '{{ remote_tmp_dir }}/dir-traversal/source/role_subdir/.keep' - content: '' - -- set_fact: - installed_roles: "{{ remote_tmp_dir | realpath }}/dir-traversal/roles" - -- name: build role with symlink to a directory in the role - script: - chdir: '{{ remote_tmp_dir }}/dir-traversal/source' - cmd: create-role-archive.py safe-link-dir.tar ./ role_subdir/.. - executable: '{{ ansible_playbook_python }}' - -- name: install role successfully - command: - cmd: 'ansible-galaxy role install --roles-path {{ remote_tmp_dir }}/dir-traversal/roles safe-link-dir.tar' - chdir: '{{ remote_tmp_dir }}/dir-traversal/source' - register: galaxy_install_ok - -- name: check for the directory symlink in the role - stat: - path: "{{ installed_roles }}/safe-link-dir.tar/symlink" - register: symlink_in_role - -- assert: - that: - - symlink_in_role.stat.exists - - symlink_in_role.stat.lnk_source == installed_roles + '/safe-link-dir.tar' - -- name: remove tarfile for next test - file: - path: '{{ remote_tmp_dir }}/dir-traversal/source/safe-link-dir.tar' - state: absent - -- name: build role with safe relative symlink - script: - chdir: '{{ remote_tmp_dir }}/dir-traversal/source' - cmd: create-role-archive.py safe.tar ./ role_subdir/../context.txt - executable: '{{ ansible_playbook_python }}' - -- name: install role successfully - command: - cmd: 'ansible-galaxy role install --roles-path {{ remote_tmp_dir }}/dir-traversal/roles safe.tar' - chdir: '{{ remote_tmp_dir }}/dir-traversal/source' - register: galaxy_install_ok - -- name: check for symlink in role - stat: - path: "{{ installed_roles }}/safe.tar/symlink" - register: symlink_in_role - -- assert: - that: - - symlink_in_role.stat.exists - - symlink_in_role.stat.lnk_source == installed_roles + '/safe.tar/context.txt' - -- name: remove test directories - file: - path: '{{ remote_tmp_dir }}/dir-traversal/{{ item }}' - state: absent - loop: - - source - - target - - roles +- delegate_to: localhost + block: + - name: Create archive + command: "tar -cf safe-symlinks.tar {{ role_path }}/files/safe-symlinks" + args: + chdir: "{{ remote_tmp_dir }}" + + - name: Install role successfully + command: ansible-galaxy role install --roles-path '{{ remote_tmp_dir }}/roles' safe-symlinks.tar + args: + chdir: "{{ remote_tmp_dir }}" + + - name: Validate each of the symlinks exists + stat: + path: "{{ remote_tmp_dir }}/roles/safe-symlinks.tar/{{ item }}" + loop: + - defaults/main.yml + - handlers/utils.yml + register: symlink_stat + + - assert: + that: + - symlink_stat.results[0].stat.exists + - symlink_stat.results[0].stat.lnk_source == ((dest, 'roles/safe-symlinks.tar/defaults/common_vars/subdir/group0/main.yml') | path_join) + - symlink_stat.results[1].stat.exists + - symlink_stat.results[1].stat.lnk_source == ((dest, 'roles/safe-symlinks.tar/tasks/utils/suite.yml') | path_join) + vars: + dest: "{{ remote_tmp_dir | realpath }}" + + always: + - name: Clean up + file: + path: "{{ item }}" + state: absent + delegate_to: localhost + loop: + - "{{ remote_tmp_dir }}/roles/" + - "{{ remote_tmp_dir }}/safe-symlinks.tar" diff -Nru ansible-core-2.14.16/test/integration/targets/ansible-vault/runme.sh ansible-core-2.14.18/test/integration/targets/ansible-vault/runme.sh --- ansible-core-2.14.16/test/integration/targets/ansible-vault/runme.sh 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/ansible-vault/runme.sh 2024-11-04 18:35:24.000000000 +0000 @@ -534,21 +534,22 @@ ansible-vault encrypt_string content ansible-vault encrypt_string content --encrypt-vault-id id3 -set +e - # Try to use a missing vault password file -ansible-vault encrypt_string content --encrypt-vault-id id1 2>&1 | tee out.txt -test $? -ne 0 -grep out.txt -e '[WARNING]: Error getting vault password file (id1)' -grep out.txt -e "ERROR! Did not find a match for --encrypt-vault-id=id2 in the known vault-ids ['id3']" +if ansible-vault encrypt_string content --encrypt-vault-id id1 > out.txt 2>&1; then + echo "command did not fail" + exit 1 +fi +grep out.txt -e '\[WARNING\]: Error getting vault password file (id1)' +grep out.txt -e "ERROR! Did not find a match for --encrypt-vault-id=id1 in the known vault-ids \['id3'\]" # Try to use an inaccessible vault password file -ansible-vault encrypt_string content --encrypt-vault-id id2 2>&1 | tee out.txt -test $? -ne 0 -grep out.txt -e "[WARNING]: Error in vault password file loading (id2)" -grep out.txt -e "ERROR! Did not find a match for --encrypt-vault-id=id2 in the known vault-ids ['id3']" +if ansible-vault encrypt_string content --encrypt-vault-id id2 > out.txt 2>&1; then + echo "command did not fail" + exit 1 +fi +grep out.txt -e "\[WARNING\]: Error in vault password file loading (id2)" +grep out.txt -e "ERROR! Did not find a match for --encrypt-vault-id=id2 in the known vault-ids \['id3'\]" -set -e unset ANSIBLE_VAULT_IDENTITY_LIST # 'real script' diff -Nru ansible-core-2.14.16/test/integration/targets/apt_repository/tasks/apt.yml ansible-core-2.14.18/test/integration/targets/apt_repository/tasks/apt.yml --- ansible-core-2.14.16/test/integration/targets/apt_repository/tasks/apt.yml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/apt_repository/tasks/apt.yml 2024-11-04 18:35:24.000000000 +0000 @@ -62,7 +62,10 @@ - 'cache_before.stat.mtime != cache_after.stat.mtime' - name: 'ensure ppa key is installed (expect: pass)' - apt_key: id='{{test_ppa_key}}' state=present + apt_key: + id: '{{test_ppa_key}}' + state: present + keyserver: keyserver.ubuntu.com # # TEST: apt_repository: repo= update_cache=no @@ -93,7 +96,10 @@ - 'cache_before.stat.mtime == cache_after.stat.mtime' - name: 'ensure ppa key is installed (expect: pass)' - apt_key: id='{{test_ppa_key}}' state=present + apt_key: + id: '{{test_ppa_key}}' + state: present + keyserver: keyserver.ubuntu.com # # TEST: apt_repository: repo= update_cache=yes @@ -124,7 +130,10 @@ - 'cache_before.stat.mtime != cache_after.stat.mtime' - name: 'ensure ppa key is installed (expect: pass)' - apt_key: id='{{test_ppa_key}}' state=present + apt_key: + id: '{{test_ppa_key}}' + state: present + keyserver: keyserver.ubuntu.com # # TEST: apt_repository: repo= @@ -136,7 +145,10 @@ register: cache_before - name: ensure ppa key is present before adding repo that requires authentication - apt_key: keyserver=keyserver.ubuntu.com id='{{test_ppa_key}}' state=present + apt_key: + id: '{{test_ppa_key}}' + state: present + keyserver: keyserver.ubuntu.com - name: 'name= (expect: pass)' apt_repository: repo='{{test_ppa_spec}}' state=present @@ -181,7 +193,10 @@ register: cache_before - name: ensure ppa key is present before adding repo that requires authentication - apt_key: keyserver=keyserver.ubuntu.com id='{{test_ppa_key}}' state=present + apt_key: + id: '{{test_ppa_key}}' + state: present + keyserver: keyserver.ubuntu.com - name: 'name= filename= (expect: pass)' apt_repository: repo='{{test_ppa_spec}}' filename='{{test_ppa_filename}}' state=present diff -Nru ansible-core-2.14.16/test/integration/targets/collections_runtime_pythonpath/ansible-collection-python-dist-boo/pyproject.toml ansible-core-2.14.18/test/integration/targets/collections_runtime_pythonpath/ansible-collection-python-dist-boo/pyproject.toml --- ansible-core-2.14.16/test/integration/targets/collections_runtime_pythonpath/ansible-collection-python-dist-boo/pyproject.toml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/collections_runtime_pythonpath/ansible-collection-python-dist-boo/pyproject.toml 2024-11-04 18:35:24.000000000 +0000 @@ -1,6 +1,5 @@ [build-system] requires = [ "setuptools >= 44", - "wheel", ] build-backend = "setuptools.build_meta" diff -Nru ansible-core-2.14.16/test/integration/targets/collections_runtime_pythonpath/runme.sh ansible-core-2.14.18/test/integration/targets/collections_runtime_pythonpath/runme.sh --- ansible-core-2.14.16/test/integration/targets/collections_runtime_pythonpath/runme.sh 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/collections_runtime_pythonpath/runme.sh 2024-11-04 18:35:24.000000000 +0000 @@ -25,11 +25,11 @@ === Test that the module \ gets picked up if installed \ into site-packages === -python -m pip install pep517 -( # Build a binary Python dist (a wheel) using PEP517: +python -m pip install build +( # Build a binary Python dist (a wheel) using build: cp -r ansible-collection-python-dist-boo "${OUTPUT_DIR}/" cd "${OUTPUT_DIR}/ansible-collection-python-dist-boo" - python -m pep517.build --binary --out-dir dist . + python -m build -w -o dist . ) # Install a pre-built dist with pip: python -m pip install \ diff -Nru ansible-core-2.14.16/test/integration/targets/dnf/filter_plugins/dnf_module_list.py ansible-core-2.14.18/test/integration/targets/dnf/filter_plugins/dnf_module_list.py --- ansible-core-2.14.16/test/integration/targets/dnf/filter_plugins/dnf_module_list.py 1970-01-01 00:00:00.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/dnf/filter_plugins/dnf_module_list.py 2024-11-04 18:35:24.000000000 +0000 @@ -0,0 +1,40 @@ +# Copyright: Contributors to the Ansible project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import annotations + +from collections import Counter + + +def parse_module_list(stdout): + lines = stdout.splitlines() + name_offset = 0 + empty_offset = -1 + modules = [] + for i, line in enumerate(lines): + if line.startswith('Name '): + name_offset = i + 1 + if not line.strip(): + empty_offset = i + for line in lines[name_offset:empty_offset]: + cols = line.split()[:3] + modules.append({ + 'name': cols[0], + 'version': cols[1], + 'profile': cols[2].rstrip(','), # Just the first profile + }) + return modules + + +def get_first_single_version_module(stdout): + modules = parse_module_list(stdout) + name = Counter([m['name'] for m in modules]).most_common()[-1][0] + module, = [m for m in modules if m['name'] == name] + return module + + +class FilterModule: + def filters(self): + return { + 'get_first_single_version_module': get_first_single_version_module, + } diff -Nru ansible-core-2.14.16/test/integration/targets/dnf/tasks/main.yml ansible-core-2.14.18/test/integration/targets/dnf/tasks/main.yml --- ansible-core-2.14.16/test/integration/targets/dnf/tasks/main.yml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/dnf/tasks/main.yml 2024-11-04 18:35:24.000000000 +0000 @@ -57,12 +57,30 @@ - ansible_distribution == 'Fedora' - ansible_distribution_major_version is version('23', '>=') -- include_tasks: modularity.yml - when: +- when: - (ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('29', '>=')) or (ansible_distribution in ['RedHat', 'CentOS'] and ansible_distribution_major_version is version('8', '>=')) - tags: - - dnf_modularity + - not dnf5|default(false) + block: + # FUTURE - look at including AppStream support in our local repo + - name: list modules + command: dnf module list -q + register: module_list + + # A module that only has a single version + - name: Find a module that meets our testing needs + set_fact: + astream_name: '@{{ module.name }}:{{ module.version }}/{{ module.profile }}' + astream_name_no_stream: '@{{ module.name }}/{{ module.profile }}' + vars: + module: '{{ module_list.stdout|get_first_single_version_module }}' + + - include_tasks: modularity.yml + tags: + - dnf_modularity + rescue: + # Just in case something crazy happens when listing or parsing modules + - meta: noop - include_tasks: logging.yml when: (ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('31', '>=')) or diff -Nru ansible-core-2.14.16/test/integration/targets/dnf/tasks/modularity.yml ansible-core-2.14.18/test/integration/targets/dnf/tasks/modularity.yml --- ansible-core-2.14.16/test/integration/targets/dnf/tasks/modularity.yml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/dnf/tasks/modularity.yml 2024-11-04 18:35:24.000000000 +0000 @@ -1,12 +1,3 @@ -# FUTURE - look at including AppStream support in our local repo -- name: Include distribution specific variables - include_vars: "{{ item }}" - with_first_found: - - files: - - "{{ ansible_facts.distribution }}-{{ ansible_facts.distribution_major_version }}.yml" - - "{{ ansible_facts.distribution }}.yml" - paths: ../vars - - name: install "{{ astream_name }}" module dnf: name: "{{ astream_name }}" diff -Nru ansible-core-2.14.16/test/integration/targets/dnf/vars/CentOS.yml ansible-core-2.14.18/test/integration/targets/dnf/vars/CentOS.yml --- ansible-core-2.14.16/test/integration/targets/dnf/vars/CentOS.yml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/dnf/vars/CentOS.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -astream_name: '@php:7.2/minimal' -astream_name_no_stream: '@php/minimal' diff -Nru ansible-core-2.14.16/test/integration/targets/dnf/vars/Fedora.yml ansible-core-2.14.18/test/integration/targets/dnf/vars/Fedora.yml --- ansible-core-2.14.16/test/integration/targets/dnf/vars/Fedora.yml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/dnf/vars/Fedora.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -astream_name: '@varnish:6.0/default' - -# For this to work, it needs to be that only shows once in `dnf module list`. -# Such packages, that exist on all the versions we test on, are hard to come by. -# TODO: This would be solved by using our own repo with modularity/streams. -astream_name_no_stream: '@varnish/default' diff -Nru ansible-core-2.14.16/test/integration/targets/dnf/vars/RedHat-9.yml ansible-core-2.14.18/test/integration/targets/dnf/vars/RedHat-9.yml --- ansible-core-2.14.16/test/integration/targets/dnf/vars/RedHat-9.yml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/dnf/vars/RedHat-9.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -astream_name: '@php:8.1/minimal' -astream_name_no_stream: '@php/minimal' diff -Nru ansible-core-2.14.16/test/integration/targets/dnf/vars/RedHat.yml ansible-core-2.14.18/test/integration/targets/dnf/vars/RedHat.yml --- ansible-core-2.14.16/test/integration/targets/dnf/vars/RedHat.yml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/dnf/vars/RedHat.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -astream_name: '@php:7.2/minimal' -astream_name_no_stream: '@php/minimal' diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/aliases ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/aliases --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/aliases 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/aliases 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -shippable/vyos/incidental -network/vyos diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/defaults/main.yaml ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/defaults/main.yaml --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/defaults/main.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/defaults/main.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ ---- -testcase: "*" -test_items: [] diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/tasks/cli.yaml ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/tasks/cli.yaml --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/tasks/cli.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/tasks/cli.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ ---- -- name: collect all cli test cases - find: - paths: "{{ role_path }}/tests/cli" - patterns: "{{ testcase }}.yaml" - register: test_cases - delegate_to: localhost - -- name: set test_items - set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}" - -- name: run test case (connection=ansible.netcommon.network_cli) - include_tasks: "file={{ test_case_to_run }}" - vars: - ansible_connection: ansible.netcommon.network_cli - with_items: "{{ test_items }}" - loop_control: - loop_var: test_case_to_run - -- name: run test case (connection=local) - include_tasks: "file={{ test_case_to_run }}" - vars: - ansible_connection: local - with_first_found: "{{ test_items }}" - loop_control: - loop_var: test_case_to_run diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/tasks/cli_config.yaml ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/tasks/cli_config.yaml --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/tasks/cli_config.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/tasks/cli_config.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ ---- -- name: collect all cli_config test cases - find: - paths: "{{ role_path }}/tests/cli_config" - patterns: "{{ testcase }}.yaml" - register: test_cases - delegate_to: localhost - -- name: set test_items - set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}" - -- name: run test case (connection=ansible.netcommon.network_cli) - include_tasks: "file={{ test_case_to_run }}" - vars: - ansible_connection: ansible.netcommon.network_cli - with_items: "{{ test_items }}" - loop_control: - loop_var: test_case_to_run diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/tasks/main.yaml ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/tasks/main.yaml --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/tasks/main.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/tasks/main.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ ---- -- {import_tasks: cli.yaml, tags: ['cli']} -- {import_tasks: cli_config.yaml, tags: ['cli_config']} diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/tests/cli/backup.yaml ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/tests/cli/backup.yaml --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/tests/cli/backup.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/tests/cli/backup.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,113 +0,0 @@ ---- -- debug: msg="START vyos/backup.yaml on connection={{ ansible_connection }}" - -- name: collect any backup files - find: - paths: "{{ role_path }}/backup" - pattern: "{{ inventory_hostname_short }}_config*" - register: backup_files - connection: local - -- name: delete backup files - file: - path: "{{ item.path }}" - state: absent - with_items: "{{backup_files.files|default([])}}" - -- name: take configure backup - vyos.vyos.vyos_config: - backup: true - register: result - -- assert: - that: - - "result.changed == true" - -- name: collect any backup files - find: - paths: "{{ role_path }}/backup" - pattern: "{{ inventory_hostname_short }}_config*" - register: backup_files - connection: local - -- assert: - that: - - "backup_files.files is defined" - -- name: delete configurable backup file path - file: - path: "{{ item }}" - state: absent - with_items: - - "{{ role_path }}/backup_test_dir/" - - "{{ role_path }}/backup/backup.cfg" - -- name: take configuration backup in custom filename and directory path - vyos.vyos.vyos_config: - backup: true - backup_options: - filename: backup.cfg - dir_path: "{{ role_path }}/backup_test_dir/{{ inventory_hostname_short }}" - become: true - register: result - -- assert: - that: - - "result.changed == true" - -- name: check if the backup file-1 exist - find: - paths: "{{ role_path }}/backup_test_dir/{{ inventory_hostname_short }}/backup.cfg" - register: backup_file - connection: local - -- assert: - that: - - "backup_file.files is defined" - -- name: take configuration backup in custom filename - vyos.vyos.vyos_config: - backup: true - backup_options: - filename: backup.cfg - become: true - register: result - -- assert: - that: - - "result.changed == true" - -- name: check if the backup file-2 exist - find: - paths: "{{ role_path }}/backup/backup.cfg" - register: backup_file - connection: local - -- assert: - that: - - "backup_file.files is defined" - -- name: take configuration backup in custom path and default filename - vyos.vyos.vyos_config: - backup: true - backup_options: - dir_path: "{{ role_path }}/backup_test_dir/{{ inventory_hostname_short }}" - become: true - register: result - -- assert: - that: - - "result.changed == true" - -- name: check if the backup file-3 exist - find: - paths: "{{ role_path }}/backup_test_dir/{{ inventory_hostname_short }}" - pattern: "{{ inventory_hostname_short }}_config*" - register: backup_file - connection: local - -- assert: - that: - - "backup_file.files is defined" - -- debug: msg="END vyos/backup.yaml on connection={{ ansible_connection }}" diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/tests/cli/check_config.yaml ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/tests/cli/check_config.yaml --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/tests/cli/check_config.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/tests/cli/check_config.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ ---- -- debug: msg="START cli/config_check.yaml on connection={{ ansible_connection }}" - -- name: setup- ensure interface is not present - vyos.vyos.vyos_config: - lines: delete interfaces loopback lo - -- name: setup- create interface - vyos.vyos.vyos_config: - lines: - - interfaces - - interfaces loopback lo - - interfaces loopback lo description test - register: result - -# note collapsing the duplicate lines doesn't work if -# lines: -# - interfaces loopback lo description test -# - interfaces loopback lo -# - interfaces - -- name: Check that multiple duplicate lines collapse into a single commands - assert: - that: - - "result.commands|length == 1" - -- name: Check that set is correctly prepended - assert: - that: - - "result.commands[0] == 'set interfaces loopback lo description test'" - -- name: configure config_check config command - vyos.vyos.vyos_config: - lines: delete interfaces loopback lo - register: result - -- assert: - that: - - "result.changed == true" - -- name: check config_check config command idempontent - vyos.vyos.vyos_config: - lines: delete interfaces loopback lo - register: result - -- assert: - that: - - "result.changed == false" - -- name: check multiple line config filter is working - vyos.vyos.vyos_config: - lines: - - set system login user esa level admin - - set system login user esa authentication encrypted-password '!abc!' - - set system login user vyos level admin - - set system login user vyos authentication encrypted-password 'abc' - register: result - -- assert: - that: - - "result.filtered|length == 2" - -- debug: msg="END cli/config_check.yaml on connection={{ ansible_connection }}" diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/tests/cli/comment.yaml ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/tests/cli/comment.yaml --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/tests/cli/comment.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/tests/cli/comment.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ ---- -- debug: msg="START cli/comment.yaml on connection={{ ansible_connection }}" - -- name: setup - vyos.vyos.vyos_config: - lines: set system host-name {{ inventory_hostname_short }} - match: none - -- name: configure using comment - vyos.vyos.vyos_config: - lines: set system host-name foo - comment: this is a test - register: result - -- assert: - that: - - "result.changed == true" - - "'set system host-name foo' in result.commands" - -- name: collect system commits - vyos.vyos.vyos_command: - commands: show system commit - register: result - -- assert: - that: - - "'this is a test' in result.stdout_lines[0][1]" - -- name: teardown - vyos.vyos.vyos_config: - lines: set system host-name {{ inventory_hostname_short }} - match: none - -- debug: msg="END cli/comment.yaml on connection={{ ansible_connection }}" diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/tests/cli/config.cfg ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/tests/cli/config.cfg --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/tests/cli/config.cfg 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/tests/cli/config.cfg 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ - set service lldp - set protocols static - diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/tests/cli/save.yaml ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/tests/cli/save.yaml --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/tests/cli/save.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/tests/cli/save.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,54 +0,0 @@ ---- -- debug: msg="START cli/save.yaml on connection={{ ansible_connection }}" - -- name: setup - vyos.vyos.vyos_config: - lines: set system host-name {{ inventory_hostname_short }} - match: none - -- name: configure hostaname and save - vyos.vyos.vyos_config: - lines: set system host-name foo - save: true - register: result - -- assert: - that: - - "result.changed == true" - - "'set system host-name foo' in result.commands" - -- name: configure hostaname and don't save - vyos.vyos.vyos_config: - lines: set system host-name bar - register: result - -- assert: - that: - - "result.changed == true" - - "'set system host-name bar' in result.commands" - -- name: save config - vyos.vyos.vyos_config: - save: true - register: result - -- assert: - that: - - "result.changed == true" - -- name: save config again - vyos.vyos.vyos_config: - save: true - register: result - -- assert: - that: - - "result.changed == false" - -- name: teardown - vyos.vyos.vyos_config: - lines: set system host-name {{ inventory_hostname_short }} - match: none - save: true - -- debug: msg="END cli/simple.yaml on connection={{ ansible_connection }}" diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/tests/cli/simple.yaml ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/tests/cli/simple.yaml --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/tests/cli/simple.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/tests/cli/simple.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ ---- -- debug: msg="START cli/simple.yaml on connection={{ ansible_connection }}" - -- name: setup - vyos.vyos.vyos_config: - lines: set system host-name {{ inventory_hostname_short }} - match: none - -- name: configure simple config command - vyos.vyos.vyos_config: - lines: set system host-name foo - register: result - -- assert: - that: - - "result.changed == true" - - "'set system host-name foo' in result.commands" - -- name: check simple config command idempontent - vyos.vyos.vyos_config: - lines: set system host-name foo - register: result - -- assert: - that: - - "result.changed == false" - -- name: Delete services - vyos.vyos.vyos_config: &del - lines: - - delete service lldp - - delete protocols static - -- name: Configuring when commands starts with whitespaces - vyos.vyos.vyos_config: - src: "{{ role_path }}/tests/cli/config.cfg" - register: result - -- assert: - that: - - "result.changed == true" - - '"set service lldp" in result.commands' - - '"set protocols static" in result.commands' - -- name: Delete services - vyos.vyos.vyos_config: *del - -- name: teardown - vyos.vyos.vyos_config: - lines: set system host-name {{ inventory_hostname_short }} - match: none - -- debug: msg="END cli/simple.yaml on connection={{ ansible_connection }}" diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/tests/cli_config/cli_backup.yaml ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/tests/cli_config/cli_backup.yaml --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/tests/cli_config/cli_backup.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/tests/cli_config/cli_backup.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,114 +0,0 @@ ---- -- debug: msg="END cli_config/backup.yaml on connection={{ ansible_connection }}" - -- name: delete configurable backup file path - file: - path: "{{ item }}" - state: absent - with_items: - - "{{ role_path }}/backup_test_dir/" - - "{{ role_path }}/backup/backup.cfg" - -- name: collect any backup files - find: - paths: "{{ role_path }}/backup" - pattern: "{{ inventory_hostname_short }}_config*" - register: backup_files - connection: local - -- name: delete backup files - file: - path: "{{ item.path }}" - state: absent - with_items: "{{backup_files.files|default([])}}" - -- name: take config backup - ansible.netcommon.cli_config: - backup: true - become: true - register: result - -- assert: - that: - - "result.changed == true" - -- name: collect any backup files - find: - paths: "{{ role_path }}/backup" - pattern: "{{ inventory_hostname_short }}_config*" - register: backup_files - connection: local - -- assert: - that: - - "backup_files.files is defined" - -- name: take configuration backup in custom filename and directory path - ansible.netcommon.cli_config: - backup: true - backup_options: - filename: backup.cfg - dir_path: "{{ role_path }}/backup_test_dir/{{ inventory_hostname_short }}" - become: true - register: result - -- assert: - that: - - "result.changed == true" - -- name: check if the backup file-1 exist - find: - paths: "{{ role_path }}/backup_test_dir/{{ inventory_hostname_short }}/backup.cfg" - register: backup_file - connection: local - -- assert: - that: - - "backup_file.files is defined" - -- name: take configuration backup in custom filename - ansible.netcommon.cli_config: - backup: true - backup_options: - filename: backup.cfg - become: true - register: result - -- assert: - that: - - "result.changed == true" - -- name: check if the backup file-2 exist - find: - paths: "{{ role_path }}/backup/backup.cfg" - register: backup_file - connection: local - -- assert: - that: - - "backup_file.files is defined" - -- name: take configuration backup in custom path and default filename - ansible.netcommon.cli_config: - backup: true - backup_options: - dir_path: "{{ role_path }}/backup_test_dir/{{ inventory_hostname_short }}" - become: true - register: result - -- assert: - that: - - "result.changed == true" - -- name: check if the backup file-3 exist - find: - paths: "{{ role_path }}/backup_test_dir/{{ inventory_hostname_short }}" - pattern: "{{ inventory_hostname_short }}_config*" - register: backup_file - connection: local - -- assert: - that: - - "backup_file.files is defined" - -- debug: msg="END cli_config/backup.yaml on connection={{ ansible_connection }}" diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/tests/cli_config/cli_basic.yaml ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/tests/cli_config/cli_basic.yaml --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/tests/cli_config/cli_basic.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/tests/cli_config/cli_basic.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ ---- -- debug: msg="START cli_config/cli_basic.yaml on connection={{ ansible_connection }}" - -- name: setup - remove interface description - ansible.netcommon.cli_config: &rm - config: delete interfaces loopback lo description - -- name: configure device with config - ansible.netcommon.cli_config: &conf - config: set interfaces loopback lo description 'this is a test' - register: result - -- assert: - that: - - "result.changed == true" - -- name: Idempotence - ansible.netcommon.cli_config: *conf - register: result - -- assert: - that: - - "result.changed == false" - -- name: teardown - ansible.netcommon.cli_config: *rm - -- debug: msg="END cli_config/cli_basic.yaml on connection={{ ansible_connection }}" diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/tests/cli_config/cli_comment.yaml ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/tests/cli_config/cli_comment.yaml --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_config/tests/cli_config/cli_comment.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_config/tests/cli_config/cli_comment.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ ---- -- debug: msg="START cli_config/cli_comment.yaml on connection={{ ansible_connection }}" - -- name: setup - ansible.netcommon.cli_config: &rm - config: set system host-name {{ inventory_hostname_short }} - -- name: configure using comment - ansible.netcommon.cli_config: - config: set system host-name foo - commit_comment: this is a test - register: result - -- assert: - that: - - "result.changed == true" - -- name: collect system commits - vyos.vyos.vyos_command: - commands: show system commit - register: result - -- assert: - that: - - "'this is a test' in result.stdout_lines[0][1]" - -- name: teardown - ansible.netcommon.cli_config: *rm - -- debug: msg="END cli_config/cli_comment.yaml on connection={{ ansible_connection }}" diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/aliases ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/aliases --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/aliases 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/aliases 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -shippable/vyos/incidental -network/vyos diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/defaults/main.yaml ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/defaults/main.yaml --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/defaults/main.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/defaults/main.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ ---- -testcase: "[^_].*" -test_items: [] diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/meta/main.yaml ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/meta/main.yaml --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/meta/main.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/meta/main.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ ---- -dependencies: - - incidental_vyos_prepare_tests diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/tasks/cli.yaml ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/tasks/cli.yaml --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/tasks/cli.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/tasks/cli.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ ---- -- name: Collect all cli test cases - find: - paths: "{{ role_path }}/tests/cli" - patterns: "{{ testcase }}.yaml" - use_regex: true - register: test_cases - delegate_to: localhost - -- name: Set test_items - set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}" - -- name: Run test case (connection=ansible.netcommon.network_cli) - include_tasks: "{{ test_case_to_run }}" - vars: - ansible_connection: ansible.netcommon.network_cli - with_items: "{{ test_items }}" - loop_control: - loop_var: test_case_to_run diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/tasks/main.yaml ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/tasks/main.yaml --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/tasks/main.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/tasks/main.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ ---- -- {import_tasks: cli.yaml, tags: ['cli']} diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/_populate.yaml ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/_populate.yaml --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/_populate.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/_populate.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ ---- -- name: Setup - ansible.netcommon.cli_config: - config: "{{ lines }}" - vars: - lines: | - set service lldp interface eth1 - set service lldp interface eth1 location civic-based country-code US - set service lldp interface eth1 location civic-based ca-type 0 ca-value ENGLISH - set service lldp interface eth2 - set service lldp interface eth2 location coordinate-based latitude 33.524449N - set service lldp interface eth2 location coordinate-based altitude 2200 - set service lldp interface eth2 location coordinate-based datum WGS84 - set service lldp interface eth2 location coordinate-based longitude 222.267255W diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/_populate_intf.yaml ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/_populate_intf.yaml --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/_populate_intf.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/_populate_intf.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ ---- -- name: Setup - ansible.netcommon.cli_config: - config: "{{ lines }}" - vars: - lines: | - set service lldp interface eth2 - set service lldp interface eth2 location civic-based country-code US - set service lldp interface eth2 location civic-based ca-type 0 ca-value ENGLISH - set service lldp interface eth2 disable diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/_remove_config.yaml ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/_remove_config.yaml --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/_remove_config.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/_remove_config.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ ---- -- name: Remove Config - ansible.netcommon.cli_config: - config: "{{ lines }}" - vars: - lines: | - delete service lldp interface - delete service lldp diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/deleted.yaml ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/deleted.yaml --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/deleted.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/deleted.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ ---- -- debug: - msg: "Start vyos_lldp_interfaces deleted integration tests ansible_connection={{ ansible_connection }}" - -- include_tasks: _populate.yaml - -- block: - - name: Delete attributes of given LLDP interfaces. - vyos.vyos.vyos_lldp_interfaces: &deleted - config: - - name: 'eth1' - - name: 'eth2' - state: deleted - register: result - - - name: Assert that the before dicts were correctly generated - assert: - that: - - "populate | symmetric_difference(result['before']) |length == 0" - - - name: Assert that the correct set of commands were generated - assert: - that: - - "deleted['commands'] | symmetric_difference(result['commands']) |length == 0" - - - name: Assert that the after dicts were correctly generated - assert: - that: - - "deleted['after'] | symmetric_difference(result['after']) |length == 0" - - - name: Delete attributes of given interfaces (IDEMPOTENT) - vyos.vyos.vyos_lldp_interfaces: *deleted - register: result - - - name: Assert that the previous task was idempotent - assert: - that: - - "result.changed == false" - - "result.commands|length == 0" - - - name: Assert that the before dicts were correctly generated - assert: - that: - - "deleted['after'] | symmetric_difference(result['before']) |length == 0" - always: - - include_tasks: _remove_config.yaml diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/empty_config.yaml ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/empty_config.yaml --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/empty_config.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/empty_config.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,36 +0,0 @@ ---- -- debug: - msg: "START vyos_lldp_interfaces empty_config integration tests on connection={{ ansible_connection }}" - -- name: Merged with empty config should give appropriate error message - vyos.vyos.vyos_lldp_interfaces: - config: - state: merged - register: result - ignore_errors: true - -- assert: - that: - - result.msg == 'value of config parameter must not be empty for state merged' - -- name: Replaced with empty config should give appropriate error message - vyos.vyos.vyos_lldp_interfaces: - config: - state: replaced - register: result - ignore_errors: true - -- assert: - that: - - result.msg == 'value of config parameter must not be empty for state replaced' - -- name: Overridden with empty config should give appropriate error message - vyos.vyos.vyos_lldp_interfaces: - config: - state: overridden - register: result - ignore_errors: true - -- assert: - that: - - result.msg == 'value of config parameter must not be empty for state overridden' diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/merged.yaml ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/merged.yaml --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/merged.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/merged.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ ---- -- debug: - msg: "START vyos_lldp_interfaces merged integration tests on connection={{ ansible_connection }}" - -- include_tasks: _remove_config.yaml - -- block: - - name: Merge the provided configuration with the exisiting running configuration - vyos.vyos.vyos_lldp_interfaces: &merged - config: - - name: 'eth1' - location: - civic_based: - country_code: 'US' - ca_info: - - ca_type: 0 - ca_value: 'ENGLISH' - - - name: 'eth2' - location: - coordinate_based: - altitude: 2200 - datum: 'WGS84' - longitude: '222.267255W' - latitude: '33.524449N' - state: merged - register: result - - - name: Assert that before dicts were correctly generated - assert: - that: "merged['before'] | symmetric_difference(result['before']) |length == 0" - - - name: Assert that correct set of commands were generated - assert: - that: - - "merged['commands'] | symmetric_difference(result['commands']) |length == 0" - - - name: Assert that after dicts was correctly generated - assert: - that: - - "merged['after'] | symmetric_difference(result['after']) |length == 0" - - - name: Merge the provided configuration with the existing running configuration (IDEMPOTENT) - vyos.vyos.vyos_lldp_interfaces: *merged - register: result - - - name: Assert that the previous task was idempotent - assert: - that: - - "result['changed'] == false" - - - name: Assert that before dicts were correctly generated - assert: - that: - - "merged['after'] | symmetric_difference(result['before']) |length == 0" - - always: - - include_tasks: _remove_config.yaml diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/overridden.yaml ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/overridden.yaml --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/overridden.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/overridden.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,49 +0,0 @@ ---- -- debug: - msg: "START vyos_lldp_interfaces overridden integration tests on connection={{ ansible_connection }}" - -- include_tasks: _remove_config.yaml - -- include_tasks: _populate_intf.yaml - -- block: - - name: Overrides all device configuration with provided configuration - vyos.vyos.vyos_lldp_interfaces: &overridden - config: - - name: 'eth2' - location: - elin: '0000000911' - state: overridden - register: result - - - name: Assert that before dicts were correctly generated - assert: - that: - - "populate_intf | symmetric_difference(result['before']) |length == 0" - - - name: Assert that correct commands were generated - assert: - that: - - "overridden['commands'] | symmetric_difference(result['commands']) |length == 0" - - - name: Assert that after dicts were correctly generated - assert: - that: - - "overridden['after'] | symmetric_difference(result['after']) |length == 0" - - - name: Overrides all device configuration with provided configurations (IDEMPOTENT) - vyos.vyos.vyos_lldp_interfaces: *overridden - register: result - - - name: Assert that the previous task was idempotent - assert: - that: - - "result['changed'] == false" - - - name: Assert that before dicts were correctly generated - assert: - that: - - "overridden['after'] | symmetric_difference(result['before']) |length == 0" - - always: - - include_tasks: _remove_config.yaml diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/replaced.yaml ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/replaced.yaml --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/replaced.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/replaced.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ ---- -- debug: - msg: "START vyos_lldp_interfaces replaced integration tests on connection={{ ansible_connection }}" - -- include_tasks: _remove_config.yaml - -- include_tasks: _populate.yaml - -- block: - - name: Replace device configurations of listed LLDP interfaces with provided configurations - vyos.vyos.vyos_lldp_interfaces: &replaced - config: - - name: 'eth2' - enable: false - location: - civic_based: - country_code: 'US' - ca_info: - - ca_type: 0 - ca_value: 'ENGLISH' - - - name: 'eth1' - enable: false - location: - coordinate_based: - altitude: 2200 - datum: 'WGS84' - longitude: '222.267255W' - latitude: '33.524449N' - state: replaced - register: result - - - name: Assert that correct set of commands were generated - assert: - that: - - "replaced['commands'] | symmetric_difference(result['commands']) |length == 0" - - - name: Assert that before dicts are correctly generated - assert: - that: - - "populate | symmetric_difference(result['before']) |length == 0" - - - name: Assert that after dict is correctly generated - assert: - that: - - "replaced['after'] | symmetric_difference(result['after']) |length == 0" - - - name: Replace device configurations of listed LLDP interfaces with provided configurarions (IDEMPOTENT) - vyos.vyos.vyos_lldp_interfaces: *replaced - register: result - - - name: Assert that task was idempotent - assert: - that: - - "result['changed'] == false" - - - name: Assert that before dict is correctly generated - assert: - that: - - "replaced['after'] | symmetric_difference(result['before']) |length == 0" - - always: - - include_tasks: _remove_config.yaml diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/rtt.yaml ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/rtt.yaml --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/rtt.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/tests/cli/rtt.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ ---- -- debug: - msg: "START vyos_lldp_interfaces round trip integration tests on connection={{ ansible_connection }}" - -- include_tasks: _remove_config.yaml - -- block: - - name: Apply the provided configuration (base config) - vyos.vyos.vyos_lldp_interfaces: - config: - - name: 'eth1' - location: - civic_based: - country_code: 'US' - ca_info: - - ca_type: 0 - ca_value: 'ENGLISH' - - state: merged - register: base_config - - - name: Gather lldp_interfaces facts - vyos.vyos.vyos_facts: - gather_subset: - - default - gather_network_resources: - - lldp_interfaces - - - name: Apply the provided configuration (config to be reverted) - vyos.vyos.vyos_lldp_interfaces: - config: - - name: 'eth2' - location: - coordinate_based: - altitude: 2200 - datum: 'WGS84' - longitude: '222.267255W' - latitude: '33.524449N' - state: merged - register: result - - - name: Assert that changes were applied - assert: - that: "{{ round_trip['after'] | symmetric_difference(result['after']) |length == 0 }}" - - - name: Revert back to base config using facts round trip - vyos.vyos.vyos_lldp_interfaces: - config: "{{ ansible_facts['network_resources']['lldp_interfaces'] }}" - state: overridden - register: revert - - - name: Assert that config was reverted - assert: - that: "{{ base_config['after'] | symmetric_difference(revert['after']) |length == 0 }}" - - always: - - include_tasks: _remove_config.yaml diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/vars/main.yaml ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/vars/main.yaml --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_lldp_interfaces/vars/main.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_lldp_interfaces/vars/main.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,130 +0,0 @@ ---- -merged: - before: [] - - - commands: - - "set service lldp interface eth1 location civic-based country-code 'US'" - - "set service lldp interface eth1 location civic-based ca-type 0 ca-value 'ENGLISH'" - - "set service lldp interface eth1" - - "set service lldp interface eth2 location coordinate-based latitude '33.524449N'" - - "set service lldp interface eth2 location coordinate-based altitude '2200'" - - "set service lldp interface eth2 location coordinate-based datum 'WGS84'" - - "set service lldp interface eth2 location coordinate-based longitude '222.267255W'" - - "set service lldp interface eth2 location coordinate-based latitude '33.524449N'" - - "set service lldp interface eth2 location coordinate-based altitude '2200'" - - "set service lldp interface eth2 location coordinate-based datum 'WGS84'" - - "set service lldp interface eth2 location coordinate-based longitude '222.267255W'" - - "set service lldp interface eth2" - - after: - - name: 'eth1' - location: - civic_based: - country_code: 'US' - ca_info: - - ca_type: 0 - ca_value: 'ENGLISH' - - - name: 'eth2' - location: - coordinate_based: - altitude: 2200 - datum: 'WGS84' - longitude: '222.267255W' - latitude: '33.524449N' - -populate: - - name: 'eth1' - location: - civic_based: - country_code: 'US' - ca_info: - - ca_type: 0 - ca_value: 'ENGLISH' - - - name: 'eth2' - location: - coordinate_based: - altitude: 2200 - datum: 'WGS84' - longitude: '222.267255W' - latitude: '33.524449N' - -replaced: - commands: - - "delete service lldp interface eth2 location" - - "set service lldp interface eth2 'disable'" - - "set service lldp interface eth2 location civic-based country-code 'US'" - - "set service lldp interface eth2 location civic-based ca-type 0 ca-value 'ENGLISH'" - - "delete service lldp interface eth1 location" - - "set service lldp interface eth1 'disable'" - - "set service lldp interface eth1 location coordinate-based latitude '33.524449N'" - - "set service lldp interface eth1 location coordinate-based altitude '2200'" - - "set service lldp interface eth1 location coordinate-based datum 'WGS84'" - - "set service lldp interface eth1 location coordinate-based longitude '222.267255W'" - - after: - - name: 'eth2' - enable: false - location: - civic_based: - country_code: 'US' - ca_info: - - ca_type: 0 - ca_value: 'ENGLISH' - - - name: 'eth1' - enable: false - location: - coordinate_based: - altitude: 2200 - datum: 'WGS84' - longitude: '222.267255W' - latitude: '33.524449N' - -populate_intf: - - name: 'eth2' - enable: false - location: - civic_based: - country_code: 'US' - ca_info: - - ca_type: 0 - ca_value: 'ENGLISH' - -overridden: - commands: - - "delete service lldp interface eth2 location" - - "delete service lldp interface eth2 'disable'" - - "set service lldp interface eth2 location elin '0000000911'" - - after: - - name: 'eth2' - location: - elin: 0000000911 - -deleted: - commands: - - "delete service lldp interface eth1" - - "delete service lldp interface eth2" - - after: [] - -round_trip: - after: - - name: 'eth1' - location: - civic_based: - country_code: 'US' - ca_info: - - ca_type: 0 - ca_value: 'ENGLISH' - - - name: 'eth2' - location: - coordinate_based: - altitude: 2200 - datum: 'WGS84' - longitude: '222.267255W' - latitude: '33.524449N' diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_prepare_tests/aliases ansible-core-2.14.18/test/integration/targets/incidental_vyos_prepare_tests/aliases --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_prepare_tests/aliases 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_prepare_tests/aliases 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -hidden diff -Nru ansible-core-2.14.16/test/integration/targets/incidental_vyos_prepare_tests/tasks/main.yaml ansible-core-2.14.18/test/integration/targets/incidental_vyos_prepare_tests/tasks/main.yaml --- ansible-core-2.14.16/test/integration/targets/incidental_vyos_prepare_tests/tasks/main.yaml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/incidental_vyos_prepare_tests/tasks/main.yaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ ---- -- name: Ensure required interfaces are present in running-config - ansible.netcommon.cli_config: - config: "{{ lines }}" - vars: - lines: | - set interfaces ethernet eth0 address dhcp - set interfaces ethernet eth0 speed auto - set interfaces ethernet eth0 duplex auto - set interfaces ethernet eth1 - set interfaces ethernet eth2 - delete interfaces loopback lo - ignore_errors: true diff -Nru ansible-core-2.14.16/test/integration/targets/include_vars-ad-hoc/dir/encrypted.yml ansible-core-2.14.18/test/integration/targets/include_vars-ad-hoc/dir/encrypted.yml --- ansible-core-2.14.16/test/integration/targets/include_vars-ad-hoc/dir/encrypted.yml 1970-01-01 00:00:00.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/include_vars-ad-hoc/dir/encrypted.yml 2024-11-04 18:35:24.000000000 +0000 @@ -0,0 +1,6 @@ +$ANSIBLE_VAULT;1.1;AES256 +31613539636636336264396235633933633839646337323533316638633336653461393036336664 +3939386435313638366366626566346135623932653238360a366261303663343034633865626132 +31646231623630333636383636383833656331643164656366623332396439306132663264663131 +6439633766376261320a616265306430366530363866356433366430633265353739373732646536 +37623661333064306162373463616231636365373231313939373230643936313362 diff -Nru ansible-core-2.14.16/test/integration/targets/include_vars-ad-hoc/runme.sh ansible-core-2.14.18/test/integration/targets/include_vars-ad-hoc/runme.sh --- ansible-core-2.14.16/test/integration/targets/include_vars-ad-hoc/runme.sh 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/include_vars-ad-hoc/runme.sh 2024-11-04 18:35:24.000000000 +0000 @@ -1,6 +1,22 @@ #!/usr/bin/env bash -set -eux +set -eux -o pipefail -ansible testhost -i ../../inventory -m include_vars -a 'dir/inc.yml' "$@" -ansible testhost -i ../../inventory -m include_vars -a 'dir=dir' "$@" +echo "single file include" +ansible testhost -i ../../inventory -m include_vars -a 'dir/inc.yml' -vvv 2>&1 | grep -q 'porter.*cable' + +echo "single file encrypted include" +ansible testhost -i ../../inventory -m include_vars -a 'dir/encrypted.yml' -vvv --vault-password-file vaultpass > output.txt 2>&1 + +echo "directory include with encrypted" +ansible testhost -i ../../inventory -m include_vars -a 'dir=dir' -vvv --vault-password-file vaultpass >> output.txt 2>&1 + +grep -q 'output has been hidden' output.txt + +# all content should be masked if any file is encrypted +if grep -e 'i am a secret' -e 'porter.*cable' output.txt; then + echo "FAIL: vault masking failed" + exit 1 +fi + +echo PASS diff -Nru ansible-core-2.14.16/test/integration/targets/include_vars-ad-hoc/vaultpass ansible-core-2.14.18/test/integration/targets/include_vars-ad-hoc/vaultpass --- ansible-core-2.14.16/test/integration/targets/include_vars-ad-hoc/vaultpass 1970-01-01 00:00:00.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/include_vars-ad-hoc/vaultpass 2024-11-04 18:35:24.000000000 +0000 @@ -0,0 +1,3 @@ +#!/bin/sh + +echo supersecurepassword diff -Nru ansible-core-2.14.16/test/integration/targets/module_utils_facts.system.selinux/tasks/main.yml ansible-core-2.14.18/test/integration/targets/module_utils_facts.system.selinux/tasks/main.yml --- ansible-core-2.14.16/test/integration/targets/module_utils_facts.system.selinux/tasks/main.yml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/module_utils_facts.system.selinux/tasks/main.yml 2024-11-04 18:35:24.000000000 +0000 @@ -22,7 +22,7 @@ register: r - set_fact: - selinux_policytype: "{{ r.stdout_lines[0] }}" + selinux_policytype: "{{ r.stdout_lines[0] | trim }}" when: r is success and r.stdout_lines - assert: diff -Nru ansible-core-2.14.16/test/integration/targets/network_cli/aliases ansible-core-2.14.18/test/integration/targets/network_cli/aliases --- ansible-core-2.14.16/test/integration/targets/network_cli/aliases 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/network_cli/aliases 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -# Keeping incidental for efficiency, to avoid spinning up another VM -shippable/vyos/incidental -network/vyos diff -Nru ansible-core-2.14.16/test/integration/targets/network_cli/passworded_user.yml ansible-core-2.14.18/test/integration/targets/network_cli/passworded_user.yml --- ansible-core-2.14.16/test/integration/targets/network_cli/passworded_user.yml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/network_cli/passworded_user.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -- hosts: vyos - gather_facts: false - - tasks: - - name: Run whoami - vyos.vyos.vyos_command: - commands: - - whoami - register: whoami - - - assert: - that: - - whoami is successful - - whoami.stdout_lines[0][0] == 'atester' diff -Nru ansible-core-2.14.16/test/integration/targets/network_cli/runme.sh ansible-core-2.14.18/test/integration/targets/network_cli/runme.sh --- ansible-core-2.14.16/test/integration/targets/network_cli/runme.sh 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/network_cli/runme.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -#!/usr/bin/env bash -set -eux -export ANSIBLE_ROLES_PATH=../ - -function cleanup { - ansible-playbook teardown.yml -i "$INVENTORY_PATH" "$@" -} - -trap cleanup EXIT - -ansible-playbook setup.yml -i "$INVENTORY_PATH" "$@" - -# We need a nonempty file to override key with (empty file gives a -# lovely "list index out of range" error) -foo=$(mktemp) -echo hello > "$foo" - -# We want to ensure that passwords make it to the network connection plugins -# because they follow a different path than the rest of the codebase. -# In setup.yml, we create a passworded user, and now we connect as that user -# to make sure the password we pass here successfully makes it to the plugin. -ansible-playbook \ - -i "$INVENTORY_PATH" \ - -e ansible_user=atester \ - -e ansible_password=testymctest \ - -e ansible_ssh_private_key_file="$foo" \ - passworded_user.yml diff -Nru ansible-core-2.14.16/test/integration/targets/network_cli/setup.yml ansible-core-2.14.18/test/integration/targets/network_cli/setup.yml --- ansible-core-2.14.16/test/integration/targets/network_cli/setup.yml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/network_cli/setup.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -- hosts: vyos - connection: ansible.netcommon.network_cli - become: true - gather_facts: false - - tasks: - - name: Create user with password - register: result - vyos.vyos.vyos_config: - lines: - - set system login user atester full-name "Ansible Tester" - - set system login user atester authentication plaintext-password testymctest - - set system login user jsmith level admin - - delete service ssh disable-password-authentication diff -Nru ansible-core-2.14.16/test/integration/targets/network_cli/teardown.yml ansible-core-2.14.18/test/integration/targets/network_cli/teardown.yml --- ansible-core-2.14.16/test/integration/targets/network_cli/teardown.yml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/network_cli/teardown.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -- hosts: vyos - connection: ansible.netcommon.network_cli - become: true - gather_facts: false - - tasks: - - name: Get rid of user (undo everything from setup.yml) - register: result - vyos.vyos.vyos_config: - lines: - - delete system login user atester full-name "Ansible Tester" - - delete system login user atester authentication plaintext-password testymctest - - delete system login user jsmith level admin - - set service ssh disable-password-authentication diff -Nru ansible-core-2.14.16/test/integration/targets/no_log/action_plugins/action_sets_no_log.py ansible-core-2.14.18/test/integration/targets/no_log/action_plugins/action_sets_no_log.py --- ansible-core-2.14.16/test/integration/targets/no_log/action_plugins/action_sets_no_log.py 1970-01-01 00:00:00.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/no_log/action_plugins/action_sets_no_log.py 2024-11-04 18:35:24.000000000 +0000 @@ -0,0 +1,8 @@ +from __future__ import annotations + +from ansible.plugins.action import ActionBase + + +class ActionModule(ActionBase): + def run(self, tmp=None, task_vars=None): + return dict(changed=False, failed=False, msg="action result should be masked", _ansible_no_log="yeppers") # ensure that a truthy non-bool works here diff -Nru ansible-core-2.14.16/test/integration/targets/no_log/ansible_no_log_in_result.yml ansible-core-2.14.18/test/integration/targets/no_log/ansible_no_log_in_result.yml --- ansible-core-2.14.16/test/integration/targets/no_log/ansible_no_log_in_result.yml 1970-01-01 00:00:00.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/no_log/ansible_no_log_in_result.yml 2024-11-04 18:35:24.000000000 +0000 @@ -0,0 +1,13 @@ +- hosts: localhost + gather_facts: no + tasks: + - action_sets_no_log: + register: res_action + + - assert: + that: + - res_action.msg == "action result should be masked" + + - action_sets_no_log: + loop: [1, 2, 3] + register: res_action diff -Nru ansible-core-2.14.16/test/integration/targets/no_log/dynamic.yml ansible-core-2.14.18/test/integration/targets/no_log/dynamic.yml --- ansible-core-2.14.16/test/integration/targets/no_log/dynamic.yml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/no_log/dynamic.yml 2024-11-04 18:35:24.000000000 +0000 @@ -1,27 +1,42 @@ - name: test dynamic no log hosts: testhost gather_facts: no - ignore_errors: yes tasks: - name: no loop, task fails, dynamic no_log - debug: - msg: "SHOW {{ var_does_not_exist }}" + raw: echo {{ var_does_not_exist }} no_log: "{{ not (unsafe_show_logs|bool) }}" + ignore_errors: yes + register: result + + - assert: + that: + - result is failed + - result.results is not defined - name: loop, task succeeds, dynamic does no_log - debug: - msg: "SHOW {{ item }}" + raw: echo {{ item }} loop: - a - b - c no_log: "{{ not (unsafe_show_logs|bool) }}" + register: result + + - assert: + that: + - result.results | length == 3 - name: loop, task fails, dynamic no_log - debug: - msg: "SHOW {{ var_does_not_exist }}" + raw: echo {{ var_does_not_exist }} loop: - a - b - c no_log: "{{ not (unsafe_show_logs|bool) }}" + ignore_errors: yes + register: result + + - assert: + that: + - result is failed + - result.results is not defined # DT needs result.results | length == 3 diff -Nru ansible-core-2.14.16/test/integration/targets/no_log/no_log_config.yml ansible-core-2.14.18/test/integration/targets/no_log/no_log_config.yml --- ansible-core-2.14.16/test/integration/targets/no_log/no_log_config.yml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/no_log/no_log_config.yml 2024-11-04 18:35:24.000000000 +0000 @@ -10,4 +10,4 @@ - debug: - debug: - loop: '{{ range(3) }}' + loop: '{{ range(3) | list }}' diff -Nru ansible-core-2.14.16/test/integration/targets/no_log/no_log_local.yml ansible-core-2.14.18/test/integration/targets/no_log/no_log_local.yml --- ansible-core-2.14.16/test/integration/targets/no_log/no_log_local.yml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/no_log/no_log_local.yml 2024-11-04 18:35:24.000000000 +0000 @@ -4,19 +4,22 @@ hosts: testhost gather_facts: no tasks: + - include_vars: secretvars.yml + no_log: true + - name: args should be logged in the absence of no_log - shell: echo "LOG_ME_TASK_SUCCEEDED" + shell: echo "{{log_me_prefix}}TASK_SUCCEEDED" - name: failed args should be logged in the absence of no_log - shell: echo "LOG_ME_TASK_FAILED" + shell: echo "{{log_me_prefix}}TASK_FAILED" failed_when: true ignore_errors: true - name: item args should be logged in the absence of no_log shell: echo {{ item }} - with_items: [ "LOG_ME_ITEM", "LOG_ME_SKIPPED", "LOG_ME_ITEM_FAILED" ] - when: item != "LOG_ME_SKIPPED" - failed_when: item == "LOG_ME_ITEM_FAILED" + with_items: [ "{{log_me_prefix}}ITEM", "{{log_me_prefix}}SKIPPED", "{{log_me_prefix}}ITEM_FAILED" ] + when: item != log_me_prefix ~ "SKIPPED" + failed_when: item == log_me_prefix ~ "ITEM_FAILED" ignore_errors: true - name: args should not be logged when task-level no_log set @@ -61,7 +64,7 @@ no_log: true - name: args should be logged when task-level no_log overrides play-level - shell: echo "LOG_ME_OVERRIDE" + shell: echo "{{log_me_prefix}}OVERRIDE" no_log: false - name: Add a fake host for next play diff -Nru ansible-core-2.14.16/test/integration/targets/no_log/no_log_suboptions.yml ansible-core-2.14.18/test/integration/targets/no_log/no_log_suboptions.yml --- ansible-core-2.14.16/test/integration/targets/no_log/no_log_suboptions.yml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/no_log/no_log_suboptions.yml 2024-11-04 18:35:24.000000000 +0000 @@ -5,20 +5,20 @@ tasks: - name: Task with suboptions module: - secret: GLAMOROUS + secret: "{{ s106 }}" subopt_dict: - str_sub_opt1: AFTERMATH + str_sub_opt1: "{{ s107 }}" str_sub_opt2: otherstring nested_subopt: - n_subopt1: MANPOWER + n_subopt1: "{{ s101 }}" subopt_list: - - subopt1: UNTAPPED + - subopt1: "{{ s102 }}" subopt2: thridstring - - subopt1: CONCERNED + - subopt1: "{{ s103 }}" - name: Task with suboptions as string module: - secret: MARLIN - subopt_dict: str_sub_opt1=FLICK + secret: "{{ s104 }}" + subopt_dict: str_sub_opt1={{ s105 }} diff -Nru ansible-core-2.14.16/test/integration/targets/no_log/no_log_suboptions_invalid.yml ansible-core-2.14.18/test/integration/targets/no_log/no_log_suboptions_invalid.yml --- ansible-core-2.14.16/test/integration/targets/no_log/no_log_suboptions_invalid.yml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/no_log/no_log_suboptions_invalid.yml 2024-11-04 18:35:24.000000000 +0000 @@ -4,42 +4,45 @@ ignore_errors: yes tasks: + - include_vars: secretvars.yml + no_log: true + - name: Task with suboptions and invalid parameter module: - secret: SUPREME + secret: "{{ s201 }}" invalid: param subopt_dict: - str_sub_opt1: IDIOM + str_sub_opt1: "{{ s202 }}" str_sub_opt2: otherstring nested_subopt: - n_subopt1: MOCKUP + n_subopt1: "{{ s203 }}" subopt_list: - - subopt1: EDUCATED + - subopt1: "{{ s204 }}" subopt2: thridstring - - subopt1: FOOTREST + - subopt1: "{{ s205 }}" - name: Task with suboptions as string with invalid parameter module: - secret: FOOTREST + secret: "{{ s213 }}" invalid: param - subopt_dict: str_sub_opt1=CRAFTY + subopt_dict: str_sub_opt1={{ s206 }} - name: Task with suboptions with dict instead of list module: - secret: FELINE + secret: "{{ s207 }}" subopt_dict: - str_sub_opt1: CRYSTAL + str_sub_opt1: "{{ s208 }}" str_sub_opt2: otherstring nested_subopt: - n_subopt1: EXPECTANT + n_subopt1: "{{ s209 }}" subopt_list: foo: bar - name: Task with suboptions with incorrect data type module: - secret: AGROUND + secret: "{{ s210 }}" subopt_dict: 9068.21361 subopt_list: - - subopt1: GOLIATH - - subopt1: FREEFALL + - subopt1: "{{ s211 }}" + - subopt1: "{{ s212 }}" diff -Nru ansible-core-2.14.16/test/integration/targets/no_log/runme.sh ansible-core-2.14.18/test/integration/targets/no_log/runme.sh --- ansible-core-2.14.16/test/integration/targets/no_log/runme.sh 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/no_log/runme.sh 2024-11-04 18:35:24.000000000 +0000 @@ -1,6 +1,12 @@ #!/usr/bin/env bash -set -eux +set -eux -o pipefail + +# ensure _ansible_no_log returned by actions is actually respected +ansible-playbook ansible_no_log_in_result.yml -vvvvv > "${OUTPUT_DIR}/output.log" 2> /dev/null + +[ "$(grep -c "action result should be masked" "${OUTPUT_DIR}/output.log")" = "0" ] +[ "$(grep -c "the output has been hidden" "${OUTPUT_DIR}/output.log")" = "4" ] # This test expects 7 loggable vars and 0 non-loggable ones. # If either mismatches it fails, run the ansible-playbook command to debug. @@ -9,18 +15,18 @@ # deal with corner cases with no log and loops # no log enabled, should produce 6 censored messages -[ "$(ansible-playbook dynamic.yml -i ../../inventory -vvvvv "$@" -e unsafe_show_logs=no|grep -c 'output has been hidden')" = "6" ] +[ "$(ansible-playbook dynamic.yml -i ../../inventory -vvvvv "$@" -e unsafe_show_logs=no|grep -c 'output has been hidden')" = "6" ] # DT needs 7 # no log disabled, should produce 0 censored [ "$(ansible-playbook dynamic.yml -i ../../inventory -vvvvv "$@" -e unsafe_show_logs=yes|grep -c 'output has been hidden')" = "0" ] # test no log for sub options -[ "$(ansible-playbook no_log_suboptions.yml -i ../../inventory -vvvvv "$@" | grep -Ec '(MANPOWER|UNTAPPED|CONCERNED|MARLIN|FLICK)')" = "0" ] +[ "$(ansible-playbook no_log_suboptions.yml -i ../../inventory -vvvvv "$@" | grep -Ec 'SECRET')" = "0" ] # test invalid data passed to a suboption -[ "$(ansible-playbook no_log_suboptions_invalid.yml -i ../../inventory -vvvvv "$@" | grep -Ec '(SUPREME|IDIOM|MOCKUP|EDUCATED|FOOTREST|CRAFTY|FELINE|CRYSTAL|EXPECTANT|AGROUND|GOLIATH|FREEFALL)')" = "0" ] +[ "$(ansible-playbook no_log_suboptions_invalid.yml -i ../../inventory -vvvvv "$@" | grep -Ec 'SECRET')" = "0" ] # test variations on ANSIBLE_NO_LOG [ "$(ansible-playbook no_log_config.yml -i ../../inventory -vvvvv "$@" | grep -Ec 'the output has been hidden')" = "1" ] [ "$(ANSIBLE_NO_LOG=0 ansible-playbook no_log_config.yml -i ../../inventory -vvvvv "$@" | grep -Ec 'the output has been hidden')" = "1" ] -[ "$(ANSIBLE_NO_LOG=1 ansible-playbook no_log_config.yml -i ../../inventory -vvvvv "$@" | grep -Ec 'the output has been hidden')" = "6" ] +[ "$(ANSIBLE_NO_LOG=1 ansible-playbook no_log_config.yml -i ../../inventory -vvvvv "$@" | grep -Ec 'the output has been hidden')" = "6" ] # DT needs 5 diff -Nru ansible-core-2.14.16/test/integration/targets/no_log/secretvars.yml ansible-core-2.14.18/test/integration/targets/no_log/secretvars.yml --- ansible-core-2.14.16/test/integration/targets/no_log/secretvars.yml 1970-01-01 00:00:00.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/no_log/secretvars.yml 2024-11-04 18:35:24.000000000 +0000 @@ -0,0 +1,32 @@ +# These values are in a separate vars file and referenced dynamically to avoid spurious counts from contextual error messages +# that show the playbook contents inline (since unencrypted playbook contents are not considered secret). +log_me_prefix: LOG_ME_ + +# Unique values are used for each secret below to ensure that one secret "learned" does not cause another non-secret +# value to be considered secret simply because they share the same value. A common substring is, however, present in +# each one to simplify searching for secret values in test output. Having a unique value for each also helps in +# debugging when unexpected output is encountered. + +# secrets for no_log_suboptions.yml +s101: SECRET101 +s102: SECRET102 +s103: SECRET103 +s104: SECRET104 +s105: SECRET105 +s106: SECRET106 +s107: SECRET107 + +# secrets for no_log_suboptions_invalid.yml +s201: SECRET201 +s202: SECRET202 +s203: SECRET203 +s204: SECRET204 +s205: SECRET205 +s206: SECRET206 +s207: SECRET207 +s208: SECRET208 +s209: SECRET209 +s210: SECRET210 +s211: SECRET211 +s212: SECRET212 +s213: SECRET213 diff -Nru ansible-core-2.14.16/test/integration/targets/user/tasks/main.yml ansible-core-2.14.18/test/integration/targets/user/tasks/main.yml --- ansible-core-2.14.16/test/integration/targets/user/tasks/main.yml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/user/tasks/main.yml 2024-11-04 18:35:24.000000000 +0000 @@ -36,7 +36,8 @@ - import_tasks: test_ssh_key_passphrase.yml - import_tasks: test_password_lock.yml - import_tasks: test_password_lock_new_user.yml -- import_tasks: test_local.yml +- include_tasks: test_local.yml when: not (ansible_distribution == 'openSUSE Leap' and ansible_distribution_version is version('15.4', '>=')) -- import_tasks: test_umask.yml +- include_tasks: test_umask.yml when: ansible_facts.system == 'Linux' +- import_tasks: ssh_keygen.yml diff -Nru ansible-core-2.14.16/test/integration/targets/user/tasks/ssh_keygen.yml ansible-core-2.14.18/test/integration/targets/user/tasks/ssh_keygen.yml --- ansible-core-2.14.16/test/integration/targets/user/tasks/ssh_keygen.yml 1970-01-01 00:00:00.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/user/tasks/ssh_keygen.yml 2024-11-04 18:35:24.000000000 +0000 @@ -0,0 +1,100 @@ +- name: user generating ssh keys tests + become: true + vars: + home: "{{ (ansible_facts['os_family'] == 'Darwin')|ternary('/Users/ansibulluser/', '/home/ansibulluser/')}}" + ssh_key_file: .ssh/ansible_test_rsa + pub_file: '{{ssh_key_file}}.pub' + key_files: + - '{{ssh_key_file}}' + - '{{pub_file}}' + block: + - name: Ensure clean/non existsing ansibulluser + user: name=ansibulluser state=absent + + - name: Test creating ssh key creation + block: + - name: Create user with ssh key + user: + name: ansibulluser + state: present + generate_ssh_key: yes + ssh_key_file: '{{ ssh_key_file}}' + + - name: check files exist + stat: + path: '{{home ~ item}}' + register: stat_keys + loop: '{{ key_files }}' + + - name: ensure they exist + assert: + that: + - stat_keys.results[item].stat.exists + loop: [0, 1] + + always: + - name: Clean ssh keys + file: path={{ home ~ item }} state=absent + loop: '{{ key_files }}' + + - name: Ensure clean/non existsing ansibulluser + user: name=ansibulluser state=absent + + - name: Ensure we don't break on conflicts + block: + - name: flag file for test + tempfile: + register: flagfile + + - name: precreate public .ssh + file: path={{home ~ '.ssh'}} state=directory + + - name: setup public key linked to flag file + file: path={{home ~ pub_file}} src={{flagfile.path}} state=link + + - name: Create user with ssh key + user: + name: ansibulluser + state: present + generate_ssh_key: yes + ssh_key_file: '{{ ssh_key_file }}' + ignore_errors: true + register: user_no_force + + - stat: path={{home ~ pub_file}} + register: check_pub + + - name: ensure we didn't overwrite + assert: + that: + - check_pub.stat.exists + - check_pub.stat.islnk + - check_pub.stat.uid == 0 + + - name: Create user with ssh key + user: + name: ansibulluser + state: present + generate_ssh_key: yes + ssh_key_file: '{{ ssh_key_file }}' + force: true + ignore_errors: true + register: user_force + + - stat: path={{home ~ pub_file}} + register: check_pub2 + + - name: ensure we failed since we didn't force overwrite + assert: + that: + - user_force is success + - check_pub2.stat.exists + - not check_pub2.stat.islnk + - check_pub2.stat.uid != 0 + always: + - name: Clean up files + file: path={{ home ~ item }} state=absent + loop: '{{ key_files + [flagfile.path] }}' + + - name: Ensure clean/non existsing ansibulluser + user: name=ansibulluser state=absent diff -Nru ansible-core-2.14.16/test/integration/targets/user/tasks/test_local.yml ansible-core-2.14.18/test/integration/targets/user/tasks/test_local.yml --- ansible-core-2.14.16/test/integration/targets/user/tasks/test_local.yml 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/integration/targets/user/tasks/test_local.yml 2024-11-04 18:35:24.000000000 +0000 @@ -39,6 +39,15 @@ tags: - user_test_local_mode +- name: Ensure no local_ansibulluser + user: + name: local_ansibulluser + state: absent + local: yes + remove: true + tags: + - user_test_local_mode + - name: Create local_ansibulluser user: name: local_ansibulluser diff -Nru ansible-core-2.14.16/test/lib/ansible_test/_data/completion/network.txt ansible-core-2.14.18/test/lib/ansible_test/_data/completion/network.txt --- ansible-core-2.14.16/test/lib/ansible_test/_data/completion/network.txt 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/lib/ansible_test/_data/completion/network.txt 2024-11-04 18:35:24.000000000 +0000 @@ -1,2 +1 @@ ios/csr1000v collection=cisco.ios connection=ansible.netcommon.network_cli provider=aws arch=x86_64 -vyos/1.1.8 collection=vyos.vyos connection=ansible.netcommon.network_cli provider=aws arch=x86_64 diff -Nru ansible-core-2.14.16/test/lib/ansible_test/_data/completion/windows.txt ansible-core-2.14.18/test/lib/ansible_test/_data/completion/windows.txt --- ansible-core-2.14.16/test/lib/ansible_test/_data/completion/windows.txt 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/lib/ansible_test/_data/completion/windows.txt 2024-11-04 18:35:24.000000000 +0000 @@ -1,5 +1,3 @@ -windows/2012 provider=azure arch=x86_64 -windows/2012-R2 provider=azure arch=x86_64 windows/2016 provider=aws arch=x86_64 windows/2019 provider=aws arch=x86_64 windows/2022 provider=aws arch=x86_64 diff -Nru ansible-core-2.14.16/test/lib/ansible_test/_internal/docker_util.py ansible-core-2.14.18/test/lib/ansible_test/_internal/docker_util.py --- ansible-core-2.14.16/test/lib/ansible_test/_internal/docker_util.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/lib/ansible_test/_internal/docker_util.py 2024-11-04 18:35:24.000000000 +0000 @@ -20,6 +20,8 @@ SubprocessError, cache, OutputStream, + InternalError, + format_command_output, ) from .util_common import ( @@ -300,7 +302,7 @@ options = ['--volume', '/sys/fs/cgroup:/probe:ro'] cmd = ['sh', '-c', ' && echo "-" && '.join(multi_line_commands)] - stdout = run_utility_container(args, f'ansible-test-probe-{args.session_name}', cmd, options)[0] + stdout, stderr = run_utility_container(args, f'ansible-test-probe-{args.session_name}', cmd, options) if args.explain: return ContainerHostProperties( @@ -313,6 +315,12 @@ blocks = stdout.split('\n-\n') + if len(blocks) != len(multi_line_commands): + message = f'Unexpected probe output. Expected {len(multi_line_commands)} blocks but found {len(blocks)}.\n' + message += format_command_output(stdout, stderr) + + raise InternalError(message.strip()) + values = blocks[0].split('\n') audit_parts = values[0].split(' ', 1) diff -Nru ansible-core-2.14.16/test/lib/ansible_test/_internal/pypi_proxy.py ansible-core-2.14.18/test/lib/ansible_test/_internal/pypi_proxy.py --- ansible-core-2.14.16/test/lib/ansible_test/_internal/pypi_proxy.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/lib/ansible_test/_internal/pypi_proxy.py 2024-11-04 18:35:24.000000000 +0000 @@ -14,6 +14,7 @@ from .host_configs import ( PosixConfig, + DockerConfig, ) from .util import ( @@ -55,8 +56,14 @@ return # user has overridden the proxy endpoint, there is nothing to provision versions_needing_proxy: tuple[str, ...] = tuple() # preserved for future use, no versions currently require this + containers_needing_proxy: set[str] = {'centos7'} posix_targets = [target for target in args.targets if isinstance(target, PosixConfig)] - need_proxy = targets_use_pypi and any(target.python.version in versions_needing_proxy for target in posix_targets) + need_proxy = targets_use_pypi and any( + target.python.version in versions_needing_proxy or + (isinstance(target, DockerConfig) and target.name in containers_needing_proxy) + for target in posix_targets + ) + use_proxy = args.pypi_proxy or need_proxy if not use_proxy: @@ -69,7 +76,7 @@ display.warning('Unable to use the PyPI proxy because Docker is not available. Installation of packages using `pip` may fail.') return - image = 'quay.io/ansible/pypi-test-container:2.0.0' + image = 'quay.io/ansible/pypi-test-container:3.1.0' port = 3141 run_support_container( diff -Nru ansible-core-2.14.16/test/lib/ansible_test/_internal/util.py ansible-core-2.14.18/test/lib/ansible_test/_internal/util.py --- ansible-core-2.14.16/test/lib/ansible_test/_internal/util.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/lib/ansible_test/_internal/util.py 2024-11-04 18:35:24.000000000 +0000 @@ -935,14 +935,7 @@ error_callback: t.Optional[c.Callable[[SubprocessError], None]] = None, ) -> None: message = 'Command "%s" returned exit status %s.\n' % (shlex.join(cmd), status) - - if stderr: - message += '>>> Standard Error\n' - message += '%s%s\n' % (stderr.strip(), Display.clear) - - if stdout: - message += '>>> Standard Output\n' - message += '%s%s\n' % (stdout.strip(), Display.clear) + message += format_command_output(stdout, stderr) self.cmd = cmd self.message = message @@ -986,6 +979,21 @@ self._callback() +def format_command_output(stdout: str, stderr: str) -> str: + """Return a formatted string containing the given stdout and stderr (if any).""" + message = '' + + if stderr := stderr.strip(): + message += '>>> Standard Error\n' + message += f'{stderr}{Display.clear}\n' + + if stdout := stdout.strip(): + message += '>>> Standard Output\n' + message += f'{stdout}{Display.clear}\n' + + return message + + def retry(func: t.Callable[..., TValue], ex_type: t.Type[BaseException] = SubprocessError, sleep: int = 10, attempts: int = 10, warn: bool = True) -> TValue: """Retry the specified function on failure.""" for dummy in range(1, attempts): diff -Nru ansible-core-2.14.16/test/lib/ansible_test/_util/target/setup/bootstrap.sh ansible-core-2.14.18/test/lib/ansible_test/_util/target/setup/bootstrap.sh --- ansible-core-2.14.16/test/lib/ansible_test/_util/target/setup/bootstrap.sh 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/lib/ansible_test/_util/target/setup/bootstrap.sh 2024-11-04 18:35:24.000000000 +0000 @@ -410,6 +410,15 @@ { # Required for newer mysql-server packages to install/upgrade on Ubuntu 16.04. rm -f /usr/sbin/policy-rc.d + + # CentOS 7 is EoL and its official repos are down; we need to the archived ones. + if grep -q '^CENTOS_MANTISBT_PROJECT="CentOS-7"$' /etc/os-release + then + sed -i \ + -e 's/mirrorlist/#mirrorlist/g' \ + -e 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' \ + /etc/yum.repos.d/CentOS-* + fi } bootstrap_remote() diff -Nru ansible-core-2.14.16/test/sanity/code-smell/package-data.py ansible-core-2.14.18/test/sanity/code-smell/package-data.py --- ansible-core-2.14.16/test/sanity/code-smell/package-data.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/sanity/code-smell/package-data.py 2024-11-04 18:35:24.000000000 +0000 @@ -95,7 +95,7 @@ def build(source_dir: str, tmp_dir: str) -> tuple[pathlib.Path, pathlib.Path]: """Create a sdist and wheel.""" create = subprocess.run( - [sys.executable, '-m', 'build', '--no-isolation', '--outdir', tmp_dir], + [sys.executable, '-m', 'build', '--outdir', tmp_dir], stdin=subprocess.DEVNULL, capture_output=True, text=True, diff -Nru ansible-core-2.14.16/test/sanity/ignore.txt ansible-core-2.14.18/test/sanity/ignore.txt --- ansible-core-2.14.16/test/sanity/ignore.txt 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/sanity/ignore.txt 2024-11-04 18:35:24.000000000 +0000 @@ -183,9 +183,6 @@ test/support/network-integration/collections/ansible_collections/ansible/netcommon/plugins/netconf/default.py pylint:unnecessary-comprehension test/support/network-integration/collections/ansible_collections/cisco/ios/plugins/cliconf/ios.py pylint:arguments-renamed test/support/network-integration/collections/ansible_collections/cisco/ios/plugins/modules/ios_config.py pep8:E501 -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/cliconf/vyos.py pylint:arguments-renamed -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_command.py pep8:E231 -test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_command.py pylint:disallowed-name test/support/windows-integration/plugins/action/win_copy.py pylint:used-before-assignment test/support/windows-integration/collections/ansible_collections/ansible/windows/plugins/module_utils/WebRequest.psm1 pslint!skip test/support/windows-integration/collections/ansible_collections/ansible/windows/plugins/modules/win_uri.ps1 pslint!skip diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/action/vyos.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/action/vyos.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/action/vyos.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/action/vyos.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,129 +0,0 @@ -# -# (c) 2016 Red Hat Inc. -# -# This file is part of Ansible -# -# Ansible is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . -# -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -import sys -import copy - -from ansible_collections.ansible.netcommon.plugins.action.network import ( - ActionModule as ActionNetworkModule, -) -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - load_provider, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( - vyos_provider_spec, -) -from ansible.utils.display import Display - -display = Display() - - -class ActionModule(ActionNetworkModule): - def run(self, tmp=None, task_vars=None): - del tmp # tmp no longer has any effect - - module_name = self._task.action.split(".")[-1] - self._config_module = True if module_name == "vyos_config" else False - persistent_connection = self._play_context.connection.split(".")[-1] - warnings = [] - - if persistent_connection == "network_cli": - provider = self._task.args.get("provider", {}) - if any(provider.values()): - display.warning( - "provider is unnecessary when using network_cli and will be ignored" - ) - del self._task.args["provider"] - elif self._play_context.connection == "local": - provider = load_provider(vyos_provider_spec, self._task.args) - pc = copy.deepcopy(self._play_context) - pc.connection = "ansible.netcommon.network_cli" - pc.network_os = "vyos.vyos.vyos" - pc.remote_addr = provider["host"] or self._play_context.remote_addr - pc.port = int(provider["port"] or self._play_context.port or 22) - pc.remote_user = ( - provider["username"] or self._play_context.connection_user - ) - pc.password = provider["password"] or self._play_context.password - pc.private_key_file = ( - provider["ssh_keyfile"] or self._play_context.private_key_file - ) - - connection = self._shared_loader_obj.connection_loader.get( - "ansible.netcommon.persistent", - pc, - sys.stdin, - task_uuid=self._task._uuid, - ) - - # TODO: Remove below code after ansible minimal is cut out - if connection is None: - pc.connection = "network_cli" - pc.network_os = "vyos" - connection = self._shared_loader_obj.connection_loader.get( - "persistent", pc, sys.stdin, task_uuid=self._task._uuid - ) - - display.vvv( - "using connection plugin %s (was local)" % pc.connection, - pc.remote_addr, - ) - - command_timeout = ( - int(provider["timeout"]) - if provider["timeout"] - else connection.get_option("persistent_command_timeout") - ) - connection.set_options( - direct={"persistent_command_timeout": command_timeout} - ) - - socket_path = connection.run() - display.vvvv("socket_path: %s" % socket_path, pc.remote_addr) - if not socket_path: - return { - "failed": True, - "msg": "unable to open shell. Please see: " - + "https://docs.ansible.com/ansible/latest/network/user_guide/network_debug_troubleshooting.html#category-unable-to-open-shell", - } - - task_vars["ansible_socket"] = socket_path - warnings.append( - [ - "connection local support for this module is deprecated and will be removed in version 2.14, use connection %s" - % pc.connection - ] - ) - else: - return { - "failed": True, - "msg": "Connection type %s is not valid for this module" - % self._play_context.connection, - } - - result = super(ActionModule, self).run(task_vars=task_vars) - if warnings: - if "warnings" in result: - result["warnings"].extend(warnings) - else: - result["warnings"] = warnings - return result diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/cliconf/vyos.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/cliconf/vyos.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/cliconf/vyos.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/cliconf/vyos.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,343 +0,0 @@ -# -# (c) 2017 Red Hat Inc. -# -# This file is part of Ansible -# -# Ansible is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . -# -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = """ ---- -author: Ansible Networking Team -cliconf: vyos -short_description: Use vyos cliconf to run command on VyOS platform -description: - - This vyos plugin provides low level abstraction apis for - sending and receiving CLI commands from VyOS network devices. -version_added: "2.4" -""" - -import re -import json - -from collections.abc import Mapping - -from ansible.errors import AnsibleConnectionFailure -from ansible.module_utils._text import to_text -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import ( - NetworkConfig, -) -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - to_list, -) -from ansible.plugins.cliconf import CliconfBase - - -class Cliconf(CliconfBase): - def get_device_info(self): - device_info = {} - - device_info["network_os"] = "vyos" - reply = self.get("show version") - data = to_text(reply, errors="surrogate_or_strict").strip() - - match = re.search(r"Version:\s*(.*)", data) - if match: - device_info["network_os_version"] = match.group(1) - - match = re.search(r"HW model:\s*(\S+)", data) - if match: - device_info["network_os_model"] = match.group(1) - - reply = self.get("show host name") - device_info["network_os_hostname"] = to_text( - reply, errors="surrogate_or_strict" - ).strip() - - return device_info - - def get_config(self, flags=None, format=None): - if format: - option_values = self.get_option_values() - if format not in option_values["format"]: - raise ValueError( - "'format' value %s is invalid. Valid values of format are %s" - % (format, ", ".join(option_values["format"])) - ) - - if not flags: - flags = [] - - if format == "text": - command = "show configuration" - else: - command = "show configuration commands" - - command += " ".join(to_list(flags)) - command = command.strip() - - out = self.send_command(command) - return out - - def edit_config( - self, candidate=None, commit=True, replace=None, comment=None - ): - resp = {} - operations = self.get_device_operations() - self.check_edit_config_capability( - operations, candidate, commit, replace, comment - ) - - results = [] - requests = [] - self.send_command("configure") - for cmd in to_list(candidate): - if not isinstance(cmd, Mapping): - cmd = {"command": cmd} - - results.append(self.send_command(**cmd)) - requests.append(cmd["command"]) - out = self.get("compare") - out = to_text(out, errors="surrogate_or_strict") - diff_config = out if not out.startswith("No changes") else None - - if diff_config: - if commit: - try: - self.commit(comment) - except AnsibleConnectionFailure as e: - msg = "commit failed: %s" % e.message - self.discard_changes() - raise AnsibleConnectionFailure(msg) - else: - self.send_command("exit") - else: - self.discard_changes() - else: - self.send_command("exit") - if ( - to_text( - self._connection.get_prompt(), errors="surrogate_or_strict" - ) - .strip() - .endswith("#") - ): - self.discard_changes() - - if diff_config: - resp["diff"] = diff_config - resp["response"] = results - resp["request"] = requests - return resp - - def get( - self, - command=None, - prompt=None, - answer=None, - sendonly=False, - output=None, - newline=True, - check_all=False, - ): - if not command: - raise ValueError("must provide value of command to execute") - if output: - raise ValueError( - "'output' value %s is not supported for get" % output - ) - - return self.send_command( - command=command, - prompt=prompt, - answer=answer, - sendonly=sendonly, - newline=newline, - check_all=check_all, - ) - - def commit(self, comment=None): - if comment: - command = 'commit comment "{0}"'.format(comment) - else: - command = "commit" - self.send_command(command) - - def discard_changes(self): - self.send_command("exit discard") - - def get_diff( - self, - candidate=None, - running=None, - diff_match="line", - diff_ignore_lines=None, - path=None, - diff_replace=None, - ): - diff = {} - device_operations = self.get_device_operations() - option_values = self.get_option_values() - - if candidate is None and device_operations["supports_generate_diff"]: - raise ValueError( - "candidate configuration is required to generate diff" - ) - - if diff_match not in option_values["diff_match"]: - raise ValueError( - "'match' value %s in invalid, valid values are %s" - % (diff_match, ", ".join(option_values["diff_match"])) - ) - - if diff_replace: - raise ValueError("'replace' in diff is not supported") - - if diff_ignore_lines: - raise ValueError("'diff_ignore_lines' in diff is not supported") - - if path: - raise ValueError("'path' in diff is not supported") - - set_format = candidate.startswith("set") or candidate.startswith( - "delete" - ) - candidate_obj = NetworkConfig(indent=4, contents=candidate) - if not set_format: - config = [c.line for c in candidate_obj.items] - commands = list() - # this filters out less specific lines - for item in config: - for index, entry in enumerate(commands): - if item.startswith(entry): - del commands[index] - break - commands.append(item) - - candidate_commands = [ - "set %s" % cmd.replace(" {", "") for cmd in commands - ] - - else: - candidate_commands = str(candidate).strip().split("\n") - - if diff_match == "none": - diff["config_diff"] = list(candidate_commands) - return diff - - running_commands = [ - str(c).replace("'", "") for c in running.splitlines() - ] - - updates = list() - visited = set() - - for line in candidate_commands: - item = str(line).replace("'", "") - - if not item.startswith("set") and not item.startswith("delete"): - raise ValueError( - "line must start with either `set` or `delete`" - ) - - elif item.startswith("set") and item not in running_commands: - updates.append(line) - - elif item.startswith("delete"): - if not running_commands: - updates.append(line) - else: - item = re.sub(r"delete", "set", item) - for entry in running_commands: - if entry.startswith(item) and line not in visited: - updates.append(line) - visited.add(line) - - diff["config_diff"] = list(updates) - return diff - - def run_commands(self, commands=None, check_rc=True): - if commands is None: - raise ValueError("'commands' value is required") - - responses = list() - for cmd in to_list(commands): - if not isinstance(cmd, Mapping): - cmd = {"command": cmd} - - output = cmd.pop("output", None) - if output: - raise ValueError( - "'output' value %s is not supported for run_commands" - % output - ) - - try: - out = self.send_command(**cmd) - except AnsibleConnectionFailure as e: - if check_rc: - raise - out = getattr(e, "err", e) - - responses.append(out) - - return responses - - def get_device_operations(self): - return { - "supports_diff_replace": False, - "supports_commit": True, - "supports_rollback": False, - "supports_defaults": False, - "supports_onbox_diff": True, - "supports_commit_comment": True, - "supports_multiline_delimiter": False, - "supports_diff_match": True, - "supports_diff_ignore_lines": False, - "supports_generate_diff": False, - "supports_replace": False, - } - - def get_option_values(self): - return { - "format": ["text", "set"], - "diff_match": ["line", "none"], - "diff_replace": [], - "output": [], - } - - def get_capabilities(self): - result = super(Cliconf, self).get_capabilities() - result["rpc"] += [ - "commit", - "discard_changes", - "get_diff", - "run_commands", - ] - result["device_operations"] = self.get_device_operations() - result.update(self.get_option_values()) - return json.dumps(result) - - def set_cli_prompt_context(self): - """ - Make sure we are in the operational cli mode - :return: None - """ - if self._connection.connected: - self._update_cli_prompt_context( - config_context="#", exit_command="exit discard" - ) diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/doc_fragments/vyos.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/doc_fragments/vyos.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/doc_fragments/vyos.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/doc_fragments/vyos.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright: (c) 2015, Peter Sprygada -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - - -class ModuleDocFragment(object): - - # Standard files documentation fragment - DOCUMENTATION = r"""options: - provider: - description: - - B(Deprecated) - - 'Starting with Ansible 2.5 we recommend using C(connection: network_cli).' - - For more information please see the L(Network Guide, ../network/getting_started/network_differences.html#multiple-communication-protocols). - - HORIZONTALLINE - - A dict object containing connection details. - type: dict - suboptions: - host: - description: - - Specifies the DNS host name or address for connecting to the remote device - over the specified transport. The value of host is used as the destination - address for the transport. - type: str - required: true - port: - description: - - Specifies the port to use when building the connection to the remote device. - type: int - default: 22 - username: - description: - - Configures the username to use to authenticate the connection to the remote - device. This value is used to authenticate the SSH session. If the value - is not specified in the task, the value of environment variable C(ANSIBLE_NET_USERNAME) - will be used instead. - type: str - password: - description: - - Specifies the password to use to authenticate the connection to the remote - device. This value is used to authenticate the SSH session. If the value - is not specified in the task, the value of environment variable C(ANSIBLE_NET_PASSWORD) - will be used instead. - type: str - timeout: - description: - - Specifies the timeout in seconds for communicating with the network device - for either connecting or sending commands. If the timeout is exceeded before - the operation is completed, the module will error. - type: int - default: 10 - ssh_keyfile: - description: - - Specifies the SSH key to use to authenticate the connection to the remote - device. This value is the path to the key used to authenticate the SSH - session. If the value is not specified in the task, the value of environment - variable C(ANSIBLE_NET_SSH_KEYFILE) will be used instead. - type: path -notes: -- For more information on using Ansible to manage network devices see the :ref:`Ansible - Network Guide ` -""" diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/facts/facts.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/facts/facts.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/facts/facts.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/facts/facts.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The arg spec for the vyos facts module. -""" -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - - -class FactsArgs(object): # pylint: disable=R0903 - """ The arg spec for the vyos facts module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - "gather_subset": dict(default=["!config"], type="list"), - "gather_network_resources": dict(type="list"), - } diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,263 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# -""" -The arg spec for the vyos_firewall_rules module -""" - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - - -class Firewall_rulesArgs(object): # pylint: disable=R0903 - """The arg spec for the vyos_firewall_rules module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - "config": { - "elements": "dict", - "options": { - "afi": { - "choices": ["ipv4", "ipv6"], - "required": True, - "type": "str", - }, - "rule_sets": { - "elements": "dict", - "options": { - "default_action": { - "choices": ["drop", "reject", "accept"], - "type": "str", - }, - "description": {"type": "str"}, - "enable_default_log": {"type": "bool"}, - "name": {"type": "str"}, - "rules": { - "elements": "dict", - "options": { - "action": { - "choices": [ - "drop", - "reject", - "accept", - "inspect", - ], - "type": "str", - }, - "description": {"type": "str"}, - "destination": { - "options": { - "address": {"type": "str"}, - "group": { - "options": { - "address_group": { - "type": "str" - }, - "network_group": { - "type": "str" - }, - "port_group": {"type": "str"}, - }, - "type": "dict", - }, - "port": {"type": "str"}, - }, - "type": "dict", - }, - "disabled": {"type": "bool"}, - "fragment": { - "choices": [ - "match-frag", - "match-non-frag", - ], - "type": "str", - }, - "icmp": { - "options": { - "code": {"type": "int"}, - "type": {"type": "int"}, - "type_name": { - "choices": [ - "any", - "echo-reply", - "destination-unreachable", - "network-unreachable", - "host-unreachable", - "protocol-unreachable", - "port-unreachable", - "fragmentation-needed", - "source-route-failed", - "network-unknown", - "host-unknown", - "network-prohibited", - "host-prohibited", - "TOS-network-unreachable", - "TOS-host-unreachable", - "communication-prohibited", - "host-precedence-violation", - "precedence-cutoff", - "source-quench", - "redirect", - "network-redirect", - "host-redirect", - "TOS-network-redirect", - "TOS-host-redirect", - "echo-request", - "router-advertisement", - "router-solicitation", - "time-exceeded", - "ttl-zero-during-transit", - "ttl-zero-during-reassembly", - "parameter-problem", - "ip-header-bad", - "required-option-missing", - "timestamp-request", - "timestamp-reply", - "address-mask-request", - "address-mask-reply", - "ping", - "pong", - "ttl-exceeded", - ], - "type": "str", - }, - }, - "type": "dict", - }, - "ipsec": { - "choices": ["match-ipsec", "match-none"], - "type": "str", - }, - "limit": { - "options": { - "burst": {"type": "int"}, - "rate": { - "options": { - "number": {"type": "int"}, - "unit": {"type": "str"}, - }, - "type": "dict", - }, - }, - "type": "dict", - }, - "number": {"required": True, "type": "int"}, - "p2p": { - "elements": "dict", - "options": { - "application": { - "choices": [ - "all", - "applejuice", - "bittorrent", - "directconnect", - "edonkey", - "gnutella", - "kazaa", - ], - "type": "str", - } - }, - "type": "list", - }, - "protocol": {"type": "str"}, - "recent": { - "options": { - "count": {"type": "int"}, - "time": {"type": "int"}, - }, - "type": "dict", - }, - "source": { - "options": { - "address": {"type": "str"}, - "group": { - "options": { - "address_group": { - "type": "str" - }, - "network_group": { - "type": "str" - }, - "port_group": {"type": "str"}, - }, - "type": "dict", - }, - "mac_address": {"type": "str"}, - "port": {"type": "str"}, - }, - "type": "dict", - }, - "state": { - "options": { - "established": {"type": "bool"}, - "invalid": {"type": "bool"}, - "new": {"type": "bool"}, - "related": {"type": "bool"}, - }, - "type": "dict", - }, - "tcp": { - "options": {"flags": {"type": "str"}}, - "type": "dict", - }, - "time": { - "options": { - "monthdays": {"type": "str"}, - "startdate": {"type": "str"}, - "starttime": {"type": "str"}, - "stopdate": {"type": "str"}, - "stoptime": {"type": "str"}, - "utc": {"type": "bool"}, - "weekdays": {"type": "str"}, - }, - "type": "dict", - }, - }, - "type": "list", - }, - }, - "type": "list", - }, - }, - "type": "list", - }, - "running_config": {"type": "str"}, - "state": { - "choices": [ - "merged", - "replaced", - "overridden", - "deleted", - "gathered", - "rendered", - "parsed", - ], - "default": "merged", - "type": "str", - }, - } # pylint: disable=C0301 diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/interfaces/interfaces.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/interfaces/interfaces.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/interfaces/interfaces.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/interfaces/interfaces.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,69 +0,0 @@ -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# -""" -The arg spec for the vyos_interfaces module -""" - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - - -class InterfacesArgs(object): # pylint: disable=R0903 - """The arg spec for the vyos_interfaces module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - "config": { - "elements": "dict", - "options": { - "description": {"type": "str"}, - "duplex": {"choices": ["full", "half", "auto"]}, - "enabled": {"default": True, "type": "bool"}, - "mtu": {"type": "int"}, - "name": {"required": True, "type": "str"}, - "speed": { - "choices": ["auto", "10", "100", "1000", "2500", "10000"], - "type": "str", - }, - "vifs": { - "elements": "dict", - "options": { - "vlan_id": {"type": "int"}, - "description": {"type": "str"}, - "enabled": {"default": True, "type": "bool"}, - "mtu": {"type": "int"}, - }, - "type": "list", - }, - }, - "type": "list", - }, - "state": { - "choices": ["merged", "replaced", "overridden", "deleted"], - "default": "merged", - "type": "str", - }, - } # pylint: disable=C0301 diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/l3_interfaces/l3_interfaces.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/l3_interfaces/l3_interfaces.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/l3_interfaces/l3_interfaces.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/l3_interfaces/l3_interfaces.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,81 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# -""" -The arg spec for the vyos_l3_interfaces module -""" - - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - - -class L3_interfacesArgs(object): # pylint: disable=R0903 - """The arg spec for the vyos_l3_interfaces module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - "config": { - "elements": "dict", - "options": { - "ipv4": { - "elements": "dict", - "options": {"address": {"type": "str"}}, - "type": "list", - }, - "ipv6": { - "elements": "dict", - "options": {"address": {"type": "str"}}, - "type": "list", - }, - "name": {"required": True, "type": "str"}, - "vifs": { - "elements": "dict", - "options": { - "ipv4": { - "elements": "dict", - "options": {"address": {"type": "str"}}, - "type": "list", - }, - "ipv6": { - "elements": "dict", - "options": {"address": {"type": "str"}}, - "type": "list", - }, - "vlan_id": {"type": "int"}, - }, - "type": "list", - }, - }, - "type": "list", - }, - "state": { - "choices": ["merged", "replaced", "overridden", "deleted"], - "default": "merged", - "type": "str", - }, - } # pylint: disable=C0301 diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/lag_interfaces/lag_interfaces.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/lag_interfaces/lag_interfaces.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/lag_interfaces/lag_interfaces.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/lag_interfaces/lag_interfaces.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# - -""" -The arg spec for the vyos_lag_interfaces module -""" -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - - -class Lag_interfacesArgs(object): # pylint: disable=R0903 - """The arg spec for the vyos_lag_interfaces module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - "config": { - "elements": "dict", - "options": { - "arp_monitor": { - "options": { - "interval": {"type": "int"}, - "target": {"type": "list"}, - }, - "type": "dict", - }, - "hash_policy": { - "choices": ["layer2", "layer2+3", "layer3+4"], - "type": "str", - }, - "members": { - "elements": "dict", - "options": {"member": {"type": "str"}}, - "type": "list", - }, - "mode": { - "choices": [ - "802.3ad", - "active-backup", - "broadcast", - "round-robin", - "transmit-load-balance", - "adaptive-load-balance", - "xor-hash", - ], - "type": "str", - }, - "name": {"required": True, "type": "str"}, - "primary": {"type": "str"}, - }, - "type": "list", - }, - "state": { - "choices": ["merged", "replaced", "overridden", "deleted"], - "default": "merged", - "type": "str", - }, - } # pylint: disable=C0301 diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/lldp_global/lldp_global.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/lldp_global/lldp_global.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/lldp_global/lldp_global.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/lldp_global/lldp_global.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,56 +0,0 @@ -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# - -""" -The arg spec for the vyos_lldp_global module -""" -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - - -class Lldp_globalArgs(object): # pylint: disable=R0903 - """The arg spec for the vyos_lldp_global module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - "config": { - "options": { - "address": {"type": "str"}, - "enable": {"type": "bool"}, - "legacy_protocols": { - "choices": ["cdp", "edp", "fdp", "sonmp"], - "type": "list", - }, - "snmp": {"type": "str"}, - }, - "type": "dict", - }, - "state": { - "choices": ["merged", "replaced", "deleted"], - "default": "merged", - "type": "str", - }, - } # pylint: disable=C0301 diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/lldp_interfaces/lldp_interfaces.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/lldp_interfaces/lldp_interfaces.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/lldp_interfaces/lldp_interfaces.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/lldp_interfaces/lldp_interfaces.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,89 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# -""" -The arg spec for the vyos_lldp_interfaces module -""" - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - - -class Lldp_interfacesArgs(object): # pylint: disable=R0903 - """The arg spec for the vyos_lldp_interfaces module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - "config": { - "elements": "dict", - "options": { - "enable": {"default": True, "type": "bool"}, - "location": { - "options": { - "civic_based": { - "options": { - "ca_info": { - "elements": "dict", - "options": { - "ca_type": {"type": "int"}, - "ca_value": {"type": "str"}, - }, - "type": "list", - }, - "country_code": { - "required": True, - "type": "str", - }, - }, - "type": "dict", - }, - "coordinate_based": { - "options": { - "altitude": {"type": "int"}, - "datum": { - "choices": ["WGS84", "NAD83", "MLLW"], - "type": "str", - }, - "latitude": {"required": True, "type": "str"}, - "longitude": {"required": True, "type": "str"}, - }, - "type": "dict", - }, - "elin": {"type": "str"}, - }, - "type": "dict", - }, - "name": {"required": True, "type": "str"}, - }, - "type": "list", - }, - "state": { - "choices": ["merged", "replaced", "overridden", "deleted"], - "default": "merged", - "type": "str", - }, - } # pylint: disable=C0301 diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/static_routes/static_routes.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/static_routes/static_routes.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/static_routes/static_routes.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/argspec/static_routes/static_routes.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,99 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# -""" -The arg spec for the vyos_static_routes module -""" - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - - -class Static_routesArgs(object): # pylint: disable=R0903 - """The arg spec for the vyos_static_routes module - """ - - def __init__(self, **kwargs): - pass - - argument_spec = { - "config": { - "elements": "dict", - "options": { - "address_families": { - "elements": "dict", - "options": { - "afi": { - "choices": ["ipv4", "ipv6"], - "required": True, - "type": "str", - }, - "routes": { - "elements": "dict", - "options": { - "blackhole_config": { - "options": { - "distance": {"type": "int"}, - "type": {"type": "str"}, - }, - "type": "dict", - }, - "dest": {"required": True, "type": "str"}, - "next_hops": { - "elements": "dict", - "options": { - "admin_distance": {"type": "int"}, - "enabled": {"type": "bool"}, - "forward_router_address": { - "required": True, - "type": "str", - }, - "interface": {"type": "str"}, - }, - "type": "list", - }, - }, - "type": "list", - }, - }, - "type": "list", - } - }, - "type": "list", - }, - "running_config": {"type": "str"}, - "state": { - "choices": [ - "merged", - "replaced", - "overridden", - "deleted", - "gathered", - "rendered", - "parsed", - ], - "default": "merged", - "type": "str", - }, - } # pylint: disable=C0301 diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/config/lldp_interfaces/lldp_interfaces.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/config/lldp_interfaces/lldp_interfaces.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/config/lldp_interfaces/lldp_interfaces.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/config/lldp_interfaces/lldp_interfaces.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,438 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The vyos_lldp_interfaces class -It is in this file where the current configuration (as dict) -is compared to the provided configuration (as dict) and the command set -necessary to bring the current configuration to it's desired end-state is -created -""" - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - - -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( - ConfigBase, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( - Facts, -) -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - to_list, - dict_diff, -) -from ansible.module_utils.six import iteritems -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( - search_obj_in_list, - search_dict_tv_in_list, - key_value_in_dict, - is_dict_element_present, -) - - -class Lldp_interfaces(ConfigBase): - """ - The vyos_lldp_interfaces class - """ - - gather_subset = [ - "!all", - "!min", - ] - - gather_network_resources = [ - "lldp_interfaces", - ] - - params = ["enable", "location", "name"] - - def __init__(self, module): - super(Lldp_interfaces, self).__init__(module) - - def get_lldp_interfaces_facts(self): - """ Get the 'facts' (the current configuration) - - :rtype: A dictionary - :returns: The current configuration as a dictionary - """ - facts, _warnings = Facts(self._module).get_facts( - self.gather_subset, self.gather_network_resources - ) - lldp_interfaces_facts = facts["ansible_network_resources"].get( - "lldp_interfaces" - ) - if not lldp_interfaces_facts: - return [] - return lldp_interfaces_facts - - def execute_module(self): - """ Execute the module - - :rtype: A dictionary - :returns: The result from module execution - """ - result = {"changed": False} - commands = list() - warnings = list() - existing_lldp_interfaces_facts = self.get_lldp_interfaces_facts() - commands.extend(self.set_config(existing_lldp_interfaces_facts)) - if commands: - if self._module.check_mode: - resp = self._connection.edit_config(commands, commit=False) - else: - resp = self._connection.edit_config(commands) - result["changed"] = True - - result["commands"] = commands - - if self._module._diff: - result["diff"] = resp["diff"] if result["changed"] else None - - changed_lldp_interfaces_facts = self.get_lldp_interfaces_facts() - result["before"] = existing_lldp_interfaces_facts - if result["changed"]: - result["after"] = changed_lldp_interfaces_facts - - result["warnings"] = warnings - return result - - def set_config(self, existing_lldp_interfaces_facts): - """ Collect the configuration from the args passed to the module, - collect the current configuration (as a dict from facts) - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - want = self._module.params["config"] - have = existing_lldp_interfaces_facts - resp = self.set_state(want, have) - return to_list(resp) - - def set_state(self, want, have): - """ Select the appropriate function based on the state provided - - :param want: the desired configuration as a dictionary - :param have: the current configuration as a dictionary - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - commands = [] - state = self._module.params["state"] - if state in ("merged", "replaced", "overridden") and not want: - self._module.fail_json( - msg="value of config parameter must not be empty for state {0}".format( - state - ) - ) - if state == "overridden": - commands.extend(self._state_overridden(want=want, have=have)) - elif state == "deleted": - if want: - for item in want: - name = item["name"] - have_item = search_obj_in_list(name, have) - commands.extend( - self._state_deleted(want=None, have=have_item) - ) - else: - for have_item in have: - commands.extend( - self._state_deleted(want=None, have=have_item) - ) - else: - for want_item in want: - name = want_item["name"] - have_item = search_obj_in_list(name, have) - if state == "merged": - commands.extend( - self._state_merged(want=want_item, have=have_item) - ) - else: - commands.extend( - self._state_replaced(want=want_item, have=have_item) - ) - return commands - - def _state_replaced(self, want, have): - """ The command generator when state is replaced - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - commands = [] - if have: - commands.extend(self._state_deleted(want, have)) - commands.extend(self._state_merged(want, have)) - return commands - - def _state_overridden(self, want, have): - """ The command generator when state is overridden - - :rtype: A list - :returns: the commands necessary to migrate the current configuration - to the desired configuration - """ - commands = [] - for have_item in have: - lldp_name = have_item["name"] - lldp_in_want = search_obj_in_list(lldp_name, want) - if not lldp_in_want: - commands.append( - self._compute_command(have_item["name"], remove=True) - ) - - for want_item in want: - name = want_item["name"] - lldp_in_have = search_obj_in_list(name, have) - commands.extend(self._state_replaced(want_item, lldp_in_have)) - return commands - - def _state_merged(self, want, have): - """ The command generator when state is merged - - :rtype: A list - :returns: the commands necessary to merge the provided into - the current configuration - """ - commands = [] - if have: - commands.extend(self._render_updates(want, have)) - else: - commands.extend(self._render_set_commands(want)) - return commands - - def _state_deleted(self, want, have): - """ The command generator when state is deleted - - :rtype: A list - :returns: the commands necessary to remove the current configuration - of the provided objects - """ - commands = [] - if want: - params = Lldp_interfaces.params - for attrib in params: - if attrib == "location": - commands.extend( - self._update_location(have["name"], want, have) - ) - - elif have: - commands.append(self._compute_command(have["name"], remove=True)) - return commands - - def _render_updates(self, want, have): - commands = [] - lldp_name = have["name"] - commands.extend(self._configure_status(lldp_name, want, have)) - commands.extend(self._add_location(lldp_name, want, have)) - - return commands - - def _render_set_commands(self, want): - commands = [] - have = {} - lldp_name = want["name"] - params = Lldp_interfaces.params - - commands.extend(self._add_location(lldp_name, want, have)) - for attrib in params: - value = want[attrib] - if value: - if attrib == "location": - commands.extend(self._add_location(lldp_name, want, have)) - elif attrib == "enable": - if not value: - commands.append( - self._compute_command(lldp_name, value="disable") - ) - else: - commands.append(self._compute_command(lldp_name)) - - return commands - - def _configure_status(self, name, want_item, have_item): - commands = [] - if is_dict_element_present(have_item, "enable"): - temp_have_item = False - else: - temp_have_item = True - if want_item["enable"] != temp_have_item: - if want_item["enable"]: - commands.append( - self._compute_command(name, value="disable", remove=True) - ) - else: - commands.append(self._compute_command(name, value="disable")) - return commands - - def _add_location(self, name, want_item, have_item): - commands = [] - have_dict = {} - have_ca = {} - set_cmd = name + " location " - want_location_type = want_item.get("location") or {} - have_location_type = have_item.get("location") or {} - - if want_location_type["coordinate_based"]: - want_dict = want_location_type.get("coordinate_based") or {} - if is_dict_element_present(have_location_type, "coordinate_based"): - have_dict = have_location_type.get("coordinate_based") or {} - location_type = "coordinate-based" - updates = dict_diff(have_dict, want_dict) - for key, value in iteritems(updates): - if value: - commands.append( - self._compute_command( - set_cmd + location_type, key, str(value) - ) - ) - - elif want_location_type["civic_based"]: - location_type = "civic-based" - want_dict = want_location_type.get("civic_based") or {} - want_ca = want_dict.get("ca_info") or [] - if is_dict_element_present(have_location_type, "civic_based"): - have_dict = have_location_type.get("civic_based") or {} - have_ca = have_dict.get("ca_info") or [] - if want_dict["country_code"] != have_dict["country_code"]: - commands.append( - self._compute_command( - set_cmd + location_type, - "country-code", - str(want_dict["country_code"]), - ) - ) - else: - commands.append( - self._compute_command( - set_cmd + location_type, - "country-code", - str(want_dict["country_code"]), - ) - ) - commands.extend(self._add_civic_address(name, want_ca, have_ca)) - - elif want_location_type["elin"]: - location_type = "elin" - if is_dict_element_present(have_location_type, "elin"): - if want_location_type.get("elin") != have_location_type.get( - "elin" - ): - commands.append( - self._compute_command( - set_cmd + location_type, - value=str(want_location_type["elin"]), - ) - ) - else: - commands.append( - self._compute_command( - set_cmd + location_type, - value=str(want_location_type["elin"]), - ) - ) - return commands - - def _update_location(self, name, want_item, have_item): - commands = [] - del_cmd = name + " location" - want_location_type = want_item.get("location") or {} - have_location_type = have_item.get("location") or {} - - if want_location_type["coordinate_based"]: - want_dict = want_location_type.get("coordinate_based") or {} - if is_dict_element_present(have_location_type, "coordinate_based"): - have_dict = have_location_type.get("coordinate_based") or {} - location_type = "coordinate-based" - for key, value in iteritems(have_dict): - only_in_have = key_value_in_dict(key, value, want_dict) - if not only_in_have: - commands.append( - self._compute_command( - del_cmd + location_type, key, str(value), True - ) - ) - else: - commands.append(self._compute_command(del_cmd, remove=True)) - - elif want_location_type["civic_based"]: - want_dict = want_location_type.get("civic_based") or {} - want_ca = want_dict.get("ca_info") or [] - if is_dict_element_present(have_location_type, "civic_based"): - have_dict = have_location_type.get("civic_based") or {} - have_ca = have_dict.get("ca_info") - commands.extend( - self._update_civic_address(name, want_ca, have_ca) - ) - else: - commands.append(self._compute_command(del_cmd, remove=True)) - - else: - if is_dict_element_present(have_location_type, "elin"): - if want_location_type.get("elin") != have_location_type.get( - "elin" - ): - commands.append( - self._compute_command(del_cmd, remove=True) - ) - else: - commands.append(self._compute_command(del_cmd, remove=True)) - return commands - - def _add_civic_address(self, name, want, have): - commands = [] - for item in want: - ca_type = item["ca_type"] - ca_value = item["ca_value"] - obj_in_have = search_dict_tv_in_list( - ca_type, ca_value, have, "ca_type", "ca_value" - ) - if not obj_in_have: - commands.append( - self._compute_command( - key=name + " location civic-based ca-type", - attrib=str(ca_type) + " ca-value", - value=ca_value, - ) - ) - return commands - - def _update_civic_address(self, name, want, have): - commands = [] - for item in have: - ca_type = item["ca_type"] - ca_value = item["ca_value"] - in_want = search_dict_tv_in_list( - ca_type, ca_value, want, "ca_type", "ca_value" - ) - if not in_want: - commands.append( - self._compute_command( - name, - "location civic-based ca-type", - str(ca_type), - remove=True, - ) - ) - return commands - - def _compute_command(self, key, attrib=None, value=None, remove=False): - if remove: - cmd = "delete service lldp interface " - else: - cmd = "set service lldp interface " - cmd += key - if attrib: - cmd += " " + attrib - if value: - cmd += " '" + value + "'" - return cmd diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/facts.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/facts.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/facts.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/facts.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,83 +0,0 @@ -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The facts class for vyos -this file validates each subset of facts and selectively -calls the appropriate facts gathering function -""" -from __future__ import absolute_import, division, print_function - -__metaclass__ = type -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.facts.facts import ( - FactsBase, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.interfaces.interfaces import ( - InterfacesFacts, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.l3_interfaces.l3_interfaces import ( - L3_interfacesFacts, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.lag_interfaces.lag_interfaces import ( - Lag_interfacesFacts, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.lldp_global.lldp_global import ( - Lldp_globalFacts, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.lldp_interfaces.lldp_interfaces import ( - Lldp_interfacesFacts, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_rules.firewall_rules import ( - Firewall_rulesFacts, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.static_routes.static_routes import ( - Static_routesFacts, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.legacy.base import ( - Default, - Neighbors, - Config, -) - - -FACT_LEGACY_SUBSETS = dict(default=Default, neighbors=Neighbors, config=Config) -FACT_RESOURCE_SUBSETS = dict( - interfaces=InterfacesFacts, - l3_interfaces=L3_interfacesFacts, - lag_interfaces=Lag_interfacesFacts, - lldp_global=Lldp_globalFacts, - lldp_interfaces=Lldp_interfacesFacts, - static_routes=Static_routesFacts, - firewall_rules=Firewall_rulesFacts, -) - - -class Facts(FactsBase): - """ The fact class for vyos - """ - - VALID_LEGACY_GATHER_SUBSETS = frozenset(FACT_LEGACY_SUBSETS.keys()) - VALID_RESOURCE_SUBSETS = frozenset(FACT_RESOURCE_SUBSETS.keys()) - - def __init__(self, module): - super(Facts, self).__init__(module) - - def get_facts( - self, legacy_facts_type=None, resource_facts_type=None, data=None - ): - """ Collect the facts for vyos - :param legacy_facts_type: List of legacy facts types - :param resource_facts_type: List of resource fact types - :param data: previously collected conf - :rtype: dict - :return: the facts gathered - """ - if self.VALID_RESOURCE_SUBSETS: - self.get_network_resources_facts( - FACT_RESOURCE_SUBSETS, resource_facts_type, data - ) - if self.VALID_LEGACY_GATHER_SUBSETS: - self.get_network_legacy_facts( - FACT_LEGACY_SUBSETS, legacy_facts_type - ) - return self.ansible_facts, self._warnings diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,380 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The vyos firewall_rules fact class -It is in this file the configuration is collected from the device -for a given resource, parsed, and the facts tree is populated -based on the configuration. -""" -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -from re import findall, search, M -from copy import deepcopy -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_rules.firewall_rules import ( - Firewall_rulesArgs, -) - - -class Firewall_rulesFacts(object): - """ The vyos firewall_rules fact class - """ - - def __init__(self, module, subspec="config", options="options"): - self._module = module - self.argument_spec = Firewall_rulesArgs.argument_spec - spec = deepcopy(self.argument_spec) - if subspec: - if options: - facts_argument_spec = spec[subspec][options] - else: - facts_argument_spec = spec[subspec] - else: - facts_argument_spec = spec - - self.generated_spec = utils.generate_dict(facts_argument_spec) - - def get_device_data(self, connection): - return connection.get_config() - - def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for firewall_rules - :param connection: the device connection - :param ansible_facts: Facts dictionary - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - if not data: - # typically data is populated from the current device configuration - # data = connection.get('show running-config | section ^interface') - # using mock data instead - data = self.get_device_data(connection) - # split the config into instances of the resource - objs = [] - v6_rules = findall( - r"^set firewall ipv6-name (?:\'*)(\S+)(?:\'*)", data, M - ) - v4_rules = findall(r"^set firewall name (?:\'*)(\S+)(?:\'*)", data, M) - if v6_rules: - config = self.get_rules(data, v6_rules, type="ipv6") - if config: - config = utils.remove_empties(config) - objs.append(config) - if v4_rules: - config = self.get_rules(data, v4_rules, type="ipv4") - if config: - config = utils.remove_empties(config) - objs.append(config) - - ansible_facts["ansible_network_resources"].pop("firewall_rules", None) - facts = {} - if objs: - facts["firewall_rules"] = [] - params = utils.validate_config( - self.argument_spec, {"config": objs} - ) - for cfg in params["config"]: - facts["firewall_rules"].append(utils.remove_empties(cfg)) - - ansible_facts["ansible_network_resources"].update(facts) - return ansible_facts - - def get_rules(self, data, rules, type): - """ - This function performs following: - - Form regex to fetch 'rule-sets' specific config from data. - - Form the rule-set list based on ip address. - :param data: configuration. - :param rules: list of rule-sets. - :param type: ip address type. - :return: generated rule-sets configuration. - """ - r_v4 = [] - r_v6 = [] - for r in set(rules): - rule_regex = r" %s .+$" % r.strip("'") - cfg = findall(rule_regex, data, M) - fr = self.render_config(cfg, r.strip("'")) - fr["name"] = r.strip("'") - if type == "ipv6": - r_v6.append(fr) - else: - r_v4.append(fr) - if r_v4: - config = {"afi": "ipv4", "rule_sets": r_v4} - if r_v6: - config = {"afi": "ipv6", "rule_sets": r_v6} - return config - - def render_config(self, conf, match): - """ - Render config as dictionary structure and delete keys - from spec for null values - - :param spec: The facts tree, generated from the argspec - :param conf: The configuration - :rtype: dictionary - :returns: The generated config - """ - conf = "\n".join(filter(lambda x: x, conf)) - a_lst = ["description", "default_action", "enable_default_log"] - config = self.parse_attr(conf, a_lst, match) - if not config: - config = {} - config["rules"] = self.parse_rules_lst(conf) - return config - - def parse_rules_lst(self, conf): - """ - This function forms the regex to fetch the 'rules' with in - 'rule-sets' - :param conf: configuration data. - :return: generated rule list configuration. - """ - r_lst = [] - rules = findall(r"rule (?:\'*)(\d+)(?:\'*)", conf, M) - if rules: - rules_lst = [] - for r in set(rules): - r_regex = r" %s .+$" % r - cfg = "\n".join(findall(r_regex, conf, M)) - obj = self.parse_rules(cfg) - obj["number"] = int(r) - if obj: - rules_lst.append(obj) - r_lst = sorted(rules_lst, key=lambda i: i["number"]) - return r_lst - - def parse_rules(self, conf): - """ - This function triggers the parsing of 'rule' attributes. - a_lst is a list having rule attributes which doesn't - have further sub attributes. - :param conf: configuration - :return: generated rule configuration dictionary. - """ - a_lst = [ - "ipsec", - "action", - "protocol", - "fragment", - "disabled", - "description", - ] - rule = self.parse_attr(conf, a_lst) - r_sub = { - "p2p": self.parse_p2p(conf), - "tcp": self.parse_tcp(conf, "tcp"), - "icmp": self.parse_icmp(conf, "icmp"), - "time": self.parse_time(conf, "time"), - "limit": self.parse_limit(conf, "limit"), - "state": self.parse_state(conf, "state"), - "recent": self.parse_recent(conf, "recent"), - "source": self.parse_src_or_dest(conf, "source"), - "destination": self.parse_src_or_dest(conf, "destination"), - } - rule.update(r_sub) - return rule - - def parse_p2p(self, conf): - """ - This function forms the regex to fetch the 'p2p' with in - 'rules' - :param conf: configuration data. - :return: generated rule list configuration. - """ - a_lst = [] - applications = findall(r"p2p (?:\'*)(\d+)(?:\'*)", conf, M) - if applications: - app_lst = [] - for r in set(applications): - obj = {"application": r.strip("'")} - app_lst.append(obj) - a_lst = sorted(app_lst, key=lambda i: i["application"]) - return a_lst - - def parse_src_or_dest(self, conf, attrib=None): - """ - This function triggers the parsing of 'source or - destination' attributes. - :param conf: configuration. - :param attrib:'source/destination'. - :return:generated source/destination configuration dictionary. - """ - a_lst = ["port", "address", "mac_address"] - cfg_dict = self.parse_attr(conf, a_lst, match=attrib) - cfg_dict["group"] = self.parse_group(conf, attrib + " group") - return cfg_dict - - def parse_recent(self, conf, attrib=None): - """ - This function triggers the parsing of 'recent' attributes - :param conf: configuration. - :param attrib: 'recent'. - :return: generated config dictionary. - """ - a_lst = ["time", "count"] - cfg_dict = self.parse_attr(conf, a_lst, match=attrib) - return cfg_dict - - def parse_tcp(self, conf, attrib=None): - """ - This function triggers the parsing of 'tcp' attributes. - :param conf: configuration. - :param attrib: 'tcp'. - :return: generated config dictionary. - """ - cfg_dict = self.parse_attr(conf, ["flags"], match=attrib) - return cfg_dict - - def parse_time(self, conf, attrib=None): - """ - This function triggers the parsing of 'time' attributes. - :param conf: configuration. - :param attrib: 'time'. - :return: generated config dictionary. - """ - a_lst = [ - "stopdate", - "stoptime", - "weekdays", - "monthdays", - "startdate", - "starttime", - ] - cfg_dict = self.parse_attr(conf, a_lst, match=attrib) - return cfg_dict - - def parse_state(self, conf, attrib=None): - """ - This function triggers the parsing of 'state' attributes. - :param conf: configuration - :param attrib: 'state'. - :return: generated config dictionary. - """ - a_lst = ["new", "invalid", "related", "established"] - cfg_dict = self.parse_attr(conf, a_lst, match=attrib) - return cfg_dict - - def parse_group(self, conf, attrib=None): - """ - This function triggers the parsing of 'group' attributes. - :param conf: configuration. - :param attrib: 'group'. - :return: generated config dictionary. - """ - a_lst = ["port_group", "address_group", "network_group"] - cfg_dict = self.parse_attr(conf, a_lst, match=attrib) - return cfg_dict - - def parse_icmp(self, conf, attrib=None): - """ - This function triggers the parsing of 'icmp' attributes. - :param conf: configuration to be parsed. - :param attrib: 'icmp'. - :return: generated config dictionary. - """ - a_lst = ["code", "type", "type_name"] - cfg_dict = self.parse_attr(conf, a_lst, match=attrib) - return cfg_dict - - def parse_limit(self, conf, attrib=None): - """ - This function triggers the parsing of 'limit' attributes. - :param conf: configuration to be parsed. - :param attrib: 'limit' - :return: generated config dictionary. - """ - cfg_dict = self.parse_attr(conf, ["burst"], match=attrib) - cfg_dict["rate"] = self.parse_rate(conf, "rate") - return cfg_dict - - def parse_rate(self, conf, attrib=None): - """ - This function triggers the parsing of 'rate' attributes. - :param conf: configuration. - :param attrib: 'rate' - :return: generated config dictionary. - """ - a_lst = ["unit", "number"] - cfg_dict = self.parse_attr(conf, a_lst, match=attrib) - return cfg_dict - - def parse_attr(self, conf, attr_list, match=None): - """ - This function peforms the following: - - Form the regex to fetch the required attribute config. - - Type cast the output in desired format. - :param conf: configuration. - :param attr_list: list of attributes. - :param match: parent node/attribute name. - :return: generated config dictionary. - """ - config = {} - for attrib in attr_list: - regex = self.map_regex(attrib) - if match: - regex = match + " " + regex - if conf: - if self.is_bool(attrib): - out = conf.find(attrib.replace("_", "-")) - - dis = conf.find(attrib.replace("_", "-") + " 'disable'") - if out >= 1: - if dis >= 1: - config[attrib] = False - else: - config[attrib] = True - else: - out = search(r"^.*" + regex + " (.+)", conf, M) - if out: - val = out.group(1).strip("'") - if self.is_num(attrib): - val = int(val) - config[attrib] = val - return config - - def map_regex(self, attrib): - """ - - This function construct the regex string. - - replace the underscore with hyphen. - :param attrib: attribute - :return: regex string - """ - regex = attrib.replace("_", "-") - if attrib == "disabled": - regex = "disable" - return regex - - def is_bool(self, attrib): - """ - This function looks for the attribute in predefined bool type set. - :param attrib: attribute. - :return: True/False - """ - bool_set = ( - "new", - "invalid", - "related", - "disabled", - "established", - "enable_default_log", - ) - return True if attrib in bool_set else False - - def is_num(self, attrib): - """ - This function looks for the attribute in predefined integer type set. - :param attrib: attribute. - :return: True/false. - """ - num_set = ("time", "code", "type", "count", "burst", "number") - return True if attrib in num_set else False diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/interfaces/interfaces.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/interfaces/interfaces.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/interfaces/interfaces.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/interfaces/interfaces.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,134 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The vyos interfaces fact class -It is in this file the configuration is collected from the device -for a given resource, parsed, and the facts tree is populated -based on the configuration. -""" - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - - -from re import findall, M -from copy import deepcopy -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.interfaces.interfaces import ( - InterfacesArgs, -) - - -class InterfacesFacts(object): - """ The vyos interfaces fact class - """ - - def __init__(self, module, subspec="config", options="options"): - self._module = module - self.argument_spec = InterfacesArgs.argument_spec - spec = deepcopy(self.argument_spec) - if subspec: - if options: - facts_argument_spec = spec[subspec][options] - else: - facts_argument_spec = spec[subspec] - else: - facts_argument_spec = spec - - self.generated_spec = utils.generate_dict(facts_argument_spec) - - def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for interfaces - :param connection: the device connection - :param ansible_facts: Facts dictionary - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - if not data: - data = connection.get_config(flags=["| grep interfaces"]) - - objs = [] - interface_names = findall( - r"^set interfaces (?:ethernet|bonding|vti|loopback|vxlan) (?:\'*)(\S+)(?:\'*)", - data, - M, - ) - if interface_names: - for interface in set(interface_names): - intf_regex = r" %s .+$" % interface.strip("'") - cfg = findall(intf_regex, data, M) - obj = self.render_config(cfg) - obj["name"] = interface.strip("'") - if obj: - objs.append(obj) - facts = {} - if objs: - facts["interfaces"] = [] - params = utils.validate_config( - self.argument_spec, {"config": objs} - ) - for cfg in params["config"]: - facts["interfaces"].append(utils.remove_empties(cfg)) - - ansible_facts["ansible_network_resources"].update(facts) - return ansible_facts - - def render_config(self, conf): - """ - Render config as dictionary structure and delete keys - from spec for null values - - :param spec: The facts tree, generated from the argspec - :param conf: The configuration - :rtype: dictionary - :returns: The generated config - """ - vif_conf = "\n".join(filter(lambda x: ("vif" in x), conf)) - eth_conf = "\n".join(filter(lambda x: ("vif" not in x), conf)) - config = self.parse_attribs( - ["description", "speed", "mtu", "duplex"], eth_conf - ) - config["vifs"] = self.parse_vifs(vif_conf) - - return utils.remove_empties(config) - - def parse_vifs(self, conf): - vif_names = findall(r"vif (?:\'*)(\d+)(?:\'*)", conf, M) - vifs_list = None - - if vif_names: - vifs_list = [] - for vif in set(vif_names): - vif_regex = r" %s .+$" % vif - cfg = "\n".join(findall(vif_regex, conf, M)) - obj = self.parse_attribs(["description", "mtu"], cfg) - obj["vlan_id"] = int(vif) - if obj: - vifs_list.append(obj) - vifs_list = sorted(vifs_list, key=lambda i: i["vlan_id"]) - - return vifs_list - - def parse_attribs(self, attribs, conf): - config = {} - for item in attribs: - value = utils.parse_conf_arg(conf, item) - if value and item == "mtu": - config[item] = int(value.strip("'")) - elif value: - config[item] = value.strip("'") - else: - config[item] = None - if "disable" in conf: - config["enabled"] = False - else: - config["enabled"] = True - - return utils.remove_empties(config) diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,143 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The vyos l3_interfaces fact class -It is in this file the configuration is collected from the device -for a given resource, parsed, and the facts tree is populated -based on the configuration. -""" - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - - -import re -from copy import deepcopy -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, -) -from ansible.module_utils.six import iteritems -from ansible_collections.ansible.netcommon.plugins.module_utils.compat import ( - ipaddress, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.l3_interfaces.l3_interfaces import ( - L3_interfacesArgs, -) - - -class L3_interfacesFacts(object): - """ The vyos l3_interfaces fact class - """ - - def __init__(self, module, subspec="config", options="options"): - self._module = module - self.argument_spec = L3_interfacesArgs.argument_spec - spec = deepcopy(self.argument_spec) - if subspec: - if options: - facts_argument_spec = spec[subspec][options] - else: - facts_argument_spec = spec[subspec] - else: - facts_argument_spec = spec - - self.generated_spec = utils.generate_dict(facts_argument_spec) - - def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for l3_interfaces - :param connection: the device connection - :param ansible_facts: Facts dictionary - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - if not data: - data = connection.get_config() - - # operate on a collection of resource x - objs = [] - interface_names = re.findall( - r"set interfaces (?:ethernet|bonding|vti|vxlan) (?:\'*)(\S+)(?:\'*)", - data, - re.M, - ) - if interface_names: - for interface in set(interface_names): - intf_regex = r" %s .+$" % interface - cfg = re.findall(intf_regex, data, re.M) - obj = self.render_config(cfg) - obj["name"] = interface.strip("'") - if obj: - objs.append(obj) - - ansible_facts["ansible_network_resources"].pop("l3_interfaces", None) - facts = {} - if objs: - facts["l3_interfaces"] = [] - params = utils.validate_config( - self.argument_spec, {"config": objs} - ) - for cfg in params["config"]: - facts["l3_interfaces"].append(utils.remove_empties(cfg)) - - ansible_facts["ansible_network_resources"].update(facts) - return ansible_facts - - def render_config(self, conf): - """ - Render config as dictionary structure and delete keys from spec for null values - :param spec: The facts tree, generated from the argspec - :param conf: The configuration - :rtype: dictionary - :returns: The generated config - """ - vif_conf = "\n".join(filter(lambda x: ("vif" in x), conf)) - eth_conf = "\n".join(filter(lambda x: ("vif" not in x), conf)) - config = self.parse_attribs(eth_conf) - config["vifs"] = self.parse_vifs(vif_conf) - - return utils.remove_empties(config) - - def parse_vifs(self, conf): - vif_names = re.findall(r"vif (\d+)", conf, re.M) - vifs_list = None - if vif_names: - vifs_list = [] - for vif in set(vif_names): - vif_regex = r" %s .+$" % vif - cfg = "\n".join(re.findall(vif_regex, conf, re.M)) - obj = self.parse_attribs(cfg) - obj["vlan_id"] = vif - if obj: - vifs_list.append(obj) - - return vifs_list - - def parse_attribs(self, conf): - config = {} - ipaddrs = re.findall(r"address (\S+)", conf, re.M) - config["ipv4"] = [] - config["ipv6"] = [] - - for item in ipaddrs: - item = item.strip("'") - if item == "dhcp": - config["ipv4"].append({"address": item}) - elif item == "dhcpv6": - config["ipv6"].append({"address": item}) - else: - ip_version = ipaddress.ip_address(item.split("/")[0]).version - if ip_version == 4: - config["ipv4"].append({"address": item}) - else: - config["ipv6"].append({"address": item}) - - for key, value in iteritems(config): - if value == []: - config[key] = None - - return utils.remove_empties(config) diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,152 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The vyos lag_interfaces fact class -It is in this file the configuration is collected from the device -for a given resource, parsed, and the facts tree is populated -based on the configuration. -""" -from __future__ import absolute_import, division, print_function - -__metaclass__ = type -from re import findall, search, M -from copy import deepcopy - -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.lag_interfaces.lag_interfaces import ( - Lag_interfacesArgs, -) - - -class Lag_interfacesFacts(object): - """ The vyos lag_interfaces fact class - """ - - def __init__(self, module, subspec="config", options="options"): - self._module = module - self.argument_spec = Lag_interfacesArgs.argument_spec - spec = deepcopy(self.argument_spec) - if subspec: - if options: - facts_argument_spec = spec[subspec][options] - else: - facts_argument_spec = spec[subspec] - else: - facts_argument_spec = spec - - self.generated_spec = utils.generate_dict(facts_argument_spec) - - def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for lag_interfaces - :param module: the module instance - :param connection: the device connection - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - if not data: - data = connection.get_config() - - objs = [] - lag_names = findall(r"^set interfaces bonding (\S+)", data, M) - if lag_names: - for lag in set(lag_names): - lag_regex = r" %s .+$" % lag - cfg = findall(lag_regex, data, M) - obj = self.render_config(cfg) - - output = connection.run_commands( - ["show interfaces bonding " + lag + " slaves"] - ) - lines = output[0].splitlines() - members = [] - member = {} - if len(lines) > 1: - for line in lines[2:]: - splitted_line = line.split() - - if len(splitted_line) > 1: - member["member"] = splitted_line[0] - members.append(member) - else: - members = [] - member = {} - obj["name"] = lag.strip("'") - if members: - obj["members"] = members - - if obj: - objs.append(obj) - - facts = {} - if objs: - facts["lag_interfaces"] = [] - params = utils.validate_config( - self.argument_spec, {"config": objs} - ) - for cfg in params["config"]: - facts["lag_interfaces"].append(utils.remove_empties(cfg)) - - ansible_facts["ansible_network_resources"].update(facts) - return ansible_facts - - def render_config(self, conf): - """ - Render config as dictionary structure and delete keys - from spec for null values - - :param spec: The facts tree, generated from the argspec - :param conf: The configuration - :rtype: dictionary - :returns: The generated config - """ - arp_monitor_conf = "\n".join( - filter(lambda x: ("arp-monitor" in x), conf) - ) - hash_policy_conf = "\n".join( - filter(lambda x: ("hash-policy" in x), conf) - ) - lag_conf = "\n".join(filter(lambda x: ("bond" in x), conf)) - config = self.parse_attribs(["mode", "primary"], lag_conf) - config["arp_monitor"] = self.parse_arp_monitor(arp_monitor_conf) - config["hash_policy"] = self.parse_hash_policy(hash_policy_conf) - - return utils.remove_empties(config) - - def parse_attribs(self, attribs, conf): - config = {} - for item in attribs: - value = utils.parse_conf_arg(conf, item) - if value: - config[item] = value.strip("'") - else: - config[item] = None - return utils.remove_empties(config) - - def parse_arp_monitor(self, conf): - arp_monitor = None - if conf: - arp_monitor = {} - target_list = [] - interval = search(r"^.*arp-monitor interval (.+)", conf, M) - targets = findall(r"^.*arp-monitor target '(.+)'", conf, M) - if targets: - for target in targets: - target_list.append(target) - arp_monitor["target"] = target_list - if interval: - value = interval.group(1).strip("'") - arp_monitor["interval"] = int(value) - return arp_monitor - - def parse_hash_policy(self, conf): - hash_policy = None - if conf: - hash_policy = search(r"^.*hash-policy (.+)", conf, M) - hash_policy = hash_policy.group(1).strip("'") - return hash_policy diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/legacy/base.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/legacy/base.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/legacy/base.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/legacy/base.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,162 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The VyOS interfaces fact class -It is in this file the configuration is collected from the device -for a given resource, parsed, and the facts tree is populated -based on the configuration. -""" - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type -import platform -import re -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( - run_commands, - get_capabilities, -) - - -class LegacyFactsBase(object): - - COMMANDS = frozenset() - - def __init__(self, module): - self.module = module - self.facts = dict() - self.warnings = list() - self.responses = None - - def populate(self): - self.responses = run_commands(self.module, list(self.COMMANDS)) - - -class Default(LegacyFactsBase): - - COMMANDS = [ - "show version", - ] - - def populate(self): - super(Default, self).populate() - data = self.responses[0] - self.facts["serialnum"] = self.parse_serialnum(data) - self.facts.update(self.platform_facts()) - - def parse_serialnum(self, data): - match = re.search(r"HW S/N:\s+(\S+)", data) - if match: - return match.group(1) - - def platform_facts(self): - platform_facts = {} - - resp = get_capabilities(self.module) - device_info = resp["device_info"] - - platform_facts["system"] = device_info["network_os"] - - for item in ("model", "image", "version", "platform", "hostname"): - val = device_info.get("network_os_%s" % item) - if val: - platform_facts[item] = val - - platform_facts["api"] = resp["network_api"] - platform_facts["python_version"] = platform.python_version() - - return platform_facts - - -class Config(LegacyFactsBase): - - COMMANDS = [ - "show configuration commands", - "show system commit", - ] - - def populate(self): - super(Config, self).populate() - - self.facts["config"] = self.responses - - commits = self.responses[1] - entries = list() - entry = None - - for line in commits.split("\n"): - match = re.match(r"(\d+)\s+(.+)by(.+)via(.+)", line) - if match: - if entry: - entries.append(entry) - - entry = dict( - revision=match.group(1), - datetime=match.group(2), - by=str(match.group(3)).strip(), - via=str(match.group(4)).strip(), - comment=None, - ) - else: - entry["comment"] = line.strip() - - self.facts["commits"] = entries - - -class Neighbors(LegacyFactsBase): - - COMMANDS = [ - "show lldp neighbors", - "show lldp neighbors detail", - ] - - def populate(self): - super(Neighbors, self).populate() - - all_neighbors = self.responses[0] - if "LLDP not configured" not in all_neighbors: - neighbors = self.parse(self.responses[1]) - self.facts["neighbors"] = self.parse_neighbors(neighbors) - - def parse(self, data): - parsed = list() - values = None - for line in data.split("\n"): - if not line: - continue - elif line[0] == " ": - values += "\n%s" % line - elif line.startswith("Interface"): - if values: - parsed.append(values) - values = line - if values: - parsed.append(values) - return parsed - - def parse_neighbors(self, data): - facts = dict() - for item in data: - interface = self.parse_interface(item) - host = self.parse_host(item) - port = self.parse_port(item) - if interface not in facts: - facts[interface] = list() - facts[interface].append(dict(host=host, port=port)) - return facts - - def parse_interface(self, data): - match = re.search(r"^Interface:\s+(\S+),", data) - return match.group(1) - - def parse_host(self, data): - match = re.search(r"SysName:\s+(.+)$", data, re.M) - if match: - return match.group(1) - - def parse_port(self, data): - match = re.search(r"PortDescr:\s+(.+)$", data, re.M) - if match: - return match.group(1) diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/lldp_global/lldp_global.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/lldp_global/lldp_global.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/lldp_global/lldp_global.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/lldp_global/lldp_global.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,116 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The vyos lldp_global fact class -It is in this file the configuration is collected from the device -for a given resource, parsed, and the facts tree is populated -based on the configuration. -""" -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -from re import findall, M -from copy import deepcopy - -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.lldp_global.lldp_global import ( - Lldp_globalArgs, -) - - -class Lldp_globalFacts(object): - """ The vyos lldp_global fact class - """ - - def __init__(self, module, subspec="config", options="options"): - self._module = module - self.argument_spec = Lldp_globalArgs.argument_spec - spec = deepcopy(self.argument_spec) - if subspec: - if options: - facts_argument_spec = spec[subspec][options] - else: - facts_argument_spec = spec[subspec] - else: - facts_argument_spec = spec - - self.generated_spec = utils.generate_dict(facts_argument_spec) - - def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for lldp_global - :param connection: the device connection - :param ansible_facts: Facts dictionary - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - if not data: - data = connection.get_config() - - objs = {} - lldp_output = findall(r"^set service lldp (\S+)", data, M) - if lldp_output: - for item in set(lldp_output): - lldp_regex = r" %s .+$" % item - cfg = findall(lldp_regex, data, M) - obj = self.render_config(cfg) - if obj: - objs.update(obj) - lldp_service = findall(r"^set service (lldp)?('lldp')", data, M) - if lldp_service or lldp_output: - lldp_obj = {} - lldp_obj["enable"] = True - objs.update(lldp_obj) - - facts = {} - params = utils.validate_config(self.argument_spec, {"config": objs}) - facts["lldp_global"] = utils.remove_empties(params["config"]) - - ansible_facts["ansible_network_resources"].update(facts) - - return ansible_facts - - def render_config(self, conf): - """ - Render config as dictionary structure and delete keys - from spec for null values - :param spec: The facts tree, generated from the argspec - :param conf: The configuration - :rtype: dictionary - :returns: The generated config - """ - protocol_conf = "\n".join( - filter(lambda x: ("legacy-protocols" in x), conf) - ) - att_conf = "\n".join( - filter(lambda x: ("legacy-protocols" not in x), conf) - ) - config = self.parse_attribs(["snmp", "address"], att_conf) - config["legacy_protocols"] = self.parse_protocols(protocol_conf) - return utils.remove_empties(config) - - def parse_protocols(self, conf): - protocol_support = None - if conf: - protocols = findall(r"^.*legacy-protocols (.+)", conf, M) - if protocols: - protocol_support = [] - for protocol in protocols: - protocol_support.append(protocol.strip("'")) - return protocol_support - - def parse_attribs(self, attribs, conf): - config = {} - for item in attribs: - value = utils.parse_conf_arg(conf, item) - if value: - config[item] = value.strip("'") - else: - config[item] = None - return utils.remove_empties(config) diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/lldp_interfaces/lldp_interfaces.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/lldp_interfaces/lldp_interfaces.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/lldp_interfaces/lldp_interfaces.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/lldp_interfaces/lldp_interfaces.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,155 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The vyos lldp_interfaces fact class -It is in this file the configuration is collected from the device -for a given resource, parsed, and the facts tree is populated -based on the configuration. -""" - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - - -from re import findall, search, M -from copy import deepcopy - -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.lldp_interfaces.lldp_interfaces import ( - Lldp_interfacesArgs, -) - - -class Lldp_interfacesFacts(object): - """ The vyos lldp_interfaces fact class - """ - - def __init__(self, module, subspec="config", options="options"): - self._module = module - self.argument_spec = Lldp_interfacesArgs.argument_spec - spec = deepcopy(self.argument_spec) - if subspec: - if options: - facts_argument_spec = spec[subspec][options] - else: - facts_argument_spec = spec[subspec] - else: - facts_argument_spec = spec - - self.generated_spec = utils.generate_dict(facts_argument_spec) - - def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for lldp_interfaces - :param connection: the device connection - :param ansible_facts: Facts dictionary - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - if not data: - data = connection.get_config() - - objs = [] - lldp_names = findall(r"^set service lldp interface (\S+)", data, M) - if lldp_names: - for lldp in set(lldp_names): - lldp_regex = r" %s .+$" % lldp - cfg = findall(lldp_regex, data, M) - obj = self.render_config(cfg) - obj["name"] = lldp.strip("'") - if obj: - objs.append(obj) - facts = {} - if objs: - facts["lldp_interfaces"] = objs - ansible_facts["ansible_network_resources"].update(facts) - - ansible_facts["ansible_network_resources"].update(facts) - return ansible_facts - - def render_config(self, conf): - """ - Render config as dictionary structure and delete keys - from spec for null values - - :param spec: The facts tree, generated from the argspec - :param conf: The configuration - :rtype: dictionary - :returns: The generated config - """ - config = {} - location = {} - - civic_conf = "\n".join(filter(lambda x: ("civic-based" in x), conf)) - elin_conf = "\n".join(filter(lambda x: ("elin" in x), conf)) - coordinate_conf = "\n".join( - filter(lambda x: ("coordinate-based" in x), conf) - ) - disable = "\n".join(filter(lambda x: ("disable" in x), conf)) - - coordinate_based_conf = self.parse_attribs( - ["altitude", "datum", "longitude", "latitude"], coordinate_conf - ) - elin_based_conf = self.parse_lldp_elin_based(elin_conf) - civic_based_conf = self.parse_lldp_civic_based(civic_conf) - if disable: - config["enable"] = False - if coordinate_conf: - location["coordinate_based"] = coordinate_based_conf - config["location"] = location - elif civic_based_conf: - location["civic_based"] = civic_based_conf - config["location"] = location - elif elin_conf: - location["elin"] = elin_based_conf - config["location"] = location - - return utils.remove_empties(config) - - def parse_attribs(self, attribs, conf): - config = {} - for item in attribs: - value = utils.parse_conf_arg(conf, item) - if value: - value = value.strip("'") - if item == "altitude": - value = int(value) - config[item] = value - else: - config[item] = None - return utils.remove_empties(config) - - def parse_lldp_civic_based(self, conf): - civic_based = None - if conf: - civic_info_list = [] - civic_add_list = findall(r"^.*civic-based ca-type (.+)", conf, M) - if civic_add_list: - for civic_add in civic_add_list: - ca = civic_add.split(" ") - c_add = {} - c_add["ca_type"] = int(ca[0].strip("'")) - c_add["ca_value"] = ca[2].strip("'") - civic_info_list.append(c_add) - - country_code = search( - r"^.*civic-based country-code (.+)", conf, M - ) - civic_based = {} - civic_based["ca_info"] = civic_info_list - civic_based["country_code"] = country_code.group(1).strip("'") - return civic_based - - def parse_lldp_elin_based(self, conf): - elin_based = None - if conf: - e_num = search(r"^.* elin (.+)", conf, M) - elin_based = e_num.group(1).strip("'") - - return elin_based diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/facts/static_routes/static_routes.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,181 +0,0 @@ -# -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The vyos static_routes fact class -It is in this file the configuration is collected from the device -for a given resource, parsed, and the facts tree is populated -based on the configuration. -""" - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type -from re import findall, search, M -from copy import deepcopy -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.static_routes.static_routes import ( - Static_routesArgs, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( - get_route_type, -) - - -class Static_routesFacts(object): - """ The vyos static_routes fact class - """ - - def __init__(self, module, subspec="config", options="options"): - self._module = module - self.argument_spec = Static_routesArgs.argument_spec - spec = deepcopy(self.argument_spec) - if subspec: - if options: - facts_argument_spec = spec[subspec][options] - else: - facts_argument_spec = spec[subspec] - else: - facts_argument_spec = spec - - self.generated_spec = utils.generate_dict(facts_argument_spec) - - def get_device_data(self, connection): - return connection.get_config() - - def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for static_routes - :param connection: the device connection - :param ansible_facts: Facts dictionary - :param data: previously collected conf - :rtype: dictionary - :returns: facts - """ - if not data: - data = self.get_device_data(connection) - # typically data is populated from the current device configuration - # data = connection.get('show running-config | section ^interface') - # using mock data instead - objs = [] - r_v4 = [] - r_v6 = [] - af = [] - static_routes = findall( - r"set protocols static route(6)? (\S+)", data, M - ) - if static_routes: - for route in set(static_routes): - route_regex = r" %s .+$" % route[1] - cfg = findall(route_regex, data, M) - sr = self.render_config(cfg) - sr["dest"] = route[1].strip("'") - afi = self.get_afi(sr["dest"]) - if afi == "ipv4": - r_v4.append(sr) - else: - r_v6.append(sr) - if r_v4: - afi_v4 = {"afi": "ipv4", "routes": r_v4} - af.append(afi_v4) - if r_v6: - afi_v6 = {"afi": "ipv6", "routes": r_v6} - af.append(afi_v6) - config = {"address_families": af} - if config: - objs.append(config) - - ansible_facts["ansible_network_resources"].pop("static_routes", None) - facts = {} - if objs: - facts["static_routes"] = [] - params = utils.validate_config( - self.argument_spec, {"config": objs} - ) - for cfg in params["config"]: - facts["static_routes"].append(utils.remove_empties(cfg)) - - ansible_facts["ansible_network_resources"].update(facts) - return ansible_facts - - def render_config(self, conf): - """ - Render config as dictionary structure and delete keys - from spec for null values - - :param spec: The facts tree, generated from the argspec - :param conf: The configuration - :rtype: dictionary - :returns: The generated config - """ - next_hops_conf = "\n".join(filter(lambda x: ("next-hop" in x), conf)) - blackhole_conf = "\n".join(filter(lambda x: ("blackhole" in x), conf)) - routes_dict = { - "blackhole_config": self.parse_blackhole(blackhole_conf), - "next_hops": self.parse_next_hop(next_hops_conf), - } - return routes_dict - - def parse_blackhole(self, conf): - blackhole = None - if conf: - distance = search(r"^.*blackhole distance (.\S+)", conf, M) - bh = conf.find("blackhole") - if distance is not None: - blackhole = {} - value = distance.group(1).strip("'") - blackhole["distance"] = int(value) - elif bh: - blackhole = {} - blackhole["type"] = "blackhole" - return blackhole - - def get_afi(self, address): - route_type = get_route_type(address) - if route_type == "route": - return "ipv4" - elif route_type == "route6": - return "ipv6" - - def parse_next_hop(self, conf): - nh_list = None - if conf: - nh_list = [] - hop_list = findall(r"^.*next-hop (.+)", conf, M) - if hop_list: - for hop in hop_list: - distance = search(r"^.*distance (.\S+)", hop, M) - interface = search(r"^.*interface (.\S+)", hop, M) - - dis = hop.find("disable") - hop_info = hop.split(" ") - nh_info = { - "forward_router_address": hop_info[0].strip("'") - } - if interface: - nh_info["interface"] = interface.group(1).strip("'") - if distance: - value = distance.group(1).strip("'") - nh_info["admin_distance"] = int(value) - elif dis >= 1: - nh_info["enabled"] = False - for element in nh_list: - if ( - element["forward_router_address"] - == nh_info["forward_router_address"] - ): - if "interface" in nh_info.keys(): - element["interface"] = nh_info["interface"] - if "admin_distance" in nh_info.keys(): - element["admin_distance"] = nh_info[ - "admin_distance" - ] - if "enabled" in nh_info.keys(): - element["enabled"] = nh_info["enabled"] - nh_info = None - if nh_info is not None: - nh_list.append(nh_info) - return nh_list diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/utils/utils.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/utils/utils.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/utils/utils.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/utils/utils.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,231 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# utils -from __future__ import absolute_import, division, print_function - -__metaclass__ = type -from ansible.module_utils.six import iteritems -from ansible_collections.ansible.netcommon.plugins.module_utils.compat import ( - ipaddress, -) - - -def search_obj_in_list(name, lst, key="name"): - for item in lst: - if item[key] == name: - return item - return None - - -def get_interface_type(interface): - """Gets the type of interface - """ - if interface.startswith("eth"): - return "ethernet" - elif interface.startswith("bond"): - return "bonding" - elif interface.startswith("vti"): - return "vti" - elif interface.startswith("lo"): - return "loopback" - - -def dict_delete(base, comparable): - """ - This function generates a dict containing key, value pairs for keys - that are present in the `base` dict but not present in the `comparable` - dict. - - :param base: dict object to base the diff on - :param comparable: dict object to compare against base - :returns: new dict object with key, value pairs that needs to be deleted. - - """ - to_delete = dict() - - for key in base: - if isinstance(base[key], dict): - sub_diff = dict_delete(base[key], comparable.get(key, {})) - if sub_diff: - to_delete[key] = sub_diff - else: - if key not in comparable: - to_delete[key] = base[key] - - return to_delete - - -def diff_list_of_dicts(want, have): - diff = [] - - set_w = set(tuple(d.items()) for d in want) - set_h = set(tuple(d.items()) for d in have) - difference = set_w.difference(set_h) - - for element in difference: - diff.append(dict((x, y) for x, y in element)) - - return diff - - -def get_lst_diff_for_dicts(want, have, lst): - """ - This function generates a list containing values - that are only in want and not in list in have dict - :param want: dict object to want - :param have: dict object to have - :param lst: list the diff on - :return: new list object with values which are only in want. - """ - if not have: - diff = want.get(lst) or [] - - else: - want_elements = want.get(lst) or {} - have_elements = have.get(lst) or {} - diff = list_diff_want_only(want_elements, have_elements) - return diff - - -def get_lst_same_for_dicts(want, have, lst): - """ - This function generates a list containing values - that are common for list in want and list in have dict - :param want: dict object to want - :param have: dict object to have - :param lst: list the comparison on - :return: new list object with values which are common in want and have. - """ - diff = None - if want and have: - want_list = want.get(lst) or {} - have_list = have.get(lst) or {} - diff = [ - i - for i in want_list and have_list - if i in have_list and i in want_list - ] - return diff - - -def list_diff_have_only(want_list, have_list): - """ - This function generated the list containing values - that are only in have list. - :param want_list: - :param have_list: - :return: new list with values which are only in have list - """ - if have_list and not want_list: - diff = have_list - elif not have_list: - diff = None - else: - diff = [ - i - for i in have_list + want_list - if i in have_list and i not in want_list - ] - return diff - - -def list_diff_want_only(want_list, have_list): - """ - This function generated the list containing values - that are only in want list. - :param want_list: - :param have_list: - :return: new list with values which are only in want list - """ - if have_list and not want_list: - diff = None - elif not have_list: - diff = want_list - else: - diff = [ - i - for i in have_list + want_list - if i in want_list and i not in have_list - ] - return diff - - -def search_dict_tv_in_list(d_val1, d_val2, lst, key1, key2): - """ - This function return the dict object if it exist in list. - :param d_val1: - :param d_val2: - :param lst: - :param key1: - :param key2: - :return: - """ - obj = next( - ( - item - for item in lst - if item[key1] == d_val1 and item[key2] == d_val2 - ), - None, - ) - if obj: - return obj - else: - return None - - -def key_value_in_dict(have_key, have_value, want_dict): - """ - This function checks whether the key and values exist in dict - :param have_key: - :param have_value: - :param want_dict: - :return: - """ - for key, value in iteritems(want_dict): - if key == have_key and value == have_value: - return True - return False - - -def is_dict_element_present(dict, key): - """ - This function checks whether the key is present in dict. - :param dict: - :param key: - :return: - """ - for item in dict: - if item == key: - return True - return False - - -def get_ip_address_version(address): - """ - This function returns the version of IP address - :param address: IP address - :return: - """ - try: - address = unicode(address) - except NameError: - address = str(address) - version = ipaddress.ip_address(address.split("/")[0]).version - return version - - -def get_route_type(address): - """ - This function returns the route type based on IP address - :param address: - :return: - """ - version = get_ip_address_version(address) - if version == 6: - return "route6" - elif version == 4: - return "route" diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/vyos.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/vyos.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/vyos.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/module_utils/network/vyos/vyos.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,124 +0,0 @@ -# This code is part of Ansible, but is an independent component. -# This particular file snippet, and this file snippet only, is BSD licensed. -# Modules you write using this snippet, which is embedded dynamically by Ansible -# still belong to the author of the module, and may assign their own license -# to the complete work. -# -# (c) 2016 Red Hat Inc. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -import json - -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import env_fallback -from ansible.module_utils.connection import Connection, ConnectionError - -_DEVICE_CONFIGS = {} - -vyos_provider_spec = { - "host": dict(), - "port": dict(type="int"), - "username": dict(fallback=(env_fallback, ["ANSIBLE_NET_USERNAME"])), - "password": dict( - fallback=(env_fallback, ["ANSIBLE_NET_PASSWORD"]), no_log=True - ), - "ssh_keyfile": dict( - fallback=(env_fallback, ["ANSIBLE_NET_SSH_KEYFILE"]), type="path" - ), - "timeout": dict(type="int"), -} -vyos_argument_spec = { - "provider": dict( - type="dict", options=vyos_provider_spec, removed_in_version=2.14 - ), -} - - -def get_provider_argspec(): - return vyos_provider_spec - - -def get_connection(module): - if hasattr(module, "_vyos_connection"): - return module._vyos_connection - - capabilities = get_capabilities(module) - network_api = capabilities.get("network_api") - if network_api == "cliconf": - module._vyos_connection = Connection(module._socket_path) - else: - module.fail_json(msg="Invalid connection type %s" % network_api) - - return module._vyos_connection - - -def get_capabilities(module): - if hasattr(module, "_vyos_capabilities"): - return module._vyos_capabilities - - try: - capabilities = Connection(module._socket_path).get_capabilities() - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) - - module._vyos_capabilities = json.loads(capabilities) - return module._vyos_capabilities - - -def get_config(module, flags=None, format=None): - flags = [] if flags is None else flags - global _DEVICE_CONFIGS - - if _DEVICE_CONFIGS != {}: - return _DEVICE_CONFIGS - else: - connection = get_connection(module) - try: - out = connection.get_config(flags=flags, format=format) - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) - cfg = to_text(out, errors="surrogate_then_replace").strip() - _DEVICE_CONFIGS = cfg - return cfg - - -def run_commands(module, commands, check_rc=True): - connection = get_connection(module) - try: - response = connection.run_commands( - commands=commands, check_rc=check_rc - ) - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) - return response - - -def load_config(module, commands, commit=False, comment=None): - connection = get_connection(module) - - try: - response = connection.edit_config( - candidate=commands, commit=commit, comment=comment - ) - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) - - return response.get("diff") diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_command.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_command.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_command.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_command.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,223 +0,0 @@ -#!/usr/bin/python -# -# This file is part of Ansible -# -# Ansible is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . -# - -ANSIBLE_METADATA = { - "metadata_version": "1.1", - "status": ["preview"], - "supported_by": "network", -} - - -DOCUMENTATION = """module: vyos_command -author: Nathaniel Case (@Qalthos) -short_description: Run one or more commands on VyOS devices -description: -- The command module allows running one or more commands on remote devices running - VyOS. This module can also be introspected to validate key parameters before returning - successfully. If the conditional statements are not met in the wait period, the - task fails. -- Certain C(show) commands in VyOS produce many lines of output and use a custom pager - that can cause this module to hang. If the value of the environment variable C(ANSIBLE_VYOS_TERMINAL_LENGTH) - is not set, the default number of 10000 is used. -extends_documentation_fragment: -- vyos.vyos.vyos -options: - commands: - description: - - The ordered set of commands to execute on the remote device running VyOS. The - output from the command execution is returned to the playbook. If the I(wait_for) - argument is provided, the module is not returned until the condition is satisfied - or the number of retries has been exceeded. - required: true - wait_for: - description: - - Specifies what to evaluate from the output of the command and what conditionals - to apply. This argument will cause the task to wait for a particular conditional - to be true before moving forward. If the conditional is not true by the configured - I(retries), the task fails. See examples. - aliases: - - waitfor - match: - description: - - The I(match) argument is used in conjunction with the I(wait_for) argument to - specify the match policy. Valid values are C(all) or C(any). If the value is - set to C(all) then all conditionals in the wait_for must be satisfied. If the - value is set to C(any) then only one of the values must be satisfied. - default: all - choices: - - any - - all - retries: - description: - - Specifies the number of retries a command should be tried before it is considered - failed. The command is run on the target device every retry and evaluated against - the I(wait_for) conditionals. - default: 10 - interval: - description: - - Configures the interval in seconds to wait between I(retries) of the command. - If the command does not pass the specified conditions, the interval indicates - how long to wait before trying the command again. - default: 1 -notes: -- Tested against VyOS 1.1.8 (helium). -- Running C(show system boot-messages all) will cause the module to hang since VyOS - is using a custom pager setting to display the output of that command. -- If a command sent to the device requires answering a prompt, it is possible to pass - a dict containing I(command), I(answer) and I(prompt). See examples. -- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). -""" - -EXAMPLES = """ -tasks: - - name: show configuration on ethernet devices eth0 and eth1 - vyos_command: - commands: - - show interfaces ethernet {{ item }} - with_items: - - eth0 - - eth1 - - - name: run multiple commands and check if version output contains specific version string - vyos_command: - commands: - - show version - - show hardware cpu - wait_for: - - "result[0] contains 'VyOS 1.1.7'" - - - name: run command that requires answering a prompt - vyos_command: - commands: - - command: 'rollback 1' - prompt: 'Proceed with reboot? [confirm][y]' - answer: y -""" - -RETURN = """ -stdout: - description: The set of responses from the commands - returned: always apart from low level errors (such as action plugin) - type: list - sample: ['...', '...'] -stdout_lines: - description: The value of stdout split into a list - returned: always - type: list - sample: [['...', '...'], ['...'], ['...']] -failed_conditions: - description: The list of conditionals that have failed - returned: failed - type: list - sample: ['...', '...'] -warnings: - description: The list of warnings (if any) generated by module based on arguments - returned: always - type: list - sample: ['...', '...'] -""" -import time - -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.parsing import ( - Conditional, -) -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( - transform_commands, - to_lines, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( - run_commands, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( - vyos_argument_spec, -) - - -def parse_commands(module, warnings): - commands = transform_commands(module) - - if module.check_mode: - for item in list(commands): - if not item["command"].startswith("show"): - warnings.append( - "Only show commands are supported when using check mode, not " - "executing %s" % item["command"] - ) - commands.remove(item) - - return commands - - -def main(): - spec = dict( - commands=dict(type="list", required=True), - wait_for=dict(type="list", aliases=["waitfor"]), - match=dict(default="all", choices=["all", "any"]), - retries=dict(default=10, type="int"), - interval=dict(default=1, type="int"), - ) - - spec.update(vyos_argument_spec) - - module = AnsibleModule(argument_spec=spec, supports_check_mode=True) - - warnings = list() - result = {"changed": False, "warnings": warnings} - commands = parse_commands(module, warnings) - wait_for = module.params["wait_for"] or list() - - try: - conditionals = [Conditional(c) for c in wait_for] - except AttributeError as exc: - module.fail_json(msg=to_text(exc)) - - retries = module.params["retries"] - interval = module.params["interval"] - match = module.params["match"] - - for _ in range(retries): - responses = run_commands(module, commands) - - for item in list(conditionals): - if item(responses): - if match == "any": - conditionals = list() - break - conditionals.remove(item) - - if not conditionals: - break - - time.sleep(interval) - - if conditionals: - failed_conditions = [item.raw for item in conditionals] - msg = "One or more conditional statements have not been satisfied" - module.fail_json(msg=msg, failed_conditions=failed_conditions) - - result.update( - {"stdout": responses, "stdout_lines": list(to_lines(responses)),} - ) - - module.exit_json(**result) - - -if __name__ == "__main__": - main() diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_config.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_config.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_config.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_config.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,354 +0,0 @@ -#!/usr/bin/python -# -# This file is part of Ansible -# -# Ansible is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . -# - -ANSIBLE_METADATA = { - "metadata_version": "1.1", - "status": ["preview"], - "supported_by": "network", -} - - -DOCUMENTATION = """module: vyos_config -author: Nathaniel Case (@Qalthos) -short_description: Manage VyOS configuration on remote device -description: -- This module provides configuration file management of VyOS devices. It provides - arguments for managing both the configuration file and state of the active configuration. - All configuration statements are based on `set` and `delete` commands in the device - configuration. -extends_documentation_fragment: -- vyos.vyos.vyos -notes: -- Tested against VyOS 1.1.8 (helium). -- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). -options: - lines: - description: - - The ordered set of configuration lines to be managed and compared with the existing - configuration on the remote device. - src: - description: - - The C(src) argument specifies the path to the source config file to load. The - source config file can either be in bracket format or set format. The source - file can include Jinja2 template variables. - match: - description: - - The C(match) argument controls the method used to match against the current - active configuration. By default, the desired config is matched against the - active config and the deltas are loaded. If the C(match) argument is set to - C(none) the active configuration is ignored and the configuration is always - loaded. - default: line - choices: - - line - - none - backup: - description: - - The C(backup) argument will backup the current devices active configuration - to the Ansible control host prior to making any changes. If the C(backup_options) - value is not given, the backup file will be located in the backup folder in - the playbook root directory or role root directory, if playbook is part of an - ansible role. If the directory does not exist, it is created. - type: bool - default: 'no' - comment: - description: - - Allows a commit description to be specified to be included when the configuration - is committed. If the configuration is not changed or committed, this argument - is ignored. - default: configured by vyos_config - config: - description: - - The C(config) argument specifies the base configuration to use to compare against - the desired configuration. If this value is not specified, the module will - automatically retrieve the current active configuration from the remote device. - save: - description: - - The C(save) argument controls whether or not changes made to the active configuration - are saved to disk. This is independent of committing the config. When set - to True, the active configuration is saved. - type: bool - default: 'no' - backup_options: - description: - - This is a dict object containing configurable options related to backup file - path. The value of this option is read only when C(backup) is set to I(yes), - if C(backup) is set to I(no) this option will be silently ignored. - suboptions: - filename: - description: - - The filename to be used to store the backup configuration. If the filename - is not given it will be generated based on the hostname, current time and - date in format defined by _config.@ - dir_path: - description: - - This option provides the path ending with directory name in which the backup - configuration file will be stored. If the directory does not exist it will - be first created and the filename is either the value of C(filename) or - default filename as described in C(filename) options description. If the - path value is not given in that case a I(backup) directory will be created - in the current working directory and backup configuration will be copied - in C(filename) within I(backup) directory. - type: path - type: dict -""" - -EXAMPLES = """ -- name: configure the remote device - vyos_config: - lines: - - set system host-name {{ inventory_hostname }} - - set service lldp - - delete service dhcp-server - -- name: backup and load from file - vyos_config: - src: vyos.cfg - backup: yes - -- name: render a Jinja2 template onto the VyOS router - vyos_config: - src: vyos_template.j2 - -- name: for idempotency, use full-form commands - vyos_config: - lines: - # - set int eth eth2 description 'OUTSIDE' - - set interface ethernet eth2 description 'OUTSIDE' - -- name: configurable backup path - vyos_config: - backup: yes - backup_options: - filename: backup.cfg - dir_path: /home/user -""" - -RETURN = """ -commands: - description: The list of configuration commands sent to the device - returned: always - type: list - sample: ['...', '...'] -filtered: - description: The list of configuration commands removed to avoid a load failure - returned: always - type: list - sample: ['...', '...'] -backup_path: - description: The full path to the backup file - returned: when backup is yes - type: str - sample: /playbooks/ansible/backup/vyos_config.2016-07-16@22:28:34 -filename: - description: The name of the backup file - returned: when backup is yes and filename is not specified in backup options - type: str - sample: vyos_config.2016-07-16@22:28:34 -shortname: - description: The full path to the backup file excluding the timestamp - returned: when backup is yes and filename is not specified in backup options - type: str - sample: /playbooks/ansible/backup/vyos_config -date: - description: The date extracted from the backup file name - returned: when backup is yes - type: str - sample: "2016-07-16" -time: - description: The time extracted from the backup file name - returned: when backup is yes - type: str - sample: "22:28:34" -""" -import re - -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.connection import ConnectionError -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( - load_config, - get_config, - run_commands, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( - vyos_argument_spec, - get_connection, -) - - -DEFAULT_COMMENT = "configured by vyos_config" - -CONFIG_FILTERS = [ - re.compile(r"set system login user \S+ authentication encrypted-password") -] - - -def get_candidate(module): - contents = module.params["src"] or module.params["lines"] - - if module.params["src"]: - contents = format_commands(contents.splitlines()) - - contents = "\n".join(contents) - return contents - - -def format_commands(commands): - """ - This function format the input commands and removes the prepend white spaces - for command lines having 'set' or 'delete' and it skips empty lines. - :param commands: - :return: list of commands - """ - return [ - line.strip() if line.split()[0] in ("set", "delete") else line - for line in commands - if len(line.strip()) > 0 - ] - - -def diff_config(commands, config): - config = [str(c).replace("'", "") for c in config.splitlines()] - - updates = list() - visited = set() - - for line in commands: - item = str(line).replace("'", "") - - if not item.startswith("set") and not item.startswith("delete"): - raise ValueError("line must start with either `set` or `delete`") - - elif item.startswith("set") and item not in config: - updates.append(line) - - elif item.startswith("delete"): - if not config: - updates.append(line) - else: - item = re.sub(r"delete", "set", item) - for entry in config: - if entry.startswith(item) and line not in visited: - updates.append(line) - visited.add(line) - - return list(updates) - - -def sanitize_config(config, result): - result["filtered"] = list() - index_to_filter = list() - for regex in CONFIG_FILTERS: - for index, line in enumerate(list(config)): - if regex.search(line): - result["filtered"].append(line) - index_to_filter.append(index) - # Delete all filtered configs - for filter_index in sorted(index_to_filter, reverse=True): - del config[filter_index] - - -def run(module, result): - # get the current active config from the node or passed in via - # the config param - config = module.params["config"] or get_config(module) - - # create the candidate config object from the arguments - candidate = get_candidate(module) - - # create loadable config that includes only the configuration updates - connection = get_connection(module) - try: - response = connection.get_diff( - candidate=candidate, - running=config, - diff_match=module.params["match"], - ) - except ConnectionError as exc: - module.fail_json(msg=to_text(exc, errors="surrogate_then_replace")) - - commands = response.get("config_diff") - sanitize_config(commands, result) - - result["commands"] = commands - - commit = not module.check_mode - comment = module.params["comment"] - - diff = None - if commands: - diff = load_config(module, commands, commit=commit, comment=comment) - - if result.get("filtered"): - result["warnings"].append( - "Some configuration commands were " - "removed, please see the filtered key" - ) - - result["changed"] = True - - if module._diff: - result["diff"] = {"prepared": diff} - - -def main(): - backup_spec = dict(filename=dict(), dir_path=dict(type="path")) - argument_spec = dict( - src=dict(type="path"), - lines=dict(type="list"), - match=dict(default="line", choices=["line", "none"]), - comment=dict(default=DEFAULT_COMMENT), - config=dict(), - backup=dict(type="bool", default=False), - backup_options=dict(type="dict", options=backup_spec), - save=dict(type="bool", default=False), - ) - - argument_spec.update(vyos_argument_spec) - - mutually_exclusive = [("lines", "src")] - - module = AnsibleModule( - argument_spec=argument_spec, - mutually_exclusive=mutually_exclusive, - supports_check_mode=True, - ) - - warnings = list() - - result = dict(changed=False, warnings=warnings) - - if module.params["backup"]: - result["__backup__"] = get_config(module=module) - - if any((module.params["src"], module.params["lines"])): - run(module, result) - - if module.params["save"]: - diff = run_commands(module, commands=["configure", "compare saved"])[1] - if diff != "[edit]": - run_commands(module, commands=["save"]) - result["changed"] = True - run_commands(module, commands=["exit"]) - - module.exit_json(**result) - - -if __name__ == "__main__": - main() diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_facts.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_facts.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_facts.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_facts.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,174 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -""" -The module file for vyos_facts -""" - - -ANSIBLE_METADATA = { - "metadata_version": "1.1", - "status": [u"preview"], - "supported_by": "network", -} - - -DOCUMENTATION = """module: vyos_facts -short_description: Get facts about vyos devices. -description: -- Collects facts from network devices running the vyos operating system. This module - places the facts gathered in the fact tree keyed by the respective resource name. The - facts module will always collect a base set of facts from the device and can enable - or disable collection of additional facts. -author: -- Nathaniel Case (@qalthos) -- Nilashish Chakraborty (@Nilashishc) -- Rohit Thakur (@rohitthakur2590) -extends_documentation_fragment: -- vyos.vyos.vyos -notes: -- Tested against VyOS 1.1.8 (helium). -- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). -options: - gather_subset: - description: - - When supplied, this argument will restrict the facts collected to a given subset. Possible - values for this argument include all, default, config, and neighbors. Can specify - a list of values to include a larger subset. Values can also be used with an - initial C(M(!)) to specify that a specific subset should not be collected. - required: false - default: '!config' - gather_network_resources: - description: - - When supplied, this argument will restrict the facts collected to a given subset. - Possible values for this argument include all and the resources like interfaces. - Can specify a list of values to include a larger subset. Values can also be - used with an initial C(M(!)) to specify that a specific subset should not be - collected. Valid subsets are 'all', 'interfaces', 'l3_interfaces', 'lag_interfaces', - 'lldp_global', 'lldp_interfaces', 'static_routes', 'firewall_rules'. - required: false -""" - -EXAMPLES = """ -# Gather all facts -- vyos_facts: - gather_subset: all - gather_network_resources: all - -# collect only the config and default facts -- vyos_facts: - gather_subset: config - -# collect everything exception the config -- vyos_facts: - gather_subset: "!config" - -# Collect only the interfaces facts -- vyos_facts: - gather_subset: - - '!all' - - '!min' - gather_network_resources: - - interfaces - -# Do not collect interfaces facts -- vyos_facts: - gather_network_resources: - - "!interfaces" - -# Collect interfaces and minimal default facts -- vyos_facts: - gather_subset: min - gather_network_resources: interfaces -""" - -RETURN = """ -ansible_net_config: - description: The running-config from the device - returned: when config is configured - type: str -ansible_net_commits: - description: The set of available configuration revisions - returned: when present - type: list -ansible_net_hostname: - description: The configured system hostname - returned: always - type: str -ansible_net_model: - description: The device model string - returned: always - type: str -ansible_net_serialnum: - description: The serial number of the device - returned: always - type: str -ansible_net_version: - description: The version of the software running - returned: always - type: str -ansible_net_neighbors: - description: The set of LLDP neighbors - returned: when interface is configured - type: list -ansible_net_gather_subset: - description: The list of subsets gathered by the module - returned: always - type: list -ansible_net_api: - description: The name of the transport - returned: always - type: str -ansible_net_python_version: - description: The Python version Ansible controller is using - returned: always - type: str -ansible_net_gather_network_resources: - description: The list of fact resource subsets collected from the device - returned: always - type: list -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.facts.facts import ( - FactsArgs, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( - Facts, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( - vyos_argument_spec, -) - - -def main(): - """ - Main entry point for module execution - - :returns: ansible_facts - """ - argument_spec = FactsArgs.argument_spec - argument_spec.update(vyos_argument_spec) - - module = AnsibleModule( - argument_spec=argument_spec, supports_check_mode=True - ) - - warnings = [] - if module.params["gather_subset"] == "!config": - warnings.append( - "default value for `gather_subset` will be changed to `min` from `!config` v2.11 onwards" - ) - - result = Facts(module).get_facts() - - ansible_facts, additional_warnings = result - warnings.extend(additional_warnings) - - module.exit_json(ansible_facts=ansible_facts, warnings=warnings) - - -if __name__ == "__main__": - main() diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_lldp_interfaces.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_lldp_interfaces.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_lldp_interfaces.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_lldp_interfaces.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,513 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# Copyright 2019 Red Hat -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -############################################# -# WARNING # -############################################# -# -# This file is auto generated by the resource -# module builder playbook. -# -# Do not edit this file manually. -# -# Changes to this file will be over written -# by the resource module builder. -# -# Changes should be made in the model used to -# generate this file or in the resource module -# builder template. -# -############################################# - -""" -The module file for vyos_lldp_interfaces -""" - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -ANSIBLE_METADATA = { - "metadata_version": "1.1", - "status": ["preview"], - "supported_by": "network", -} - -DOCUMENTATION = """module: vyos_lldp_interfaces -short_description: Manages attributes of lldp interfaces on VyOS devices. -description: This module manages attributes of lldp interfaces on VyOS network devices. -notes: -- Tested against VyOS 1.1.8 (helium). -- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). -author: -- Rohit Thakur (@rohitthakur2590) -options: - config: - description: A list of lldp interfaces configurations. - type: list - suboptions: - name: - description: - - Name of the lldp interface. - type: str - required: true - enable: - description: - - to disable lldp on the interface. - type: bool - default: true - location: - description: - - LLDP-MED location data. - type: dict - suboptions: - civic_based: - description: - - Civic-based location data. - type: dict - suboptions: - ca_info: - description: LLDP-MED address info - type: list - suboptions: - ca_type: - description: LLDP-MED Civic Address type. - type: int - required: true - ca_value: - description: LLDP-MED Civic Address value. - type: str - required: true - country_code: - description: Country Code - type: str - required: true - coordinate_based: - description: - - Coordinate-based location. - type: dict - suboptions: - altitude: - description: Altitude in meters. - type: int - datum: - description: Coordinate datum type. - type: str - choices: - - WGS84 - - NAD83 - - MLLW - latitude: - description: Latitude. - type: str - required: true - longitude: - description: Longitude. - type: str - required: true - elin: - description: Emergency Call Service ELIN number (between 10-25 numbers). - type: str - state: - description: - - The state of the configuration after module completion. - type: str - choices: - - merged - - replaced - - overridden - - deleted - default: merged -""" -EXAMPLES = """ -# Using merged -# -# Before state: -# ------------- -# -# vyos@vyos:~$ show configuration commands | grep lldp -# -- name: Merge provided configuration with device configuration - vyos_lldp_interfaces: - config: - - name: 'eth1' - location: - civic_based: - country_code: 'US' - ca_info: - - ca_type: 0 - ca_value: 'ENGLISH' - - - name: 'eth2' - location: - coordinate_based: - altitude: 2200 - datum: 'WGS84' - longitude: '222.267255W' - latitude: '33.524449N' - state: merged -# -# -# ------------------------- -# Module Execution Result -# ------------------------- -# -# before": [] -# -# "commands": [ -# "set service lldp interface eth1 location civic-based country-code 'US'", -# "set service lldp interface eth1 location civic-based ca-type 0 ca-value 'ENGLISH'", -# "set service lldp interface eth1", -# "set service lldp interface eth2 location coordinate-based latitude '33.524449N'", -# "set service lldp interface eth2 location coordinate-based altitude '2200'", -# "set service lldp interface eth2 location coordinate-based datum 'WGS84'", -# "set service lldp interface eth2 location coordinate-based longitude '222.267255W'", -# "set service lldp interface eth2 location coordinate-based latitude '33.524449N'", -# "set service lldp interface eth2 location coordinate-based altitude '2200'", -# "set service lldp interface eth2 location coordinate-based datum 'WGS84'", -# "set service lldp interface eth2 location coordinate-based longitude '222.267255W'", -# "set service lldp interface eth2" -# -# "after": [ -# { -# "location": { -# "coordinate_based": { -# "altitude": 2200, -# "datum": "WGS84", -# "latitude": "33.524449N", -# "longitude": "222.267255W" -# } -# }, -# "name": "eth2" -# }, -# { -# "location": { -# "civic_based": { -# "ca_info": [ -# { -# "ca_type": 0, -# "ca_value": "ENGLISH" -# } -# ], -# "country_code": "US" -# } -# }, -# "name": "eth1" -# } -# ], -# -# After state: -# ------------- -# -# vyos@vyos:~$ show configuration commands | grep lldp -# set service lldp interface eth1 location civic-based ca-type 0 ca-value 'ENGLISH' -# set service lldp interface eth1 location civic-based country-code 'US' -# set service lldp interface eth2 location coordinate-based altitude '2200' -# set service lldp interface eth2 location coordinate-based datum 'WGS84' -# set service lldp interface eth2 location coordinate-based latitude '33.524449N' -# set service lldp interface eth2 location coordinate-based longitude '222.267255W' - - -# Using replaced -# -# Before state: -# ------------- -# -# vyos@vyos:~$ show configuration commands | grep lldp -# set service lldp interface eth1 location civic-based ca-type 0 ca-value 'ENGLISH' -# set service lldp interface eth1 location civic-based country-code 'US' -# set service lldp interface eth2 location coordinate-based altitude '2200' -# set service lldp interface eth2 location coordinate-based datum 'WGS84' -# set service lldp interface eth2 location coordinate-based latitude '33.524449N' -# set service lldp interface eth2 location coordinate-based longitude '222.267255W' -# -- name: Replace device configurations of listed LLDP interfaces with provided configurations - vyos_lldp_interfaces: - config: - - name: 'eth2' - location: - civic_based: - country_code: 'US' - ca_info: - - ca_type: 0 - ca_value: 'ENGLISH' - - - name: 'eth1' - location: - coordinate_based: - altitude: 2200 - datum: 'WGS84' - longitude: '222.267255W' - latitude: '33.524449N' - state: replaced -# -# -# ------------------------- -# Module Execution Result -# ------------------------- -# -# "before": [ -# { -# "location": { -# "coordinate_based": { -# "altitude": 2200, -# "datum": "WGS84", -# "latitude": "33.524449N", -# "longitude": "222.267255W" -# } -# }, -# "name": "eth2" -# }, -# { -# "location": { -# "civic_based": { -# "ca_info": [ -# { -# "ca_type": 0, -# "ca_value": "ENGLISH" -# } -# ], -# "country_code": "US" -# } -# }, -# "name": "eth1" -# } -# ] -# -# "commands": [ -# "delete service lldp interface eth2 location", -# "set service lldp interface eth2 'disable'", -# "set service lldp interface eth2 location civic-based country-code 'US'", -# "set service lldp interface eth2 location civic-based ca-type 0 ca-value 'ENGLISH'", -# "delete service lldp interface eth1 location", -# "set service lldp interface eth1 'disable'", -# "set service lldp interface eth1 location coordinate-based latitude '33.524449N'", -# "set service lldp interface eth1 location coordinate-based altitude '2200'", -# "set service lldp interface eth1 location coordinate-based datum 'WGS84'", -# "set service lldp interface eth1 location coordinate-based longitude '222.267255W'" -# ] -# -# "after": [ -# { -# "location": { -# "civic_based": { -# "ca_info": [ -# { -# "ca_type": 0, -# "ca_value": "ENGLISH" -# } -# ], -# "country_code": "US" -# } -# }, -# "name": "eth2" -# }, -# { -# "location": { -# "coordinate_based": { -# "altitude": 2200, -# "datum": "WGS84", -# "latitude": "33.524449N", -# "longitude": "222.267255W" -# } -# }, -# "name": "eth1" -# } -# ] -# -# After state: -# ------------- -# -# vyos@vyos:~$ show configuration commands | grep lldp -# set service lldp interface eth1 'disable' -# set service lldp interface eth1 location coordinate-based altitude '2200' -# set service lldp interface eth1 location coordinate-based datum 'WGS84' -# set service lldp interface eth1 location coordinate-based latitude '33.524449N' -# set service lldp interface eth1 location coordinate-based longitude '222.267255W' -# set service lldp interface eth2 'disable' -# set service lldp interface eth2 location civic-based ca-type 0 ca-value 'ENGLISH' -# set service lldp interface eth2 location civic-based country-code 'US' - - -# Using overridden -# -# Before state -# -------------- -# -# vyos@vyos:~$ show configuration commands | grep lldp -# set service lldp interface eth1 'disable' -# set service lldp interface eth1 location coordinate-based altitude '2200' -# set service lldp interface eth1 location coordinate-based datum 'WGS84' -# set service lldp interface eth1 location coordinate-based latitude '33.524449N' -# set service lldp interface eth1 location coordinate-based longitude '222.267255W' -# set service lldp interface eth2 'disable' -# set service lldp interface eth2 location civic-based ca-type 0 ca-value 'ENGLISH' -# set service lldp interface eth2 location civic-based country-code 'US' -# -- name: Overrides all device configuration with provided configuration - vyos_lag_interfaces: - config: - - name: 'eth2' - location: - elin: 0000000911 - - state: overridden -# -# -# ------------------------- -# Module Execution Result -# ------------------------- -# -# "before": [ -# { -# "enable": false, -# "location": { -# "civic_based": { -# "ca_info": [ -# { -# "ca_type": 0, -# "ca_value": "ENGLISH" -# } -# ], -# "country_code": "US" -# } -# }, -# "name": "eth2" -# }, -# { -# "enable": false, -# "location": { -# "coordinate_based": { -# "altitude": 2200, -# "datum": "WGS84", -# "latitude": "33.524449N", -# "longitude": "222.267255W" -# } -# }, -# "name": "eth1" -# } -# ] -# -# "commands": [ -# "delete service lldp interface eth2 location", -# "delete service lldp interface eth2 disable", -# "set service lldp interface eth2 location elin 0000000911" -# -# -# "after": [ -# { -# "location": { -# "elin": 0000000911 -# }, -# "name": "eth2" -# } -# ] -# -# -# After state -# ------------ -# -# vyos@vyos# run show configuration commands | grep lldp -# set service lldp interface eth2 location elin '0000000911' - - -# Using deleted -# -# Before state -# ------------- -# -# vyos@vyos# run show configuration commands | grep lldp -# set service lldp interface eth2 location elin '0000000911' -# -- name: Delete lldp interface attributes of given interfaces. - vyos_lag_interfaces: - config: - - name: 'eth2' - state: deleted -# -# -# ------------------------ -# Module Execution Results -# ------------------------ -# - "before": [ - { - "location": { - "elin": 0000000911 - }, - "name": "eth2" - } - ] -# "commands": [ -# "commands": [ -# "delete service lldp interface eth2" -# ] -# -# "after": [] -# After state -# ------------ -# vyos@vyos# run show configuration commands | grep lldp -# set service 'lldp' - - -""" -RETURN = """ -before: - description: The configuration as structured data prior to module invocation. - returned: always - type: list - sample: > - The configuration returned will always be in the same format - of the parameters above. -after: - description: The configuration as structured data after module completion. - returned: when changed - type: list - sample: > - The configuration returned will always be in the same format - of the parameters above. -commands: - description: The set of commands pushed to the remote device. - returned: always - type: list - sample: - - "set service lldp interface eth2 'disable'" - - "delete service lldp interface eth1 location" -""" - - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.lldp_interfaces.lldp_interfaces import ( - Lldp_interfacesArgs, -) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.lldp_interfaces.lldp_interfaces import ( - Lldp_interfaces, -) - - -def main(): - """ - Main entry point for module execution - - :returns: the result form module invocation - """ - required_if = [ - ("state", "merged", ("config",)), - ("state", "replaced", ("config",)), - ("state", "overridden", ("config",)), - ] - module = AnsibleModule( - argument_spec=Lldp_interfacesArgs.argument_spec, - required_if=required_if, - supports_check_mode=True, - ) - - result = Lldp_interfaces(module).execute_module() - module.exit_json(**result) - - -if __name__ == "__main__": - main() diff -Nru ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/terminal/vyos.py ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/terminal/vyos.py --- ansible-core-2.14.16/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/terminal/vyos.py 2024-04-22 18:07:35.000000000 +0000 +++ ansible-core-2.14.18/test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/terminal/vyos.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -# -# (c) 2016 Red Hat Inc. -# -# This file is part of Ansible -# -# Ansible is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . -# -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -import os -import re - -from ansible.plugins.terminal import TerminalBase -from ansible.errors import AnsibleConnectionFailure - - -class TerminalModule(TerminalBase): - - terminal_stdout_re = [ - re.compile(br"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"), - re.compile(br"\@[\w\-\.]+:\S+?[>#\$] ?$"), - ] - - terminal_stderr_re = [ - re.compile(br"\n\s*Invalid command:"), - re.compile(br"\nCommit failed"), - re.compile(br"\n\s+Set failed"), - ] - - terminal_length = os.getenv("ANSIBLE_VYOS_TERMINAL_LENGTH", 10000) - - def on_open_shell(self): - try: - for cmd in (b"set terminal length 0", b"set terminal width 512"): - self._exec_cli_command(cmd) - self._exec_cli_command( - b"set terminal length %d" % self.terminal_length - ) - except AnsibleConnectionFailure: - raise AnsibleConnectionFailure("unable to set terminal parameters")