Compare commits
113 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0f8a689f95 | |||
| d8b05366b6 | |||
| fca7eec760 | |||
| 948f82c6f4 | |||
| 9b65337e5c | |||
| abc7d4d50a | |||
| 6da8fedda5 | |||
| 78da8341a5 | |||
| d61d285a6f | |||
| 115b919cfd | |||
| eb6a2408db | |||
| 5cc5be07fb | |||
| 4f8c82ca2c | |||
| 7774b37626 | |||
| b550320f14 | |||
| caf7885ef4 | |||
| c41c3b4160 | |||
| 05c6df53f0 | |||
| fac1405bcc | |||
| 992f4468d6 | |||
| a0a7fd0297 | |||
| 2f66f26991 | |||
| 67c117607c | |||
| 1cf3df53f0 | |||
| 5dd58923a1 | |||
| 615d53ab42 | |||
| d8111b39a4 | |||
| a95ba6ca39 | |||
| 7b93d6e31b | |||
| c46d799dc5 | |||
| a40241f99b | |||
| 503c4b9f97 | |||
| 9693c67197 | |||
| c68e1aafeb | |||
| d2268dcb37 | |||
| 6db2c6d9ac | |||
| 3dd444d170 | |||
| 0bd9a73d47 | |||
| 70c011481a | |||
| 8d86820308 | |||
| 116554bee3 | |||
| ab2f54d864 | |||
| 441dfd4cbe | |||
| d779f25fa1 | |||
| f5c9977771 | |||
| d2eb4c59d3 | |||
| 3dbb029f18 | |||
| 2fbee1d183 | |||
| 30250f34ea | |||
| 73cfb88d0f | |||
| 96ab73a874 | |||
| 6870700a23 | |||
| f46998e0bd | |||
| da79fdf998 | |||
| 623cafb709 | |||
| d5a84098a8 | |||
| fcaf69f312 | |||
| 0172576fb7 | |||
| 18ecb90c7c | |||
| eb520de517 | |||
| 414c72508d | |||
| 9b594f9dec | |||
| 2e60b83cd5 | |||
| 2042830bab | |||
| 46f189d937 | |||
| 36414cc518 | |||
| 6e90277c69 | |||
| 85626291bf | |||
| e7740b80fd | |||
| 395b30c617 | |||
| 0be82f2b08 | |||
| fe2c9cdf13 | |||
| c12f41907a | |||
| e889001b3a | |||
| e23eefe60e | |||
| 5ee5e4a35c | |||
| a23dfce2c9 | |||
| c3e1269ecb | |||
| e0e266d070 | |||
| daac0514ef | |||
| 28632756d6 | |||
| 643726860b | |||
| e459893601 | |||
| 66de502798 | |||
| 8d7c4140db | |||
| d1621ba67d | |||
| 1a14c50ab7 | |||
| 3f9a08b056 | |||
| 25f656413d | |||
| 135b85e0b7 | |||
| 268beeb9f9 | |||
| 47f3f4ebe3 | |||
| cd8208a4aa | |||
| e750df9c8a | |||
| 948ef78c28 | |||
| 67f3c27a3b | |||
| 0b39c66db7 | |||
| 1eae95d919 | |||
| 520a2bf89a | |||
| 1565eabe3e | |||
| 9b5dd1ead3 | |||
| ffe364dd5d | |||
| 312b821501 | |||
| dfdc82eb2a | |||
| 305adf92ab | |||
| 341e1a0f64 | |||
| 5207991748 | |||
| 35bbcd737a | |||
| 21c4eb8db5 | |||
| cadccbe323 | |||
| 864ddbdb5c | |||
| bce9d21945 | |||
| 6c448e38e1 |
@ -1,11 +1,15 @@
|
||||
prune hacking
|
||||
include README.rst
|
||||
include COPYING
|
||||
include SYMLINK_CACHE.json
|
||||
include requirements.txt
|
||||
include .coveragerc
|
||||
include shippable.yml
|
||||
include tox.ini
|
||||
include bin/ansible-test
|
||||
include examples/hosts
|
||||
include examples/ansible.cfg
|
||||
include examples/scripts/ConfigureRemotingForAnsible.ps1
|
||||
include examples/scripts/upgrade_to_ps3.ps1
|
||||
recursive-include lib/ansible/module_utils/powershell *
|
||||
recursive-include lib/ansible/modules *
|
||||
recursive-include lib/ansible/galaxy/data *
|
||||
|
||||
2
Makefile
2
Makefile
@ -122,7 +122,7 @@ ifneq ($(REPOTAG),)
|
||||
endif
|
||||
|
||||
# ansible-test parameters
|
||||
ANSIBLE_TEST ?= test/runner/ansible-test
|
||||
ANSIBLE_TEST ?= bin/ansible-test
|
||||
TEST_FLAGS ?=
|
||||
|
||||
# ansible-test units parameters (make test / make test-py3)
|
||||
|
||||
@ -263,6 +263,8 @@ def main():
|
||||
|
||||
if rc == 0:
|
||||
process.run()
|
||||
else:
|
||||
process.shutdown()
|
||||
|
||||
sys.exit(rc)
|
||||
|
||||
|
||||
15
bin/ansible-test
Executable file
15
bin/ansible-test
Executable file
@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env python
|
||||
# PYTHON_ARGCOMPLETE_OK
|
||||
"""Primary entry point for ansible-test."""
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'test', 'runner')))
|
||||
import lib.cli
|
||||
lib.cli.main()
|
||||
@ -456,3 +456,54 @@ releases:
|
||||
- win_wait_for-timeout.yaml
|
||||
- zypper-on-ubuntu.yaml
|
||||
release_date: '2018-09-06'
|
||||
2.6.5:
|
||||
codename: Heartbreaker
|
||||
fragments:
|
||||
- 45203-sysvinit-fix-unassigned-variable.yml
|
||||
- 45628-fetch_url-error-headers.yaml
|
||||
- 45700-docker_container-dont-parse-absent.yml
|
||||
- ajson-nested-decode.yaml
|
||||
- azure_rm_deployment_fix_45941.yaml
|
||||
- dd-put-empty-files.yaml
|
||||
- docker_container-idempotency.yaml
|
||||
- ec2_group_fix_target_containing_list_within_list.yaml
|
||||
- fix_44984.yaml
|
||||
- fix_ec2_group_target_vpc_precedence.yaml
|
||||
- fix_ec2_group_vpc_precedence_classic.yaml
|
||||
- fix_vyos_facts.yaml
|
||||
- get-url-fix-idempotency.yaml
|
||||
- loop_undefined_delegate_to.yaml
|
||||
- mysql-migrate_to_pymysql.yaml
|
||||
- nxos_ambiguous_command_check.yaml
|
||||
- nxos_facts_26.yaml
|
||||
- nxos_terminal_plugin.yaml
|
||||
- piped-transfer-empty-files.yaml
|
||||
- plugin-filters-cfg.yaml
|
||||
- script-module-no-file-path.yaml
|
||||
- v2.6.5_summary.yaml
|
||||
- win_copy-dest-quote.yaml
|
||||
- win_group_membership-com-marshal.yaml
|
||||
- win_keep_remote_file_python26.yaml
|
||||
- win_say-fix.yaml
|
||||
- winrm_pexpect.yaml
|
||||
release_date: '2018-09-28'
|
||||
2.6.6:
|
||||
codename: Heartbreaker
|
||||
fragments:
|
||||
- 34863-rabbitmq_user_unpack_fix.yaml
|
||||
- 44755-win_nssm_fixes.yaml
|
||||
- 45921-os_router-ignores-enable_snat-no.yml
|
||||
- 46049-route53-caa-ordering.txt
|
||||
- 46245-systemd-fix-service-enable-logic.yaml
|
||||
- 46322-docker_container-image-not-given.yaml
|
||||
- 46594-docker_container-publish-all-ports.yml
|
||||
- 46595-docker_container-expected_ports.yml
|
||||
- azure-version.yaml
|
||||
- blockinfile-bytes-fix.yaml
|
||||
- callback_fixes.yml
|
||||
- fix_flatten.yml
|
||||
- free-strategy-include-var-tags.yaml
|
||||
- lineinfile-insertbefore-index-out-of-range.yaml
|
||||
- user-docs-underlying-tools.yaml
|
||||
- v2.6.6_summary.yaml
|
||||
release_date: '2018-10-19'
|
||||
|
||||
@ -2,6 +2,93 @@
|
||||
Ansible 2.6 "Heartbreaker" Release Notes
|
||||
========================================
|
||||
|
||||
v2.6.6
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
| Release Date: 2018-10-19
|
||||
| `Porting Guide <https://docs.ansible.com/ansible/devel/porting_guides.html>`_
|
||||
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- win_nssm - Drop support of literal YAML dictionnary for ``app_parameters`` option. Use the ``key=value;`` string form instead
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Ignore empty result of rabbitmqctl list_user_permissions.
|
||||
- In systemd module, fix check if a systemd+initd service is enabled - disabled in systemd means disabled
|
||||
- Update callbacks to use Ansible's JSON encoder to avoid known serialization issues
|
||||
- blockinfile - use bytes rather than a native string to prevent a stacktrace in Python 3 when writing to the file (https://github.com/ansible/ansible/issues/46237)
|
||||
- docker_container - ``publish_ports: all`` was not used correctly when checking idempotency.
|
||||
- docker_container - fix idempotency check for published_ports in some special cases.
|
||||
- docker_container - the behavior is improved in case ``image`` is not specified, but needed for (re-)creating the container.
|
||||
- dynamic includes - Use the copied and merged task for calculating task vars in the free strategy (https://github.com/ansible/ansible/issues/47024)
|
||||
- fix flatten to properly handle multiple lists in lists https://github.com/ansible/ansible/issues/46343
|
||||
- lineinfile - fix index out of range error when using insertbefore on a file with only one line (https://github.com/ansible/ansible/issues/46043)
|
||||
- os_router - ``enable_snat: no`` was ignored.
|
||||
- route53 - fix CAA record ordering for idempotency.
|
||||
- use proper module_util to get Ansible version for Azure requests
|
||||
- user - add documentation on what underlying tools are used on each platform (https://github.com/ansible/ansible/issues/44266)
|
||||
- win_nssm - Add missing space between parameters with ``app_parameters``
|
||||
- win_nssm - Correctly escape argument line when a parameter contains spaces, quotes or backslashes
|
||||
- win_nssm - Fix error when several services were given to the ``dependencies`` option
|
||||
- win_nssm - Fix extra space added in argument line with ``app_parameters`` or ``app_parameters_free_form`` when a parameter start by a dash and is followed by a period (https://github.com/ansible/ansible/issues/44079)
|
||||
- win_nssm - Fix service not started when ``state=started`` (https://github.com/ansible/ansible/issues/35442)
|
||||
- win_nssm - Fix several issues and idempotency problems (https://github.com/ansible/ansible/pull/44755)
|
||||
|
||||
v2.6.5
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
| Release Date: 2018-09-28
|
||||
| `Porting Guide <https://docs.ansible.com/ansible/devel/porting_guides.html>`_
|
||||
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Add ambiguous command check as the error message is not persistent on nexus devices (https://github.com/ansible/ansible/pull/45337).
|
||||
- Ansible JSON Decoder - Switch from decode to object_hook to support nested use of __ansible_vault and __ansible_unsafe (https://github.com/ansible/ansible/pull/45514)
|
||||
- Don't parse parameters and options when ``state`` is ``absent`` (https://github.com/ansible/ansible/pull/45700).
|
||||
- Fix python2.6 `nothing to repeat` nxos terminal plugin bug (https://github.com/ansible/ansible/pull/45271).
|
||||
- Fix referenced before assignment in sysvinit module
|
||||
- PLUGIN_FILTERS_CFG - Ensure that the value is treated as type=path, and that we use the standard section of ``defaults`` instead of ``default`` (https://github.com/ansible/ansible/pull/45994)
|
||||
- The patch fixing the regression of no longer preferring matching security groups in the same VPC https://github.com/ansible/ansible/pull/45787 (which was also backported to 2.6) broke EC2-Classic accounts. https://github.com/ansible/ansible/pull/46242 removes the assumption that security groups must be in a VPC.
|
||||
- azure_rm_deployment - fixed regression that prevents resource group from being created (https://github.com/ansible/ansible/issues/45941)
|
||||
- chroot connection - Support empty files with copying to target (https://github.com/ansible/ansible/issues/36725)
|
||||
- cloudfront - fix bug when CloudFrontOriginAccessIdentityList is missing (https://github.com/ansible/ansible/pull/44984)
|
||||
- docker connection - Support empty files with copying to target (https://github.com/ansible/ansible/issues/36725)
|
||||
- docker_container - Fix idempotency problems with ``cap_drop`` and ``groups`` (when numeric group IDs were used).
|
||||
- docker_container - Fix type conversion errors for ``log_options``.
|
||||
- docker_container - Fixing various comparison/idempotency problems related to wrong comparisons. In particular, comparisons for ``command`` and ``entrypoint`` (both lists) no longer ignore missing elements during idempotency checks.
|
||||
- docker_container - Makes ``blkio_weight``, ``cpuset_mems``, ``dns_opts`` and ``uts`` options actually work.
|
||||
- ec2_group - Sanitize the ingress and egress rules before operating on them by flattening any lists within lists describing the target CIDR(s) into a list of strings. Prior to Ansible 2.6 the ec2_group module accepted a list of strings, a list of lists, or a combination of strings and lists within a list. https://github.com/ansible/ansible/pull/45594
|
||||
- ec2_group - There can be multiple security groups with the same name in different VPCs. Prior to 2.6 if a target group name was provided, the group matching the name and VPC had highest precedence. Restore this behavior by updated the dictionary with the groups matching the VPC last.
|
||||
- fetch_url did not always return lower-case header names in case of HTTP errors (https://github.com/ansible/ansible/pull/45628).
|
||||
- fix nxos_facts indefinite hang for text based output (https://github.com/ansible/ansible/pull/45845).
|
||||
- get_url - Don't re-download files unnecessarily when force=no (https://github.com/ansible/ansible/issues/45491)
|
||||
- jail connection - Support empty files with copying to target (https://github.com/ansible/ansible/issues/36725)
|
||||
- kubectl connection - Support empty files with copying to target (https://github.com/ansible/ansible/issues/36725)
|
||||
- libvirt_lxc connection - Support empty files with copying to target (https://github.com/ansible/ansible/issues/36725)
|
||||
- loop - Ensure that a loop with a when condition that evaluates to false and delegate_to, will short circuit if the loop references an undefined variable. This matches the behavior in the same scenario without delegate_to (https://github.com/ansible/ansible/issues/45189)
|
||||
- mysql_*, proxysql_* - PyMySQL (a pure-Python MySQL driver) is now a preferred dependency also supporting Python 3.X.
|
||||
- powershell - Fix issue where setting ANSIBLE_KEEP_REMOTE_FILES fails when using Python 2.6 - https://github.com/ansible/ansible/issues/45490
|
||||
- script inventory plugin - Don't pass file_name to DataLoader.load, which will prevent misleading error messages (https://github.com/ansible/ansible/issues/34164)
|
||||
- ssh connection - Support empty files with piped transfer_method (https://github.com/ansible/ansible/issues/45426)
|
||||
- vyos_facts - fix vyos_facts not returning version number issue (https://github.com/ansible/ansible/pull/39115)
|
||||
- win_copy - Fix issue where the dest return value would be enclosed in single quote when dest is a folder - https://github.com/ansible/ansible/issues/45281
|
||||
- win_group_membership - fix intermittent issue where it failed to convert the ADSI object to the .NET object after using it once
|
||||
- win_say - fix syntax error in module and get tests working
|
||||
- winrm - Only use pexpect for auto kerb auth if it is installed and contains the required kwargs - https://github.com/ansible/ansible/issues/43462
|
||||
- zone connection - Support empty files with copying to target (https://github.com/ansible/ansible/issues/36725)
|
||||
|
||||
v2.6.4
|
||||
======
|
||||
|
||||
|
||||
2
changelogs/fragments/34863-rabbitmq_user_unpack_fix.yaml
Normal file
2
changelogs/fragments/34863-rabbitmq_user_unpack_fix.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- Ignore empty result of rabbitmqctl list_user_permissions.
|
||||
10
changelogs/fragments/44755-win_nssm_fixes.yaml
Normal file
10
changelogs/fragments/44755-win_nssm_fixes.yaml
Normal file
@ -0,0 +1,10 @@
|
||||
bugfixes:
|
||||
- win_nssm - Fix several issues and idempotency problems (https://github.com/ansible/ansible/pull/44755)
|
||||
- win_nssm - Fix service not started when ``state=started`` (https://github.com/ansible/ansible/issues/35442)
|
||||
- win_nssm - Correctly escape argument line when a parameter contains spaces, quotes or backslashes
|
||||
- win_nssm - Add missing space between parameters with ``app_parameters``
|
||||
- win_nssm - Fix extra space added in argument line with ``app_parameters`` or ``app_parameters_free_form`` when a parameter start by a dash and is followed by a period (https://github.com/ansible/ansible/issues/44079)
|
||||
- win_nssm - Fix error when several services were given to the ``dependencies`` option
|
||||
|
||||
minor_changes:
|
||||
- win_nssm - Drop support of literal YAML dictionnary for ``app_parameters`` option. Use the ``key=value;`` string form instead
|
||||
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- Fix referenced before assignment in sysvinit module
|
||||
2
changelogs/fragments/45628-fetch_url-error-headers.yaml
Normal file
2
changelogs/fragments/45628-fetch_url-error-headers.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- "fetch_url did not always return lower-case header names in case of HTTP errors (https://github.com/ansible/ansible/pull/45628)."
|
||||
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- "Don't parse parameters and options when ``state`` is ``absent`` (https://github.com/ansible/ansible/pull/45700)."
|
||||
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- "os_router - ``enable_snat: no`` was ignored."
|
||||
2
changelogs/fragments/46049-route53-caa-ordering.txt
Normal file
2
changelogs/fragments/46049-route53-caa-ordering.txt
Normal file
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- "route53 - fix CAA record ordering for idempotency."
|
||||
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- In systemd module, fix check if a systemd+initd service is enabled - disabled in systemd means disabled
|
||||
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- "docker_container - the behavior is improved in case ``image`` is not specified, but needed for (re-)creating the container."
|
||||
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- "docker_container - ``publish_ports: all`` was not used correctly when checking idempotency."
|
||||
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- "docker_container - fix idempotency check for published_ports in some special cases."
|
||||
2
changelogs/fragments/ajson-nested-decode.yaml
Normal file
2
changelogs/fragments/ajson-nested-decode.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- Ansible JSON Decoder - Switch from decode to object_hook to support nested use of __ansible_vault and __ansible_unsafe (https://github.com/ansible/ansible/pull/45514)
|
||||
2
changelogs/fragments/azure-version.yaml
Normal file
2
changelogs/fragments/azure-version.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- use proper module_util to get Ansible version for Azure requests
|
||||
2
changelogs/fragments/azure_rm_deployment_fix_45941.yaml
Normal file
2
changelogs/fragments/azure_rm_deployment_fix_45941.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- azure_rm_deployment - fixed regression that prevents resource group from being created (https://github.com/ansible/ansible/issues/45941)
|
||||
2
changelogs/fragments/blockinfile-bytes-fix.yaml
Normal file
2
changelogs/fragments/blockinfile-bytes-fix.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- blockinfile - use bytes rather than a native string to prevent a stacktrace in Python 3 when writing to the file (https://github.com/ansible/ansible/issues/46237)
|
||||
2
changelogs/fragments/callback_fixes.yml
Normal file
2
changelogs/fragments/callback_fixes.yml
Normal file
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- Update callbacks to use Ansible's JSON encoder to avoid known serialization issues
|
||||
7
changelogs/fragments/dd-put-empty-files.yaml
Normal file
7
changelogs/fragments/dd-put-empty-files.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
bugfixes:
|
||||
- docker connection - Support empty files with copying to target (https://github.com/ansible/ansible/issues/36725)
|
||||
- chroot connection - Support empty files with copying to target (https://github.com/ansible/ansible/issues/36725)
|
||||
- jail connection - Support empty files with copying to target (https://github.com/ansible/ansible/issues/36725)
|
||||
- kubectl connection - Support empty files with copying to target (https://github.com/ansible/ansible/issues/36725)
|
||||
- libvirt_lxc connection - Support empty files with copying to target (https://github.com/ansible/ansible/issues/36725)
|
||||
- zone connection - Support empty files with copying to target (https://github.com/ansible/ansible/issues/36725)
|
||||
7
changelogs/fragments/docker_container-idempotency.yaml
Normal file
7
changelogs/fragments/docker_container-idempotency.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
bugfixes:
|
||||
- "docker_container - Makes ``blkio_weight``, ``cpuset_mems``, ``dns_opts`` and ``uts`` options actually work."
|
||||
- "docker_container - Fix idempotency problems with ``cap_drop`` and ``groups`` (when numeric group IDs were used)."
|
||||
- "docker_container - Fix type conversion errors for ``log_options``."
|
||||
- "docker_container - Fixing various comparison/idempotency problems related to wrong comparisons.
|
||||
In particular, comparisons for ``command`` and ``entrypoint`` (both lists) no longer ignore missing
|
||||
elements during idempotency checks."
|
||||
@ -0,0 +1,6 @@
|
||||
---
|
||||
bugfixes:
|
||||
- ec2_group - Sanitize the ingress and egress rules before operating on them by flattening any lists
|
||||
within lists describing the target CIDR(s) into a list of strings. Prior to Ansible 2.6 the ec2_group
|
||||
module accepted a list of strings, a list of lists, or a combination of strings and lists within a list.
|
||||
https://github.com/ansible/ansible/pull/45594
|
||||
2
changelogs/fragments/fix_44984.yaml
Normal file
2
changelogs/fragments/fix_44984.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- cloudfront - fix bug when CloudFrontOriginAccessIdentityList is missing (https://github.com/ansible/ansible/pull/44984)
|
||||
@ -0,0 +1,6 @@
|
||||
---
|
||||
bugfixes:
|
||||
- ec2_group - There can be multiple security groups with the same name in
|
||||
different VPCs. Prior to 2.6 if a target group name was provided, the group
|
||||
matching the name and VPC had highest precedence. Restore this behavior by
|
||||
updated the dictionary with the groups matching the VPC last.
|
||||
@ -0,0 +1,7 @@
|
||||
---
|
||||
bugfixes:
|
||||
- The patch fixing the regression of no longer preferring matching security
|
||||
groups in the same VPC https://github.com/ansible/ansible/pull/45787
|
||||
(which was also backported to 2.6) broke EC2-Classic accounts.
|
||||
https://github.com/ansible/ansible/pull/46242 removes the assumption that
|
||||
security groups must be in a VPC.
|
||||
2
changelogs/fragments/fix_flatten.yml
Normal file
2
changelogs/fragments/fix_flatten.yml
Normal file
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- fix flatten to properly handle multiple lists in lists https://github.com/ansible/ansible/issues/46343
|
||||
3
changelogs/fragments/fix_vyos_facts.yaml
Normal file
3
changelogs/fragments/fix_vyos_facts.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
---
|
||||
bugfixes:
|
||||
- vyos_facts - fix vyos_facts not returning version number issue (https://github.com/ansible/ansible/pull/39115)
|
||||
2
changelogs/fragments/free-strategy-include-var-tags.yaml
Normal file
2
changelogs/fragments/free-strategy-include-var-tags.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- dynamic includes - Use the copied and merged task for calculating task vars in the free strategy (https://github.com/ansible/ansible/issues/47024)
|
||||
2
changelogs/fragments/get-url-fix-idempotency.yaml
Normal file
2
changelogs/fragments/get-url-fix-idempotency.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- get_url - Don't re-download files unnecessarily when force=no (https://github.com/ansible/ansible/issues/45491)
|
||||
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- lineinfile - fix index out of range error when using insertbefore on a file with only one line (https://github.com/ansible/ansible/issues/46043)
|
||||
4
changelogs/fragments/loop_undefined_delegate_to.yaml
Normal file
4
changelogs/fragments/loop_undefined_delegate_to.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
bugfixes:
|
||||
- loop - Ensure that a loop with a when condition that evaluates to false and delegate_to, will short circuit if the
|
||||
loop references an undefined variable. This matches the behavior in the same scenario without delegate_to
|
||||
(https://github.com/ansible/ansible/issues/45189)
|
||||
2
changelogs/fragments/mysql-migrate_to_pymysql.yaml
Normal file
2
changelogs/fragments/mysql-migrate_to_pymysql.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- mysql_*, proxysql_* - PyMySQL (a pure-Python MySQL driver) is now a preferred dependency also supporting Python 3.X.
|
||||
2
changelogs/fragments/nxos_ambiguous_command_check.yaml
Normal file
2
changelogs/fragments/nxos_ambiguous_command_check.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- Add ambiguous command check as the error message is not persistent on nexus devices (https://github.com/ansible/ansible/pull/45337).
|
||||
2
changelogs/fragments/nxos_facts_26.yaml
Normal file
2
changelogs/fragments/nxos_facts_26.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- fix nxos_facts indefinite hang for text based output (https://github.com/ansible/ansible/pull/45845).
|
||||
2
changelogs/fragments/nxos_terminal_plugin.yaml
Normal file
2
changelogs/fragments/nxos_terminal_plugin.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- Fix python2.6 `nothing to repeat` nxos terminal plugin bug (https://github.com/ansible/ansible/pull/45271).
|
||||
2
changelogs/fragments/piped-transfer-empty-files.yaml
Normal file
2
changelogs/fragments/piped-transfer-empty-files.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- ssh connection - Support empty files with piped transfer_method (https://github.com/ansible/ansible/issues/45426)
|
||||
2
changelogs/fragments/plugin-filters-cfg.yaml
Normal file
2
changelogs/fragments/plugin-filters-cfg.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- PLUGIN_FILTERS_CFG - Ensure that the value is treated as type=path, and that we use the standard section of ``defaults`` instead of ``default`` (https://github.com/ansible/ansible/pull/45994)
|
||||
2
changelogs/fragments/script-module-no-file-path.yaml
Normal file
2
changelogs/fragments/script-module-no-file-path.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- script inventory plugin - Don't pass file_name to DataLoader.load, which will prevent misleading error messages (https://github.com/ansible/ansible/issues/34164)
|
||||
2
changelogs/fragments/user-docs-underlying-tools.yaml
Normal file
2
changelogs/fragments/user-docs-underlying-tools.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- user - add documentation on what underlying tools are used on each platform (https://github.com/ansible/ansible/issues/44266)
|
||||
3
changelogs/fragments/v2.6.5_summary.yaml
Normal file
3
changelogs/fragments/v2.6.5_summary.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
release_summary: |
|
||||
| Release Date: 2018-09-28
|
||||
| `Porting Guide <https://docs.ansible.com/ansible/devel/porting_guides.html>`_
|
||||
3
changelogs/fragments/v2.6.6_summary.yaml
Normal file
3
changelogs/fragments/v2.6.6_summary.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
release_summary: |
|
||||
| Release Date: 2018-10-19
|
||||
| `Porting Guide <https://docs.ansible.com/ansible/devel/porting_guides.html>`_
|
||||
2
changelogs/fragments/win_copy-dest-quote.yaml
Normal file
2
changelogs/fragments/win_copy-dest-quote.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- win_copy - Fix issue where the dest return value would be enclosed in single quote when dest is a folder - https://github.com/ansible/ansible/issues/45281
|
||||
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- win_group_membership - fix intermittent issue where it failed to convert the ADSI object to the .NET object after using it once
|
||||
2
changelogs/fragments/win_keep_remote_file_python26.yaml
Normal file
2
changelogs/fragments/win_keep_remote_file_python26.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- powershell - Fix issue where setting ANSIBLE_KEEP_REMOTE_FILES fails when using Python 2.6 - https://github.com/ansible/ansible/issues/45490
|
||||
2
changelogs/fragments/win_say-fix.yaml
Normal file
2
changelogs/fragments/win_say-fix.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- win_say - fix syntax error in module and get tests working
|
||||
2
changelogs/fragments/winrm_pexpect.yaml
Normal file
2
changelogs/fragments/winrm_pexpect.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
bugfixes:
|
||||
- winrm - Only use pexpect for auto kerb auth if it is installed and contains the required kwargs - https://github.com/ansible/ansible/issues/43462
|
||||
@ -0,0 +1,7 @@
|
||||
.. _contributor_license_agreement:
|
||||
|
||||
******************************
|
||||
Contributors License Agreement
|
||||
******************************
|
||||
|
||||
By contributing you agree that these contributions are your own (or approved by your employer) and you grant a full, complete, irrevocable copyright license to all users and developers of the project, present and future, pursuant to the license of the project.
|
||||
@ -21,8 +21,5 @@ To get started, please read and understand the :ref:`code_of_conduct`, and then
|
||||
maintainers
|
||||
release_managers
|
||||
communication
|
||||
contributor_license_agreement
|
||||
other_tools_and_programs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -117,8 +117,8 @@ Read the complete :ref:`module metadata specification <ansible_metadata_block>`
|
||||
serializable. A common pitfall is to try returning an object via
|
||||
exit_json(). Instead, convert the fields you need from the object into the
|
||||
fields of a dictionary and return the dictionary.
|
||||
* When fetching URLs, please use either fetch_url or open_url from ansible.module_utils.urls
|
||||
rather than urllib2; urllib2 does not natively verify TLS certificates and so is insecure for https.
|
||||
* When fetching URLs, please use either fetch_url or open_url from ansible.module_utils.urls
|
||||
rather than urllib2; urllib2 does not natively verify TLS certificates and so is insecure for https.
|
||||
* facts modules must return facts in the ansible_facts field of the :ref:`result
|
||||
dictionary<common_return_values>`.
|
||||
* modules that are purely about fact gathering need to implement check_mode.
|
||||
|
||||
@ -8,7 +8,6 @@ The purpose of this guide is to document all of the paths available to you for i
|
||||
|
||||
To get started, select one of the following topics.
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
|
||||
@ -42,6 +42,7 @@ Aliases can be used to skip platforms using one of the following:
|
||||
- ``skip/freebsd`` - Skip tests on FreeBSD.
|
||||
- ``skip/osx`` - Skip tests on macOS / OS X.
|
||||
- ``skip/rhel`` - Skip tests on RHEL.
|
||||
- ``skip/docker`` - Skip tests when running on a Docker container
|
||||
|
||||
Aliases can be used to skip Python major versions using one of the following:
|
||||
|
||||
|
||||
6
docs/docsite/rst/dev_guide/testing/sanity/symlinks.rst
Normal file
6
docs/docsite/rst/dev_guide/testing/sanity/symlinks.rst
Normal file
@ -0,0 +1,6 @@
|
||||
Sanity Tests » symlinks
|
||||
=======================
|
||||
|
||||
Symbolic links are only permitted for files that exist to ensure proper tarball generation during a release.
|
||||
|
||||
If other types of symlinks are needed for tests they must be created as part of the test.
|
||||
@ -2,7 +2,7 @@
|
||||
Run Your First Command and Playbook
|
||||
***************************************************
|
||||
|
||||
Put the concepts you learned to work with this quick tutorial. Install Ansible, execute a network configuration command manually, execute the same command with Ansible, then create a playbook so you can execute the command any time on multiple network devices.
|
||||
Put the concepts you learned to work with this quick tutorial. Install Ansible, execute a network configuration command manually, execute the same command with Ansible, then create a playbook so you can execute the command any time on multiple network devices.
|
||||
|
||||
.. contents:: Topics
|
||||
|
||||
@ -86,22 +86,17 @@ The playbook contains one play with two tasks, and should generate output like t
|
||||
.. code-block:: bash
|
||||
|
||||
$ ansible-playbook -i vyos.example.net, -u ansible -k -e ansible_network_os=vyos first_playbook.yml
|
||||
|
||||
|
||||
PLAY [First Playbook]
|
||||
***************************************************************************************************************************
|
||||
|
||||
TASK [Gathering Facts]
|
||||
***************************************************************************************************************************
|
||||
ok: [vyos.example.net]
|
||||
|
||||
TASK [Get config for VyOS devices]
|
||||
***************************************************************************************************************************
|
||||
ok: [vyos.example.net]
|
||||
|
||||
|
||||
TASK [Display the config]
|
||||
***************************************************************************************************************************
|
||||
ok: [vyos.example.net] => {
|
||||
"failed": false,
|
||||
"msg": "The hostname is vyos and the OS is VyOS"
|
||||
}
|
||||
|
||||
@ -114,15 +109,11 @@ The extended first playbook has four tasks in a single play. Run it with the sam
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ ansible-playbook -i vyos.example.net, -u ansible -k -e ansible_network_os=vyos first_playbook_ext.yml
|
||||
$ ansible-playbook -i vyos.example.net, -u ansible -k -e ansible_network_os=vyos first_playbook_ext.yml
|
||||
|
||||
PLAY [First Playbook]
|
||||
************************************************************************************************************************************
|
||||
|
||||
TASK [Gathering Facts]
|
||||
***********************************************************************************************************************************
|
||||
ok: [vyos.example.net]
|
||||
|
||||
|
||||
TASK [Get config for VyOS devices]
|
||||
**********************************************************************************************************************************
|
||||
ok: [vyos.example.net]
|
||||
@ -130,10 +121,9 @@ The extended first playbook has four tasks in a single play. Run it with the sam
|
||||
TASK [Display the config]
|
||||
*************************************************************************************************************************************
|
||||
ok: [vyos.example.net] => {
|
||||
"failed": false,
|
||||
"msg": "The hostname is vyos and the OS is VyOS"
|
||||
}
|
||||
|
||||
|
||||
TASK [Update the hostname]
|
||||
*************************************************************************************************************************************
|
||||
changed: [vyos.example.net]
|
||||
@ -141,17 +131,16 @@ The extended first playbook has four tasks in a single play. Run it with the sam
|
||||
TASK [Get changed config for VyOS devices]
|
||||
*************************************************************************************************************************************
|
||||
ok: [vyos.example.net]
|
||||
|
||||
|
||||
TASK [Display the changed config]
|
||||
*************************************************************************************************************************************
|
||||
ok: [vyos.example.net] => {
|
||||
"failed": false,
|
||||
"msg": "The hostname is vyos-changed and the OS is VyOS"
|
||||
}
|
||||
|
||||
|
||||
PLAY RECAP
|
||||
************************************************************************************************************************************
|
||||
vyos.example.net : ok=6 changed=1 unreachable=0 failed=0
|
||||
vyos.example.net : ok=6 changed=1 unreachable=0 failed=0
|
||||
|
||||
|
||||
This playbook is useful. However, running it still requires several command-line flags. Also, running a playbook against a single device is not a huge efficiency gain over making the same change manually. The next step to harnessing the full power of Ansible is to use an inventory file to organize your managed nodes into groups with information like the ``ansible_network_os`` and the SSH user.
|
||||
This playbook is useful. However, running it still requires several command-line flags. Also, running a playbook against a single device is not a huge efficiency gain over making the same change manually. The next step to harnessing the full power of Ansible is to use an inventory file to organize your managed nodes into groups with information like the ``ansible_network_os`` and the SSH user.
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
- name: Network Getting Started First Playbook
|
||||
connection: network_cli
|
||||
gather_facts: false
|
||||
hosts: all
|
||||
tasks:
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
- name: Network Getting Started First Playbook Extended
|
||||
connection: network_cli
|
||||
gather_facts: false
|
||||
hosts: all
|
||||
tasks:
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
|
||||
Inventory Plugins
|
||||
-----------------
|
||||
=================
|
||||
|
||||
Inventory plugins allow users to point at data sources to compile the inventory of hosts that Ansible uses to target tasks, either via the ``-i /path/to/file`` and/or ``-i 'host1, host2'`` command line parameters or from other configuration sources.
|
||||
|
||||
@ -10,7 +10,7 @@ Inventory plugins allow users to point at data sources to compile the inventory
|
||||
.. _enabling_inventory:
|
||||
|
||||
Enabling Inventory Plugins
|
||||
++++++++++++++++++++++++++
|
||||
--------------------------
|
||||
|
||||
Most inventory plugins shipped with Ansible are disabled by default and need to be whitelisted in your
|
||||
:ref:`ansible.cfg <ansible_configuration_settings>` file in order to function. This is how the default whitelist looks in the
|
||||
@ -19,7 +19,7 @@ config file that ships with Ansible:
|
||||
.. code-block:: ini
|
||||
|
||||
[inventory]
|
||||
enable_plugins = host_list, script, yaml, ini
|
||||
enable_plugins = host_list, script, yaml, ini, auto
|
||||
|
||||
This list also establishes the order in which each plugin tries to parse an inventory source. Any plugins left out of the list will not be considered, so you can 'optimize' your inventory loading by minimizing it to what you actually use. For example:
|
||||
|
||||
@ -32,7 +32,7 @@ This list also establishes the order in which each plugin tries to parse an inve
|
||||
.. _using_inventory:
|
||||
|
||||
Using Inventory Plugins
|
||||
+++++++++++++++++++++++
|
||||
-----------------------
|
||||
|
||||
The only requirement for using an inventory plugin after it is enabled is to provide an inventory source to parse.
|
||||
Ansible will try to use the list of enabled inventory plugins, in order, against each inventory source provided.
|
||||
@ -42,7 +42,7 @@ Once an inventory plugin succeeds at parsing a source, the any remaining invento
|
||||
.. _inventory_plugin_list:
|
||||
|
||||
Plugin List
|
||||
+++++++++++
|
||||
-----------
|
||||
|
||||
You can use ``ansible-doc -t inventory -l`` to see the list of available plugins.
|
||||
Use ``ansible-doc -t inventory <plugin name>`` to see plugin-specific documentation and examples.
|
||||
|
||||
@ -50,10 +50,10 @@ Noteworthy module changes
|
||||
-------------------------
|
||||
|
||||
* The ``upgrade`` module option for ``win_chocolatey`` has been removed; use ``state: latest`` instead.
|
||||
* The ``reboot`` module option for ``win_feature`` has been removed; use the ``win_reboot`` action plugin instead
|
||||
* The ``win_iis_webapppool`` module no longer accepts a string for the ``attributes`` module option; use the free form dictionary value instead
|
||||
* The ``name`` module option for ``win_package`` has been removed; this is not used anywhere and should just be removed from your playbooks
|
||||
* The ``win_regedit`` module no longer automatically corrects the hive path ``HCCC`` to ``HKCC``; use ``HKCC`` because this is the correct hive path
|
||||
* The ``reboot`` module option for ``win_feature`` has been removed; use the ``win_reboot`` action plugin instead.
|
||||
* The ``win_iis_webapppool`` module no longer accepts a string for the ``attributes`` module option; use the free form dictionary value instead.
|
||||
* The ``name`` module option for ``win_package`` has been removed; this is not used anywhere and should just be removed from your playbooks.
|
||||
* The ``win_regedit`` module no longer automatically corrects the hive path ``HCCC`` to ``HKCC``; use ``HKCC`` because this is the correct hive path.
|
||||
* The :ref:`file_module` now emits a deprecation warning when ``src`` is specified with a state
|
||||
other than ``hard`` or ``link`` as it is only supposed to be useful with those. This could have
|
||||
an effect on people who were depending on a buggy interaction between src and other state's to
|
||||
@ -65,6 +65,7 @@ Noteworthy module changes
|
||||
destination path like this::
|
||||
|
||||
$ ansible localhost -m file -a 'path=/tmp/lib state=directory'
|
||||
|
||||
* The ``k8s_raw`` and ``openshift_raw`` modules have been aliased to the new ``k8s`` module.
|
||||
* The ``k8s`` module supports all Kubernetes resources including those from Custom Resource Definitions and aggregated API servers. This includes all OpenShift resources.
|
||||
* The ``k8s`` module will not accept resources where subkeys have been snake_cased. This was a workaround that was suggested with the ``k8s_raw`` and ``openshift_raw`` modules.
|
||||
@ -77,8 +78,7 @@ Noteworthy module changes
|
||||
Since an empty regexp matches every line in a file, it will replace the last line in a file rather
|
||||
than inserting. If this is the desired behavior, use ``'^'`` which will match every line and
|
||||
will not trigger the warning.
|
||||
|
||||
|
||||
* Openstack modules are no longer using the ``shade`` library. Instead ``openstacksdk`` is used. Since ``openstacksdk`` should be already present as a dependency to ``shade`` no additional actions are required.
|
||||
|
||||
Plugins
|
||||
=======
|
||||
@ -108,3 +108,8 @@ Networking
|
||||
==========
|
||||
|
||||
No notable changes.
|
||||
|
||||
Dynamic inventory scripts
|
||||
=========================
|
||||
|
||||
* ``contrib/inventory/openstack.py`` has been renamed to ``contrib/inventory/openstack_inventory.py``. If you have used ``openstack.py`` as a name for your OpenStack dynamic inventory file, change it to ``openstack_inventory.py``. Otherwise the file name will conflict with imports from ``openstacksdk``.
|
||||
|
||||
@ -19,9 +19,6 @@ Ansible has a graduated support structure that extends to three major releases.
|
||||
For more information, read about the :ref:`development_and_stable_version_maintenance_workflow` or
|
||||
see the chart in :ref:`release_schedule` for the degrees to which current releases are supported.
|
||||
|
||||
.. note:: Support for three major releases began with Ansible-2.4. Ansible-2.3 and older versions
|
||||
are only supported for two releases.
|
||||
|
||||
If you are using a release of Ansible that is no longer supported, we strongly
|
||||
encourage you to upgrade as soon as possible in order to benefit from the
|
||||
latest features and security fixes.
|
||||
@ -40,10 +37,11 @@ Release status
|
||||
=============== ========================== =================================================
|
||||
Ansible Release Latest Version Status
|
||||
=============== ========================== =================================================
|
||||
devel 2.7 (unreleased, trunk) In development
|
||||
`2.6`_ 2.6.1 (2018-07-05) Supported (security **and** general bug fixes)
|
||||
`2.5`_ 2.5.6 (2018-07-05) Supported (security **and** critical bug fixes)
|
||||
`2.4`_ 2.4.6 (2018-07-05) Supported (security fixes)
|
||||
devel 2.8 (unreleased, trunk) In development
|
||||
`2.7`_ 2.7.0 (2018-10-04) Supported (security **and** general bug fixes)
|
||||
`2.6`_ 2.6.5 (2018-09-28) Supported (security **and** critical bug fixes)
|
||||
`2.5`_ 2.5.9 (2018-09-10) Supported (security fixes)
|
||||
`2.4`_ 2.4.6 (2018-07-05) Unsupported (end of life)
|
||||
`2.3`_ 2.3.3 (2017-12-20) Unsupported (end of life)
|
||||
`2.2`_ 2.2.3 (2017-05-09) Unsupported (end of life)
|
||||
`2.1`_ 2.1.6 (2017-06-01) Unsupported (end of life)
|
||||
@ -58,6 +56,7 @@ devel 2.7 (unreleased, trunk) In development
|
||||
|
||||
.. Comment: devel used to point here but we're currently revamping our changelog process and have no
|
||||
link to a static changelog for devel _2.6: https://github.com/ansible/ansible/blob/devel/CHANGELOG.md
|
||||
.. _2.7: https://github.com/ansible/ansible/blob/stable-2.7/changelogs/CHANGELOG-v2.7.rst
|
||||
.. _2.6: https://github.com/ansible/ansible/blob/stable-2.6/changelogs/CHANGELOG-v2.6.rst
|
||||
.. _2.5: https://github.com/ansible/ansible/blob/stable-2.5/changelogs/CHANGELOG-v2.5.rst
|
||||
.. _2.4: https://github.com/ansible/ansible/blob/stable-2.4/CHANGELOG.md
|
||||
|
||||
@ -87,7 +87,7 @@ Omitting Parameters
|
||||
As of Ansible 1.8, it is possible to use the default filter to omit module parameters using the special `omit` variable::
|
||||
|
||||
- name: touch files with an optional mode
|
||||
file: dest={{item.path}} state=touch mode={{item.mode|default(omit)}}
|
||||
file: dest={{ item.path }} state=touch mode={{ item.mode | default(omit) }}
|
||||
loop:
|
||||
- path: /tmp/foo
|
||||
- path: /tmp/bar
|
||||
@ -123,11 +123,11 @@ To get the maximum value from a list of numbers::
|
||||
|
||||
Flatten a list (same thing the `flatten` lookup does)::
|
||||
|
||||
{{ [3, [4, 2] ]|flatten }}
|
||||
{{ [3, [4, 2] ] | flatten }}
|
||||
|
||||
Flatten only the first level of a list (akin to the `items` lookup)::
|
||||
|
||||
{{ [3, [4, [2]] ]|flatten(levels=1) }}
|
||||
{{ [3, [4, [2]] ] | flatten(levels=1) }}
|
||||
|
||||
|
||||
.. _set_theory_filters:
|
||||
@ -191,7 +191,7 @@ subelements Filter
|
||||
|
||||
Produces a product of an object, and subelement values of that object, similar to the ``subelements`` lookup::
|
||||
|
||||
{{ users|subelements('groups', skip_missing=True) }}
|
||||
{{ users | subelements('groups', skip_missing=True) }}
|
||||
|
||||
Which turns::
|
||||
|
||||
@ -241,7 +241,7 @@ An example of using this filter with ``loop``::
|
||||
authorized_key:
|
||||
user: "{{ item.0.name }}"
|
||||
key: "{{ lookup('file', item.1) }}"
|
||||
loop: "{{ users|subelements('authorized') }}"
|
||||
loop: "{{ users | subelements('authorized') }}"
|
||||
|
||||
.. _random_filter:
|
||||
|
||||
@ -255,29 +255,29 @@ items), but can also generate a random number based on a range.
|
||||
|
||||
To get a random item from a list::
|
||||
|
||||
"{{ ['a','b','c']|random }}"
|
||||
"{{ ['a','b','c'] | random }}"
|
||||
# => 'c'
|
||||
|
||||
To get a random number between 0 and a specified number::
|
||||
|
||||
"{{ 60 |random}} * * * * root /script/from/cron"
|
||||
"{{ 60 | random }} * * * * root /script/from/cron"
|
||||
# => '21 * * * * root /script/from/cron'
|
||||
|
||||
Get a random number from 0 to 100 but in steps of 10::
|
||||
|
||||
{{ 101 |random(step=10) }}
|
||||
{{ 101 | random(step=10) }}
|
||||
# => 70
|
||||
|
||||
Get a random number from 1 to 100 but in steps of 10::
|
||||
|
||||
{{ 101 |random(1, 10) }}
|
||||
{{ 101 | random(1, 10) }}
|
||||
# => 31
|
||||
{{ 101 |random(start=1, step=10) }}
|
||||
{{ 101 | random(start=1, step=10) }}
|
||||
# => 51
|
||||
|
||||
As of Ansible version 2.3, it's also possible to initialize the random number generator from a seed. This way, you can create random-but-idempotent numbers::
|
||||
|
||||
"{{ 60 |random(seed=inventory_hostname) }} * * * * root /script/from/cron"
|
||||
"{{ 60 | random(seed=inventory_hostname) }} * * * * root /script/from/cron"
|
||||
|
||||
|
||||
Shuffle Filter
|
||||
@ -289,14 +289,14 @@ This filter will randomize an existing list, giving a different order every invo
|
||||
|
||||
To get a random list from an existing list::
|
||||
|
||||
{{ ['a','b','c']|shuffle }}
|
||||
{{ ['a','b','c'] | shuffle }}
|
||||
# => ['c','a','b']
|
||||
{{ ['a','b','c']|shuffle }}
|
||||
{{ ['a','b','c'] | shuffle }}
|
||||
# => ['b','c','a']
|
||||
|
||||
As of Ansible version 2.3, it's also possible to shuffle a list idempotent. All you need is a seed.::
|
||||
|
||||
{{ ['a','b','c']|shuffle(seed=inventory_hostname) }}
|
||||
{{ ['a','b','c'] | shuffle(seed=inventory_hostname) }}
|
||||
# => ['b','a','c']
|
||||
|
||||
note that when used with a non 'listable' item it is a noop, otherwise it always returns a list
|
||||
@ -651,31 +651,31 @@ Hashing filters
|
||||
|
||||
To get the sha1 hash of a string::
|
||||
|
||||
{{ 'test1'|hash('sha1') }}
|
||||
{{ 'test1' | hash('sha1') }}
|
||||
|
||||
To get the md5 hash of a string::
|
||||
|
||||
{{ 'test1'|hash('md5') }}
|
||||
{{ 'test1' | hash('md5') }}
|
||||
|
||||
Get a string checksum::
|
||||
|
||||
{{ 'test2'|checksum }}
|
||||
{{ 'test2' | checksum }}
|
||||
|
||||
Other hashes (platform dependent)::
|
||||
|
||||
{{ 'test2'|hash('blowfish') }}
|
||||
{{ 'test2' | hash('blowfish') }}
|
||||
|
||||
To get a sha512 password hash (random salt)::
|
||||
|
||||
{{ 'passwordsaresecret'|password_hash('sha512') }}
|
||||
{{ 'passwordsaresecret' | password_hash('sha512') }}
|
||||
|
||||
To get a sha256 password hash with a specific salt::
|
||||
|
||||
{{ 'secretpassword'|password_hash('sha256', 'mysecretsalt') }}
|
||||
{{ 'secretpassword' | password_hash('sha256', 'mysecretsalt') }}
|
||||
|
||||
An idempotent method to generate unique hashes per system is to use a salt that is consistent between runs::
|
||||
|
||||
{{ 'secretpassword'|password_hash('sha512', 65534|random(seed=inventory_hostname)|string) }}
|
||||
{{ 'secretpassword' | password_hash('sha512', 65534 | random(seed=inventory_hostname) | string) }}
|
||||
|
||||
Hash types available depend on the master system running ansible,
|
||||
'hash' depends on hashlib password_hash depends on passlib (http://passlib.readthedocs.io/en/stable/lib/passlib.hash.html).
|
||||
@ -690,7 +690,7 @@ Combining hashes/dictionaries
|
||||
The `combine` filter allows hashes to be merged. For example, the
|
||||
following would override keys in one hash::
|
||||
|
||||
{{ {'a':1, 'b':2}|combine({'b':3}) }}
|
||||
{{ {'a':1, 'b':2} | combine({'b':3}) }}
|
||||
|
||||
The resulting hash would be::
|
||||
|
||||
@ -702,7 +702,7 @@ hashes and merge their keys too
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{{ {'a':{'foo':1, 'bar':2}, 'b':2}|combine({'a':{'bar':3, 'baz':4}}, recursive=True) }}
|
||||
{{ {'a':{'foo':1, 'bar':2}, 'b':2} | combine({'a':{'bar':3, 'baz':4}}, recursive=True) }}
|
||||
|
||||
This would result in::
|
||||
|
||||
@ -710,7 +710,7 @@ This would result in::
|
||||
|
||||
The filter can also take multiple arguments to merge::
|
||||
|
||||
{{ a|combine(b, c, d) }}
|
||||
{{ a | combine(b, c, d) }}
|
||||
|
||||
In this case, keys in `d` would override those in `c`, which would
|
||||
override those in `b`, and so on.
|
||||
@ -728,8 +728,8 @@ Extracting values from containers
|
||||
The `extract` filter is used to map from a list of indices to a list of
|
||||
values from a container (hash or array)::
|
||||
|
||||
{{ [0,2]|map('extract', ['x','y','z'])|list }}
|
||||
{{ ['x','y']|map('extract', {'x': 42, 'y': 31})|list }}
|
||||
{{ [0,2] | map('extract', ['x','y','z']) | list }}
|
||||
{{ ['x','y'] | map('extract', {'x': 42, 'y': 31}) | list }}
|
||||
|
||||
The results of the above expressions would be::
|
||||
|
||||
@ -738,7 +738,7 @@ The results of the above expressions would be::
|
||||
|
||||
The filter can take another argument::
|
||||
|
||||
{{ groups['x']|map('extract', hostvars, 'ec2_ip_address')|list }}
|
||||
{{ groups['x'] | map('extract', hostvars, 'ec2_ip_address') | list }}
|
||||
|
||||
This takes the list of hosts in group 'x', looks them up in `hostvars`,
|
||||
and then looks up the `ec2_ip_address` of the result. The final result
|
||||
@ -747,7 +747,7 @@ is a list of IP addresses for the hosts in group 'x'.
|
||||
The third argument to the filter can also be a list, for a recursive
|
||||
lookup inside the container::
|
||||
|
||||
{{ ['a']|map('extract', b, ['x','y'])|list }}
|
||||
{{ ['a'] | map('extract', b, ['x','y']) | list }}
|
||||
|
||||
This would return a list containing the value of `b['a']['x']['y']`.
|
||||
|
||||
@ -1029,19 +1029,19 @@ doesn't know it is a boolean value::
|
||||
To make use of one attribute from each item in a list of complex variables, use the "map" filter (see the `Jinja2 map() docs`_ for more)::
|
||||
|
||||
# get a comma-separated list of the mount points (e.g. "/,/mnt/stuff") on a host
|
||||
{{ ansible_mounts|map(attribute='mount')|join(',') }}
|
||||
{{ ansible_mounts | map(attribute='mount') | join(',') }}
|
||||
|
||||
To get date object from string use the `to_datetime` filter, (new in version in 2.2)::
|
||||
|
||||
# Get total amount of seconds between two dates. Default date format is %Y-%m-%d %H:%M:%S but you can pass your own format
|
||||
{{ (("2016-08-14 20:00:12"|to_datetime) - ("2015-12-25"|to_datetime('%Y-%m-%d'))).total_seconds() }}
|
||||
{{ (("2016-08-14 20:00:12" | to_datetime) - ("2015-12-25" | to_datetime('%Y-%m-%d'))).total_seconds() }}
|
||||
|
||||
# Get remaining seconds after delta has been calculated. NOTE: This does NOT convert years, days, hours, etc to seconds. For that, use total_seconds()
|
||||
{{ (("2016-08-14 20:00:12"|to_datetime) - ("2016-08-14 18:00:00"|to_datetime)).seconds }}
|
||||
{{ (("2016-08-14 20:00:12" | to_datetime) - ("2016-08-14 18:00:00" | to_datetime)).seconds }}
|
||||
# This expression evaluates to "12" and not "132". Delta is 2 hours, 12 seconds
|
||||
|
||||
# get amount of days between two dates. This returns only number of days and discards remaining hours, minutes, and seconds
|
||||
{{ (("2016-08-14 20:00:12"|to_datetime) - ("2015-12-25"|to_datetime('%Y-%m-%d'))).days }}
|
||||
{{ (("2016-08-14 20:00:12" | to_datetime) - ("2015-12-25" | to_datetime('%Y-%m-%d'))).days }}
|
||||
|
||||
Combination Filters
|
||||
````````````````````
|
||||
@ -1053,34 +1053,34 @@ To get permutations of a list::
|
||||
|
||||
- name: give me largest permutations (order matters)
|
||||
debug:
|
||||
msg: "{{ [1,2,3,4,5]|permutations|list }}"
|
||||
msg: "{{ [1,2,3,4,5] | permutations | list }}"
|
||||
|
||||
- name: give me permutations of sets of three
|
||||
debug:
|
||||
msg: "{{ [1,2,3,4,5]|permutations(3)|list }}"
|
||||
msg: "{{ [1,2,3,4,5] | permutations(3) | list }}"
|
||||
|
||||
Combinations always require a set size::
|
||||
|
||||
- name: give me combinations for sets of two
|
||||
debug:
|
||||
msg: "{{ [1,2,3,4,5]|combinations(2)|list }}"
|
||||
msg: "{{ [1,2,3,4,5] | combinations(2) | list }}"
|
||||
|
||||
|
||||
To get a list combining the elements of other lists use ``zip``::
|
||||
|
||||
- name: give me list combo of two lists
|
||||
debug:
|
||||
msg: "{{ [1,2,3,4,5]|zip(['a','b','c','d','e','f'])|list }}"
|
||||
msg: "{{ [1,2,3,4,5] | zip(['a','b','c','d','e','f']) | list }}"
|
||||
|
||||
- name: give me shortest combo of two lists
|
||||
debug:
|
||||
msg: "{{ [1,2,3]|zip(['a','b','c','d','e','f'])|list }}"
|
||||
msg: "{{ [1,2,3] | zip(['a','b','c','d','e','f']) | list }}"
|
||||
|
||||
To always exhaust all list use ``zip_longest``::
|
||||
|
||||
- name: give me longest combo of three lists , fill with X
|
||||
debug:
|
||||
msg: "{{ [1,2,3]|zip_longest(['a','b','c','d','e','f'], [21, 22, 23], fillvalue='X')|list }}"
|
||||
msg: "{{ [1,2,3] | zip_longest(['a','b','c','d','e','f'], [21, 22, 23], fillvalue='X') | list }}"
|
||||
|
||||
|
||||
.. versionadded:: 2.4
|
||||
|
||||
16
docs/templates/plugin.rst.j2
vendored
16
docs/templates/plugin.rst.j2
vendored
@ -384,7 +384,7 @@ Status
|
||||
------
|
||||
{% if not deprecated %}
|
||||
|
||||
{% set support = { 'core': 'The Ansible Core Team', 'network': 'The Ansible Network Team', 'certified': 'an Ansible Partner', 'community': 'The Ansible Community', 'curated': 'A Third Party'} %}
|
||||
{% set support = { 'core': 'the Ansible Core Team', 'network': 'the Ansible Network Team', 'certified': 'an Ansible Partner', 'community': 'the Ansible Community', 'curated': 'a Third Party'} %}
|
||||
{% set module_states = { 'preview': 'it is not guaranteed to have a backwards compatible interface', 'stableinterface': 'the maintainers for this module guarantee that no backward incompatible interface changes will be made'} %}
|
||||
|
||||
{% if metadata %}
|
||||
@ -396,12 +396,24 @@ This module is flagged as **@{cur_state}@** which means that @{module_states[cur
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% if metadata.supported_by in ('core', 'network') %}
|
||||
{% if metadata.supported_by %}
|
||||
|
||||
Maintenance
|
||||
-----------
|
||||
|
||||
{% set supported_by = support[metadata.supported_by] %}
|
||||
This @{ plugin_type }@ is flagged as **@{metadata.supported_by}@** which means that it is maintained by @{ supported_by }@. See :ref:`Module Maintenance & Support <modules_support>` for more info.
|
||||
|
||||
For a list of other modules that are also maintained by @{ supported_by }@, see :ref:`here <@{ metadata.supported_by }@_supported>`.
|
||||
|
||||
{% if metadata.supported_by in ('core', 'network') %}
|
||||
|
||||
Support
|
||||
~~~~~~~
|
||||
|
||||
For more information about Red Hat's support of this @{ plugin_type }@,
|
||||
please refer to this `Knowledge Base article <https://access.redhat.com/articles/rhel-top-support-policies/>`_
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
@ -308,7 +308,7 @@
|
||||
#any_errors_fatal = False
|
||||
|
||||
[inventory]
|
||||
# enable inventory plugins, default: 'host_list', 'script', 'yaml', 'ini'
|
||||
# enable inventory plugins, default: 'host_list', 'script', 'yaml', 'ini', 'auto'
|
||||
#enable_plugins = host_list, virtualbox, yaml, constructed
|
||||
|
||||
# ignore these extensions when parsing a directory as inventory source
|
||||
|
||||
@ -47,7 +47,7 @@ FULL_PATH=$($PYTHON_BIN -c "import os; print(os.path.realpath('$HACKING_DIR'))")
|
||||
export ANSIBLE_HOME="$(dirname "$FULL_PATH")"
|
||||
|
||||
PREFIX_PYTHONPATH="$ANSIBLE_HOME/lib"
|
||||
PREFIX_PATH="$ANSIBLE_HOME/bin:$ANSIBLE_HOME/test/runner"
|
||||
PREFIX_PATH="$ANSIBLE_HOME/bin"
|
||||
PREFIX_MANPATH="$ANSIBLE_HOME/docs/man"
|
||||
|
||||
expr "$PYTHONPATH" : "${PREFIX_PYTHONPATH}.*" > /dev/null || prepend_path PYTHONPATH "$PREFIX_PYTHONPATH"
|
||||
|
||||
@ -5,7 +5,7 @@ set HACKING_DIR (dirname (status -f))
|
||||
set FULL_PATH (python -c "import os; print(os.path.realpath('$HACKING_DIR'))")
|
||||
set ANSIBLE_HOME (dirname $FULL_PATH)
|
||||
set PREFIX_PYTHONPATH $ANSIBLE_HOME/lib
|
||||
set PREFIX_PATH $ANSIBLE_HOME/bin $ANSIBLE_HOME/test/runner
|
||||
set PREFIX_PATH $ANSIBLE_HOME/bin
|
||||
set PREFIX_MANPATH $ANSIBLE_HOME/docs/man
|
||||
|
||||
# set quiet flag
|
||||
|
||||
@ -1502,6 +1502,13 @@ PLUGIN_FILTERS_CFG:
|
||||
ini:
|
||||
- key: plugin_filters_cfg
|
||||
section: default
|
||||
deprecated:
|
||||
why: Specifying "plugin_filters_cfg" under the "default" section is deprecated
|
||||
version: "2.12"
|
||||
alternatives: the "defaults" section instead
|
||||
- key: plugin_filters_cfg
|
||||
section: defaults
|
||||
type: path
|
||||
RETRY_FILES_ENABLED:
|
||||
name: Retry files
|
||||
default: True
|
||||
|
||||
@ -93,7 +93,7 @@ class CloudFrontFactsServiceManager(object):
|
||||
def list_origin_access_identities(self):
|
||||
try:
|
||||
paginator = self.client.get_paginator('list_cloud_front_origin_access_identities')
|
||||
result = paginator.paginate().build_full_result()['CloudFrontOriginAccessIdentityList']
|
||||
result = paginator.paginate().build_full_result().get('CloudFrontOriginAccessIdentityList', {})
|
||||
return result.get('Items', [])
|
||||
except botocore.exceptions.ClientError as e:
|
||||
self.module.fail_json_aws(e, msg="Error listing cloud front origin access identities")
|
||||
|
||||
@ -13,12 +13,9 @@ import traceback
|
||||
from os.path import expanduser
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ansible_release import __version__ as ANSIBLE_VERSION
|
||||
from ansible.module_utils.six.moves import configparser
|
||||
import ansible.module_utils.six.moves.urllib.parse as urlparse
|
||||
try:
|
||||
from ansible.release import __version__ as ANSIBLE_VERSION
|
||||
except ImportError:
|
||||
ANSIBLE_VERSION = 'unknown'
|
||||
|
||||
AZURE_COMMON_ARGS = dict(
|
||||
auth_source=dict(
|
||||
|
||||
@ -30,10 +30,14 @@
|
||||
import os
|
||||
|
||||
try:
|
||||
import MySQLdb
|
||||
mysqldb_found = True
|
||||
import pymysql as mysql_driver
|
||||
except ImportError:
|
||||
mysqldb_found = False
|
||||
try:
|
||||
import MySQLdb as mysql_driver
|
||||
except ImportError:
|
||||
mysql_driver = None
|
||||
|
||||
mysql_driver_fail_msg = 'The PyMySQL (Python 2.7 and Python 3.X) or MySQL-python (Python 2.X) module is required.'
|
||||
|
||||
|
||||
def mysql_connect(module, login_user=None, login_password=None, config_file='', ssl_cert=None, ssl_key=None, ssl_ca=None, db=None, cursor_class=None,
|
||||
@ -69,8 +73,8 @@ def mysql_connect(module, login_user=None, login_password=None, config_file='',
|
||||
if connect_timeout is not None:
|
||||
config['connect_timeout'] = connect_timeout
|
||||
|
||||
db_connection = MySQLdb.connect(**config)
|
||||
db_connection = mysql_driver.connect(**config)
|
||||
if cursor_class is not None:
|
||||
return db_connection.cursor(cursorclass=MySQLdb.cursors.DictCursor)
|
||||
return db_connection.cursor(cursorclass=mysql_driver.cursors.DictCursor)
|
||||
else:
|
||||
return db_connection.cursor()
|
||||
|
||||
@ -156,7 +156,7 @@ class Cli:
|
||||
|
||||
if network_api == 'cliconf' and out:
|
||||
for index, resp in enumerate(out):
|
||||
if 'Invalid command at' in resp and 'json' in resp:
|
||||
if ('Invalid command at' in resp or 'Ambiguous command at' in resp) and 'json' in resp:
|
||||
if commands[index]['output'] == 'json':
|
||||
commands[index]['output'] = 'text'
|
||||
out = connection.run_commands(commands, check_rc)
|
||||
|
||||
@ -38,7 +38,16 @@ Function Get-AnsibleItem {
|
||||
[Parameter(Mandatory=$true)][string]$Path
|
||||
)
|
||||
# Replacement for Get-Item
|
||||
$file_attributes = [System.IO.File]::GetAttributes($Path)
|
||||
try {
|
||||
$file_attributes = [System.IO.File]::GetAttributes($Path)
|
||||
} catch {
|
||||
# if -ErrorAction SilentlyCotinue is set on the cmdlet and we failed to
|
||||
# get the attributes, just return $null, otherwise throw the error
|
||||
if ($ErrorActionPreference -ne "SilentlyContinue") {
|
||||
throw $_
|
||||
}
|
||||
return $null
|
||||
}
|
||||
if ([Int32]$file_attributes -eq -1) {
|
||||
throw New-Object -TypeName System.Management.Automation.ItemNotFoundException -ArgumentList "Cannot find path '$Path' because it does not exist."
|
||||
} elseif ($file_attributes.HasFlag([System.IO.FileAttributes]::Directory)) {
|
||||
|
||||
@ -1099,7 +1099,8 @@ def fetch_url(module, url, data=None, headers=None, method=None,
|
||||
|
||||
# Try to add exception info to the output but don't fail if we can't
|
||||
try:
|
||||
info.update(dict(**e.info()))
|
||||
# Lowercase keys, to conform to py2 behavior, so that py3 and py2 are predictable
|
||||
info.update(dict((k.lower(), v) for k, v in e.info().items()))
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@ -290,6 +290,7 @@ owner_id:
|
||||
|
||||
import json
|
||||
import re
|
||||
from copy import deepcopy
|
||||
from time import sleep
|
||||
from collections import namedtuple
|
||||
from ansible.module_utils.aws.core import AnsibleAWSModule
|
||||
@ -835,6 +836,9 @@ def group_exists(client, module, vpc_id, group_id, name):
|
||||
if security_groups:
|
||||
groups = dict((group['GroupId'], group) for group in all_groups)
|
||||
groups.update(dict((group['GroupName'], group) for group in all_groups))
|
||||
if vpc_id:
|
||||
vpc_wins = dict((group['GroupName'], group) for group in all_groups if group.get('VpcId') and group['VpcId'] == vpc_id)
|
||||
groups.update(vpc_wins)
|
||||
# maintain backwards compatibility by using the last matching group
|
||||
return security_groups[-1], groups
|
||||
return None, {}
|
||||
@ -847,6 +851,27 @@ def verify_rules_with_descriptions_permitted(client, module, rules, rules_egress
|
||||
module.fail_json(msg="Using rule descriptions requires botocore version >= 1.7.2.")
|
||||
|
||||
|
||||
def flatten_nested_targets(module, rules):
|
||||
def _flatten(targets):
|
||||
for target in targets:
|
||||
if isinstance(target, list):
|
||||
for t in _flatten(target):
|
||||
yield t
|
||||
elif isinstance(target, string_types):
|
||||
yield target
|
||||
|
||||
if rules is not None:
|
||||
for rule in rules:
|
||||
target_list_type = None
|
||||
if isinstance(rule.get('cidr_ip'), list):
|
||||
target_list_type = 'cidr_ip'
|
||||
elif isinstance(rule.get('cidr_ipv6'), list):
|
||||
target_list_type = 'cidr_ipv6'
|
||||
if target_list_type is not None:
|
||||
rule[target_list_type] = list(_flatten(rule[target_list_type]))
|
||||
return rules
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = dict(
|
||||
name=dict(),
|
||||
@ -872,8 +897,10 @@ def main():
|
||||
group_id = module.params['group_id']
|
||||
description = module.params['description']
|
||||
vpc_id = module.params['vpc_id']
|
||||
rules = deduplicate_rules_args(rules_expand_sources(rules_expand_ports(module.params['rules'])))
|
||||
rules_egress = deduplicate_rules_args(rules_expand_sources(rules_expand_ports(module.params['rules_egress'])))
|
||||
rules = flatten_nested_targets(module, deepcopy(module.params['rules']))
|
||||
rules_egress = flatten_nested_targets(module, deepcopy(module.params['rules_egress']))
|
||||
rules = deduplicate_rules_args(rules_expand_sources(rules_expand_ports(rules)))
|
||||
rules_egress = deduplicate_rules_args(rules_expand_sources(rules_expand_ports(rules_egress)))
|
||||
state = module.params.get('state')
|
||||
purge_rules = module.params['purge_rules']
|
||||
purge_rules_egress = module.params['purge_rules_egress']
|
||||
|
||||
@ -466,8 +466,8 @@ class Ec2Metadata(object):
|
||||
new_fields = {}
|
||||
for key, value in fields.items():
|
||||
split_fields = key[len(uri):].split('/')
|
||||
if len(split_fields) == 3 and split_fields[0:2] == ['iam', 'security-credentials']:
|
||||
new_fields[self._prefix % "iam-instance-profile-role"] = split_fields[2]
|
||||
if len(split_fields) == 2 and split_fields[0:2] == ['iam', 'info_instanceprofilearn']:
|
||||
new_fields[self._prefix % "iam-instance-profile-role"] = value.split('/')[1]
|
||||
if len(split_fields) > 1 and split_fields[1]:
|
||||
new_key = "-".join(split_fields)
|
||||
new_fields[self._prefix % new_key] = value
|
||||
|
||||
@ -574,6 +574,13 @@ def main():
|
||||
else:
|
||||
wanted_rset.add_value(v)
|
||||
|
||||
need_to_sort_records = (type_in == 'CAA')
|
||||
|
||||
# Sort records for wanted_rset if necessary (keep original list)
|
||||
unsorted_records = wanted_rset.resource_records
|
||||
if need_to_sort_records:
|
||||
wanted_rset.resource_records = sorted(unsorted_records)
|
||||
|
||||
sets = invoke_with_throttling_retries(conn.get_all_rrsets, zone.id, name=record_in,
|
||||
type=type_in, identifier=identifier_in)
|
||||
sets_iter = iter(sets)
|
||||
@ -593,13 +600,14 @@ def main():
|
||||
identifier_in = str(identifier_in)
|
||||
|
||||
if rset.type == type_in and decoded_name.lower() == record_in.lower() and rset.identifier == identifier_in:
|
||||
if need_to_sort_records:
|
||||
# Sort records
|
||||
rset.resource_records = sorted(rset.resource_records)
|
||||
found_record = True
|
||||
record['zone'] = zone_in
|
||||
record['type'] = rset.type
|
||||
record['record'] = decoded_name
|
||||
record['ttl'] = rset.ttl
|
||||
record['value'] = ','.join(sorted(rset.resource_records))
|
||||
record['values'] = sorted(rset.resource_records)
|
||||
if hosted_zone_id_in:
|
||||
record['hosted_zone_id'] = hosted_zone_id_in
|
||||
record['identifier'] = rset.identifier
|
||||
@ -652,6 +660,8 @@ def main():
|
||||
command = 'UPSERT'
|
||||
else:
|
||||
command = command_in.upper()
|
||||
# Restore original order of records
|
||||
wanted_rset.resource_records = unsorted_records
|
||||
changes.add_change_record(command, wanted_rset)
|
||||
|
||||
if not module.check_mode:
|
||||
|
||||
@ -491,7 +491,8 @@ class AzureRMDeploymentManager(AzureRMModuleBase):
|
||||
|
||||
if self.append_tags and self.tags:
|
||||
try:
|
||||
rg = self.get_resource_group(self.resource_group_name)
|
||||
# fetch the RG directly (instead of using the base helper) since we don't want to exit if it's missing
|
||||
rg = self.rm_client.resource_groups.get(self.resource_group_name)
|
||||
if rg.tags:
|
||||
self.tags = dict(self.tags, **rg.tags)
|
||||
except CloudError:
|
||||
|
||||
@ -173,7 +173,8 @@ class AzureRMStorageAccount(AzureRMModuleBase):
|
||||
|
||||
if HAS_AZURE:
|
||||
for key in self.storage_models.SkuName:
|
||||
self.module_arg_spec['account_type']['choices'].append(getattr(key, 'value'))
|
||||
if getattr(key, 'value') not in self.module_arg_spec['account_type']['choices']:
|
||||
self.module_arg_spec['account_type']['choices'].append(getattr(key, 'value'))
|
||||
|
||||
self.results = dict(
|
||||
changed=False,
|
||||
|
||||
@ -267,7 +267,7 @@ options:
|
||||
- "Use docker CLI syntax: C(8000), C(9000:8000), or C(0.0.0.0:9000:8000), where 8000 is a
|
||||
container port, 9000 is a host port, and 0.0.0.0 is a host interface."
|
||||
- Container ports must be exposed either in the Dockerfile or via the C(expose) option.
|
||||
- A value of all will publish all exposed container ports to random host ports, ignoring
|
||||
- A value of C(all) will publish all exposed container ports to random host ports, ignoring
|
||||
any other mappings.
|
||||
- If C(networks) parameter is provided, will inspect each network to see if there exists
|
||||
a bridge network with optional parameter com.docker.network.bridge.host_binding_ipv4.
|
||||
@ -729,6 +729,17 @@ class TaskParameters(DockerBaseClass):
|
||||
|
||||
for key, value in client.module.params.items():
|
||||
setattr(self, key, value)
|
||||
self.comparisons = client.comparisons
|
||||
|
||||
if self.groups:
|
||||
# In case integers are passed as groups, we need to convert them to
|
||||
# strings as docker internally treats them as strings.
|
||||
self.groups = [str(g) for g in self.groups]
|
||||
|
||||
# If state is 'absent', parameters do not have to be parsed or interpreted.
|
||||
# Only the container's name is needed.
|
||||
if self.state == 'absent':
|
||||
return
|
||||
|
||||
for param_name in REQUIRES_CONVERSION_TO_BYTES:
|
||||
if client.module.params.get(param_name):
|
||||
@ -798,7 +809,6 @@ class TaskParameters(DockerBaseClass):
|
||||
'''
|
||||
|
||||
update_parameters = dict(
|
||||
blkio_weight='blkio_weight',
|
||||
cpu_period='cpu_period',
|
||||
cpu_quota='cpu_quota',
|
||||
cpu_shares='cpu_shares',
|
||||
@ -808,6 +818,15 @@ class TaskParameters(DockerBaseClass):
|
||||
memswap_limit='memory_swap',
|
||||
kernel_memory='kernel_memory',
|
||||
)
|
||||
|
||||
if self.client.HAS_BLKIO_WEIGHT_OPT:
|
||||
# blkio_weight is only supported in docker>=1.9
|
||||
update_parameters['blkio_weight'] = 'blkio_weight'
|
||||
|
||||
if self.client.HAS_CPUSET_MEMS_OPT:
|
||||
# cpuset_mems is only supported in docker>=2.3
|
||||
update_parameters['cpuset_mems'] = 'cpuset_mems'
|
||||
|
||||
result = dict()
|
||||
for key, value in update_parameters.items():
|
||||
if getattr(self, value, None) is not None:
|
||||
@ -904,6 +923,7 @@ class TaskParameters(DockerBaseClass):
|
||||
links='links',
|
||||
privileged='privileged',
|
||||
dns='dns_servers',
|
||||
dns_opt='dns_opts',
|
||||
dns_search='dns_search_domains',
|
||||
binds='volume_binds',
|
||||
volumes_from='volumes_from',
|
||||
@ -933,6 +953,10 @@ class TaskParameters(DockerBaseClass):
|
||||
# auto_remove is only supported in docker>=2
|
||||
host_config_params['auto_remove'] = 'auto_remove'
|
||||
|
||||
if self.client.HAS_BLKIO_WEIGHT_OPT:
|
||||
# blkio_weight is only supported in docker>=1.9
|
||||
host_config_params['blkio_weight'] = 'blkio_weight'
|
||||
|
||||
if HAS_DOCKER_PY_3:
|
||||
# cpu_shares and volume_driver moved to create_host_config in > 3
|
||||
host_config_params['cpu_shares'] = 'cpu_shares'
|
||||
@ -941,6 +965,9 @@ class TaskParameters(DockerBaseClass):
|
||||
if self.client.HAS_INIT_OPT:
|
||||
host_config_params['init'] = 'init'
|
||||
|
||||
if self.client.HAS_UTS_MODE_OPT:
|
||||
host_config_params['uts_mode'] = 'uts'
|
||||
|
||||
params = dict()
|
||||
for key, value in host_config_params.items():
|
||||
if getattr(self, value, None) is not None:
|
||||
@ -1122,7 +1149,9 @@ class TaskParameters(DockerBaseClass):
|
||||
)
|
||||
|
||||
if self.log_options is not None:
|
||||
options['Config'] = self.log_options
|
||||
options['Config'] = dict()
|
||||
for k, v in self.log_options.items():
|
||||
options['Config'][k] = str(v)
|
||||
|
||||
try:
|
||||
return LogConfig(**options)
|
||||
@ -1192,6 +1221,19 @@ class Container(DockerBaseClass):
|
||||
self.parameters.expected_sysctls = None
|
||||
self.parameters.expected_etc_hosts = None
|
||||
self.parameters.expected_env = None
|
||||
self.parameters_map = dict()
|
||||
self.parameters_map['expected_links'] = 'links'
|
||||
self.parameters_map['expected_ports'] = 'expected_ports'
|
||||
self.parameters_map['expected_exposed'] = 'exposed_ports'
|
||||
self.parameters_map['expected_volumes'] = 'volumes'
|
||||
self.parameters_map['expected_ulimits'] = 'ulimits'
|
||||
self.parameters_map['expected_sysctls'] = 'sysctls'
|
||||
self.parameters_map['expected_etc_hosts'] = 'etc_hosts'
|
||||
self.parameters_map['expected_env'] = 'env'
|
||||
self.parameters_map['expected_entrypoint'] = 'entrypoint'
|
||||
self.parameters_map['expected_binds'] = 'volumes'
|
||||
self.parameters_map['expected_cmd'] = 'command'
|
||||
self.parameters_map['expected_devices'] = 'devices'
|
||||
|
||||
def fail(self, msg):
|
||||
self.parameters.client.module.fail_json(msg=msg)
|
||||
@ -1207,6 +1249,80 @@ class Container(DockerBaseClass):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _compare_dict_allow_more_present(self, av, bv):
|
||||
'''
|
||||
Compare two dictionaries for whether every entry of the first is in the second.
|
||||
'''
|
||||
for key, value in av.items():
|
||||
if key not in bv:
|
||||
return False
|
||||
if bv[key] != value:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _compare(self, a, b, compare):
|
||||
'''
|
||||
Compare values a and b as described in compare.
|
||||
'''
|
||||
method = compare['comparison']
|
||||
if method == 'ignore':
|
||||
return True
|
||||
# If a or b is None:
|
||||
if a is None or b is None:
|
||||
# If both are None: equality
|
||||
if a == b:
|
||||
return True
|
||||
# Otherwise, not equal for values, and equal
|
||||
# if the other is empty for set/list/dict
|
||||
if compare['type'] == 'value':
|
||||
return False
|
||||
return len(b if a is None else a) == 0
|
||||
# Do proper comparison (both objects not None)
|
||||
if compare['type'] == 'value':
|
||||
return a == b
|
||||
elif compare['type'] == 'list':
|
||||
if method == 'strict':
|
||||
return a == b
|
||||
else:
|
||||
set_a = set(a)
|
||||
set_b = set(b)
|
||||
return set_b >= set_a
|
||||
elif compare['type'] == 'dict':
|
||||
if method == 'strict':
|
||||
return a == b
|
||||
else:
|
||||
return self._compare_dict_allow_more_present(a, b)
|
||||
elif compare['type'] == 'set':
|
||||
set_a = set(a)
|
||||
set_b = set(b)
|
||||
if method == 'strict':
|
||||
return set_a == set_b
|
||||
else:
|
||||
return set_b >= set_a
|
||||
elif compare['type'] == 'set(dict)':
|
||||
for av in a:
|
||||
found = False
|
||||
for bv in b:
|
||||
if self._compare_dict_allow_more_present(av, bv):
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
return False
|
||||
if method == 'strict':
|
||||
# If we would know that both a and b do not contain duplicates,
|
||||
# we could simply compare len(a) to len(b) to finish this test.
|
||||
# We can assume that b has no duplicates (as it is returned by
|
||||
# docker), but we don't know for a.
|
||||
for bv in b:
|
||||
found = False
|
||||
for av in a:
|
||||
if self._compare_dict_allow_more_present(av, bv):
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
return False
|
||||
return True
|
||||
|
||||
def has_different_configuration(self, image):
|
||||
'''
|
||||
Diff parameters vs existing container config. Returns tuple: (True | False, List of differences)
|
||||
@ -1292,7 +1408,8 @@ class Container(DockerBaseClass):
|
||||
expected_volumes=config.get('Volumes'),
|
||||
expected_binds=host_config.get('Binds'),
|
||||
volumes_from=host_config.get('VolumesFrom'),
|
||||
working_dir=config.get('WorkingDir')
|
||||
working_dir=config.get('WorkingDir'),
|
||||
publish_all_ports=host_config.get('PublishAllPorts'),
|
||||
)
|
||||
if self.parameters.restart_policy:
|
||||
config_mapping['restart_retries'] = restart_policy.get('MaximumRetryCount')
|
||||
@ -1310,36 +1427,10 @@ class Container(DockerBaseClass):
|
||||
|
||||
differences = []
|
||||
for key, value in config_mapping.items():
|
||||
self.log('check differences %s %s vs %s' % (key, getattr(self.parameters, key), str(value)))
|
||||
compare = self.parameters.client.comparisons[self.parameters_map.get(key, key)]
|
||||
self.log('check differences %s %s vs %s (%s)' % (key, getattr(self.parameters, key), str(value), compare))
|
||||
if getattr(self.parameters, key, None) is not None:
|
||||
if isinstance(getattr(self.parameters, key), list) and isinstance(value, list):
|
||||
if len(getattr(self.parameters, key)) > 0 and isinstance(getattr(self.parameters, key)[0], dict):
|
||||
# compare list of dictionaries
|
||||
self.log("comparing list of dict: %s" % key)
|
||||
match = self._compare_dictionary_lists(getattr(self.parameters, key), value)
|
||||
else:
|
||||
# compare two lists. Is list_a in list_b?
|
||||
self.log("comparing lists: %s" % key)
|
||||
set_a = set(getattr(self.parameters, key))
|
||||
set_b = set(value)
|
||||
match = (set_b >= set_a)
|
||||
elif isinstance(getattr(self.parameters, key), list) and not len(getattr(self.parameters, key)) \
|
||||
and value is None:
|
||||
# an empty list and None are ==
|
||||
continue
|
||||
elif isinstance(getattr(self.parameters, key), dict) and isinstance(value, dict):
|
||||
# compare two dicts
|
||||
self.log("comparing two dicts: %s" % key)
|
||||
match = self._compare_dicts(getattr(self.parameters, key), value)
|
||||
|
||||
elif isinstance(getattr(self.parameters, key), dict) and \
|
||||
not len(list(getattr(self.parameters, key).keys())) and value is None:
|
||||
# an empty dict and None are ==
|
||||
continue
|
||||
else:
|
||||
# primitive compare
|
||||
self.log("primitive compare: %s" % key)
|
||||
match = (getattr(self.parameters, key) == value)
|
||||
match = self._compare(getattr(self.parameters, key), value, compare)
|
||||
|
||||
if not match:
|
||||
# no match. record the differences
|
||||
@ -1353,43 +1444,6 @@ class Container(DockerBaseClass):
|
||||
has_differences = True if len(differences) > 0 else False
|
||||
return has_differences, differences
|
||||
|
||||
def _compare_dictionary_lists(self, list_a, list_b):
|
||||
'''
|
||||
If all of list_a exists in list_b, return True
|
||||
'''
|
||||
if not isinstance(list_a, list) or not isinstance(list_b, list):
|
||||
return False
|
||||
matches = 0
|
||||
for dict_a in list_a:
|
||||
for dict_b in list_b:
|
||||
if self._compare_dicts(dict_a, dict_b):
|
||||
matches += 1
|
||||
break
|
||||
result = (matches == len(list_a))
|
||||
return result
|
||||
|
||||
def _compare_dicts(self, dict_a, dict_b):
|
||||
'''
|
||||
If dict_a in dict_b, return True
|
||||
'''
|
||||
if not isinstance(dict_a, dict) or not isinstance(dict_b, dict):
|
||||
return False
|
||||
for key, value in dict_a.items():
|
||||
if isinstance(value, dict):
|
||||
match = self._compare_dicts(value, dict_b.get(key))
|
||||
elif isinstance(value, list):
|
||||
if len(value) > 0 and isinstance(value[0], dict):
|
||||
match = self._compare_dictionary_lists(value, dict_b.get(key))
|
||||
else:
|
||||
set_a = set(value)
|
||||
set_b = set(dict_b.get(key))
|
||||
match = (set_a == set_b)
|
||||
else:
|
||||
match = (value == dict_b.get(key))
|
||||
if not match:
|
||||
return False
|
||||
return True
|
||||
|
||||
def has_different_resource_limits(self):
|
||||
'''
|
||||
Diff parameters and container resource limits
|
||||
@ -1403,7 +1457,6 @@ class Container(DockerBaseClass):
|
||||
cpu_period=host_config.get('CpuPeriod'),
|
||||
cpu_quota=host_config.get('CpuQuota'),
|
||||
cpuset_cpus=host_config.get('CpusetCpus'),
|
||||
cpuset_mems=host_config.get('CpusetMems'),
|
||||
kernel_memory=host_config.get("KernelMemory"),
|
||||
memory=host_config.get('Memory'),
|
||||
memory_reservation=host_config.get('MemoryReservation'),
|
||||
@ -1412,20 +1465,32 @@ class Container(DockerBaseClass):
|
||||
oom_killer=host_config.get('OomKillDisable'),
|
||||
)
|
||||
|
||||
if self.parameters.client.HAS_BLKIO_WEIGHT_OPT:
|
||||
# blkio_weight is only supported in docker>=1.9
|
||||
config_mapping['blkio_weight'] = host_config.get('BlkioWeight')
|
||||
|
||||
if self.parameters.client.HAS_CPUSET_MEMS_OPT:
|
||||
# cpuset_mems is only supported in docker>=2.3
|
||||
config_mapping['cpuset_mems'] = host_config.get('CpusetMems')
|
||||
|
||||
if HAS_DOCKER_PY_3:
|
||||
# cpu_shares moved to create_host_config in > 3
|
||||
config_mapping['cpu_shares'] = host_config.get('CpuShares')
|
||||
|
||||
differences = []
|
||||
for key, value in config_mapping.items():
|
||||
if getattr(self.parameters, key, None) and getattr(self.parameters, key) != value:
|
||||
# no match. record the differences
|
||||
item = dict()
|
||||
item[key] = dict(
|
||||
parameter=getattr(self.parameters, key),
|
||||
container=value
|
||||
)
|
||||
differences.append(item)
|
||||
if getattr(self.parameters, key, None):
|
||||
compare = self.parameters.client.comparisons[self.parameters_map.get(key, key)]
|
||||
match = self._compare(getattr(self.parameters, key), value, compare)
|
||||
|
||||
if not match:
|
||||
# no match. record the differences
|
||||
item = dict()
|
||||
item[key] = dict(
|
||||
parameter=getattr(self.parameters, key),
|
||||
container=value
|
||||
)
|
||||
differences.append(item)
|
||||
different = (len(differences) > 0)
|
||||
return different, differences
|
||||
|
||||
@ -1753,11 +1818,19 @@ class ContainerManager(DockerBaseClass):
|
||||
|
||||
def present(self, state):
|
||||
container = self._get_container(self.parameters.name)
|
||||
|
||||
# If the image parameter was passed then we need to deal with the image
|
||||
# version comparison. Otherwise we handle this depending on whether
|
||||
# the container already runs or not; in the former case, in case the
|
||||
# container needs to be restarted, we use the existing container's
|
||||
# image ID.
|
||||
image = self._get_image()
|
||||
self.log(image, pretty_print=True)
|
||||
if not container.exists:
|
||||
# New container
|
||||
self.log('No container found')
|
||||
if not self.parameters.image:
|
||||
self.fail('Cannot create container when image is not specified!')
|
||||
new_container = self.container_create(self.parameters.image, self.parameters.create_parameters)
|
||||
if new_container:
|
||||
container = new_container
|
||||
@ -1765,7 +1838,7 @@ class ContainerManager(DockerBaseClass):
|
||||
# Existing container
|
||||
different, differences = container.has_different_configuration(image)
|
||||
image_different = False
|
||||
if not self.parameters.ignore_image:
|
||||
if self.parameters.comparisons['image']['comparison'] == 'strict':
|
||||
image_different = self._image_is_different(image, container)
|
||||
if image_different or different or self.parameters.recreate:
|
||||
self.diff['differences'] = differences
|
||||
@ -1773,10 +1846,15 @@ class ContainerManager(DockerBaseClass):
|
||||
self.diff['image_different'] = True
|
||||
self.log("differences")
|
||||
self.log(differences, pretty_print=True)
|
||||
image_to_use = self.parameters.image
|
||||
if not image_to_use and container and container.Image:
|
||||
image_to_use = container.Image
|
||||
if not image_to_use:
|
||||
self.fail('Cannot recreate container when image is not specified or cannot be extracted from current container!')
|
||||
if container.running:
|
||||
self.container_stop(container.Id)
|
||||
self.container_remove(container.Id)
|
||||
new_container = self.container_create(self.parameters.image, self.parameters.create_parameters)
|
||||
new_container = self.container_create(image_to_use, self.parameters.create_parameters)
|
||||
if new_container:
|
||||
container = new_container
|
||||
|
||||
@ -2020,6 +2098,54 @@ class ContainerManager(DockerBaseClass):
|
||||
|
||||
class AnsibleDockerClientContainer(AnsibleDockerClient):
|
||||
|
||||
def _setup_comparisons(self):
|
||||
comparisons = {}
|
||||
comp_aliases = {}
|
||||
# Put in defaults
|
||||
explicit_types = dict(
|
||||
command='list',
|
||||
devices='set(dict)',
|
||||
dns_search_domains='list',
|
||||
dns_servers='list',
|
||||
env='set',
|
||||
entrypoint='list',
|
||||
etc_hosts='set',
|
||||
ulimits='set(dict)',
|
||||
)
|
||||
for option, data in self.module.argument_spec.items():
|
||||
# Ignore options which aren't used as container properties
|
||||
if option in ('docker_host', 'tls_hostname', 'api_version', 'timeout', 'cacert_path', 'cert_path',
|
||||
'key_path', 'ssl_version', 'tls', 'tls_verify', 'debug', 'env_file', 'force_kill',
|
||||
'keep_volumes', 'ignore_image', 'name', 'pull', 'purge_networks', 'recreate',
|
||||
'restart', 'state', 'stop_timeout', 'trust_image_content', 'networks'):
|
||||
continue
|
||||
# Determine option type
|
||||
if option in explicit_types:
|
||||
type = explicit_types[option]
|
||||
elif data['type'] == 'list':
|
||||
type = 'set'
|
||||
elif data['type'] == 'dict':
|
||||
type = 'dict'
|
||||
else:
|
||||
type = 'value'
|
||||
# Determine comparison type
|
||||
if type in ('list', 'value'):
|
||||
comparison = 'strict'
|
||||
else:
|
||||
comparison = 'allow_more_present'
|
||||
comparisons[option] = dict(type=type, comparison=comparison, name=option)
|
||||
# Keep track of aliases
|
||||
comp_aliases[option] = option
|
||||
for alias in data.get('aliases', []):
|
||||
comp_aliases[alias] = option
|
||||
# Process legacy ignore options
|
||||
if self.module.params['ignore_image']:
|
||||
comparisons['image']['comparison'] = 'ignore'
|
||||
# Add implicit options
|
||||
comparisons['publish_all_ports'] = dict(type='value', comparison='strict', name='published_ports')
|
||||
comparisons['expected_ports'] = dict(type='dict', comparison=comparisons['published_ports']['comparison'], name='expected_ports')
|
||||
self.comparisons = comparisons
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(AnsibleDockerClientContainer, self).__init__(**kwargs)
|
||||
|
||||
@ -2033,11 +2159,30 @@ class AnsibleDockerClientContainer(AnsibleDockerClient):
|
||||
self.fail("docker or docker-py version is %s. Minimum version required is 2.2 to set init option. "
|
||||
"If you use the 'docker-py' module, you have to switch to the docker 'Python' package." % (docker_version,))
|
||||
|
||||
uts_mode_supported = LooseVersion(docker_version) >= LooseVersion('3.5')
|
||||
if self.module.params.get("uts") is not None and not uts_mode_supported:
|
||||
self.fail("docker or docker-py version is %s. Minimum version required is 3.5 to set uts option. "
|
||||
"If you use the 'docker-py' module, you have to switch to the docker 'Python' package." % (docker_version,))
|
||||
|
||||
blkio_weight_supported = LooseVersion(docker_version) >= LooseVersion('1.9')
|
||||
if self.module.params.get("blkio_weight") is not None and not blkio_weight_supported:
|
||||
self.fail("docker or docker-py version is %s. Minimum version required is 1.9 to set blkio_weight option.")
|
||||
|
||||
cpuset_mems_supported = LooseVersion(docker_version) >= LooseVersion('2.3')
|
||||
if self.module.params.get("cpuset_mems") is not None and not cpuset_mems_supported:
|
||||
self.fail("docker or docker-py version is %s. Minimum version required is 2.3 to set cpuset_mems option. "
|
||||
"If you use the 'docker-py' module, you have to switch to the docker 'Python' package." % (docker_version,))
|
||||
|
||||
self.HAS_INIT_OPT = init_supported
|
||||
self.HAS_UTS_MODE_OPT = uts_mode_supported
|
||||
self.HAS_BLKIO_WEIGHT_OPT = blkio_weight_supported
|
||||
self.HAS_CPUSET_MEMS_OPT = cpuset_mems_supported
|
||||
self.HAS_AUTO_REMOVE_OPT = HAS_DOCKER_PY_2 or HAS_DOCKER_PY_3
|
||||
if self.module.params.get('auto_remove') and not self.HAS_AUTO_REMOVE_OPT:
|
||||
self.fail("'auto_remove' is not compatible with the 'docker-py' Python package. It requires the newer 'docker' Python package.")
|
||||
|
||||
self._setup_comparisons()
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = dict(
|
||||
@ -2057,9 +2202,9 @@ def main():
|
||||
dns_opts=dict(type='list'),
|
||||
dns_search_domains=dict(type='list'),
|
||||
domainname=dict(type='str'),
|
||||
entrypoint=dict(type='list'),
|
||||
env=dict(type='dict'),
|
||||
env_file=dict(type='path'),
|
||||
entrypoint=dict(type='list'),
|
||||
etc_hosts=dict(type='dict'),
|
||||
exposed_ports=dict(type='list', aliases=['exposed', 'expose']),
|
||||
force_kill=dict(type='bool', default=False, aliases=['forcekill']),
|
||||
@ -2084,7 +2229,6 @@ def main():
|
||||
memory_swappiness=dict(type='int'),
|
||||
name=dict(type='str', required=True),
|
||||
network_mode=dict(type='str'),
|
||||
userns_mode=dict(type='str'),
|
||||
networks=dict(type='list'),
|
||||
oom_killer=dict(type='bool'),
|
||||
oom_score_adj=dict(type='int'),
|
||||
@ -2099,21 +2243,22 @@ def main():
|
||||
restart=dict(type='bool', default=False),
|
||||
restart_policy=dict(type='str', choices=['no', 'on-failure', 'always', 'unless-stopped']),
|
||||
restart_retries=dict(type='int', default=None),
|
||||
shm_size=dict(type='str'),
|
||||
security_opts=dict(type='list'),
|
||||
shm_size=dict(type='str'),
|
||||
state=dict(type='str', choices=['absent', 'present', 'started', 'stopped'], default='started'),
|
||||
stop_signal=dict(type='str'),
|
||||
stop_timeout=dict(type='int'),
|
||||
sysctls=dict(type='dict'),
|
||||
tmpfs=dict(type='list'),
|
||||
trust_image_content=dict(type='bool', default=False),
|
||||
tty=dict(type='bool', default=False),
|
||||
ulimits=dict(type='list'),
|
||||
sysctls=dict(type='dict'),
|
||||
user=dict(type='str'),
|
||||
userns_mode=dict(type='str'),
|
||||
uts=dict(type='str'),
|
||||
volume_driver=dict(type='str'),
|
||||
volumes=dict(type='list'),
|
||||
volumes_from=dict(type='list'),
|
||||
volume_driver=dict(type='str'),
|
||||
working_dir=dict(type='str'),
|
||||
)
|
||||
|
||||
|
||||
@ -306,7 +306,7 @@ def _build_kwargs(cloud, module, router, network):
|
||||
if network:
|
||||
kwargs['ext_gateway_net_id'] = network['id']
|
||||
# can't send enable_snat unless we have a network
|
||||
if module.params['enable_snat']:
|
||||
if module.params.get('enable_snat') is not None:
|
||||
kwargs['enable_snat'] = module.params['enable_snat']
|
||||
|
||||
if module.params['external_fixed_ips']:
|
||||
|
||||
@ -61,7 +61,9 @@ requirements:
|
||||
- mysql (command line binary)
|
||||
- mysqldump (command line binary)
|
||||
notes:
|
||||
- Requires the python-mysqldb package on the remote host, as well as mysql and mysqldump binaries.
|
||||
- Requires the PyMySQL (Python 2.7 and Python 3.X) or MySQL-python (Python 2.X) package on the remote host,
|
||||
as well as mysql and mysqldump binaries.
|
||||
- This module is B(not idempotent) when I(state) is C(import), and will import the dump file each time if run more than once.
|
||||
extends_documentation_fragment: mysql
|
||||
'''
|
||||
|
||||
@ -100,16 +102,9 @@ import pipes
|
||||
import subprocess
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import MySQLdb
|
||||
except ImportError:
|
||||
mysqldb_found = False
|
||||
else:
|
||||
mysqldb_found = True
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.database import mysql_quote_identifier
|
||||
from ansible.module_utils.mysql import mysql_connect, mysqldb_found
|
||||
from ansible.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
|
||||
@ -268,8 +263,8 @@ def main():
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
if not mysqldb_found:
|
||||
module.fail_json(msg="The MySQL-python module is required.")
|
||||
if mysql_driver is None:
|
||||
module.fail_json(msg=mysql_driver_fail_msg)
|
||||
|
||||
db = module.params["name"]
|
||||
encoding = module.params["encoding"]
|
||||
|
||||
@ -118,15 +118,8 @@ EXAMPLES = '''
|
||||
import os
|
||||
import warnings
|
||||
|
||||
try:
|
||||
import MySQLdb
|
||||
except ImportError:
|
||||
mysqldb_found = False
|
||||
else:
|
||||
mysqldb_found = True
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.mysql import mysql_connect
|
||||
from ansible.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
|
||||
@ -239,16 +232,16 @@ def main():
|
||||
connect_timeout = module.params['connect_timeout']
|
||||
config_file = module.params['config_file']
|
||||
|
||||
if not mysqldb_found:
|
||||
module.fail_json(msg="The MySQL-python module is required.")
|
||||
if mysql_driver is None:
|
||||
module.fail_json(msg=mysql_driver_fail_msg)
|
||||
else:
|
||||
warnings.filterwarnings('error', category=MySQLdb.Warning)
|
||||
warnings.filterwarnings('error', category=mysql_driver.Warning)
|
||||
|
||||
login_password = module.params["login_password"]
|
||||
login_user = module.params["login_user"]
|
||||
|
||||
try:
|
||||
cursor = mysql_connect(module, login_user, login_password, config_file, ssl_cert, ssl_key, ssl_ca, None, 'MySQLdb.cursors.DictCursor',
|
||||
cursor = mysql_connect(module, login_user, login_password, config_file, ssl_cert, ssl_key, ssl_ca, None, 'mysql_driver.cursors.DictCursor',
|
||||
connect_timeout=connect_timeout)
|
||||
except Exception as e:
|
||||
if os.path.exists(config_file):
|
||||
@ -325,7 +318,7 @@ def main():
|
||||
chm.append("MASTER_AUTO_POSITION = 1")
|
||||
try:
|
||||
changemaster(cursor, chm, chm_params)
|
||||
except MySQLdb.Warning as e:
|
||||
except mysql_driver.Warning as e:
|
||||
result['warning'] = to_native(e)
|
||||
except Exception as e:
|
||||
module.fail_json(msg='%s. Query == CHANGE MASTER TO %s' % (to_native(e), chm))
|
||||
|
||||
@ -196,16 +196,9 @@ import re
|
||||
import string
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import MySQLdb
|
||||
except ImportError:
|
||||
mysqldb_found = False
|
||||
else:
|
||||
mysqldb_found = True
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.database import SQLParseError
|
||||
from ansible.module_utils.mysql import mysql_connect, mysqldb_found
|
||||
from ansible.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg
|
||||
from ansible.module_utils.six import iteritems
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
@ -578,8 +571,8 @@ def main():
|
||||
db = 'mysql'
|
||||
sql_log_bin = module.params["sql_log_bin"]
|
||||
|
||||
if not mysqldb_found:
|
||||
module.fail_json(msg="The MySQL-python module is required.")
|
||||
if mysql_driver is None:
|
||||
module.fail_json(msg=mysql_driver_fail_msg)
|
||||
|
||||
cursor = None
|
||||
try:
|
||||
@ -618,14 +611,14 @@ def main():
|
||||
else:
|
||||
changed = user_mod(cursor, user, host, host_all, None, encrypted, priv, append_privs, module)
|
||||
|
||||
except (SQLParseError, InvalidPrivsError, MySQLdb.Error) as e:
|
||||
except (SQLParseError, InvalidPrivsError, mysql_driver.Error) as e:
|
||||
module.fail_json(msg=to_native(e), exception=traceback.format_exc())
|
||||
else:
|
||||
if host_all:
|
||||
module.fail_json(msg="host_all parameter cannot be used when adding a user")
|
||||
try:
|
||||
changed = user_add(cursor, user, host, host_all, password, encrypted, priv, module.check_mode)
|
||||
except (SQLParseError, InvalidPrivsError, MySQLdb.Error) as e:
|
||||
except (SQLParseError, InvalidPrivsError, mysql_driver.Error) as e:
|
||||
module.fail_json(msg=to_native(e), exception=traceback.format_exc())
|
||||
elif state == "absent":
|
||||
if user_exists(cursor, user, host, host_all):
|
||||
|
||||
@ -50,16 +50,9 @@ import os
|
||||
import warnings
|
||||
from re import match
|
||||
|
||||
try:
|
||||
import MySQLdb
|
||||
except ImportError:
|
||||
mysqldb_found = False
|
||||
else:
|
||||
mysqldb_found = True
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.database import SQLParseError, mysql_quote_identifier
|
||||
from ansible.module_utils.mysql import mysql_connect, mysqldb_found
|
||||
from ansible.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
|
||||
@ -148,10 +141,10 @@ def main():
|
||||
module.fail_json(msg="Cannot run without variable to operate with")
|
||||
if match('^[0-9a-z_]+$', mysqlvar) is None:
|
||||
module.fail_json(msg="invalid variable name \"%s\"" % mysqlvar)
|
||||
if not mysqldb_found:
|
||||
module.fail_json(msg="The MySQL-python module is required.")
|
||||
if mysql_driver is None:
|
||||
module.fail_json(msg=mysql_driver_fail_msg)
|
||||
else:
|
||||
warnings.filterwarnings('error', category=MySQLdb.Warning)
|
||||
warnings.filterwarnings('error', category=mysql_driver.Warning)
|
||||
|
||||
try:
|
||||
cursor = mysql_connect(module, user, password, config_file, ssl_cert, ssl_key, ssl_ca, db,
|
||||
|
||||
@ -148,18 +148,10 @@ ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.mysql import mysql_connect
|
||||
from ansible.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg
|
||||
from ansible.module_utils.six import iteritems
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
try:
|
||||
import MySQLdb
|
||||
import MySQLdb.cursors
|
||||
except ImportError:
|
||||
MYSQLDB_FOUND = False
|
||||
else:
|
||||
MYSQLDB_FOUND = True
|
||||
|
||||
# ===========================================
|
||||
# proxysql module specific support methods.
|
||||
#
|
||||
@ -192,10 +184,8 @@ def perform_checks(module):
|
||||
msg="max_replication_lag must be set between 0 and 102400"
|
||||
)
|
||||
|
||||
if not MYSQLDB_FOUND:
|
||||
module.fail_json(
|
||||
msg="the python mysqldb module is required"
|
||||
)
|
||||
if mysql_driver is None:
|
||||
module.fail_json(msg=mysql_driver_fail_msg)
|
||||
|
||||
|
||||
def save_config_to_disk(cursor):
|
||||
@ -457,8 +447,8 @@ def main():
|
||||
login_user,
|
||||
login_password,
|
||||
config_file,
|
||||
cursor_class=MySQLdb.cursors.DictCursor)
|
||||
except MySQLdb.Error as e:
|
||||
cursor_class=mysql_driver.cursors.DictCursor)
|
||||
except mysql_driver.Error as e:
|
||||
module.fail_json(
|
||||
msg="unable to connect to ProxySQL Admin Module.. %s" % to_native(e)
|
||||
)
|
||||
@ -487,7 +477,7 @@ def main():
|
||||
" and doesn't need to be updated.")
|
||||
result['server'] = \
|
||||
proxysql_server.get_server_config(cursor)
|
||||
except MySQLdb.Error as e:
|
||||
except mysql_driver.Error as e:
|
||||
module.fail_json(
|
||||
msg="unable to modify server.. %s" % to_native(e)
|
||||
)
|
||||
@ -502,7 +492,7 @@ def main():
|
||||
result['changed'] = False
|
||||
result['msg'] = ("The server is already absent from the" +
|
||||
" mysql_hosts memory configuration")
|
||||
except MySQLdb.Error as e:
|
||||
except mysql_driver.Error as e:
|
||||
module.fail_json(
|
||||
msg="unable to remove server.. %s" % to_native(e)
|
||||
)
|
||||
|
||||
@ -73,17 +73,9 @@ ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.mysql import mysql_connect
|
||||
from ansible.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
try:
|
||||
import MySQLdb
|
||||
import MySQLdb.cursors
|
||||
except ImportError:
|
||||
MYSQLDB_FOUND = False
|
||||
else:
|
||||
MYSQLDB_FOUND = True
|
||||
|
||||
# ===========================================
|
||||
# proxysql module specific support methods.
|
||||
#
|
||||
@ -96,10 +88,8 @@ def perform_checks(module):
|
||||
msg="login_port must be a valid unix port number (0-65535)"
|
||||
)
|
||||
|
||||
if not MYSQLDB_FOUND:
|
||||
module.fail_json(
|
||||
msg="the python mysqldb module is required"
|
||||
)
|
||||
if mysql_driver is None:
|
||||
module.fail_json(msg=mysql_driver_fail_msg)
|
||||
|
||||
|
||||
def save_config_to_disk(variable, cursor):
|
||||
@ -211,8 +201,8 @@ def main():
|
||||
login_user,
|
||||
login_password,
|
||||
config_file,
|
||||
cursor_class=MySQLdb.cursors.DictCursor)
|
||||
except MySQLdb.Error as e:
|
||||
cursor_class=mysql_driver.cursors.DictCursor)
|
||||
except mysql_driver.Error as e:
|
||||
module.fail_json(
|
||||
msg="unable to connect to ProxySQL Admin Module.. %s" % to_native(e)
|
||||
)
|
||||
@ -231,7 +221,7 @@ def main():
|
||||
msg="The variable \"%s\" was not found" % variable
|
||||
)
|
||||
|
||||
except MySQLdb.Error as e:
|
||||
except mysql_driver.Error as e:
|
||||
module.fail_json(
|
||||
msg="unable to get config.. %s" % to_native(e)
|
||||
)
|
||||
@ -264,7 +254,7 @@ def main():
|
||||
msg="The variable \"%s\" was not found" % variable
|
||||
)
|
||||
|
||||
except MySQLdb.Error as e:
|
||||
except mysql_driver.Error as e:
|
||||
module.fail_json(
|
||||
msg="unable to set config.. %s" % to_native(e)
|
||||
)
|
||||
|
||||
@ -96,16 +96,9 @@ ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.mysql import mysql_connect
|
||||
from ansible.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
try:
|
||||
import MySQLdb
|
||||
except ImportError:
|
||||
MYSQLDB_FOUND = False
|
||||
else:
|
||||
MYSQLDB_FOUND = True
|
||||
|
||||
# ===========================================
|
||||
# proxysql module specific support methods.
|
||||
#
|
||||
@ -140,10 +133,8 @@ def perform_checks(module):
|
||||
" with the CONFIG config_layer")
|
||||
module.fail_json(msg=msg_string % module.params["direction"])
|
||||
|
||||
if not MYSQLDB_FOUND:
|
||||
module.fail_json(
|
||||
msg="the python mysqldb module is required"
|
||||
)
|
||||
if mysql_driver is None:
|
||||
module.fail_json(msg=mysql_driver_fail_msg)
|
||||
|
||||
|
||||
def manage_config(manage_config_settings, cursor):
|
||||
@ -201,7 +192,7 @@ def main():
|
||||
login_user,
|
||||
login_password,
|
||||
config_file)
|
||||
except MySQLdb.Error as e:
|
||||
except mysql_driver.Error as e:
|
||||
module.fail_json(
|
||||
msg="unable to connect to ProxySQL Admin Module.. %s" % to_native(e)
|
||||
)
|
||||
@ -214,7 +205,7 @@ def main():
|
||||
try:
|
||||
result['changed'] = manage_config(manage_config_settings,
|
||||
cursor)
|
||||
except MySQLdb.Error as e:
|
||||
except mysql_driver.Error as e:
|
||||
module.fail_json(
|
||||
msg="unable to manage config.. %s" % to_native(e)
|
||||
)
|
||||
|
||||
@ -137,18 +137,10 @@ ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.mysql import mysql_connect
|
||||
from ansible.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg
|
||||
from ansible.module_utils.six import iteritems
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
try:
|
||||
import MySQLdb
|
||||
import MySQLdb.cursors
|
||||
except ImportError:
|
||||
MYSQLDB_FOUND = False
|
||||
else:
|
||||
MYSQLDB_FOUND = True
|
||||
|
||||
# ===========================================
|
||||
# proxysql module specific support methods.
|
||||
#
|
||||
@ -161,10 +153,8 @@ def perform_checks(module):
|
||||
msg="login_port must be a valid unix port number (0-65535)"
|
||||
)
|
||||
|
||||
if not MYSQLDB_FOUND:
|
||||
module.fail_json(
|
||||
msg="the python mysqldb module is required"
|
||||
)
|
||||
if mysql_driver is None:
|
||||
module.fail_json(msg=mysql_driver_fail_msg)
|
||||
|
||||
|
||||
def save_config_to_disk(cursor):
|
||||
@ -424,8 +414,8 @@ def main():
|
||||
login_user,
|
||||
login_password,
|
||||
config_file,
|
||||
cursor_class=MySQLdb.cursors.DictCursor)
|
||||
except MySQLdb.Error as e:
|
||||
cursor_class=mysql_driver.cursors.DictCursor)
|
||||
except mysql_driver.Error as e:
|
||||
module.fail_json(
|
||||
msg="unable to connect to ProxySQL Admin Module.. %s" % to_native(e)
|
||||
)
|
||||
@ -454,7 +444,7 @@ def main():
|
||||
" and doesn't need to be updated.")
|
||||
result['user'] = \
|
||||
proxysql_user.get_user_config(cursor)
|
||||
except MySQLdb.Error as e:
|
||||
except mysql_driver.Error as e:
|
||||
module.fail_json(
|
||||
msg="unable to modify user.. %s" % to_native(e)
|
||||
)
|
||||
@ -469,7 +459,7 @@ def main():
|
||||
result['changed'] = False
|
||||
result['msg'] = ("The user is already absent from the" +
|
||||
" mysql_users memory configuration")
|
||||
except MySQLdb.Error as e:
|
||||
except mysql_driver.Error as e:
|
||||
module.fail_json(
|
||||
msg="unable to remove user.. %s" % to_native(e)
|
||||
)
|
||||
|
||||
@ -220,18 +220,10 @@ ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.mysql import mysql_connect
|
||||
from ansible.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg
|
||||
from ansible.module_utils.six import iteritems
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
try:
|
||||
import MySQLdb
|
||||
import MySQLdb.cursors
|
||||
except ImportError:
|
||||
MYSQLDB_FOUND = False
|
||||
else:
|
||||
MYSQLDB_FOUND = True
|
||||
|
||||
# ===========================================
|
||||
# proxysql module specific support methods.
|
||||
#
|
||||
@ -244,10 +236,8 @@ def perform_checks(module):
|
||||
msg="login_port must be a valid unix port number (0-65535)"
|
||||
)
|
||||
|
||||
if not MYSQLDB_FOUND:
|
||||
module.fail_json(
|
||||
msg="the python mysqldb module is required"
|
||||
)
|
||||
if mysql_driver is None:
|
||||
module.fail_json(msg=mysql_driver_fail_msg)
|
||||
|
||||
|
||||
def save_config_to_disk(cursor):
|
||||
@ -551,8 +541,8 @@ def main():
|
||||
login_user,
|
||||
login_password,
|
||||
config_file,
|
||||
cursor_class=MySQLdb.cursors.DictCursor)
|
||||
except MySQLdb.Error as e:
|
||||
cursor_class=mysql_driver.cursors.DictCursor)
|
||||
except mysql_driver.Error as e:
|
||||
module.fail_json(
|
||||
msg="unable to connect to ProxySQL Admin Module.. %s" % to_native(e)
|
||||
)
|
||||
@ -582,7 +572,7 @@ def main():
|
||||
result['rules'] = \
|
||||
proxysql_query_rule.get_rule_config(cursor)
|
||||
|
||||
except MySQLdb.Error as e:
|
||||
except mysql_driver.Error as e:
|
||||
module.fail_json(
|
||||
msg="unable to modify rule.. %s" % to_native(e)
|
||||
)
|
||||
@ -605,7 +595,7 @@ def main():
|
||||
result['changed'] = False
|
||||
result['msg'] = ("The rule is already absent from the" +
|
||||
" mysql_query_rules memory configuration")
|
||||
except MySQLdb.Error as e:
|
||||
except mysql_driver.Error as e:
|
||||
module.fail_json(
|
||||
msg="unable to remove rule.. %s" % to_native(e)
|
||||
)
|
||||
|
||||
@ -95,17 +95,9 @@ ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.mysql import mysql_connect
|
||||
from ansible.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
try:
|
||||
import MySQLdb
|
||||
import MySQLdb.cursors
|
||||
except ImportError:
|
||||
MYSQLDB_FOUND = False
|
||||
else:
|
||||
MYSQLDB_FOUND = True
|
||||
|
||||
# ===========================================
|
||||
# proxysql module specific support methods.
|
||||
#
|
||||
@ -135,10 +127,8 @@ def perform_checks(module):
|
||||
msg="reader_hostgroup cannot equal writer_hostgroup"
|
||||
)
|
||||
|
||||
if not MYSQLDB_FOUND:
|
||||
module.fail_json(
|
||||
msg="the python mysqldb module is required"
|
||||
)
|
||||
if mysql_driver is None:
|
||||
module.fail_json(msg=mysql_driver_fail_msg)
|
||||
|
||||
|
||||
def save_config_to_disk(cursor):
|
||||
@ -328,8 +318,8 @@ def main():
|
||||
login_user,
|
||||
login_password,
|
||||
config_file,
|
||||
cursor_class=MySQLdb.cursors.DictCursor)
|
||||
except MySQLdb.Error as e:
|
||||
cursor_class=mysql_driver.cursors.DictCursor)
|
||||
except mysql_driver.Error as e:
|
||||
module.fail_json(
|
||||
msg="unable to connect to ProxySQL Admin Module.. %s" % to_native(e)
|
||||
)
|
||||
@ -360,7 +350,7 @@ def main():
|
||||
result['repl_group'] = \
|
||||
proxysql_repl_group.get_repl_group_config(cursor)
|
||||
|
||||
except MySQLdb.Error as e:
|
||||
except mysql_driver.Error as e:
|
||||
module.fail_json(
|
||||
msg="unable to modify replication hostgroup.. %s" % to_native(e)
|
||||
)
|
||||
@ -378,7 +368,7 @@ def main():
|
||||
" mysql_replication_hostgroups memory" +
|
||||
" configuration")
|
||||
|
||||
except MySQLdb.Error as e:
|
||||
except mysql_driver.Error as e:
|
||||
module.fail_json(
|
||||
msg="unable to delete replication hostgroup.. %s" % to_native(e)
|
||||
)
|
||||
|
||||
@ -125,18 +125,10 @@ ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.mysql import mysql_connect
|
||||
from ansible.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg
|
||||
from ansible.module_utils.six import iteritems
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
try:
|
||||
import MySQLdb
|
||||
import MySQLdb.cursors
|
||||
except ImportError:
|
||||
MYSQLDB_FOUND = False
|
||||
else:
|
||||
MYSQLDB_FOUND = True
|
||||
|
||||
# ===========================================
|
||||
# proxysql module specific support methods.
|
||||
#
|
||||
@ -155,10 +147,8 @@ def perform_checks(module):
|
||||
msg="interval_ms must between 100ms & 100000000ms"
|
||||
)
|
||||
|
||||
if not MYSQLDB_FOUND:
|
||||
module.fail_json(
|
||||
msg="the python mysqldb module is required"
|
||||
)
|
||||
if mysql_driver is None:
|
||||
module.fail_json(msg=mysql_driver_fail_msg)
|
||||
|
||||
|
||||
def save_config_to_disk(cursor):
|
||||
@ -366,8 +356,8 @@ def main():
|
||||
login_user,
|
||||
login_password,
|
||||
config_file,
|
||||
cursor_class=MySQLdb.cursors.DictCursor)
|
||||
except MySQLdb.Error as e:
|
||||
cursor_class=mysql_driver.cursors.DictCursor)
|
||||
except mysql_driver.Error as e:
|
||||
module.fail_json(
|
||||
msg="unable to connect to ProxySQL Admin Module.. %s" % to_native(e)
|
||||
)
|
||||
@ -390,7 +380,7 @@ def main():
|
||||
" need to be updated.")
|
||||
result['schedules'] = \
|
||||
proxysql_schedule.get_schedule_config(cursor)
|
||||
except MySQLdb.Error as e:
|
||||
except mysql_driver.Error as e:
|
||||
module.fail_json(
|
||||
msg="unable to modify schedule.. %s" % to_native(e)
|
||||
)
|
||||
@ -413,7 +403,7 @@ def main():
|
||||
result['changed'] = False
|
||||
result['msg'] = ("The schedule is already absent from the" +
|
||||
" memory configuration")
|
||||
except MySQLdb.Error as e:
|
||||
except mysql_driver.Error as e:
|
||||
module.fail_json(
|
||||
msg="unable to remove schedule.. %s" % to_native(e)
|
||||
)
|
||||
|
||||
@ -309,7 +309,7 @@ def main():
|
||||
if original is None or original.endswith(b('\n')):
|
||||
result += b('\n')
|
||||
else:
|
||||
result = ''
|
||||
result = b''
|
||||
|
||||
if module._diff:
|
||||
diff['after'] = result
|
||||
|
||||
@ -328,7 +328,7 @@ def present(module, dest, regexp, line, insertafter, insertbefore, create,
|
||||
elif insertbefore and insertbefore != 'BOF':
|
||||
# If the line to insert before is at the beginning of the file
|
||||
# use the appropriate index value.
|
||||
if index[1] == 0:
|
||||
if index[1] <= 0:
|
||||
if b_lines[index[1]].rstrip(b('\r\n')) != b_line:
|
||||
b_lines.insert(index[1], b_line + b_linesep)
|
||||
msg = 'line replaced'
|
||||
|
||||
@ -172,7 +172,7 @@ class RabbitMqUser(object):
|
||||
return False
|
||||
|
||||
def _get_permissions(self):
|
||||
perms_out = self._exec(['list_user_permissions', self.username], True)
|
||||
perms_out = [perm for perm in self._exec(['list_user_permissions', self.username], True) if perm.strip()]
|
||||
|
||||
perms_list = list()
|
||||
for perm in perms_out:
|
||||
|
||||
@ -450,18 +450,20 @@ def main():
|
||||
if not force and checksum != '':
|
||||
destination_checksum = module.digest_from_file(dest, algorithm)
|
||||
|
||||
if checksum == destination_checksum:
|
||||
# Not forcing redownload, unless checksum does not match
|
||||
# allow file attribute changes
|
||||
module.params['path'] = dest
|
||||
file_args = module.load_file_common_arguments(module.params)
|
||||
file_args['path'] = dest
|
||||
changed = module.set_fs_attributes_if_different(file_args, False)
|
||||
if changed:
|
||||
module.exit_json(msg="file already exists but file attributes changed", dest=dest, url=url, changed=changed)
|
||||
module.exit_json(msg="file already exists", dest=dest, url=url, changed=changed)
|
||||
if checksum != destination_checksum:
|
||||
checksum_mismatch = True
|
||||
|
||||
checksum_mismatch = True
|
||||
# Not forcing redownload, unless checksum does not match
|
||||
if not force and not checksum_mismatch:
|
||||
# Not forcing redownload, unless checksum does not match
|
||||
# allow file attribute changes
|
||||
module.params['path'] = dest
|
||||
file_args = module.load_file_common_arguments(module.params)
|
||||
file_args['path'] = dest
|
||||
changed = module.set_fs_attributes_if_different(file_args, False)
|
||||
if changed:
|
||||
module.exit_json(msg="file already exists but file attributes changed", dest=dest, url=url, changed=changed)
|
||||
module.exit_json(msg="file already exists", dest=dest, url=url, changed=changed)
|
||||
|
||||
# If the file already exists, prepare the last modified time for the
|
||||
# request.
|
||||
|
||||
@ -433,7 +433,7 @@ class ModuleParameters(Parameters):
|
||||
elif self._values['address'] == 'any6':
|
||||
return 'any6'
|
||||
try:
|
||||
addr = netaddr.IPAddress(self._values['address'])
|
||||
addr = netaddr.IPAddress(self._values['address'].split('%')[0])
|
||||
return str(addr)
|
||||
except netaddr.AddrFormatError:
|
||||
raise F5ModuleError(
|
||||
|
||||
@ -630,7 +630,7 @@ class Interfaces(FactsBase):
|
||||
fact = dict()
|
||||
fact['port'] = self.parse_lldp_port(item)
|
||||
fact['sysname'] = self.parse_lldp_sysname(item)
|
||||
facts[local_intf].append(facts)
|
||||
facts[local_intf].append(fact)
|
||||
|
||||
return facts
|
||||
|
||||
|
||||
@ -135,7 +135,7 @@ class Default(FactsBase):
|
||||
self.facts['hostname'] = self.responses[1]
|
||||
|
||||
def parse_version(self, data):
|
||||
match = re.search(r'Version:\s*(\S+)', data)
|
||||
match = re.search(r'Version:\s*(.*)', data)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
|
||||
@ -404,7 +404,8 @@ def main():
|
||||
# if not a user service and both init script and unit file exist stdout should have enabled/disabled, otherwise use rc entries
|
||||
if not module.params['user'] and \
|
||||
is_initd and \
|
||||
(not out.strip().endswith('disabled') or sysv_is_enabled(unit)):
|
||||
not out.strip().endswith('disabled') and \
|
||||
sysv_is_enabled(unit):
|
||||
enabled = True
|
||||
|
||||
# default to current state
|
||||
|
||||
@ -175,12 +175,12 @@ def main():
|
||||
|
||||
# figure out started status, everyone does it different!
|
||||
is_started = False
|
||||
worked = False
|
||||
|
||||
# user knows other methods fail and supplied pattern
|
||||
if pattern:
|
||||
is_started = get_ps(module, pattern)
|
||||
worked = is_started = get_ps(module, pattern)
|
||||
else:
|
||||
worked = False
|
||||
if location.get('service'):
|
||||
# standard tool that has been 'destandarized' by reimplementation in other OS/distros
|
||||
cmd = '%s %s status' % (location['service'], name)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user