mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-04-29 09:56:53 +00:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22b72e6684 | ||
|
|
8e7bee4217 | ||
|
|
cef6b81e5b | ||
|
|
182c365d87 | ||
|
|
587cdc82e7 | ||
|
|
cb1a50a273 | ||
|
|
f0df50e665 | ||
|
|
47aa93d970 | ||
|
|
e89648a114 | ||
|
|
6f1bdb3e49 | ||
|
|
fbf11668f4 | ||
|
|
3376442aa2 | ||
|
|
868edfa664 | ||
|
|
2fcb77f7fb | ||
|
|
17135dd082 | ||
|
|
7516018cfb | ||
|
|
58df1df107 | ||
|
|
e9b3705809 | ||
|
|
743e9c851f | ||
|
|
a7883ee489 | ||
|
|
518af70b77 | ||
|
|
ce7d98aa6f | ||
|
|
9f91f4b5cd | ||
|
|
c45c38f04b | ||
|
|
f7efb2e394 | ||
|
|
093b83c34f | ||
|
|
579fdbbc1c | ||
|
|
c970c14c71 |
@@ -69,6 +69,19 @@ stages:
|
||||
- test: 3
|
||||
- test: 4
|
||||
- test: extra
|
||||
- stage: Sanity_2_13
|
||||
displayName: Sanity 2.13
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Test {0}
|
||||
testFormat: 2.13/sanity/{0}
|
||||
targets:
|
||||
- test: 1
|
||||
- test: 2
|
||||
- test: 3
|
||||
- test: 4
|
||||
- stage: Sanity_2_12
|
||||
displayName: Sanity 2.12
|
||||
dependsOn: []
|
||||
@@ -138,6 +151,19 @@ stages:
|
||||
- test: 3.8
|
||||
- test: 3.9
|
||||
- test: '3.10'
|
||||
- stage: Units_2_13
|
||||
displayName: Units 2.13
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Python {0}
|
||||
testFormat: 2.13/units/{0}/1
|
||||
targets:
|
||||
- test: 2.7
|
||||
- test: 3.6
|
||||
- test: 3.8
|
||||
- test: 3.9
|
||||
- stage: Units_2_12
|
||||
displayName: Units 2.12
|
||||
dependsOn: []
|
||||
@@ -148,12 +174,8 @@ stages:
|
||||
testFormat: 2.12/units/{0}/1
|
||||
targets:
|
||||
- test: 2.6
|
||||
- test: 2.7
|
||||
- test: 3.5
|
||||
- test: 3.6
|
||||
- test: 3.7
|
||||
- test: 3.8
|
||||
- test: '3.10'
|
||||
- stage: Units_2_11
|
||||
displayName: Units 2.11
|
||||
dependsOn: []
|
||||
@@ -166,9 +188,6 @@ stages:
|
||||
- test: 2.6
|
||||
- test: 2.7
|
||||
- test: 3.5
|
||||
- test: 3.6
|
||||
- test: 3.7
|
||||
- test: 3.8
|
||||
- test: 3.9
|
||||
- stage: Units_2_10
|
||||
displayName: Units 2.10
|
||||
@@ -191,11 +210,7 @@ stages:
|
||||
testFormat: 2.9/units/{0}/1
|
||||
targets:
|
||||
- test: 2.6
|
||||
- test: 2.7
|
||||
- test: 3.5
|
||||
- test: 3.6
|
||||
- test: 3.7
|
||||
- test: 3.8
|
||||
|
||||
## Remote
|
||||
- stage: Remote_devel
|
||||
@@ -220,6 +235,22 @@ stages:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- stage: Remote_2_13
|
||||
displayName: Remote 2.13
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.13/{0}
|
||||
targets:
|
||||
- name: macOS 12.0
|
||||
test: macos/12.0
|
||||
- name: RHEL 8.5
|
||||
test: rhel/8.5
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- stage: Remote_2_12
|
||||
displayName: Remote 2.12
|
||||
dependsOn: []
|
||||
@@ -302,9 +333,7 @@ stages:
|
||||
test: fedora34
|
||||
- name: Fedora 35
|
||||
test: fedora35
|
||||
- name: openSUSE 15 py2
|
||||
test: opensuse15py2
|
||||
- name: openSUSE 15 py3
|
||||
- name: openSUSE 15
|
||||
test: opensuse15
|
||||
- name: Ubuntu 18.04
|
||||
test: ubuntu1804
|
||||
@@ -316,6 +345,24 @@ stages:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- stage: Docker_2_13
|
||||
displayName: Docker 2.13
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.13/linux/{0}
|
||||
targets:
|
||||
- name: Fedora 35
|
||||
test: fedora35
|
||||
- name: openSUSE 15 py2
|
||||
test: opensuse15py2
|
||||
- name: Alpine 3
|
||||
test: alpine3
|
||||
groups:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- stage: Docker_2_12
|
||||
displayName: Docker 2.12
|
||||
dependsOn: []
|
||||
@@ -328,8 +375,6 @@ stages:
|
||||
test: centos6
|
||||
- name: Fedora 34
|
||||
test: fedora34
|
||||
- name: openSUSE 15 py3
|
||||
test: opensuse15
|
||||
- name: Ubuntu 20.04
|
||||
test: ubuntu2004
|
||||
groups:
|
||||
@@ -344,12 +389,8 @@ stages:
|
||||
parameters:
|
||||
testFormat: 2.11/linux/{0}
|
||||
targets:
|
||||
- name: CentOS 7
|
||||
test: centos7
|
||||
- name: Fedora 33
|
||||
test: fedora33
|
||||
- name: openSUSE 15 py2
|
||||
test: opensuse15py2
|
||||
- name: Alpine 3
|
||||
test: alpine3
|
||||
groups:
|
||||
@@ -380,8 +421,6 @@ stages:
|
||||
targets:
|
||||
- name: Fedora 31
|
||||
test: fedora31
|
||||
- name: openSUSE 15 py3
|
||||
test: opensuse15
|
||||
groups:
|
||||
- 2
|
||||
- 3
|
||||
@@ -417,6 +456,16 @@ stages:
|
||||
testFormat: devel/cloud/{0}/1
|
||||
targets:
|
||||
- test: 2.7
|
||||
- test: '3.10'
|
||||
- stage: Cloud_2_13
|
||||
displayName: Cloud 2.13
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: Python {0}
|
||||
testFormat: 2.13/cloud/{0}/1
|
||||
targets:
|
||||
- test: 3.9
|
||||
- stage: Cloud_2_12
|
||||
displayName: Cloud 2.12
|
||||
@@ -466,26 +515,31 @@ stages:
|
||||
- Sanity_2_10
|
||||
- Sanity_2_11
|
||||
- Sanity_2_12
|
||||
- Sanity_2_13
|
||||
- Units_devel
|
||||
- Units_2_9
|
||||
- Units_2_10
|
||||
- Units_2_11
|
||||
- Units_2_12
|
||||
- Units_2_13
|
||||
- Remote_devel
|
||||
- Remote_2_9
|
||||
- Remote_2_10
|
||||
- Remote_2_11
|
||||
- Remote_2_12
|
||||
- Remote_2_13
|
||||
- Docker_devel
|
||||
- Docker_2_9
|
||||
- Docker_2_10
|
||||
- Docker_2_11
|
||||
- Docker_2_12
|
||||
- Docker_2_13
|
||||
- Docker_community_devel
|
||||
- Cloud_devel
|
||||
- Cloud_2_9
|
||||
- Cloud_2_10
|
||||
- Cloud_2_11
|
||||
- Cloud_2_12
|
||||
- Cloud_2_13
|
||||
jobs:
|
||||
- template: templates/coverage.yml
|
||||
|
||||
@@ -6,6 +6,47 @@ Community General Release Notes
|
||||
|
||||
This changelog describes changes after version 3.0.0.
|
||||
|
||||
v4.7.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
Regular bugfix and feature release.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- ipa_service - add ``skip_host_check`` parameter. (https://github.com/ansible-collections/community.general/pull/4417).
|
||||
- keycloak_client - add ``always_display_in_console`` parameter (https://github.com/ansible-collections/community.general/issues/4390).
|
||||
- keycloak_client - add ``default_client_scopes`` and ``optional_client_scopes`` parameters. (https://github.com/ansible-collections/community.general/pull/4385).
|
||||
- proxmox inventory plugin - add support for templating the ``url``, ``user``, and ``password`` options (https://github.com/ansible-collections/community.general/pull/4418).
|
||||
- sudoers - add support for ``runas`` parameter (https://github.com/ansible-collections/community.general/issues/4379).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- dsv lookup plugin - raise an Ansible error if the wrong ``python-dsv-sdk`` version is installed (https://github.com/ansible-collections/community.general/pull/4422).
|
||||
- keycloak_* - the documented ``validate_certs`` parameter was not taken into account when calling the ``open_url`` function in some cases, thus enforcing certificate validation even when ``validate_certs`` was set to ``false``. (https://github.com/ansible-collections/community.general/pull/4382)
|
||||
- nmcli - fix returning "changed" when routes parameters set, also suggest new routes4 and routes6 format (https://github.com/ansible-collections/community.general/issues/4131).
|
||||
- proxmox inventory plugin - fixed the ``tags_parsed`` field when Proxmox returns a single space for the ``tags`` entry (https://github.com/ansible-collections/community.general/pull/4378).
|
||||
- zypper - fixed bug that caused zypper to always report [ok] and do nothing on ``state=present`` when all packages in ``name`` had a version specification (https://github.com/ansible-collections/community.general/issues/4371, https://github.com/ansible-collections/community.general/pull/4421).
|
||||
|
||||
v4.6.1
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
Extraordinary bugfix release to fix a breaking change in ``terraform``.
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- lxd inventory plugin - do not crash if OS and release metadata are not present
|
||||
(https://github.com/ansible-collections/community.general/pull/4351).
|
||||
- terraform - revert bugfix https://github.com/ansible-collections/community.general/pull/4281 that tried to fix ``variable`` handling to allow complex values. It turned out that this was breaking several valid use-cases (https://github.com/ansible-collections/community.general/issues/4367, https://github.com/ansible-collections/community.general/pull/4370).
|
||||
|
||||
v4.6.0
|
||||
======
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ If you encounter abusive behavior violating the [Ansible Code of Conduct](https:
|
||||
|
||||
## Tested with Ansible
|
||||
|
||||
Tested with the current Ansible 2.9, ansible-base 2.10, ansible-core 2.11, ansible-core 2.12 releases and the current development version of ansible-core. Ansible versions before 2.9.10 are not supported.
|
||||
Tested with the current Ansible 2.9, ansible-base 2.10, ansible-core 2.11, ansible-core 2.12, ansible-core 2.13 releases and the current development version of ansible-core. Ansible versions before 2.9.10 are not supported.
|
||||
|
||||
## External requirements
|
||||
|
||||
|
||||
@@ -1585,3 +1585,59 @@ releases:
|
||||
- 4352-proxmox-inventory-filters.yml
|
||||
- 4355-ldap-recursive-delete.yml
|
||||
release_date: '2022-03-15'
|
||||
4.6.1:
|
||||
changes:
|
||||
bugfixes:
|
||||
- 'lxd inventory plugin - do not crash if OS and release metadata are not present
|
||||
|
||||
(https://github.com/ansible-collections/community.general/pull/4351).
|
||||
|
||||
'
|
||||
- terraform - revert bugfix https://github.com/ansible-collections/community.general/pull/4281
|
||||
that tried to fix ``variable`` handling to allow complex values. It turned
|
||||
out that this was breaking several valid use-cases (https://github.com/ansible-collections/community.general/issues/4367,
|
||||
https://github.com/ansible-collections/community.general/pull/4370).
|
||||
release_summary: Extraordinary bugfix release to fix a breaking change in ``terraform``.
|
||||
fragments:
|
||||
- 4.6.1.yml
|
||||
- 4351-inventory-lxd-handling_metadata_wo_os_and_release.yml
|
||||
- 4368-reverts-4281.yml
|
||||
release_date: '2022-03-16'
|
||||
4.7.0:
|
||||
changes:
|
||||
bugfixes:
|
||||
- dsv lookup plugin - raise an Ansible error if the wrong ``python-dsv-sdk``
|
||||
version is installed (https://github.com/ansible-collections/community.general/pull/4422).
|
||||
- keycloak_* - the documented ``validate_certs`` parameter was not taken into
|
||||
account when calling the ``open_url`` function in some cases, thus enforcing
|
||||
certificate validation even when ``validate_certs`` was set to ``false``.
|
||||
(https://github.com/ansible-collections/community.general/pull/4382)
|
||||
- nmcli - fix returning "changed" when routes parameters set, also suggest new
|
||||
routes4 and routes6 format (https://github.com/ansible-collections/community.general/issues/4131).
|
||||
- proxmox inventory plugin - fixed the ``tags_parsed`` field when Proxmox returns
|
||||
a single space for the ``tags`` entry (https://github.com/ansible-collections/community.general/pull/4378).
|
||||
- zypper - fixed bug that caused zypper to always report [ok] and do nothing
|
||||
on ``state=present`` when all packages in ``name`` had a version specification
|
||||
(https://github.com/ansible-collections/community.general/issues/4371, https://github.com/ansible-collections/community.general/pull/4421).
|
||||
minor_changes:
|
||||
- ipa_service - add ``skip_host_check`` parameter. (https://github.com/ansible-collections/community.general/pull/4417).
|
||||
- keycloak_client - add ``always_display_in_console`` parameter (https://github.com/ansible-collections/community.general/issues/4390).
|
||||
- keycloak_client - add ``default_client_scopes`` and ``optional_client_scopes``
|
||||
parameters. (https://github.com/ansible-collections/community.general/pull/4385).
|
||||
- proxmox inventory plugin - add support for templating the ``url``, ``user``,
|
||||
and ``password`` options (https://github.com/ansible-collections/community.general/pull/4418).
|
||||
- sudoers - add support for ``runas`` parameter (https://github.com/ansible-collections/community.general/issues/4379).
|
||||
release_summary: Regular bugfix and feature release.
|
||||
fragments:
|
||||
- 4.7.0.yml
|
||||
- 4131-nmcli_fix_reports_changed_for_routes4_parameter.yml
|
||||
- 4378-proxmox-inventory-tags.yml
|
||||
- 4380-sudoers-runas-parameter.yml
|
||||
- 4382-keycloak-add-missing-validate_certs-parameters.yml
|
||||
- 4385-keycloak-client-default-optional-scopes.yml
|
||||
- 4386-proxmox-support-templating-in-inventory-file.yml
|
||||
- 4417-ipa_service-add-skip_host_check.yml
|
||||
- 4421-zypper_package_version_handling_fix.yml
|
||||
- 4422-warn-user-if-incorrect-SDK-version-is-installed.yaml
|
||||
- 4429-keycloak-client-add-always-display-in-console.yml
|
||||
release_date: '2022-04-05'
|
||||
|
||||
23
docs/docsite/links.yml
Normal file
23
docs/docsite/links.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
edit_on_github:
|
||||
repository: ansible-collections/community.general
|
||||
branch: main
|
||||
path_prefix: ''
|
||||
|
||||
extra_links:
|
||||
- description: Submit a bug report
|
||||
url: https://github.com/ansible-collections/community.general/issues/new?assignees=&labels=&template=bug_report.yml
|
||||
- description: Request a feature
|
||||
url: https://github.com/ansible-collections/community.general/issues/new?assignees=&labels=&template=feature_request.yml
|
||||
|
||||
communication:
|
||||
matrix_rooms:
|
||||
- topic: General usage and support questions
|
||||
room: '#users:ansible.im'
|
||||
irc_channels:
|
||||
- topic: General usage and support questions
|
||||
network: Libera
|
||||
channel: '#ansible'
|
||||
mailing_lists:
|
||||
- topic: Ansible Project List
|
||||
url: https://groups.google.com/g/ansible-project
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace: community
|
||||
name: general
|
||||
version: 4.6.0
|
||||
version: 4.7.0
|
||||
readme: README.md
|
||||
authors:
|
||||
- Ansible (https://github.com/ansible)
|
||||
|
||||
@@ -38,8 +38,10 @@ options:
|
||||
version_added: 2.0.0
|
||||
server_uri:
|
||||
description:
|
||||
- A URI to the LDAP server.
|
||||
- The I(server_uri) parameter may be a comma- or whitespace-separated list of URIs containing only the schema, the host, and the port fields.
|
||||
- The default value lets the underlying LDAP client library look for a UNIX domain socket in its default location.
|
||||
- Note that when using multiple URIs you cannot determine to which URI your client gets connected.
|
||||
- For URIs containing additional fields, particularly when using commas, behavior is undefined.
|
||||
type: str
|
||||
default: ldapi:///
|
||||
start_tls:
|
||||
|
||||
@@ -666,9 +666,13 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
# add network informations
|
||||
self.build_inventory_network(instance_name)
|
||||
# add os
|
||||
self.inventory.set_variable(instance_name, 'ansible_lxd_os', self._get_data_entry('inventory/{0}/os'.format(instance_name)).lower())
|
||||
v = self._get_data_entry('inventory/{0}/os'.format(instance_name))
|
||||
if v:
|
||||
self.inventory.set_variable(instance_name, 'ansible_lxd_os', v.lower())
|
||||
# add release
|
||||
self.inventory.set_variable(instance_name, 'ansible_lxd_release', self._get_data_entry('inventory/{0}/release'.format(instance_name)).lower())
|
||||
v = self._get_data_entry('inventory/{0}/release'.format(instance_name))
|
||||
if v:
|
||||
self.inventory.set_variable(instance_name, 'ansible_lxd_release', v.lower())
|
||||
# add profile
|
||||
self.inventory.set_variable(instance_name, 'ansible_lxd_profile', self._get_data_entry('inventory/{0}/profile'.format(instance_name)))
|
||||
# add state
|
||||
|
||||
@@ -31,6 +31,7 @@ DOCUMENTATION = '''
|
||||
description:
|
||||
- URL to Proxmox cluster.
|
||||
- If the value is not specified in the inventory configuration, the value of environment variable C(PROXMOX_URL) will be used instead.
|
||||
- Since community.general 4.7.0 you can also use templating to specify the value of the I(url).
|
||||
default: 'http://localhost:8006'
|
||||
type: str
|
||||
env:
|
||||
@@ -40,6 +41,7 @@ DOCUMENTATION = '''
|
||||
description:
|
||||
- Proxmox authentication user.
|
||||
- If the value is not specified in the inventory configuration, the value of environment variable C(PROXMOX_USER) will be used instead.
|
||||
- Since community.general 4.7.0 you can also use templating to specify the value of the I(user).
|
||||
required: yes
|
||||
type: str
|
||||
env:
|
||||
@@ -49,6 +51,7 @@ DOCUMENTATION = '''
|
||||
description:
|
||||
- Proxmox authentication password.
|
||||
- If the value is not specified in the inventory configuration, the value of environment variable C(PROXMOX_PASSWORD) will be used instead.
|
||||
- Since community.general 4.7.0 you can also use templating to specify the value of the I(password).
|
||||
required: yes
|
||||
type: str
|
||||
env:
|
||||
@@ -136,6 +139,14 @@ compose:
|
||||
my_inv_var_1: "'my_var1_value'"
|
||||
my_inv_var_2: >
|
||||
"my_var_2_value"
|
||||
|
||||
# Specify the url, user and password using templating
|
||||
# my.proxmox.yml
|
||||
plugin: community.general.proxmox
|
||||
url: "{{ lookup('ansible.builtin.ini', 'url', section='proxmox', file='file.ini') }}"
|
||||
user: "{{ lookup('ansible.builtin.env','PM_USER') | default('ansible@pve') }}"
|
||||
password: "{{ lookup('community.general.random_string', base64=True) }}"
|
||||
|
||||
'''
|
||||
|
||||
import itertools
|
||||
@@ -148,6 +159,7 @@ from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cachea
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlencode
|
||||
from ansible.utils.display import Display
|
||||
from ansible.template import Templar
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
|
||||
|
||||
@@ -323,8 +335,10 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
|
||||
# Additional field containing parsed tags as list
|
||||
if config == 'tags':
|
||||
parsed_key = self.to_safe('%s%s' % (key, "_parsed"))
|
||||
properties[parsed_key] = [tag.strip() for tag in value.split(",")]
|
||||
stripped_value = value.strip()
|
||||
if stripped_value:
|
||||
parsed_key = key + "_parsed"
|
||||
properties[parsed_key] = [tag.strip() for tag in stripped_value.split(",")]
|
||||
|
||||
# The first field in the agent string tells you whether the agent is enabled
|
||||
# the rest of the comma separated string is extra config for the agent
|
||||
@@ -498,10 +512,24 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
# read config from file, this sets 'options'
|
||||
self._read_config_data(path)
|
||||
|
||||
t = Templar(loader=loader)
|
||||
|
||||
# read options
|
||||
self.proxmox_url = self.get_option('url').rstrip('/')
|
||||
self.proxmox_user = self.get_option('user')
|
||||
self.proxmox_password = self.get_option('password')
|
||||
proxmox_url = self.get_option('url')
|
||||
if t.is_template(proxmox_url):
|
||||
proxmox_url = t.template(variable=proxmox_url, disable_lookups=False)
|
||||
self.proxmox_url = proxmox_url.rstrip('/')
|
||||
|
||||
proxmox_user = self.get_option('user')
|
||||
if t.is_template(proxmox_user):
|
||||
proxmox_user = t.template(variable=proxmox_user, disable_lookups=False)
|
||||
self.proxmox_user = proxmox_user
|
||||
|
||||
proxmox_password = self.get_option('password')
|
||||
if t.is_template(proxmox_password):
|
||||
proxmox_password = t.template(variable=proxmox_password, disable_lookups=False)
|
||||
self.proxmox_password = proxmox_password
|
||||
|
||||
self.cache_key = self.get_cache_key(path)
|
||||
self.use_cache = cache and self.get_option('cache')
|
||||
self.host_filters = self.get_option('filters')
|
||||
|
||||
@@ -105,11 +105,15 @@ display = Display()
|
||||
class LookupModule(LookupBase):
|
||||
@staticmethod
|
||||
def Client(vault_parameters):
|
||||
return SecretsVault(**vault_parameters)
|
||||
try:
|
||||
vault = SecretsVault(**vault_parameters)
|
||||
return vault
|
||||
except TypeError:
|
||||
raise AnsibleError("python-dsv-sdk==0.0.1 must be installed to use this plugin")
|
||||
|
||||
def run(self, terms, variables, **kwargs):
|
||||
if sdk_is_missing:
|
||||
raise AnsibleError("python-dsv-sdk must be installed to use this plugin")
|
||||
raise AnsibleError("python-dsv-sdk==0.0.1 must be installed to use this plugin")
|
||||
|
||||
self.set_options(var_options=variables, direct=kwargs)
|
||||
|
||||
|
||||
@@ -1237,7 +1237,7 @@ class KeycloakAPI(object):
|
||||
authentication_flow = {}
|
||||
# Check if the authentication flow exists on the Keycloak serveraders
|
||||
authentications = json.load(open_url(URL_AUTHENTICATION_FLOWS.format(url=self.baseurl, realm=realm), method='GET',
|
||||
headers=self.restheaders, timeout=self.connection_timeout))
|
||||
headers=self.restheaders, timeout=self.connection_timeout, validate_certs=self.validate_certs))
|
||||
for authentication in authentications:
|
||||
if authentication["alias"] == alias:
|
||||
authentication_flow = authentication
|
||||
@@ -1281,14 +1281,16 @@ class KeycloakAPI(object):
|
||||
method='POST',
|
||||
headers=self.restheaders,
|
||||
data=json.dumps(new_name),
|
||||
timeout=self.connection_timeout)
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
flow_list = json.load(
|
||||
open_url(
|
||||
URL_AUTHENTICATION_FLOWS.format(url=self.baseurl,
|
||||
realm=realm),
|
||||
method='GET',
|
||||
headers=self.restheaders,
|
||||
timeout=self.connection_timeout))
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs))
|
||||
for flow in flow_list:
|
||||
if flow["alias"] == config["alias"]:
|
||||
return flow
|
||||
@@ -1318,7 +1320,8 @@ class KeycloakAPI(object):
|
||||
method='POST',
|
||||
headers=self.restheaders,
|
||||
data=json.dumps(new_flow),
|
||||
timeout=self.connection_timeout)
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
flow_list = json.load(
|
||||
open_url(
|
||||
URL_AUTHENTICATION_FLOWS.format(
|
||||
@@ -1326,7 +1329,8 @@ class KeycloakAPI(object):
|
||||
realm=realm),
|
||||
method='GET',
|
||||
headers=self.restheaders,
|
||||
timeout=self.connection_timeout))
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs))
|
||||
for flow in flow_list:
|
||||
if flow["alias"] == config["alias"]:
|
||||
return flow
|
||||
@@ -1351,7 +1355,8 @@ class KeycloakAPI(object):
|
||||
method='PUT',
|
||||
headers=self.restheaders,
|
||||
data=json.dumps(updatedExec),
|
||||
timeout=self.connection_timeout)
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to update executions %s: %s" % (updatedExec, str(e)))
|
||||
|
||||
@@ -1371,7 +1376,8 @@ class KeycloakAPI(object):
|
||||
method='POST',
|
||||
headers=self.restheaders,
|
||||
data=json.dumps(authenticationConfig),
|
||||
timeout=self.connection_timeout)
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to add authenticationConfig %s: %s" % (executionId, str(e)))
|
||||
|
||||
@@ -1395,7 +1401,8 @@ class KeycloakAPI(object):
|
||||
method='POST',
|
||||
headers=self.restheaders,
|
||||
data=json.dumps(newSubFlow),
|
||||
timeout=self.connection_timeout)
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to create new subflow %s: %s" % (subflowName, str(e)))
|
||||
|
||||
@@ -1418,7 +1425,8 @@ class KeycloakAPI(object):
|
||||
method='POST',
|
||||
headers=self.restheaders,
|
||||
data=json.dumps(newExec),
|
||||
timeout=self.connection_timeout)
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to create new execution %s: %s" % (execution["provider"], str(e)))
|
||||
|
||||
@@ -1440,7 +1448,8 @@ class KeycloakAPI(object):
|
||||
id=executionId),
|
||||
method='POST',
|
||||
headers=self.restheaders,
|
||||
timeout=self.connection_timeout)
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
elif diff < 0:
|
||||
for i in range(-diff):
|
||||
open_url(
|
||||
@@ -1450,7 +1459,8 @@ class KeycloakAPI(object):
|
||||
id=executionId),
|
||||
method='POST',
|
||||
headers=self.restheaders,
|
||||
timeout=self.connection_timeout)
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs)
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Unable to change execution priority %s: %s" % (executionId, str(e)))
|
||||
|
||||
@@ -1471,7 +1481,8 @@ class KeycloakAPI(object):
|
||||
flowalias=quote(config["alias"])),
|
||||
method='GET',
|
||||
headers=self.restheaders,
|
||||
timeout=self.connection_timeout))
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs))
|
||||
for execution in executions:
|
||||
if "authenticationConfig" in execution:
|
||||
execConfigId = execution["authenticationConfig"]
|
||||
@@ -1483,7 +1494,8 @@ class KeycloakAPI(object):
|
||||
id=execConfigId),
|
||||
method='GET',
|
||||
headers=self.restheaders,
|
||||
timeout=self.connection_timeout))
|
||||
timeout=self.connection_timeout,
|
||||
validate_certs=self.validate_certs))
|
||||
execution["authenticationConfig"] = execConfig
|
||||
return executions
|
||||
except Exception as e:
|
||||
|
||||
@@ -443,7 +443,7 @@ def main():
|
||||
for k, v in variables.items():
|
||||
variables_args.extend([
|
||||
'-var',
|
||||
'{0}={1}'.format(k, json.dumps(v))
|
||||
'{0}={1}'.format(k, v)
|
||||
])
|
||||
if variables_files:
|
||||
for f in variables_files:
|
||||
|
||||
@@ -32,6 +32,14 @@ options:
|
||||
- Force principal name even if host is not in DNS.
|
||||
required: false
|
||||
type: bool
|
||||
skip_host_check:
|
||||
description:
|
||||
- Force service to be created even when host object does not exist to manage it.
|
||||
- This is only used on creation, not for updating existing services.
|
||||
required: false
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 4.7.0
|
||||
state:
|
||||
description: State to ensure.
|
||||
required: false
|
||||
@@ -111,17 +119,19 @@ class ServiceIPAClient(IPAClient):
|
||||
return self._post_json(method='service_remove_host', name=name, item={'host': item})
|
||||
|
||||
|
||||
def get_service_dict(force=None, krbcanonicalname=None):
|
||||
def get_service_dict(force=None, krbcanonicalname=None, skip_host_check=None):
|
||||
data = {}
|
||||
if force is not None:
|
||||
data['force'] = force
|
||||
if krbcanonicalname is not None:
|
||||
data['krbcanonicalname'] = krbcanonicalname
|
||||
if skip_host_check is not None:
|
||||
data['skip_host_check'] = skip_host_check
|
||||
return data
|
||||
|
||||
|
||||
def get_service_diff(client, ipa_host, module_service):
|
||||
non_updateable_keys = ['force', 'krbcanonicalname']
|
||||
non_updateable_keys = ['force', 'krbcanonicalname', 'skip_host_check']
|
||||
for key in non_updateable_keys:
|
||||
if key in module_service:
|
||||
del module_service[key]
|
||||
@@ -135,7 +145,7 @@ def ensure(module, client):
|
||||
hosts = module.params['hosts']
|
||||
|
||||
ipa_service = client.service_find(name=name)
|
||||
module_service = get_service_dict(force=module.params['force'])
|
||||
module_service = get_service_dict(force=module.params['force'], skip_host_check=module.params['skip_host_check'])
|
||||
changed = False
|
||||
if state in ['present', 'enabled', 'disabled']:
|
||||
if not ipa_service:
|
||||
@@ -183,6 +193,7 @@ def main():
|
||||
argument_spec.update(
|
||||
krbcanonicalname=dict(type='str', required=True, aliases=['name']),
|
||||
force=dict(type='bool', required=False),
|
||||
skip_host_check=dict(type='bool', default=False, required=False),
|
||||
hosts=dict(type='list', required=False, elements='str'),
|
||||
state=dict(type='str', required=False, default='present',
|
||||
choices=['present', 'absent']))
|
||||
|
||||
@@ -301,6 +301,15 @@ options:
|
||||
- useTemplateMappers
|
||||
type: bool
|
||||
|
||||
always_display_in_console:
|
||||
description:
|
||||
- Whether or not to display this client in account console, even if the
|
||||
user does not have an active session.
|
||||
aliases:
|
||||
- alwaysDisplayInConsole
|
||||
type: bool
|
||||
version_added: 4.7.0
|
||||
|
||||
surrogate_auth_required:
|
||||
description:
|
||||
- Whether or not surrogate auth is required.
|
||||
@@ -326,6 +335,24 @@ options:
|
||||
- authenticationFlowBindingOverrides
|
||||
version_added: 3.4.0
|
||||
|
||||
default_client_scopes:
|
||||
description:
|
||||
- List of default client scopes.
|
||||
aliases:
|
||||
- defaultClientScopes
|
||||
type: list
|
||||
elements: str
|
||||
version_added: 4.7.0
|
||||
|
||||
optional_client_scopes:
|
||||
description:
|
||||
- List of optional client scopes.
|
||||
aliases:
|
||||
- optionalClientScopes
|
||||
type: list
|
||||
elements: str
|
||||
version_added: 4.7.0
|
||||
|
||||
protocol_mappers:
|
||||
description:
|
||||
- a list of dicts defining protocol mappers for this client.
|
||||
@@ -593,6 +620,7 @@ EXAMPLES = '''
|
||||
use_template_config: False
|
||||
use_template_scope: false
|
||||
use_template_mappers: no
|
||||
always_display_in_console: true
|
||||
registered_nodes:
|
||||
node01.example.com: 1507828202
|
||||
registration_access_token: eyJWT_TOKEN
|
||||
@@ -786,9 +814,12 @@ def main():
|
||||
use_template_config=dict(type='bool', aliases=['useTemplateConfig']),
|
||||
use_template_scope=dict(type='bool', aliases=['useTemplateScope']),
|
||||
use_template_mappers=dict(type='bool', aliases=['useTemplateMappers']),
|
||||
always_display_in_console=dict(type='bool', aliases=['alwaysDisplayInConsole']),
|
||||
authentication_flow_binding_overrides=dict(type='dict', aliases=['authenticationFlowBindingOverrides']),
|
||||
protocol_mappers=dict(type='list', elements='dict', options=protmapper_spec, aliases=['protocolMappers']),
|
||||
authorization_settings=dict(type='dict', aliases=['authorizationSettings']),
|
||||
default_client_scopes=dict(type='list', elements='str', aliases=['defaultClientScopes']),
|
||||
optional_client_scopes=dict(type='list', elements='str', aliases=['optionalClientScopes']),
|
||||
)
|
||||
|
||||
argument_spec.update(meta_args)
|
||||
|
||||
@@ -104,7 +104,7 @@ author:
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Map a client role to a group, authentication with credentials
|
||||
community.general.keycloak_client_rolemappings:
|
||||
community.general.keycloak_client_rolemapping:
|
||||
realm: MyCustomRealm
|
||||
auth_client_id: admin-cli
|
||||
auth_keycloak_url: https://auth.example.com/auth
|
||||
@@ -122,7 +122,7 @@ EXAMPLES = '''
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Map a client role to a group, authentication with token
|
||||
community.general.keycloak_client_rolemappings:
|
||||
community.general.keycloak_client_rolemapping:
|
||||
realm: MyCustomRealm
|
||||
auth_client_id: admin-cli
|
||||
auth_keycloak_url: https://auth.example.com/auth
|
||||
@@ -138,7 +138,7 @@ EXAMPLES = '''
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Unmap client role from a group
|
||||
community.general.keycloak_client_rolemappings:
|
||||
community.general.keycloak_client_rolemapping:
|
||||
realm: MyCustomRealm
|
||||
auth_client_id: admin-cli
|
||||
auth_keycloak_url: https://auth.example.com/auth
|
||||
|
||||
@@ -294,7 +294,7 @@ class OnePasswordInfo(object):
|
||||
except AnsibleModuleError as e:
|
||||
module.fail_json(msg="Failed to perform initial sign in to 1Password: %s" % to_native(e))
|
||||
else:
|
||||
module.fail_json(msg="Unable to perform an initial sign in to 1Password. Please run '%s sigin' "
|
||||
module.fail_json(msg="Unable to perform an initial sign in to 1Password. Please run '%s signin' "
|
||||
"or define credentials in 'auto_login'. See the module documentation for details." % self.cli_path)
|
||||
|
||||
def get_token(self):
|
||||
|
||||
@@ -90,11 +90,53 @@ options:
|
||||
version_added: 3.2.0
|
||||
routes4:
|
||||
description:
|
||||
- The list of ipv4 routes.
|
||||
- Use the format '192.0.3.0/24 192.0.2.1'
|
||||
- The list of IPv4 routes.
|
||||
- Use the format C(192.0.3.0/24 192.0.2.1).
|
||||
- To specify more complex routes, use the I(routes4_extended) option.
|
||||
type: list
|
||||
elements: str
|
||||
version_added: 2.0.0
|
||||
routes4_extended:
|
||||
description:
|
||||
- The list of IPv4 routes.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
ip:
|
||||
description:
|
||||
- IP or prefix of route.
|
||||
- Use the format C(192.0.3.0/24).
|
||||
type: str
|
||||
required: true
|
||||
next_hop:
|
||||
description:
|
||||
- Use the format C(192.0.2.1).
|
||||
type: str
|
||||
metric:
|
||||
description:
|
||||
- Route metric.
|
||||
type: int
|
||||
table:
|
||||
description:
|
||||
- The table to add this route to.
|
||||
- The default depends on C(ipv4.route-table).
|
||||
type: int
|
||||
cwnd:
|
||||
description:
|
||||
- The clamp for congestion window.
|
||||
type: int
|
||||
mtu:
|
||||
description:
|
||||
- If non-zero, only transmit packets of the specified size or smaller.
|
||||
type: int
|
||||
onlink:
|
||||
description:
|
||||
- Pretend that the nexthop is directly attached to this link, even if it does not match any interface prefix.
|
||||
type: bool
|
||||
tos:
|
||||
description:
|
||||
- The Type Of Service.
|
||||
type: int
|
||||
route_metric4:
|
||||
description:
|
||||
- Set metric level of ipv4 routes configured on interface.
|
||||
@@ -165,9 +207,47 @@ options:
|
||||
description:
|
||||
- The list of IPv6 routes.
|
||||
- Use the format C(fd12:3456:789a:1::/64 2001:dead:beef::1).
|
||||
- To specify more complex routes, use the I(routes6_extended) option.
|
||||
type: list
|
||||
elements: str
|
||||
version_added: 4.4.0
|
||||
routes6_extended:
|
||||
description:
|
||||
- The list of IPv6 routes but with parameters.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
ip:
|
||||
description:
|
||||
- IP or prefix of route.
|
||||
- Use the format C(fd12:3456:789a:1::/64).
|
||||
type: str
|
||||
required: true
|
||||
next_hop:
|
||||
description:
|
||||
- Use the format C(2001:dead:beef::1).
|
||||
type: str
|
||||
metric:
|
||||
description:
|
||||
- Route metric.
|
||||
type: int
|
||||
table:
|
||||
description:
|
||||
- The table to add this route to.
|
||||
- The default depends on C(ipv6.route-table).
|
||||
type: int
|
||||
cwnd:
|
||||
description:
|
||||
- The clamp for congestion window.
|
||||
type: int
|
||||
mtu:
|
||||
description:
|
||||
- If non-zero, only transmit packets of the specified size or smaller.
|
||||
type: int
|
||||
onlink:
|
||||
description:
|
||||
- Pretend that the nexthop is directly attached to this link, even if it does not match any interface prefix.
|
||||
type: bool
|
||||
route_metric6:
|
||||
description:
|
||||
- Set metric level of IPv6 routes configured on interface.
|
||||
@@ -1260,6 +1340,7 @@ class Nmcli(object):
|
||||
self.gw4 = module.params['gw4']
|
||||
self.gw4_ignore_auto = module.params['gw4_ignore_auto']
|
||||
self.routes4 = module.params['routes4']
|
||||
self.routes4_extended = module.params['routes4_extended']
|
||||
self.route_metric4 = module.params['route_metric4']
|
||||
self.routing_rules4 = module.params['routing_rules4']
|
||||
self.never_default4 = module.params['never_default4']
|
||||
@@ -1272,6 +1353,7 @@ class Nmcli(object):
|
||||
self.gw6 = module.params['gw6']
|
||||
self.gw6_ignore_auto = module.params['gw6_ignore_auto']
|
||||
self.routes6 = module.params['routes6']
|
||||
self.routes6_extended = module.params['routes6_extended']
|
||||
self.route_metric6 = module.params['route_metric6']
|
||||
self.dns6 = module.params['dns6']
|
||||
self.dns6_search = module.params['dns6_search']
|
||||
@@ -1371,7 +1453,7 @@ class Nmcli(object):
|
||||
'ipv4.ignore-auto-dns': self.dns4_ignore_auto,
|
||||
'ipv4.gateway': self.gw4,
|
||||
'ipv4.ignore-auto-routes': self.gw4_ignore_auto,
|
||||
'ipv4.routes': self.routes4,
|
||||
'ipv4.routes': self.enforce_routes_format(self.routes4, self.routes4_extended),
|
||||
'ipv4.route-metric': self.route_metric4,
|
||||
'ipv4.routing-rules': self.routing_rules4,
|
||||
'ipv4.never-default': self.never_default4,
|
||||
@@ -1383,7 +1465,7 @@ class Nmcli(object):
|
||||
'ipv6.ignore-auto-dns': self.dns6_ignore_auto,
|
||||
'ipv6.gateway': self.gw6,
|
||||
'ipv6.ignore-auto-routes': self.gw6_ignore_auto,
|
||||
'ipv6.routes': self.routes6,
|
||||
'ipv6.routes': self.enforce_routes_format(self.routes6, self.routes6_extended),
|
||||
'ipv6.route-metric': self.route_metric6,
|
||||
'ipv6.method': self.ipv6_method,
|
||||
'ipv6.ip6-privacy': self.ip_privacy6,
|
||||
@@ -1614,6 +1696,29 @@ class Nmcli(object):
|
||||
return None
|
||||
return [address if '/' in address else address + '/128' for address in ip6_addresses]
|
||||
|
||||
def enforce_routes_format(self, routes, routes_extended):
|
||||
if routes is not None:
|
||||
return routes
|
||||
elif routes_extended is not None:
|
||||
return [self.route_to_string(route) for route in routes_extended]
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def route_to_string(route):
|
||||
result_str = ''
|
||||
result_str += route['ip']
|
||||
if route.get('next_hop') is not None:
|
||||
result_str += ' ' + route['next_hop']
|
||||
if route.get('metric') is not None:
|
||||
result_str += ' ' + str(route['metric'])
|
||||
|
||||
for attribute, value in sorted(route.items()):
|
||||
if attribute not in ('ip', 'next_hop', 'metric') and value is not None:
|
||||
result_str += ' {0}={1}'.format(attribute, str(value).lower())
|
||||
|
||||
return result_str
|
||||
|
||||
@staticmethod
|
||||
def bool_to_string(boolean):
|
||||
if boolean:
|
||||
@@ -1657,6 +1762,20 @@ class Nmcli(object):
|
||||
return list
|
||||
return str
|
||||
|
||||
def get_route_params(self, raw_values):
|
||||
routes_params = []
|
||||
for raw_value in raw_values:
|
||||
route_params = {}
|
||||
for parameter, value in re.findall(r'([\w-]*)\s?=\s?([^\s,}]*)', raw_value):
|
||||
if parameter == 'nh':
|
||||
route_params['next_hop'] = value
|
||||
elif parameter == 'mt':
|
||||
route_params['metric'] = value
|
||||
else:
|
||||
route_params[parameter] = value
|
||||
routes_params.append(route_params)
|
||||
return [self.route_to_string(route_params) for route_params in routes_params]
|
||||
|
||||
def list_connection_info(self):
|
||||
cmd = [self.nmcli_bin, '--fields', 'name', '--terse', 'con', 'show']
|
||||
(rc, out, err) = self.execute_command(cmd)
|
||||
@@ -1852,13 +1971,7 @@ class Nmcli(object):
|
||||
if key in conn_info:
|
||||
current_value = conn_info[key]
|
||||
if key in ('ipv4.routes', 'ipv6.routes') and current_value is not None:
|
||||
# ipv4.routes and ipv6.routes do not have same options and show_connection() format
|
||||
# options: ['10.11.0.0/24 10.10.0.2', '10.12.0.0/24 10.10.0.2 200']
|
||||
# show_connection(): ['{ ip = 10.11.0.0/24, nh = 10.10.0.2 }', '{ ip = 10.12.0.0/24, nh = 10.10.0.2, mt = 200 }']
|
||||
# Need to convert in order to compare both
|
||||
current_value = [re.sub(r'^{\s*ip\s*=\s*([^, ]+),\s*nh\s*=\s*([^} ]+),\s*mt\s*=\s*([^} ]+)\s*}', r'\1 \2 \3',
|
||||
route) for route in current_value]
|
||||
current_value = [re.sub(r'^{\s*ip\s*=\s*([^, ]+),\s*nh\s*=\s*([^} ]+)\s*}', r'\1 \2', route) for route in current_value]
|
||||
current_value = self.get_route_params(current_value)
|
||||
if key == self.mac_setting:
|
||||
# MAC addresses are case insensitive, nmcli always reports them in uppercase
|
||||
value = value.upper()
|
||||
@@ -1942,6 +2055,18 @@ def main():
|
||||
gw4=dict(type='str'),
|
||||
gw4_ignore_auto=dict(type='bool', default=False),
|
||||
routes4=dict(type='list', elements='str'),
|
||||
routes4_extended=dict(type='list',
|
||||
elements='dict',
|
||||
options=dict(
|
||||
ip=dict(type='str', required=True),
|
||||
next_hop=dict(type='str'),
|
||||
metric=dict(type='int'),
|
||||
table=dict(type='int'),
|
||||
tos=dict(type='int'),
|
||||
cwnd=dict(type='int'),
|
||||
mtu=dict(type='int'),
|
||||
onlink=dict(type='bool')
|
||||
)),
|
||||
route_metric4=dict(type='int'),
|
||||
routing_rules4=dict(type='list', elements='str'),
|
||||
never_default4=dict(type='bool', default=False),
|
||||
@@ -1958,6 +2083,17 @@ def main():
|
||||
dns6_search=dict(type='list', elements='str'),
|
||||
dns6_ignore_auto=dict(type='bool', default=False),
|
||||
routes6=dict(type='list', elements='str'),
|
||||
routes6_extended=dict(type='list',
|
||||
elements='dict',
|
||||
options=dict(
|
||||
ip=dict(type='str', required=True),
|
||||
next_hop=dict(type='str'),
|
||||
metric=dict(type='int'),
|
||||
table=dict(type='int'),
|
||||
cwnd=dict(type='int'),
|
||||
mtu=dict(type='int'),
|
||||
onlink=dict(type='bool')
|
||||
)),
|
||||
route_metric6=dict(type='int'),
|
||||
method6=dict(type='str', choices=['ignore', 'auto', 'dhcp', 'link-local', 'manual', 'shared', 'disabled']),
|
||||
ip_privacy6=dict(type='str', choices=['disabled', 'prefer-public-addr', 'prefer-temp-addr', 'unknown']),
|
||||
@@ -2014,7 +2150,9 @@ def main():
|
||||
gsm=dict(type='dict'),
|
||||
wireguard=dict(type='dict'),
|
||||
),
|
||||
mutually_exclusive=[['never_default4', 'gw4']],
|
||||
mutually_exclusive=[['never_default4', 'gw4'],
|
||||
['routes4_extended', 'routes4'],
|
||||
['routes6_extended', 'routes6']],
|
||||
required_if=[("type", "wifi", [("ssid")])],
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
@@ -427,7 +427,9 @@ def package_present(m, name, want_latest):
|
||||
# if a version is given leave the package in to let zypper handle the version
|
||||
# resolution
|
||||
packageswithoutversion = [p for p in packages if not p.version]
|
||||
prerun_state = get_installed_state(m, packageswithoutversion)
|
||||
prerun_state = {}
|
||||
if packageswithoutversion:
|
||||
prerun_state = get_installed_state(m, packageswithoutversion)
|
||||
# generate lists of packages to install or remove
|
||||
packages = [p for p in packages if p.shouldinstall != (p.name in prerun_state)]
|
||||
|
||||
|
||||
@@ -41,6 +41,11 @@ options:
|
||||
- Whether a password will be required to run the sudo'd command.
|
||||
default: true
|
||||
type: bool
|
||||
runas:
|
||||
description:
|
||||
- Specify the target user the command(s) will run as.
|
||||
type: str
|
||||
version_added: 4.7.0
|
||||
sudoers_path:
|
||||
description:
|
||||
- The path which sudoers config files will be managed in.
|
||||
@@ -69,6 +74,14 @@ EXAMPLES = '''
|
||||
user: backup
|
||||
commands: /usr/local/bin/backup
|
||||
|
||||
- name: Allow the bob user to run any commands as alice with sudo -u alice
|
||||
community.general.sudoers:
|
||||
name: bob-do-as-alice
|
||||
state: present
|
||||
user: bob
|
||||
runas: alice
|
||||
commands: ANY
|
||||
|
||||
- name: >-
|
||||
Allow the monitoring group to run sudo /usr/local/bin/gather-app-metrics
|
||||
without requiring a password
|
||||
@@ -108,6 +121,7 @@ class Sudoers(object):
|
||||
self.group = module.params['group']
|
||||
self.state = module.params['state']
|
||||
self.nopassword = module.params['nopassword']
|
||||
self.runas = module.params['runas']
|
||||
self.sudoers_path = module.params['sudoers_path']
|
||||
self.file = os.path.join(self.sudoers_path, self.name)
|
||||
self.commands = module.params['commands']
|
||||
@@ -140,7 +154,8 @@ class Sudoers(object):
|
||||
|
||||
commands_str = ', '.join(self.commands)
|
||||
nopasswd_str = 'NOPASSWD:' if self.nopassword else ''
|
||||
return "{owner} ALL={nopasswd} {commands}\n".format(owner=owner, nopasswd=nopasswd_str, commands=commands_str)
|
||||
runas_str = '({runas})'.format(runas=self.runas) if self.runas is not None else ''
|
||||
return "{owner} ALL={runas}{nopasswd} {commands}\n".format(owner=owner, runas=runas_str, nopasswd=nopasswd_str, commands=commands_str)
|
||||
|
||||
def run(self):
|
||||
if self.state == 'absent' and self.exists():
|
||||
@@ -168,6 +183,10 @@ def main():
|
||||
'type': 'bool',
|
||||
'default': True,
|
||||
},
|
||||
'runas': {
|
||||
'type': 'str',
|
||||
'default': None,
|
||||
},
|
||||
'sudoers_path': {
|
||||
'type': 'str',
|
||||
'default': '/etc/sudoers.d',
|
||||
|
||||
@@ -3,115 +3,117 @@
|
||||
# and should not be used as examples of how to write Ansible roles #
|
||||
####################################################################
|
||||
|
||||
- when:
|
||||
- not (ansible_os_family == 'Alpine' and ansible_distribution_version is version('3.15', '<')) # TODO
|
||||
block:
|
||||
- name: Create EMAIL cron var
|
||||
cronvar:
|
||||
name: EMAIL
|
||||
value: doug@ansibmod.con.com
|
||||
register: create_cronvar1
|
||||
- name: Ensure /etc/cron.d directory exists
|
||||
file:
|
||||
path: /etc/cron.d
|
||||
state: directory
|
||||
|
||||
- name: Create EMAIL cron var again
|
||||
cronvar:
|
||||
name: EMAIL
|
||||
value: doug@ansibmod.con.com
|
||||
register: create_cronvar2
|
||||
- name: Create EMAIL cron var
|
||||
cronvar:
|
||||
name: EMAIL
|
||||
value: doug@ansibmod.con.com
|
||||
register: create_cronvar1
|
||||
|
||||
- name: Check cron var value
|
||||
shell: crontab -l -u root | grep -c EMAIL=doug@ansibmod.con.com
|
||||
register: varcheck1
|
||||
- name: Create EMAIL cron var again
|
||||
cronvar:
|
||||
name: EMAIL
|
||||
value: doug@ansibmod.con.com
|
||||
register: create_cronvar2
|
||||
|
||||
- name: Modify EMAIL cron var
|
||||
cronvar:
|
||||
name: EMAIL
|
||||
value: jane@ansibmod.con.com
|
||||
register: create_cronvar3
|
||||
- name: Check cron var value
|
||||
shell: crontab -l -u root | grep -c EMAIL=doug@ansibmod.con.com
|
||||
register: varcheck1
|
||||
|
||||
- name: Check cron var value again
|
||||
shell: crontab -l -u root | grep -c EMAIL=jane@ansibmod.con.com
|
||||
register: varcheck2
|
||||
- name: Modify EMAIL cron var
|
||||
cronvar:
|
||||
name: EMAIL
|
||||
value: jane@ansibmod.con.com
|
||||
register: create_cronvar3
|
||||
|
||||
- name: Remove EMAIL cron var
|
||||
cronvar:
|
||||
name: EMAIL
|
||||
state: absent
|
||||
register: remove_cronvar1
|
||||
- name: Check cron var value again
|
||||
shell: crontab -l -u root | grep -c EMAIL=jane@ansibmod.con.com
|
||||
register: varcheck2
|
||||
|
||||
- name: Remove EMAIL cron var again
|
||||
cronvar:
|
||||
name: EMAIL
|
||||
state: absent
|
||||
register: remove_cronvar2
|
||||
- name: Remove EMAIL cron var
|
||||
cronvar:
|
||||
name: EMAIL
|
||||
state: absent
|
||||
register: remove_cronvar1
|
||||
|
||||
- name: Check cron var value again
|
||||
shell: crontab -l -u root | grep -c EMAIL
|
||||
register: varcheck3
|
||||
failed_when: varcheck3.rc == 0
|
||||
- name: Remove EMAIL cron var again
|
||||
cronvar:
|
||||
name: EMAIL
|
||||
state: absent
|
||||
register: remove_cronvar2
|
||||
|
||||
- name: Add cron var to custom file
|
||||
cronvar:
|
||||
name: TESTVAR
|
||||
value: somevalue
|
||||
cron_file: cronvar_test
|
||||
register: custom_cronfile1
|
||||
- name: Check cron var value again
|
||||
shell: crontab -l -u root | grep -c EMAIL
|
||||
register: varcheck3
|
||||
failed_when: varcheck3.rc == 0
|
||||
|
||||
- name: Add cron var to custom file again
|
||||
cronvar:
|
||||
name: TESTVAR
|
||||
value: somevalue
|
||||
cron_file: cronvar_test
|
||||
register: custom_cronfile2
|
||||
- name: Add cron var to custom file
|
||||
cronvar:
|
||||
name: TESTVAR
|
||||
value: somevalue
|
||||
cron_file: cronvar_test
|
||||
register: custom_cronfile1
|
||||
|
||||
- name: Check cron var value in custom file
|
||||
command: grep -c TESTVAR=somevalue {{ cron_config_path }}/cronvar_test
|
||||
register: custom_varcheck1
|
||||
- name: Add cron var to custom file again
|
||||
cronvar:
|
||||
name: TESTVAR
|
||||
value: somevalue
|
||||
cron_file: cronvar_test
|
||||
register: custom_cronfile2
|
||||
|
||||
- name: Change cron var in custom file
|
||||
cronvar:
|
||||
name: TESTVAR
|
||||
value: newvalue
|
||||
cron_file: cronvar_test
|
||||
register: custom_cronfile3
|
||||
- name: Check cron var value in custom file
|
||||
command: grep -c TESTVAR=somevalue {{ cron_config_path }}/cronvar_test
|
||||
register: custom_varcheck1
|
||||
|
||||
- name: Check cron var value in custom file
|
||||
command: grep -c TESTVAR=newvalue {{ cron_config_path }}/cronvar_test
|
||||
register: custom_varcheck2
|
||||
- name: Change cron var in custom file
|
||||
cronvar:
|
||||
name: TESTVAR
|
||||
value: newvalue
|
||||
cron_file: cronvar_test
|
||||
register: custom_cronfile3
|
||||
|
||||
- name: Remove cron var from custom file
|
||||
cronvar:
|
||||
name: TESTVAR
|
||||
value: newvalue
|
||||
cron_file: cronvar_test
|
||||
state: absent
|
||||
register: custom_remove_cronvar1
|
||||
- name: Check cron var value in custom file
|
||||
command: grep -c TESTVAR=newvalue {{ cron_config_path }}/cronvar_test
|
||||
register: custom_varcheck2
|
||||
|
||||
- name: Remove cron var from custom file again
|
||||
cronvar:
|
||||
name: TESTVAR
|
||||
value: newvalue
|
||||
cron_file: cronvar_test
|
||||
state: absent
|
||||
register: custom_remove_cronvar2
|
||||
- name: Remove cron var from custom file
|
||||
cronvar:
|
||||
name: TESTVAR
|
||||
value: newvalue
|
||||
cron_file: cronvar_test
|
||||
state: absent
|
||||
register: custom_remove_cronvar1
|
||||
|
||||
- name: Check cron var value
|
||||
command: grep -c TESTVAR=newvalue {{ cron_config_path }}/cronvar_test
|
||||
register: custom_varcheck3
|
||||
failed_when: custom_varcheck3.rc == 0
|
||||
- name: Remove cron var from custom file again
|
||||
cronvar:
|
||||
name: TESTVAR
|
||||
value: newvalue
|
||||
cron_file: cronvar_test
|
||||
state: absent
|
||||
register: custom_remove_cronvar2
|
||||
|
||||
- name: Esure cronvar tasks did the right thing
|
||||
assert:
|
||||
that:
|
||||
- create_cronvar1 is changed
|
||||
- create_cronvar2 is not changed
|
||||
- create_cronvar3 is changed
|
||||
- remove_cronvar1 is changed
|
||||
- remove_cronvar2 is not changed
|
||||
- varcheck1.stdout == '1'
|
||||
- varcheck2.stdout == '1'
|
||||
- varcheck3.stdout == '0'
|
||||
- custom_remove_cronvar1 is changed
|
||||
- custom_remove_cronvar2 is not changed
|
||||
- custom_varcheck1.stdout == '1'
|
||||
- custom_varcheck2.stdout == '1'
|
||||
- custom_varcheck3.stdout == '0'
|
||||
- name: Check cron var value
|
||||
command: grep -c TESTVAR=newvalue {{ cron_config_path }}/cronvar_test
|
||||
register: custom_varcheck3
|
||||
failed_when: custom_varcheck3.rc == 0
|
||||
|
||||
- name: Ensure cronvar tasks did the right thing
|
||||
assert:
|
||||
that:
|
||||
- create_cronvar1 is changed
|
||||
- create_cronvar2 is not changed
|
||||
- create_cronvar3 is changed
|
||||
- remove_cronvar1 is changed
|
||||
- remove_cronvar2 is not changed
|
||||
- varcheck1.stdout == '1'
|
||||
- varcheck2.stdout == '1'
|
||||
- varcheck3.stdout == '0'
|
||||
- custom_remove_cronvar1 is changed
|
||||
- custom_remove_cronvar2 is not changed
|
||||
- custom_varcheck1.stdout == '1'
|
||||
- custom_varcheck2.stdout == '1'
|
||||
- custom_varcheck3.stdout == '0'
|
||||
|
||||
@@ -37,8 +37,6 @@
|
||||
command: >-
|
||||
dnf update -y
|
||||
--setopt=obsoletes=0 {{ _packages | join(' ') }}
|
||||
args:
|
||||
warn: false
|
||||
register: update_locked_packages
|
||||
changed_when: '"Nothing to do" not in update_locked_packages.stdout'
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
- name: "{{ reason }} ('up')"
|
||||
command: "curl -sf http://localhost:8082/hello"
|
||||
args:
|
||||
warn: false
|
||||
when: service_state == 'up'
|
||||
register: curl_result
|
||||
until: not curl_result.failed
|
||||
@@ -10,8 +8,6 @@
|
||||
|
||||
- name: "{{ reason }} ('down')"
|
||||
command: "curl -sf http://localhost:8082/hello"
|
||||
args:
|
||||
warn: false
|
||||
register: curl_result
|
||||
failed_when: curl_result == 0
|
||||
when: service_state == 'down'
|
||||
|
||||
@@ -43,6 +43,18 @@
|
||||
src: httpd_echo.py
|
||||
dest: "{{ process_file }}"
|
||||
|
||||
- name: Install virtualenv
|
||||
package:
|
||||
name: virtualenv
|
||||
state: present
|
||||
when: ansible_distribution == 'CentOS' and ansible_distribution_major_version == '8'
|
||||
|
||||
- name: Install virtualenv
|
||||
package:
|
||||
name: python-virtualenv
|
||||
state: present
|
||||
when: ansible_os_family == 'Archlinux'
|
||||
|
||||
- name: install dependencies
|
||||
pip:
|
||||
name: "{{ item }}"
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/usr/bin/env bash
|
||||
"$1" 100 &
|
||||
"$1" 100 &
|
||||
echo "$!" > "$2"
|
||||
|
||||
12
tests/integration/targets/pids/files/sleeper.c
Normal file
12
tests/integration/targets/pids/files/sleeper.c
Normal file
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* (c) 2022, Alexei Znamensky <russoz@gmail.com>
|
||||
* GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int delay = atoi(argv[1]);
|
||||
sleep(delay);
|
||||
}
|
||||
@@ -7,112 +7,105 @@
|
||||
# Copyright: (c) 2019, Saranya Sridharan
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
- when:
|
||||
- not (ansible_os_family == 'Alpine') # TODO
|
||||
block:
|
||||
- name: Attempt installation of latest 'psutil' version
|
||||
pip:
|
||||
name: psutil
|
||||
ignore_errors: true
|
||||
register: psutil_latest_install
|
||||
|
||||
- name: Attempt installation of latest 'psutil' version
|
||||
pip:
|
||||
name: psutil
|
||||
ignore_errors: true
|
||||
register: psutil_latest_install
|
||||
- name: Install greatest 'psutil' version which will work with all pip versions
|
||||
pip:
|
||||
name: psutil < 5.7.0
|
||||
when: psutil_latest_install is failed
|
||||
|
||||
- name: Install greatest 'psutil' version which will work with all pip versions
|
||||
pip:
|
||||
name: psutil < 5.7.0
|
||||
when: psutil_latest_install is failed
|
||||
- name: "Checking the empty result"
|
||||
pids:
|
||||
name: "blahblah"
|
||||
register: emptypids
|
||||
|
||||
- name: "Checking the empty result"
|
||||
pids:
|
||||
name: "blahblah"
|
||||
register: emptypids
|
||||
- name: "Verify that the list of Process IDs (PIDs) returned is empty"
|
||||
assert:
|
||||
that:
|
||||
- emptypids is not changed
|
||||
- emptypids.pids == []
|
||||
|
||||
- name: "Verify that the list of Process IDs (PIDs) returned is empty"
|
||||
assert:
|
||||
that:
|
||||
- emptypids is not changed
|
||||
- emptypids.pids == []
|
||||
- name: "Picking a random process name"
|
||||
set_fact:
|
||||
random_name: some-random-long-name-{{ 99999999 | random }}
|
||||
|
||||
- name: "Picking a random process name"
|
||||
set_fact:
|
||||
random_name: some-random-long-name-{{ 99999999 | random }}
|
||||
- name: Copy the fake 'sleep' source code
|
||||
copy:
|
||||
src: sleeper.c
|
||||
dest: "{{ remote_tmp_dir }}/sleeper.c"
|
||||
mode: 0644
|
||||
|
||||
- name: "finding the 'sleep' binary"
|
||||
command: which sleep
|
||||
register: find_sleep
|
||||
- name: Compile fake 'sleep' binary
|
||||
command: cc {{ remote_tmp_dir }}/sleeper.c -o {{ remote_tmp_dir }}/{{ random_name }}
|
||||
|
||||
- name: "Copying 'sleep' binary"
|
||||
command: cp {{ find_sleep.stdout }} {{ remote_tmp_dir }}/{{ random_name }}
|
||||
# The following does not work on macOS 11.1 (it uses shutil.copystat, and that will die with a PermissionError):
|
||||
# copy:
|
||||
# src: "{{ find_sleep.stdout }}"
|
||||
# dest: "{{ remote_tmp_dir }}/{{ random_name }}"
|
||||
# mode: "0777"
|
||||
# remote_src: true
|
||||
- name: Copy helper script
|
||||
copy:
|
||||
src: obtainpid.sh
|
||||
dest: "{{ remote_tmp_dir }}/obtainpid.sh"
|
||||
mode: 0755
|
||||
|
||||
- name: Copy helper script
|
||||
copy:
|
||||
src: obtainpid.sh
|
||||
dest: "{{ remote_tmp_dir }}/obtainpid.sh"
|
||||
- name: "Run the fake 'sleep' binary"
|
||||
command: "sh {{ remote_tmp_dir }}/obtainpid.sh '{{ remote_tmp_dir }}/{{ random_name }}' '{{ remote_tmp_dir }}/obtainpid.txt'"
|
||||
|
||||
- name: "Running the copy of 'sleep' binary"
|
||||
command: "sh {{ remote_tmp_dir }}/obtainpid.sh '{{ remote_tmp_dir }}/{{ random_name }}' '{{ remote_tmp_dir }}/obtainpid.txt'"
|
||||
async: 100
|
||||
poll: 0
|
||||
|
||||
async: 100
|
||||
poll: 0
|
||||
- name: "Wait for one second to make sure that the fake 'sleep' binary has actually been started"
|
||||
pause:
|
||||
seconds: 1
|
||||
|
||||
- name: "Wait for one second to make sure that the sleep copy has actually been started"
|
||||
pause:
|
||||
seconds: 1
|
||||
- name: "Checking the process IDs (PIDs) of fake 'sleep' binary"
|
||||
pids:
|
||||
name: "{{ random_name }}"
|
||||
register: pids
|
||||
|
||||
- name: "Checking the process IDs (PIDs) of sleep binary"
|
||||
pids:
|
||||
name: "{{ random_name }}"
|
||||
register: pids
|
||||
- name: "Checking that exact non-substring matches are required"
|
||||
pids:
|
||||
name: "{{ random_name[0:5] }}"
|
||||
register: exactpidmatch
|
||||
|
||||
- name: "Checking that exact non-substring matches are required"
|
||||
pids:
|
||||
name: "{{ random_name[0:5] }}"
|
||||
register: exactpidmatch
|
||||
- name: "Checking that patterns can be used with the pattern option"
|
||||
pids:
|
||||
pattern: "{{ random_name[0:5] }}"
|
||||
register: pattern_pid_match
|
||||
|
||||
- name: "Checking that patterns can be used with the pattern option"
|
||||
pids:
|
||||
pattern: "{{ random_name[0:5] }}"
|
||||
register: pattern_pid_match
|
||||
- name: "Checking that case-insensitive patterns can be used with the pattern option"
|
||||
pids:
|
||||
pattern: "{{ random_name[0:5] | upper }}"
|
||||
ignore_case: true
|
||||
register: caseinsensitive_pattern_pid_match
|
||||
|
||||
- name: "Checking that case-insensitive patterns can be used with the pattern option"
|
||||
pids:
|
||||
pattern: "{{ random_name[0:5] | upper }}"
|
||||
ignore_case: true
|
||||
register: caseinsensitive_pattern_pid_match
|
||||
- name: "Checking that .* includes test pid"
|
||||
pids:
|
||||
pattern: .*
|
||||
register: match_all
|
||||
|
||||
- name: "Checking that .* includes test pid"
|
||||
pids:
|
||||
pattern: .*
|
||||
register: match_all
|
||||
- name: "Reading pid from the file"
|
||||
slurp:
|
||||
src: "{{ remote_tmp_dir }}/obtainpid.txt"
|
||||
register: newpid
|
||||
|
||||
- name: "Reading pid from the file"
|
||||
slurp:
|
||||
src: "{{ remote_tmp_dir }}/obtainpid.txt"
|
||||
register: newpid
|
||||
- name: "Verify that the Process IDs (PIDs) returned is not empty and also equal to the PIDs obtained in console"
|
||||
assert:
|
||||
that:
|
||||
- "pids.pids | join(' ') == newpid.content | b64decode | trim"
|
||||
- "pids.pids | length > 0"
|
||||
- "exactpidmatch.pids == []"
|
||||
- "pattern_pid_match.pids | join(' ') == newpid.content | b64decode | trim"
|
||||
- "caseinsensitive_pattern_pid_match.pids | join(' ') == newpid.content | b64decode | trim"
|
||||
- newpid.content | b64decode | trim | int in match_all.pids
|
||||
|
||||
- name: "Verify that the Process IDs (PIDs) returned is not empty and also equal to the PIDs obtained in console"
|
||||
assert:
|
||||
that:
|
||||
- "pids.pids | join(' ') == newpid.content | b64decode | trim"
|
||||
- "pids.pids | length > 0"
|
||||
- "exactpidmatch.pids == []"
|
||||
- "pattern_pid_match.pids | join(' ') == newpid.content | b64decode | trim"
|
||||
- "caseinsensitive_pattern_pid_match.pids | join(' ') == newpid.content | b64decode | trim"
|
||||
- newpid.content | b64decode | trim | int in match_all.pids
|
||||
- name: "Register output of bad input pattern"
|
||||
pids:
|
||||
pattern: (unterminated
|
||||
register: bad_pattern_result
|
||||
ignore_errors: true
|
||||
|
||||
- name: "Register output of bad input pattern"
|
||||
pids:
|
||||
pattern: (unterminated
|
||||
register: bad_pattern_result
|
||||
ignore_errors: true
|
||||
|
||||
- name: "Verify that bad input pattern result is failed"
|
||||
assert:
|
||||
that:
|
||||
- bad_pattern_result is failed
|
||||
- name: "Verify that bad input pattern result is failed"
|
||||
assert:
|
||||
that:
|
||||
- bad_pattern_result is failed
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
- '{{ pkgng_test_outofdate_pkg_tempdir.path }}'
|
||||
- '--manifest'
|
||||
- '{{ pkgng_test_outofdate_pkg_tempdir.path }}/MANIFEST'
|
||||
warn: no
|
||||
|
||||
# pkg switched from .txz to .pkg in version 1.17.0
|
||||
# Might as well look for all valid pkg extensions.
|
||||
|
||||
@@ -32,8 +32,6 @@
|
||||
until: faketime_package_installed is success
|
||||
- name: Find libfaketime path
|
||||
shell: '{{ list_pkg_files }} {{ faketime_pkg }} | grep -F libfaketime.so.1'
|
||||
args:
|
||||
warn: false
|
||||
register: libfaketime_path
|
||||
- when: ansible_service_mgr == 'systemd'
|
||||
block:
|
||||
|
||||
@@ -165,8 +165,6 @@
|
||||
|
||||
- name: Reinstall internationalization files
|
||||
shell: yum -y reinstall glibc-common || yum -y install glibc-common
|
||||
args:
|
||||
warn: false
|
||||
when: locale_present is failed
|
||||
|
||||
- name: Generate locale (RedHat)
|
||||
|
||||
@@ -102,6 +102,21 @@
|
||||
src: "{{ alt_sudoers_path }}/my-sudo-rule-5"
|
||||
register: rule_5_contents
|
||||
|
||||
- name: Create rule to runas another user
|
||||
community.general.sudoers:
|
||||
name: my-sudo-rule-6
|
||||
state: present
|
||||
user: alice
|
||||
commands: /usr/local/bin/command
|
||||
runas: bob
|
||||
sudoers_path: "{{ sudoers_path }}"
|
||||
register: rule_6
|
||||
|
||||
- name: Grab contents of my-sudo-rule-6 (in alternative directory)
|
||||
ansible.builtin.slurp:
|
||||
src: "{{ sudoers_path }}/my-sudo-rule-6"
|
||||
register: rule_6_contents
|
||||
|
||||
|
||||
- name: Revoke rule 1
|
||||
community.general.sudoers:
|
||||
@@ -133,6 +148,7 @@
|
||||
- "rule_3_contents['content'] | b64decode == 'alice ALL= /usr/local/bin/command\n'"
|
||||
- "rule_4_contents['content'] | b64decode == '%students ALL=NOPASSWD: /usr/local/bin/command\n'"
|
||||
- "rule_5_contents['content'] | b64decode == 'alice ALL=NOPASSWD: /usr/local/bin/command\n'"
|
||||
- "rule_6_contents['content'] | b64decode == 'alice ALL=(bob)NOPASSWD: /usr/local/bin/command\n'"
|
||||
|
||||
- name: Check stats
|
||||
ansible.builtin.assert:
|
||||
|
||||
@@ -14,7 +14,7 @@ def main():
|
||||
"""Main entry point."""
|
||||
if not os.path.isdir(os.path.join('docs', 'docsite')):
|
||||
return
|
||||
p = subprocess.run(['antsibull-lint', 'collection-docs', '.'], check=False)
|
||||
p = subprocess.run(['antsibull-docs', 'lint-collection-docs', '.'], check=False)
|
||||
if p.returncode not in (0, 3):
|
||||
print('{0}:0:0: unexpected return code {1}'.format(sys.argv[0], p.returncode))
|
||||
|
||||
|
||||
50
tests/sanity/ignore-2.14.txt
Normal file
50
tests/sanity/ignore-2.14.txt
Normal file
@@ -0,0 +1,50 @@
|
||||
.azure-pipelines/scripts/publish-codecov.py replace-urlopen
|
||||
plugins/modules/cloud/lxc/lxc_container.py use-argspec-type-path
|
||||
plugins/modules/cloud/lxc/lxc_container.py validate-modules:use-run-command-not-popen
|
||||
plugins/modules/cloud/misc/rhevm.py validate-modules:parameter-state-invalid-choice
|
||||
plugins/modules/cloud/rackspace/rax.py use-argspec-type-path # fix needed
|
||||
plugins/modules/cloud/rackspace/rax_files.py validate-modules:parameter-state-invalid-choice
|
||||
plugins/modules/cloud/rackspace/rax_files_objects.py use-argspec-type-path
|
||||
plugins/modules/cloud/rackspace/rax_scaling_group.py use-argspec-type-path # fix needed, expanduser() applied to dict values
|
||||
plugins/modules/cloud/scaleway/scaleway_organization_info.py validate-modules:return-syntax-error
|
||||
plugins/modules/cloud/smartos/vmadm.py validate-modules:parameter-type-not-in-doc
|
||||
plugins/modules/cloud/smartos/vmadm.py validate-modules:undocumented-parameter
|
||||
plugins/modules/cloud/spotinst/spotinst_aws_elastigroup.py validate-modules:parameter-list-no-elements
|
||||
plugins/modules/cloud/spotinst/spotinst_aws_elastigroup.py validate-modules:parameter-type-not-in-doc
|
||||
plugins/modules/cloud/spotinst/spotinst_aws_elastigroup.py validate-modules:undocumented-parameter
|
||||
plugins/modules/cloud/univention/udm_share.py validate-modules:parameter-list-no-elements
|
||||
plugins/modules/cloud/univention/udm_user.py validate-modules:parameter-list-no-elements
|
||||
plugins/modules/clustering/consul/consul.py validate-modules:doc-missing-type
|
||||
plugins/modules/clustering/consul/consul.py validate-modules:undocumented-parameter
|
||||
plugins/modules/clustering/consul/consul_session.py validate-modules:parameter-state-invalid-choice
|
||||
plugins/modules/packaging/language/composer.py validate-modules:parameter-invalid # invalid alias - removed in 5.0.0
|
||||
plugins/modules/packaging/language/yarn.py use-argspec-type-path
|
||||
plugins/modules/packaging/os/apt_rpm.py validate-modules:parameter-invalid # invalid alias - removed in 5.0.0
|
||||
plugins/modules/packaging/os/homebrew.py validate-modules:parameter-invalid # invalid alias - removed in 5.0.0
|
||||
plugins/modules/packaging/os/homebrew_cask.py validate-modules:parameter-invalid # invalid alias - removed in 5.0.0
|
||||
plugins/modules/packaging/os/opkg.py validate-modules:parameter-invalid # invalid alias - removed in 5.0.0
|
||||
plugins/modules/packaging/os/pacman.py validate-modules:parameter-invalid # invalid alias - removed in 5.0.0
|
||||
plugins/modules/packaging/os/redhat_subscription.py validate-modules:return-syntax-error
|
||||
plugins/modules/packaging/os/slackpkg.py validate-modules:parameter-invalid # invalid alias - removed in 5.0.0
|
||||
plugins/modules/packaging/os/urpmi.py validate-modules:parameter-invalid # invalid alias - removed in 5.0.0
|
||||
plugins/modules/packaging/os/xbps.py validate-modules:parameter-invalid # invalid alias - removed in 5.0.0
|
||||
plugins/modules/remote_management/hpilo/hpilo_boot.py validate-modules:parameter-type-not-in-doc
|
||||
plugins/modules/remote_management/hpilo/hpilo_info.py validate-modules:parameter-type-not-in-doc
|
||||
plugins/modules/remote_management/hpilo/hponcfg.py validate-modules:parameter-type-not-in-doc
|
||||
plugins/modules/remote_management/manageiq/manageiq_policies.py validate-modules:parameter-state-invalid-choice
|
||||
plugins/modules/remote_management/manageiq/manageiq_provider.py validate-modules:doc-choices-do-not-match-spec # missing docs on suboptions
|
||||
plugins/modules/remote_management/manageiq/manageiq_provider.py validate-modules:doc-missing-type # missing docs on suboptions
|
||||
plugins/modules/remote_management/manageiq/manageiq_provider.py validate-modules:parameter-type-not-in-doc # missing docs on suboptions
|
||||
plugins/modules/remote_management/manageiq/manageiq_provider.py validate-modules:undocumented-parameter # missing docs on suboptions
|
||||
plugins/modules/remote_management/manageiq/manageiq_tags.py validate-modules:parameter-state-invalid-choice
|
||||
plugins/modules/source_control/github/github_deploy_key.py validate-modules:parameter-invalid
|
||||
plugins/modules/system/gconftool2.py validate-modules:parameter-state-invalid-choice
|
||||
plugins/modules/system/iptables_state.py validate-modules:undocumented-parameter
|
||||
plugins/modules/system/osx_defaults.py validate-modules:parameter-state-invalid-choice
|
||||
plugins/modules/system/parted.py validate-modules:parameter-state-invalid-choice
|
||||
plugins/modules/system/puppet.py use-argspec-type-path
|
||||
plugins/modules/system/puppet.py validate-modules:parameter-invalid # invalid alias - removed in 7.0.0
|
||||
plugins/modules/system/ssh_config.py use-argspec-type-path # Required since module uses other methods to specify path
|
||||
plugins/modules/system/xfconf.py validate-modules:parameter-state-invalid-choice # state get removed in 5.0.0
|
||||
plugins/modules/system/xfconf.py validate-modules:return-syntax-error
|
||||
plugins/modules/web_infrastructure/jenkins_plugin.py use-argspec-type-path
|
||||
@@ -169,6 +169,17 @@ TESTCASE_ETHERNET_ADD_IPV6_INT_WITH_ROUTE = [
|
||||
'state': 'present',
|
||||
'_ansible_check_mode': False,
|
||||
},
|
||||
{
|
||||
'type': 'ethernet',
|
||||
'conn_name': 'non_existent_nw_device',
|
||||
'ifname': 'ethernet_non_existant',
|
||||
'ip6': '2001:beef:cafe:10::1/64',
|
||||
'routes6_extended': [{'ip': 'fd2e:446f:d85d:5::/64',
|
||||
'next_hop': '2001:beef:cafe:10::2'}],
|
||||
'method6': 'manual',
|
||||
'state': 'present',
|
||||
'_ansible_check_mode': False,
|
||||
},
|
||||
]
|
||||
|
||||
TESTCASE_ETHERNET_ADD_IPV6_INT_WITH_ROUTE_SHOW_OUTPUT = """\
|
||||
@@ -197,6 +208,14 @@ TESTCASE_ETHERNET_MOD_IPV4_INT_WITH_ROUTE_AND_METRIC = [
|
||||
'state': 'present',
|
||||
'_ansible_check_mode': False,
|
||||
},
|
||||
{
|
||||
'type': 'ethernet',
|
||||
'conn_name': 'non_existent_nw_device',
|
||||
'routes4_extended': [{'ip': '192.168.200.0/24', 'next_hop': '192.168.1.1'}],
|
||||
'route_metric4': 10,
|
||||
'state': 'present',
|
||||
'_ansible_check_mode': False,
|
||||
},
|
||||
]
|
||||
|
||||
TESTCASE_ETHERNET_MOD_IPV4_INT_WITH_ROUTE_AND_METRIC_SHOW_OUTPUT = """\
|
||||
@@ -218,6 +237,14 @@ TESTCASE_ETHERNET_MOD_IPV6_INT_WITH_ROUTE_AND_METRIC = [
|
||||
'state': 'present',
|
||||
'_ansible_check_mode': False,
|
||||
},
|
||||
{
|
||||
'type': 'ethernet',
|
||||
'conn_name': 'non_existent_nw_device',
|
||||
'routes6_extended': [{'ip': 'fd2e:446f:d85d:5::/64', 'next_hop': '2001:beef:cafe:10::2'}],
|
||||
'route_metric6': 10,
|
||||
'state': 'present',
|
||||
'_ansible_check_mode': False,
|
||||
},
|
||||
]
|
||||
|
||||
TESTCASE_ETHERNET_MOD_IPV6_INT_WITH_ROUTE_AND_METRIC_SHOW_OUTPUT = """\
|
||||
@@ -241,6 +268,17 @@ TESTCASE_ETHERNET_ADD_IPV6_INT_WITH_MULTIPLE_ROUTES = [
|
||||
'state': 'present',
|
||||
'_ansible_check_mode': False,
|
||||
},
|
||||
{
|
||||
'type': 'ethernet',
|
||||
'conn_name': 'non_existent_nw_device',
|
||||
'ifname': 'ethernet_non_existant',
|
||||
'ip6': '2001:beef:cafe:10::1/64',
|
||||
'routes6_extended': [{'ip': 'fd2e:446f:d85d:5::/64', 'next_hop': '2001:beef:cafe:10::2'},
|
||||
{'ip': 'fd2e:8890:abcd:25::/64', 'next_hop': '2001:beef:cafe:10::5'}],
|
||||
'method6': 'manual',
|
||||
'state': 'present',
|
||||
'_ansible_check_mode': False,
|
||||
},
|
||||
]
|
||||
|
||||
TESTCASE_ETHERNET_ADD_IPV6_INT_WITH_MULTIPLE_ROUTES_SHOW_OUTPUT = """\
|
||||
@@ -273,6 +311,18 @@ TESTCASE_ETHERNET_ADD_IPV6_INT_WITH_ROUTE_AND_METRIC = [
|
||||
'state': 'present',
|
||||
'_ansible_check_mode': False,
|
||||
},
|
||||
{
|
||||
'type': 'ethernet',
|
||||
'conn_name': 'non_existent_nw_device',
|
||||
'ifname': 'ethernet_non_existant',
|
||||
'method4': 'disabled',
|
||||
'ip6': '2001:beef:cafe:10::1/64',
|
||||
'routes6_extended': [{'ip': 'fd2e:446f:d85d:5::/64', 'next_hop': '2001:beef:cafe:10::2'}],
|
||||
'route_metric6': 5,
|
||||
'method6': 'manual',
|
||||
'state': 'present',
|
||||
'_ansible_check_mode': False,
|
||||
},
|
||||
]
|
||||
|
||||
TESTCASE_ETHERNET_ADD_IPV6_INT_WITH_ROUTE_AND_METRIC_SHOW_OUTPUT = """\
|
||||
@@ -305,6 +355,19 @@ TESTCASE_ETHERNET_ADD_IPV6_INT_WITH_MULTIPLE_ROUTES_AND_METRIC = [
|
||||
'state': 'present',
|
||||
'_ansible_check_mode': False,
|
||||
},
|
||||
{
|
||||
'type': 'ethernet',
|
||||
'conn_name': 'non_existent_nw_device',
|
||||
'ifname': 'ethernet_non_existant',
|
||||
'method4': 'disabled',
|
||||
'ip6': '2001:beef:cafe:10::1/64',
|
||||
'routes6_extended': [{'ip': 'fd2e:446f:d85d:5::/64', 'next_hop': '2001:beef:cafe:10::2'},
|
||||
{'ip': 'fd2e:8890:abcd:25::/64', 'next_hop': '2001:beef:cafe:10::5'}],
|
||||
'route_metric6': 5,
|
||||
'method6': 'manual',
|
||||
'state': 'present',
|
||||
'_ansible_check_mode': False,
|
||||
},
|
||||
]
|
||||
|
||||
TESTCASE_ETHERNET_ADD_IPV6_INT_WITH_MULTIPLE_ROUTES_AND_METRIC_SHOW_OUTPUT = """\
|
||||
|
||||
Reference in New Issue
Block a user