Compare commits

...

45 Commits

Author SHA1 Message Date
0a6c643e8e New release v2.6.0rc1 2018-06-05 19:55:46 -07:00
ddb60ce345 Implement new changelog generator.
(cherry picked from commit 70c475da6c)
2018-06-05 19:38:32 -07:00
14acce0b44 pin azure deps to exact versions
* added warnings for newer-than-expected versions
* prevent passing both api_version and profile for newer clients
2018-06-05 18:20:45 -07:00
9823432ec3 fix nxos_snapshot issues (#41034)
* fix nxos_snapshot issues

* review comments

* doc change

(cherry picked from commit 6733bffd5d)
2018-06-05 09:19:29 -07:00
25d52697af adds ansible-doc JSON plugin data dump
* used for changelog generation of new plugins

(cherry picked from commit 096d243526)
2018-06-04 18:05:48 -07:00
121139b755 Changelog fragment cleanup. 2018-06-04 17:10:44 -07:00
73b5c997f4 fix nxos_udld failing with httpapi (#41074)
Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>
(cherry picked from commit 72d630b831)
2018-06-04 13:42:03 -07:00
74a56f0ca1 fix nxos_banner issue (#41026)
(cherry picked from commit b6afb77a8a)
2018-06-04 13:42:03 -07:00
e24743ff19 Fixes to ios_logging (#41029)
* Logging size may not show up in config

* This is much simpler

* Avoid repetition in tests

* Both options of buffered are optional

(cherry picked from commit 92a95368fe)
2018-06-04 13:40:34 -07:00
773fdeb6f0 ovirt: Backport of bug fixes for 2.6 (#41092)
* ovirt_vms: Fix change cd (#40939)

* ovirt_vms: Fix attaching of CD

* ovirt_vms: Fix idemptency

* ovirt_templates: Add searching by cluster (#40934)

* ovirt_templates: Add searching by cluster

* ovirt_disks: Add searching by sd and vm_name

* Fix python syntax
2018-06-04 13:39:52 -07:00
e8d8fa4e1a Fix test-module failing to validate args (#41004)
* Fix test-module failing to validate args

The test-module pass a wrong argument _ansible_tmp cause the validation failed.
Change the argument _ansible_tmp to _ansible_tmpdir to fix this.

* Add a integration test for test-module.

Prior to this change, we don't have a test for test-module.

This change ensure the correctness of test-module script.
2018-06-04 13:38:19 -07:00
1ab973c3d3 Fixes not using the parent parameter (#41102)
The parent parameter was not being used in the module. This meant
that all child profiles would use the system-defined parent instead
of the parent specified in the module

(cherry picked from commit 457c813d46)
2018-06-04 13:35:31 -07:00
fb3f0a53d9 More correct fix for status problem (#41045)
Status is not being determined right when monitors are enabled but
the state is up or down. This patch fixes it.

(cherry picked from commit 033adf8cd5)
2018-06-04 08:42:46 -07:00
205b66ee7c Don't try to reuse ansible_port, it doesn't work that way (yet?) (#41022)
(cherry picked from commit 70e3a7c9e1)
2018-06-01 12:42:20 -07:00
eedf5d6c6d Fix cache timeout behavior
(cherry picked from commit c1400ce909)
2018-06-01 12:40:30 -07:00
8d345c7cd6 Do not try to import simplejson in jsonfile.py (#40983)
With the addition on ajson.py in cbb6a7f4e8, two
new classes were created: AnsibleJSONDecoder and AnsibleJSONEncoder. These
classes are used when calling json.looads() and json.dumps().

This works fine with everything except the jsonfile.py cache plugin, which would
first try to import simplejson as json, then fall back to json. When simplejson
is installed, the load() or dump methods from simplejson are called, which then
try to use the AnsibleJSONEncoder/AnsibleJSONDecoder subclass from ajson.py.
But asjon.py imports json, not simplejson, and things blow up.

(cherry picked from commit 479b26fe31)
2018-06-01 12:39:03 -07:00
08aeeefa90 Fix error reporting on bad type for config setting
(cherry picked from commit c86fd6e2df)
2018-06-01 10:38:19 -07:00
7d3ce99a6f Use colspan on td instead of divs for hierarchical tables (#39948)
Address Firefox table-rendering issues in docs. Refactor to use colspan to provide table cells which can vary in width and indentation; the outermost has the greatest colspan, and each nested key has a colspan of one less than the parent, with padding cells for indentation.
Apply styling to table cells to get the table height to work without hacks or browser-specific
styling.  Simplify the markup and CSS by removing extra divs. Use two passes over the options, return values, and return facts in the Jinja2 module-docs template: one to determine the maximum nesting depth to compute the maximum colspan needed, plus one to lay out the rows.
(cherry picked from commit fa5c0282a4)
2018-06-01 10:37:39 -07:00
4536fcc722 Re-allow SSH due to broken delegation (#40978)
Removes the SSH enforcement because it breaks situations where, for example,
an admin maintains Ansible Tower and another team needs to use Ansible
with the BIG-IP modules, but the Tower admin does not want to install
the F5-SDK on the Ansible Tower instance.

In this situation, the user needs to delegate the execution of the BIG-IP
module to another linux host that does have the dependencies installed.

Usually this is done with delegation, often over SSH. Limiting the SSH
connection here prevents this from working.

(cherry picked from commit 8bccd0830b)
2018-06-01 10:34:21 -07:00
cfc67ce177 Add docs detailing how to convert many with_X style loops to use loop and filters (#40964)
* Add docs detailing how to convert many with_X style loops to use loop and filters. Fixes #40727

* Switch lookup used in query vs lookup comparison, to not recommend use of nested lookup

* Improve docs based on feedback

(cherry picked from commit 1cec3c8daf)
2018-06-01 10:31:20 -07:00
6e7bdb04ee enforce required config
(cherry picked from commit cbedbd3c9c)
(cherry picked from commit bcabbe33c8)
2018-05-31 18:27:02 -07:00
8f2c34fc69 Add changelog entries from deleted CHANGELOG.md. 2018-05-31 16:06:49 -07:00
741e09f984 Pass cloud parameter to get_one call
When we switch this to pull from the CloudRegion object, we missed
passing the parameter along.
2018-05-31 13:54:19 -07:00
65d96bf354 K8s dynamic collected changes (#40745)
* Move k8s modules to dynamic backend

* update required openshift version

* update -> patch

* use new dynamic client exceptions

* style

* guard urllib3 import

* guard ansibleerror import

* give more information about error cause

* format in variable

* style

* rename tests

* Search for provided kind in a few more places to match old behavior, properly handle failure

* make common code use fail instead of fail_json, to work for lookup plugins as well

* update docs

* move openshift_raw tests into k8s tests

* fix typo

* Use diff of response and resource to determine change, don't do any checking client-side before making requests

* remove duplicate yaml blocks

* Update porting guide for k8s module

* remove invalid doc refs

* If fuzzy searching finds a resource, update resource_definition to match proper kind and version

* remote unsupported openshift_raw variables

* properly check environment variables when determining auth method:
2018-05-31 13:32:42 -07:00
cad351328d Search for macaddresses more stringently (#40901)
* Search for macaddresses more stringently

Fixes #40829

(cherry picked from commit e7afd3d378)
2018-05-31 13:18:15 -07:00
ca3c0dfaea Do not gather mem facts if command invalid (#40820)
* Do not gather mem facts if command invalid

In some firmwares, 'show memory statistics' fail, thus
do not populate mem if we got a failure after running that command.

* Fix pep8

* Warn if got error when running 'sh memory statistics'

* Fix pep8

(cherry picked from commit 669949e6a3)
2018-05-31 13:17:15 -07:00
42661ba459 Fix for updating the name in case of host record and network view and also display meaningful error in case of connection timeout (#40597)
* To fix following github issues 35774, 36574 and 39494

* To fix following github issues 35774, 36574 and 39494

* To fix following github issues 35774, 36574 and 39494

* To fix following github issues 35774, 36574 and 39494

* To fix following github issues 35774, 36574 and 39494

* To fix following github issues 35774, 36574 and 39494

* removed old_name new entry to make ui cleaner

* removed old_name new entry to make ui cleaner

* removed old_name new entry to make ui cleaner

* removed old_name new entry to make ui cleaner

* removed old_name new entry to make ui cleaner

* removed old_name new entry to make ui cleaner

* to resolve the bug 40709

* reslove shippable error

* reslove shippable error

* reslove shippable error

* reslove shippable error

* reslove shippable error

* reslove shippable error

* reslove shippable error

* reslove shippable error

* reslove shippable error

* to fix shippable nios automation error

* modified the name input parsing method

* modified the name input parsing method

* modified the name input parsing method

* modified the name input parsing method

* modified the name input parsing method

* modified the name input parsing method

* modified the name input parsing method

* modified the name input parsing method

* modified the name input parsing method

* shippable error fix

* shippable error fix

* shippable error fix

* shippable error fix

* shippable error fix

* review comment fix

* shippable error fix

* shippable error fix

(cherry picked from commit 81510970ae)
2018-05-31 13:10:55 -07:00
7f50d11806 Command authorization failed ios regex (#40819)
* Add 'Command authorization failed' to stderr regex list

* Add missing comma

* Remove superfluous comma

(cherry picked from commit 026de6bcb0)
2018-05-31 13:00:58 -07:00
1ea5790f56 Fixes eos_logging idempotence issue #31862 (#40604)
* eos_logging idempotence fix

* fixed eos_logging idempotence issue

* Fixed pylint and pep8 errors

* Added tests for eos_logging & minor fix

* Removed q statements

(cherry picked from commit b9ea646839)
2018-05-31 10:01:03 -07:00
a3178a1153 Fixes ios_logging idempotency issues (#40701)
* Fixes ios_logging idempotency issues

* Added intergration tests & minor fixes

* Minor fixes in tests

* Minor fixes in tests #2

* eos_logging fixes after PR review

(cherry picked from commit a013cdc747)
2018-05-31 10:00:42 -07:00
079a1c3d06 fix nxos output truncated issue (#40961)
Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>
2018-05-31 12:35:41 -04:00
9bb260b543 Update Ansible release version to 2.6.0.dev0. 2018-05-30 19:18:27 -07:00
707f069fe8 New release v2.6.0a2 2018-05-30 17:47:09 -07:00
87a163064f Backport #40833 for 2.6 - synchronize _remote_is_local (#40918)
* Use _remote_is_local=True for local connection in synchronize (#40833)

* All instances of local connection should use _remote_is_local=True. Fixes #40551

* Switch to instance attribute for synchronize

* Add test that shows that synchronize _remote_is_local addresses tmpdir building

(cherry picked from commit ad7ba91f75)

* Add changelog entry for #40833
2018-05-30 16:53:50 -05:00
a2ca2e2a25 reinstates connection:local as an option for nxapi (#40834) (#40916)
(cherry picked from commit 0e0a5243c8)
2018-05-30 16:06:32 -05:00
0c907779a4 updates network examples to 2.6 (#40831) (#40899)
(cherry picked from commit e2146a7696)
2018-05-30 15:29:06 -05:00
b184e73b89 Fix FreeBSD package install in test setup.sh.
(cherry picked from commit c3d3b6cedc)
2018-05-30 09:00:43 -07:00
848327a6b5 Fix eapi tests to use become (#40838) (#40848)
* Fix eapi tests to use become (#40838)

* Return the expected prompt character based on become status

* Update eos_banner tests for eapi

* Update eos_config tests for eapi

* Update eos_facts tests for eapi

* Update eos_interface tests for eapi

* Update eos_l3_interface tests for eapi

* Update eos_lldp tests for eapi

* Update eos_logging tests for eapi

* Update eos_smoke tests for eapi

* Update eos_system tests for eapi

(cherry picked from commit 20f93816d6)

* Remove local from most eos tests now that provider is out
2018-05-30 09:56:31 -04:00
592c6877b5 fix nxos dci failures (#40871) (#40873)
* fix structured output for nxos_linkagg

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* fix TypeError for nxos_interface

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>
(cherry picked from commit 786ec14e54)
2018-05-30 15:35:46 +05:30
58f018e367 nxos and httpapi cherry-pick (#40859)
* httpapi fix nxos (#40806)

* httpapi fix nxos

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* nxos_hsrp fix

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>
(cherry picked from commit a7421e8611)

* fix nxos_vrf and migrate get_interface_type to module_utils (#40825)

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>
(cherry picked from commit b4baa2d484)

* nxos_vlan fix (#40822)

* nxos_vlan fix

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* uncomment mode test as nxapi now has get_capabilities

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>
(cherry picked from commit 17b6ecf101)

* Update nxos_install_os module (#40102)

* Add nxos_install_os integration tests

* Update call to check timers

* Update check_ansible_timer method

* Modify network_cli integration tests

* Add timer check for nxos_install_os

* Add comments for clear_persistent_sockets

* Update connection info for tests

* More updates

* Restructure files for provider and non-provider testing

* Update env var name and add check for ISSU switchover

(cherry picked from commit 9f026309a6)
2018-05-30 10:40:25 +05:30
828d878bf9 Bump python support version for openstack modules (#40751)
The openstack modules do not support python 2.6 as the underlying
library dependency (openstacksdk) does not support python 2.6. Update
the docs to make this clear.
2018-05-29 08:52:51 -04:00
b97a47ac2c Fix connection error reporting (#40758) (#40759)
The variables used for reporting errors were incorrect. Additionally,
the fetching of connection related params was using an old format.

(cherry picked from commit dc08bca1fd)
2018-05-26 17:38:26 -07:00
a018a1e6d4 Fixes parameters to fail_json (#40756) (#40757)
The number of parameters was incorrect. This patch fixes it.

(cherry picked from commit 547a1ed67d)
2018-05-26 16:06:00 -07:00
882f249f1d Revert "Move k8s modules to dynamic backend (#39632)" (#40743) (#40744)
This reverts commit 4373b155a5.
2018-05-26 00:32:40 -05:00
3f83e7e473 Initial stable-2.6 branch commit. 2018-05-25 21:15:12 -07:00
243 changed files with 3905 additions and 1897 deletions

1
.gitignore vendored
View File

@ -103,3 +103,4 @@ htmlcov/
# ansible-test coverage results
test/units/.coverage.*
/test/integration/cloud-config-azure.yml
changelogs/.plugin-cache.yaml

View File

@ -239,9 +239,9 @@ sdist: clean docs
sdist_upload: clean docs
$(PYTHON) setup.py sdist upload 2>&1 |tee upload.log
.PHONY: changelog_reno
changelog_reno:
reno -d changelogs/ report --title 'Ansible $(MAJOR_VERSION) "$(CODENAME)" Release Notes' --collapse-pre-release --no-show-source --earliest-version v$(MAJOR_VERSION).0a1 --output changelogs/CHANGELOG-v$(MAJOR_VERSION).rst
.PHONY: changelog
changelog:
packaging/release/changelogs/changelog.py release -vv && packaging/release/changelogs/changelog.py generate -vv
.PHONY: rpmcommon
rpmcommon: sdist

280
changelogs/.changes.yaml Normal file
View File

@ -0,0 +1,280 @@
releases:
2.6.0a1:
codename: Heartbreaker
fragments:
- ansible-managed-encoding.yaml
- azure-lb-sku.yaml
- base64_filter_encoding.yaml
- command-support-argv.yaml
- copy-files-default-follow.yaml
- copy-no-chmod.yaml
- copy-recursive-copy.yml
- encoding-docs-plugin-parsing.yaml
- file-disallow-src.yaml
- file-nonexistent-link.yaml
- file-nonexitent-link-recurse.yaml
- file-succeed-if-already-absent.yaml
- gem-custom-home.yaml
- grafana-deprecation-warnings.yaml
- includes.yaml
- mode-preserve.yaml
- pause-ctrl-c.yaml
- postgresql_user-encrypted-fixes.yml
- py36-spwd.yaml
- template-filename-encoding.yaml
- user-expires.yaml
- v2.6.0a1_catchup.yaml
- v2.6.0a1_summary.yaml
- win_environment-do-not-delete-on-null.yaml
- win_file-special-char-fix.yaml
- win_get_url-ftp-support.yaml
- win_module_utils_sid-conversion.yaml
- win_template-dest-dir-fix.yaml
- win_updates_typo_whitelist_fix.yaml
- window_become-better-errors.yaml
- windows-deprecation-removal.yaml
- winrm-encoding-fix
- winrm-get_correct_user.yaml
- yaml-callback-unicode.yaml
modules:
- aci_l3out
- avi_autoscalelaunchconfig
- avi_l4policyset
- avi_useraccount
- aws_caller_facts
- aws_inspector_target
- aws_ses_identity_policy
- aws_sgw_facts
- azure_rm_aks
- azure_rm_aks_facts
- bigip_data_group
- bigip_device_license
- bigip_gtm_global
- bigip_gtm_monitor_bigip
- bigip_gtm_monitor_firepass
- bigip_gtm_monitor_http
- bigip_gtm_monitor_https
- bigip_gtm_monitor_tcp
- bigip_gtm_monitor_tcp_half_open
- bigip_gtm_pool_member
- bigip_gtm_virtual_server
- bigip_log_destination
- bigip_log_publisher
- bigip_management_route
- bigip_profile_dns
- bigip_profile_tcp
- bigip_profile_udp
- bigip_service_policy
- bigip_smtp
- bigip_snmp_community
- bigip_timer_policy
- bigip_trunk
- bigiq_application_fasthttp
- bigiq_application_fastl4_tcp
- bigiq_application_fastl4_udp
- bigiq_application_http
- bigiq_application_https_offload
- bigiq_application_https_waf
- bigiq_regkey_license_assignment
- cnos_command
- cnos_config
- cs_role_permission
- digital_ocean_account_facts
- digital_ocean_certificate_facts
- digital_ocean_domain_facts
- digital_ocean_image_facts
- digital_ocean_load_balancer_facts
- digital_ocean_region_facts
- digital_ocean_size_facts
- digital_ocean_snapshot_facts
- digital_ocean_tag_facts
- digital_ocean_volume_facts
- ec2_eip_facts
- ec2_vpc_vpn_facts
- exos_command
- fortios_webfilter
- gcp_compute_address
- gcp_compute_backend_bucket
- gcp_compute_backend_service
- gcp_compute_health_check
- gcp_compute_http_health_check
- gcp_compute_https_health_check
- gcp_compute_instance
- gcp_compute_instance_template
- gcp_compute_network
- gcp_compute_route
- gcp_compute_subnetwork
- gcp_compute_target_http_proxy
- gcp_compute_target_https_proxy
- gcp_compute_target_tcp_proxy
- gcp_container_cluster
- gcp_container_node_pool
- gcp_dns_resource_record_set
- gcp_pubsub_subscription
- gcp_pubsub_topic
- gcp_storage_bucket
- gcp_storage_bucket_access_control
- gitlab_deploy_key
- gitlab_hooks
- k8s
- ldap_passwd
- memset_zone
- meraki_network
- meraki_organization
- na_ontap_broadcast_domain
- net_get
- net_put
- netconf_get
- one_host
- one_image
- one_image_facts
- one_vm
- os_server_metadata
- os_volume_snapshot
- psexec
- purefa_ds
- purefa_facts
- purefa_pgsnap
- purefb_fs
- purefb_snap
- rds_instance_facts
- rds_snapshot_facts
- scaleway_compute
- scaleway_sshkey
- slxos_command
- slxos_config
- slxos_facts
- slxos_interface
- slxos_l2_interface
- slxos_linkagg
- spectrum_device
- sysvinit
- vmware_cluster_facts
- vmware_datastore_cluster
- vmware_datastore_maintenancemode
- vmware_guest_disk_facts
- vmware_guest_snapshot_facts
- vmware_host_capability_facts
- vmware_host_powerstate
- vmware_local_user_facts
- vmware_portgroup_facts
- vmware_resource_pool_facts
- vmware_tag
- vmware_tag_facts
- vmware_vswitch_facts
- win_domain_computer
- win_hostname
- win_pester
- yarn
- zabbix_group_facts
plugins:
callback:
- cgroup_memory_recap
- grafana_annotations
- sumologic
connection:
- httpapi
inventory:
- foreman
- generator
- nmap
lookup:
- onepassword
- onepassword_raw
release_date: '2018-05-21'
2.6.0a2:
codename: Heartbreaker
fragments:
- always_run_removal.yaml
- aws_ses_identity_check_mode.yaml
- powershell_use_set_tmpdir.yaml
- selinux-check-mode.yaml
- synchronize_remote_is_local.yaml
- v2.6.0a2_catchup.yaml
- v2.6.0a2_summary.yaml
- win_updates-add-scheduled-tasks-back.yaml
- winrm_kinit_error-fix.yaml
- xenserver-facts-fix.yaml
modules:
- acme_account
- aws_config_aggregation_authorization
- aws_config_aggregator
- aws_config_delivery_channel
- aws_config_recorder
- aws_config_rule
- aws_glue_connection
- aws_glue_job
- azure_rm_resource
- azure_rm_resource_facts
- bigip_gtm_monitor_external
- bigip_monitor_external
- bigiq_utility_license
- cloud_init_data_facts
- elb_network_lb
- flatpak
- flatpak_remote
- gcp_compute_disk
- gcp_compute_firewall
- gcp_compute_forwarding_rule
- gcp_compute_global_address
- gcp_compute_global_forwarding_rule
- gcp_compute_image
- gcp_compute_instance_group
- gcp_compute_instance_group_manager
- gcp_compute_ssl_certificate
- gcp_compute_target_pool
- gcp_compute_target_ssl_proxy
- gcp_compute_url_map
- gluster_peer
- heroku_collaborator
- memset_dns_reload
- memset_zone_domain
- memset_zone_record
- meraki_admin
- meraki_snmp
- na_ontap_aggregate
- na_ontap_broadcast_domain_ports
- na_ontap_cifs
- na_ontap_cifs_acl
- na_ontap_cifs_server
- na_ontap_cluster
- na_ontap_cluster_ha
- na_ontap_export_policy
- na_ontap_export_policy_rule
- na_ontap_igroup
- na_ontap_interface
- na_ontap_iscsi
- na_ontap_job_schedule
- na_ontap_license
- na_ontap_lun
- na_ontap_lun_map
- na_ontap_net_ifgrp
- na_ontap_net_port
- na_ontap_net_routes
- na_ontap_net_vlan
- na_ontap_nfs
- na_ontap_ntp
- na_ontap_qtree
- na_ontap_service_processor_network
- na_ontap_snapshot
- na_ontap_snmp
- na_ontap_svm
- na_ontap_ucadapter
- na_ontap_user
- na_ontap_user_role
- na_ontap_volume
- na_ontap_volume_clone
- netconf_rpc
- one_service
- slxos_l3_interface
- slxos_vlan
plugins:
inventory:
- gcp_compute
release_date: '2018-05-30'
2.6.0rc1:
codename: Heartbreaker
fragments:
- v2.6.0rc1_catchup.yaml
- v2.6.0rc1_summary.yaml
release_date: '2018-06-05'

View File

@ -2,13 +2,204 @@
Ansible 2.6 "Heartbreaker" Release Notes
========================================
.. _Ansible 2.6 "Heartbreaker" Release Notes_v2.6.0a1:
v2.6.0rc1
=========
Release Summary
---------------
| Release Date: 2018-06-05
| `Porting Guide <https://docs.ansible.com/ansible/devel/porting_guides.html>`_
v2.6.0a2
========
Release Summary
---------------
| Release Date: 2018-05-30
| `Porting Guide <https://docs.ansible.com/ansible/devel/porting_guides.html>`_
Minor Changes
-------------
- The aws_ses_identity module supports check mode
Removed Features (previously deprecated)
----------------------------------------
- removed the deprecated always_run task option, please use `check_mode: no` instead
Bugfixes
--------
- powershell - use the tmpdir set by `remote_tmp` for become/async tasks instead of the generic $env:TEMP - https://github.com/ansible/ansible/pull/40210
- selinux - correct check mode behavior to report same changes as normal mode (https://github.com/ansible/ansible/pull/40721)
- synchronize - Ensure the local connection created by synchronize uses _remote_is_local=True, which causes ActionBase to build a local tmpdir (https://github.com/ansible/ansible/pull/40833)
- win_updates - Added the ability to run on a scheduled task for older hosts so async starts working again - https://github.com/ansible/ansible/issues/38364
- winrm - Add better error handling when the kinit process fails
- xenserver_facts - ensure module works with newer versions of XenServer (https://github.com/ansible/ansible/pull/35821)
New Plugins
-----------
Inventory
~~~~~~~~~
- gcp_compute - Google Cloud Compute Engine inventory source
New Modules
-----------
Cloud
~~~~~
amazon
^^^^^^
- aws_config_aggregation_authorization - Manage cross-account AWS Config authorizations
- aws_config_aggregator - Manage AWS Config aggregations across multiple accounts
- aws_config_delivery_channel - Manage AWS Config delivery channels
- aws_config_recorder - Manage AWS Config Recorders
- aws_config_rule - Manage AWS Config resources
- aws_glue_connection - Manage an AWS Glue connection
- aws_glue_job - Manage an AWS Glue job
- elb_network_lb - Manage a Network Load Balancer
azure
^^^^^
- azure_rm_resource - Create any Azure resource.
- azure_rm_resource_facts - Generic facts of Azure resources.
google
^^^^^^
- gcp_compute_disk - Creates a GCP Disk
- gcp_compute_firewall - Creates a GCP Firewall
- gcp_compute_forwarding_rule - Creates a GCP ForwardingRule
- gcp_compute_global_address - Creates a GCP GlobalAddress
- gcp_compute_global_forwarding_rule - Creates a GCP GlobalForwardingRule
- gcp_compute_image - Creates a GCP Image
- gcp_compute_instance_group - Creates a GCP InstanceGroup
- gcp_compute_instance_group_manager - Creates a GCP InstanceGroupManager
- gcp_compute_ssl_certificate - Creates a GCP SslCertificate
- gcp_compute_target_pool - Creates a GCP TargetPool
- gcp_compute_target_ssl_proxy - Creates a GCP TargetSslProxy
- gcp_compute_url_map - Creates a GCP UrlMap
heroku
^^^^^^
- heroku_collaborator - Add or delete app collaborators on Heroku
memset
^^^^^^
- memset_dns_reload - Request reload of Memset's DNS infrastructure,
- memset_zone_domain - Create and delete domains in Memset DNS zones.
- memset_zone_record - Create and delete records in Memset DNS zones.
misc
^^^^
- cloud_init_data_facts - Retrieve facts of cloud-init.
opennebula
^^^^^^^^^^
- one_service - Deploy and manage OpenNebula services
Network
~~~~~~~
f5
^^
- bigip_gtm_monitor_external - Manages external GTM monitors on a BIG-IP
- bigip_monitor_external - Manages external LTM monitors on a BIG-IP
- bigiq_utility_license - Manage utility licenses on a BIG-IQ
meraki
^^^^^^
- meraki_admin - Manage administrators in the Meraki cloud
- meraki_snmp - Manage organizations in the Meraki cloud
netconf
^^^^^^^
- netconf_rpc - Execute operations on NETCONF enabled network devices.
slxos
^^^^^
- slxos_l3_interface - Manage L3 interfaces on Extreme Networks SLXOS network devices.
- slxos_vlan - Manage VLANs on Extreme Networks SLX-OS network devices
Packaging
~~~~~~~~~
os
^^
- flatpak - Manage flatpaks
- flatpak_remote - Manage flatpak repository remotes
Storage
~~~~~~~
glusterfs
^^^^^^^^^
- gluster_peer - Attach/Detach peers to/from the cluster
netapp
^^^^^^
- na_ontap_aggregate - Manage NetApp ONTAP aggregates.
- na_ontap_broadcast_domain_ports - Manage NetApp Ontap broadcast domain ports
- na_ontap_cifs - Manage NetApp cifs-share
- na_ontap_cifs_acl - Manage NetApp cifs-share-access-control
- na_ontap_cifs_server - cifs server configuration
- na_ontap_cluster - Create/Join ONTAP cluster. Apply license to cluster
- na_ontap_cluster_ha - Manage HA status for cluster
- na_ontap_export_policy - Manage NetApp ONTAP export-policy
- na_ontap_export_policy_rule - Manage ONTAP Export rules
- na_ontap_igroup - ONTAP iSCSI igroup configuration
- na_ontap_interface - ONTAP LIF configuration
- na_ontap_iscsi - Manage NetApp Ontap iscsi service
- na_ontap_job_schedule - Manage NetApp Ontap Job Schedule
- na_ontap_license - Manage NetApp ONTAP protocol and feature licenses
- na_ontap_lun - Manage NetApp Ontap luns
- na_ontap_lun_map - Manage NetApp Ontap lun maps
- na_ontap_net_ifgrp - Create, modify, destroy the network interface group
- na_ontap_net_port - Manage NetApp Ontap network ports.
- na_ontap_net_routes - Manage NetApp Ontap network routes
- na_ontap_net_vlan - Manage NetApp Ontap network vlan
- na_ontap_nfs - Manage Ontap NFS status
- na_ontap_ntp - Create/Delete/modify_version ONTAP NTP server
- na_ontap_qtree - Manage qtrees
- na_ontap_service_processor_network - Manage NetApp Ontap service processor network
- na_ontap_snapshot - Manage NetApp Sanpshots
- na_ontap_snmp - Manage NetApp SNMP community
- na_ontap_svm - Manage NetApp Ontap svm
- na_ontap_ucadapter - ONTAP UC adapter configuration
- na_ontap_user - useradmin configuration and management
- na_ontap_user_role - useradmin configuration and management
- na_ontap_volume - Manage NetApp ONTAP volumes.
- na_ontap_volume_clone - Manage NetApp Ontap volume clones.
Web Infrastructure
~~~~~~~~~~~~~~~~~~
- acme_account - Create, modify or delete accounts with Let's Encrypt
v2.6.0a1
========
.. _Ansible 2.6 "Heartbreaker" Release Notes_v2.6.0a1_Release Summary:
Release Summary
---------------
@ -16,116 +207,378 @@ Release Summary
| `Porting Guide <https://docs.ansible.com/ansible/devel/porting_guides.html>`_
.. _Ansible 2.6 "Heartbreaker" Release Notes_v2.6.0a1_Minor Changes:
Minor Changes
-------------
- azure_rm_loadbalancer - add support for sku
- azure_rm_publicipaddress - add support for sku
- Added an ``encoding`` option to the ``b64encode`` and ``b64decode`` filters to specify the encoding of the string that is base64 encoded.
- import/include - Cache task_vars to speed up IncludedFile.process_include_results (https://github.com/ansible/ansible/pull/39026)
- PowerShell modules that use Convert-ToSID in Ansible.ModuleUtils.SID.psm1 like win_user_right now accept an actual SID as an input string. This means any local or domain accounts that are named like a SID need to be prefixed with the domain, hostname, or . to ensure it converts to that accounts SID https://github.com/ansible/ansible/issues/38502
- `postgresql_user` module changed `encrypted=yes` to be the default. This shouldn't break any current playbooks, the module will just store passwords hashed by default. This change was done because Postgres 10 dropped support for `UNENCRYPTED` passwords and because all versions since Postgres 7.2 support storing encrypted passwords.
- azure_rm_loadbalancer - add support for sku
- azure_rm_publicipaddress - add support for sku
- cloudflare_dns module - Removed restriction from protocol to allow other protocols than tcp and udp to be specified.
- command module - Added argv option to allow command to be specified as a list vs. a string (https://github.com/ansible/ansible/issues/19392)
- gem - add ability to specify a custom directory for installing gems (https://github.com/ansible/ansible/pull/38195)
- import/include - Cache task_vars to speed up IncludedFile.process_include_results (https://github.com/ansible/ansible/pull/39026)
- postgresql_user module - Changed encrypted=yes to be the default. This shouldn't break any current playbooks, the module will just store passwords hashed by default. This change was done because Postgres 10 dropped support for UNENCRYPTED passwords and because all versions since Postgres 7.2 support storing encrypted passwords.
- vmware_target_canonical_facts module - The target_id parameter is an optional parameter.
Deprecated Features
-------------------
.. _Ansible 2.6 "Heartbreaker" Release Notes_v2.6.0a1_Removed Features (previously deprecated):
- nxos_igmp_interface module - The oif_prefix and oif_source properties are deprecated. Use the oif_ps parameter with a dictionary of prefix and source to values instead.
Removed Features (previously deprecated)
----------------------------------------
- win_chocolatey - removed deprecated upgrade option and choco_* output return values
- win_feature - removed deprecated reboot option
- win_iis_webapppool - removed the ability to supply attributes as a string in favour of a dictionary
- win_package - removed deprecated name option
- win_regedit - removed deprecated support for specifying HKCC as HCCC
.. _Ansible 2.6 "Heartbreaker" Release Notes_v2.6.0a1_Bugfixes:
Bugfixes
--------
- template - Fix for encoding issues when a template path contains non-ascii characters and using the template path in ansible_managed (https://github.com/ansible/ansible/issues/27262)
- copy - fixed copy to only follow symlinks for files in the non-recursive case
- file - fixed the default follow behaviour of file to be true
- copy module - The copy module was attempting to change the mode of files for remote_src=True even if mode was not set as a parameter. This failed on filesystems which do not have permission bits (https://github.com/ansible/ansible/pull/40099)
- Fix an encoding issue when parsing the examples from a plugins' documentation
- file module - The file module allowed the user to specify src as a parameter when state was not link or hard. This is documented as only applying to state=link or state=hard but in previous Ansible, this could have an effect in rare cornercases. For instance, "ansible -m file -a 'state=directory path=/tmp src=/var/lib'" would create /tmp/lib. This has been disabled and a warning emitted (will change to an error in Ansible-2.10).
- file module - Fix error when running a task which assures a symlink to a nonexistent file exists for the second and subsequent times (https://github.com/ansible/ansible/issues/39558)
- file module - Fix error when recursively assigning permissions and a symlink to a nonexistent file is present in the directory tree (https://github.com/ansible/ansible/issues/39456)
- file module - Eliminate an error if we're asked to remove a file but something removes it while we are processing the request (https://github.com/ansible/ansible/pull/39466)
- Various grafana_* modules - Port away from the deprecated b64encodestring function to the b64encode function instead. https://github.com/ansible/ansible/pull/38388
- import/include - Update TaskInclude _raw_params with the expanded/templated path to file allowing nested includes using host vars in file (https://github.com/ansible/ansible/pull/39365)
- dynamic includes - Don't treat undefined vars for conditional includes as truthy (https://github.com/ansible/ansible/pull/39377)
- import/include - Ensure role handlers have the proper parent, allowing for correct attribute inheritance (https://github.com/ansible/ansible/pull/39426)
- include_role/import_role - Use the computed role name for include_role/import_role so to diffentiate between names computed from host vars (https://github.com/ansible/ansible/pull/39516)
- import_playbook - Pass vars applied to import_playbook into parsing of the playbook as they may be needed to parse the imported plays (https://github.com/ansible/ansible/pull/39521)
- include_role/import_role - Don't overwrite included role handlers with play handlers on parse (https://github.com/ansible/ansible/pull/39563)
- dynamic includes - Improved performance by fixing re-parenting on copy (https://github.com/ansible/ansible/pull/38747)
- dynamic includes - Fix IncludedFile comparison for free strategy (https://github.com/ansible/ansible/pull/37083)
- dynamic includes - Allow inheriting attributes from static parents (https://github.com/ansible/ansible/pull/38827)
- include_role/import_role - improved performance and recursion depth (https://github.com/ansible/ansible/pull/36470)
- include_role/import_role - Fix parameter templating (https://github.com/ansible/ansible/pull/36372)
- dynamic includes - Use the copied and merged task for calculating task vars (https://github.com/ansible/ansible/pull/39762)
- Implement mode=preserve for the template module
- Fix mode=preserve with remote_src=True for the copy module
- Document mode=preserve for both the copy and template module
- pause - ensure ctrl+c interrupt works in all cases (https://github.com/ansible/ansible/issues/35372)
- spwd - With python 3.6 spwd.getspnam returns PermissionError instead of KeyError if user does not have privileges (https://github.com/ansible/ansible/issues/39472)
- template action plugin - fix the encoding of filenames to avoid tracebacks on Python2 when characters that are not present in the user's locale are present. (https://github.com/ansible/ansible/pull/39424)
- user - only change the expiration time when necessary (https://github.com/ansible/ansible/issues/13235)
- win_environment - Fix for issue where the environment value was deleted when a null value or empty string was set - https://github.com/ansible/ansible/issues/40450
- win_file - fix issue where special chars like [ and ] were not being handled correctly https://github.com/ansible/ansible/pull/37901
- win_get_url - fixed a few bugs around authentication and force no when using an FTP URL
- win_template - fix when specifying the dest option as a directory with and without the trailing slash https://github.com/ansible/ansible/issues/39886
- win_updates - Fix typo that hid the download error when a download failed
- win_updates - Fix logic when using a whitelist for multiple updates
- windows become - Show better error messages when the become process fails
- winrm - allow `ansible_user` or `ansible_winrm_user` to override `ansible_ssh_user` when both are defined in an inventory - https://github.com/ansible/ansible/issues/39844
- Fix an encoding issue when parsing the examples from a plugins' documentation
- Fix mode=preserve with remote_src=True for the copy module
- Implement mode=preserve for the template module
- The yaml callback plugin now allows non-ascii characters to be displayed.
- Various grafana_* modules - Port away from the deprecated b64encodestring function to the b64encode function instead. https://github.com/ansible/ansible/pull/38388
- copy - fixed copy to only follow symlinks for files in the non-recursive case
- copy module - The copy module was attempting to change the mode of files for remote_src=True even if mode was not set as a parameter. This failed on filesystems which do not have permission bits (https://github.com/ansible/ansible/pull/40099)
- copy module - fixed recursive copy with relative paths (https://github.com/ansible/ansible/pull/40166)
- dynamic includes - Allow inheriting attributes from static parents (https://github.com/ansible/ansible/pull/38827)
- dynamic includes - Don't treat undefined vars for conditional includes as truthy (https://github.com/ansible/ansible/pull/39377)
- dynamic includes - Fix IncludedFile comparison for free strategy (https://github.com/ansible/ansible/pull/37083)
- dynamic includes - Improved performance by fixing re-parenting on copy (https://github.com/ansible/ansible/pull/38747)
- dynamic includes - Use the copied and merged task for calculating task vars (https://github.com/ansible/ansible/pull/39762)
- file - fixed the default follow behaviour of file to be true
- file module - Eliminate an error if we're asked to remove a file but something removes it while we are processing the request (https://github.com/ansible/ansible/pull/39466)
- file module - Fix error when recursively assigning permissions and a symlink to a nonexistent file is present in the directory tree (https://github.com/ansible/ansible/issues/39456)
- file module - Fix error when running a task which assures a symlink to a nonexistent file exists for the second and subsequent times (https://github.com/ansible/ansible/issues/39558)
- file module - The file module allowed the user to specify src as a parameter when state was not link or hard. This is documented as only applying to state=link or state=hard but in previous Ansible, this could have an effect in rare cornercases. For instance, "ansible -m file -a 'state=directory path=/tmp src=/var/lib'" would create /tmp/lib. This has been disabled and a warning emitted (will change to an error in Ansible-2.10).
- import/include - Ensure role handlers have the proper parent, allowing for correct attribute inheritance (https://github.com/ansible/ansible/pull/39426)
- import/include - Update TaskInclude _raw_params with the expanded/templated path to file allowing nested includes using host vars in file (https://github.com/ansible/ansible/pull/39365)
- import_playbook - Pass vars applied to import_playbook into parsing of the playbook as they may be needed to parse the imported plays (https://github.com/ansible/ansible/pull/39521)
- include_role/import_role - Don't overwrite included role handlers with play handlers on parse (https://github.com/ansible/ansible/pull/39563)
- include_role/import_role - Fix parameter templating (https://github.com/ansible/ansible/pull/36372)
- include_role/import_role - Use the computed role name for include_role/import_role so to diffentiate between names computed from host vars (https://github.com/ansible/ansible/pull/39516)
- include_role/import_role - improved performance and recursion depth (https://github.com/ansible/ansible/pull/36470)
- pause - ensure ctrl+c interrupt works in all cases (https://github.com/ansible/ansible/issues/35372)
- spwd - With python 3.6 spwd.getspnam returns PermissionError instead of KeyError if user does not have privileges (https://github.com/ansible/ansible/issues/39472)
- template - Fix for encoding issues when a template path contains non-ascii characters and using the template path in ansible_managed (https://github.com/ansible/ansible/issues/27262)
- template action plugin - fix the encoding of filenames to avoid tracebacks on Python2 when characters that are not present in the user's locale are present. (https://github.com/ansible/ansible/pull/39424)
- user - only change the expiration time when necessary (https://github.com/ansible/ansible/issues/13235)
- win_environment - Fix for issue where the environment value was deleted when a null value or empty string was set - https://github.com/ansible/ansible/issues/40450
- win_file - fix issue where special chars like [ and ] were not being handled correctly https://github.com/ansible/ansible/pull/37901
- win_get_url - fixed a few bugs around authentication and force no when using an FTP URL
- win_template - fix when specifying the dest option as a directory with and without the trailing slash https://github.com/ansible/ansible/issues/39886
- win_updates - Fix logic when using a whitelist for multiple updates
- win_updates - Fix typo that hid the download error when a download failed
- windows become - Show better error messages when the become process fails
- winrm - allow `ansible_user` or `ansible_winrm_user` to override `ansible_ssh_user` when both are defined in an inventory - https://github.com/ansible/ansible/issues/39844
- winrm connection plugin - Fix exception messages sometimes raising a traceback when the winrm connection plugin encounters an unrecoverable error. https://github.com/ansible/ansible/pull/39333
New Plugins
-----------
Callback
~~~~~~~~
- cgroup_memory_recap - Profiles maximum memory usage of tasks and full execution using cgroups
- grafana_annotations - send ansible events as annotations on charts to grafana over http api.
- sumologic - Sends task result events to Sumologic
Connection
~~~~~~~~~~
- httpapi - Use httpapi to run command on network appliances
Inventory
~~~~~~~~~
- foreman - foreman inventory source
- generator - Uses Jinja2 to construct hosts and groups from patterns
- nmap - Uses nmap to find hosts to target
Lookup
~~~~~~
- onepassword - fetch field values from 1Password
- onepassword_raw - fetch raw json data from 1Password
New Modules
-----------
Cloud
~~~~~
amazon
^^^^^^
- aws_caller_facts - Get facts about the user and account being used to make AWS calls.
- aws_inspector_target - Create, Update and Delete Amazon Inspector Assessment Targets
- aws_ses_identity_policy - Manages SES sending authorization policies
- aws_sgw_facts - Fetch AWS Storage Gateway facts
- ec2_eip_facts - List EC2 EIP details
- ec2_vpc_vpn_facts - Gather facts about VPN Connections in AWS.
- rds_instance_facts - obtain facts about one or more RDS instances
- rds_snapshot_facts - obtain facts about one or more RDS snapshots
azure
^^^^^
- azure_rm_aks - Manage a managed Azure Container Service (AKS) Instance.
- azure_rm_aks_facts - Get Azure Kubernetes Service facts.
cloudstack
^^^^^^^^^^
- cs_role_permission - Manages role permissions on Apache CloudStack based clouds.
digital_ocean
^^^^^^^^^^^^^
- digital_ocean_account_facts - Gather facts about DigitalOcean User account
- digital_ocean_certificate_facts - Gather facts about DigitalOcean certificates
- digital_ocean_domain_facts - Gather facts about DigitalOcean Domains
- digital_ocean_image_facts - Gather facts about DigitalOcean images
- digital_ocean_load_balancer_facts - Gather facts about DigitalOcean load balancers
- digital_ocean_region_facts - Gather facts about DigitalOcean regions
- digital_ocean_size_facts - Gather facts about DigitalOcean Droplet sizes
- digital_ocean_snapshot_facts - Gather facts about DigitalOcean Snapshot
- digital_ocean_tag_facts - Gather facts about DigitalOcean tags
- digital_ocean_volume_facts - Gather facts about DigitalOcean volumes
google
^^^^^^
- gcp_compute_address - Creates a GCP Address
- gcp_compute_backend_bucket - Creates a GCP BackendBucket
- gcp_compute_backend_service - Creates a GCP BackendService
- gcp_compute_health_check - Creates a GCP HealthCheck
- gcp_compute_http_health_check - Creates a GCP HttpHealthCheck
- gcp_compute_https_health_check - Creates a GCP HttpsHealthCheck
- gcp_compute_instance - Creates a GCP Instance
- gcp_compute_instance_template - Creates a GCP InstanceTemplate
- gcp_compute_network - Creates a GCP Network
- gcp_compute_route - Creates a GCP Route
- gcp_compute_subnetwork - Creates a GCP Subnetwork
- gcp_compute_target_http_proxy - Creates a GCP TargetHttpProxy
- gcp_compute_target_https_proxy - Creates a GCP TargetHttpsProxy
- gcp_compute_target_tcp_proxy - Creates a GCP TargetTcpProxy
- gcp_container_cluster - Creates a GCP Cluster
- gcp_container_node_pool - Creates a GCP NodePool
- gcp_dns_resource_record_set - Creates a GCP ResourceRecordSet
- gcp_pubsub_subscription - Creates a GCP Subscription
- gcp_pubsub_topic - Creates a GCP Topic
- gcp_storage_bucket - Creates a GCP Bucket
- gcp_storage_bucket_access_control - Creates a GCP BucketAccessControl
memset
^^^^^^
- memset_zone - Creates and deletes Memset DNS zones.
opennebula
^^^^^^^^^^
- one_host - Manages OpenNebula Hosts
- one_image - Manages OpenNebula images
- one_image_facts - Gather facts about OpenNebula images
- one_vm - Creates or terminates OpenNebula instances
openstack
^^^^^^^^^
- os_server_metadata - Add/Update/Delete Metadata in Compute Instances from OpenStack
- os_volume_snapshot - Create/Delete Cinder Volume Snapshots
scaleway
^^^^^^^^
- scaleway_compute - Scaleway compute management module
- scaleway_sshkey - Scaleway SSH keys management module
vmware
^^^^^^
- vmware_cluster_facts - Gather facts about clusters available in given vCenter
- vmware_datastore_cluster - Manage VMware vSphere datastore clusters
- vmware_datastore_maintenancemode - Place a datastore into maintenance mode
- vmware_guest_disk_facts - Gather facts about disks of given virtual machine
- vmware_guest_snapshot_facts - Gather facts about virtual machine's snapshots in vCenter
- vmware_host_capability_facts - Gathers facts about an ESXi host's capability information
- vmware_host_powerstate - Manages power states of host systems in vCenter
- vmware_local_user_facts - Gather facts about users on the given ESXi host
- vmware_portgroup_facts - Gathers facts about an ESXi host's portgroup configuration
- vmware_resource_pool_facts - Gathers facts about resource pool information
- vmware_tag - Manage VMware tags
- vmware_tag_facts - Manage VMware tag facts
- vmware_vswitch_facts - Gathers facts about an ESXi host's vswitch configurations
Clustering
~~~~~~~~~~
k8s
^^^
- k8s - Manage Kubernetes (K8s) objects
Commands
~~~~~~~~
- psexec - Runs commands on a remote Windows host based on the PsExec model
Monitoring
~~~~~~~~~~
- spectrum_device - Creates/deletes devices in CA Spectrum.
zabbix
^^^^^^
- zabbix_group_facts - Gather facts about Zabbix hostgroup
Net Tools
~~~~~~~~~
ldap
^^^^
- ldap_passwd - Set passwords in LDAP.
Network
~~~~~~~
aci
^^^
- aci_l3out - Manage Layer 3 Outside (L3Out) objects (l3ext:Out)
avi
^^^
- avi_autoscalelaunchconfig - Module for setup of AutoScaleLaunchConfig Avi RESTful Object
- avi_l4policyset - Module for setup of L4PolicySet Avi RESTful Object
- avi_useraccount - Avi UserAccount Module
cnos
^^^^
- cnos_command - Run arbitrary commands on Lenovo CNOS devices
- cnos_config - Manage Lenovo CNOS configuration sections
exos
^^^^
- exos_command - Run commands on remote devices running Extreme EXOS
f5
^^
- bigip_data_group - Manage data groups on a BIG-IP
- bigip_device_license - Manage license installation and activation on BIG-IP devices
- bigip_gtm_global - Manages global GTM settings
- bigip_gtm_monitor_bigip - Manages F5 BIG-IP GTM BIG-IP monitors
- bigip_gtm_monitor_firepass - Manages F5 BIG-IP GTM FirePass monitors
- bigip_gtm_monitor_http - Manages F5 BIG-IP GTM http monitors
- bigip_gtm_monitor_https - Manages F5 BIG-IP GTM https monitors
- bigip_gtm_monitor_tcp - Manages F5 BIG-IP GTM tcp monitors
- bigip_gtm_monitor_tcp_half_open - Manages F5 BIG-IP GTM tcp half-open monitors
- bigip_gtm_pool_member - Manage GTM pool member settings
- bigip_gtm_virtual_server - Manages F5 BIG-IP GTM virtual servers
- bigip_log_destination - Manages log destinations on a BIG-IP.
- bigip_log_publisher - Manages log publishers on a BIG-IP
- bigip_management_route - Manage system management routes on a BIG-IP
- bigip_profile_dns - Manage DNS profiles on a BIG-IP
- bigip_profile_tcp - Manage TCP profiles on a BIG-IP
- bigip_profile_udp - Manage UDP profiles on a BIG-IP
- bigip_service_policy - Manages service policies on a BIG-IP.
- bigip_smtp - Manages SMTP settings on the BIG-IP
- bigip_snmp_community - Manages SNMP communities on a BIG-IP.
- bigip_timer_policy - Manage timer policies on a BIG-IP
- bigip_trunk - Manage trunks on a BIG-IP
- bigiq_application_fasthttp - Manages BIG-IQ FastHTTP applications
- bigiq_application_fastl4_tcp - Manages BIG-IQ FastL4 TCP applications
- bigiq_application_fastl4_udp - Manages BIG-IQ FastL4 UDP applications
- bigiq_application_http - Manages BIG-IQ HTTP applications
- bigiq_application_https_offload - Manages BIG-IQ HTTPS offload applications
- bigiq_application_https_waf - Manages BIG-IQ HTTPS WAF applications
- bigiq_regkey_license_assignment - Manage regkey license assignment on BIG-IPs from a BIG-IQ.
files
^^^^^
- net_get - Copy files from a network device to Ansible Controller
- net_put - Copy files from Ansibe controller to a network device
fortios
^^^^^^^
- fortios_webfilter - Configure webfilter capabilities of FortiGate and FortiOS.
meraki
^^^^^^
- meraki_network - Manage networks in the Meraki cloud
- meraki_organization - Manage organizations in the Meraki cloud
netconf
^^^^^^^
- netconf_get - Fetch configuration/state data from NETCONF enabled network devices.
slxos
^^^^^
- slxos_command - Run commands on remote devices running Extreme Networks SLX-OS
- slxos_config - Manage Extreme Networks SLX-OS configuration sections
- slxos_facts - Collect facts from devices running Extreme SLX-OS
- slxos_interface - Manage Interfaces on Extreme SLX-OS network devices
- slxos_l2_interface - Manage Layer-2 interface on Extreme Networks SLXOS devices.
- slxos_linkagg - Manage link aggregation groups on Extreme Networks SLXOS network devices
Packaging
~~~~~~~~~
language
^^^^^^^^
- yarn - Manage node.js packages with Yarn
Source Control
~~~~~~~~~~~~~~
- gitlab_deploy_key - Manages GitLab project deploy keys.
- gitlab_hooks - Manages GitLab project hooks.
Storage
~~~~~~~
netapp
^^^^^^
- na_ontap_broadcast_domain - Manage NetApp ONTAP broadcast domains.
purestorage
^^^^^^^^^^^
- purefa_ds - Configure FlashArray Directory Service
- purefa_facts - Collect facts from Pure Storage FlashArray
- purefa_pgsnap - Manage protection group snapshots on Pure Storage FlashArrays
- purefb_fs - Manage filesystemon Pure Storage FlashBlade`
- purefb_snap - Manage filesystem snapshots on Pure Storage FlashBlades
System
~~~~~~
- sysvinit - Manage SysV services.
Windows
~~~~~~~
- win_domain_computer - Manage computers in Active Directory
- win_hostname - Manages local Windows computer name.
- win_pester - Run Pester tests on Windows hosts

View File

@ -3,16 +3,11 @@ release_tag_re: '(v(?:[\d.ab\-]|rc)+)'
pre_release_tag_re: '(?P<pre_release>(?:[ab]|rc)+\d*)$'
notesdir: fragments
prelude_section_name: release_summary
new_plugins_after_name: removed_features
sections:
- ['major_changes', 'Major Changes']
- ['minor_changes', 'Minor Changes']
- ['deprecated_features', 'Deprecated Features']
- ['removed_features', 'Removed Features (previously deprecated)']
- ['new_lookup_plugins', 'New Lookup Plugins']
- ['new_callback_plugins', 'New Callback Plugins']
- ['new_connection_plugins', 'New Connection Plugins']
- ['new_test_plugins', 'New Test Plugins']
- ['new_filter_plugins', 'New Filter Plugins']
- ['new_modules', 'New Modules']
- ['bugfixes', 'Bugfixes']
- ['known_issues', 'Known Issues']

View File

@ -1,2 +1,2 @@
removed_features:
- removed the deprecated always_run task option, please use `check_mode: no` instead
- "removed the deprecated always_run task option, please use `check_mode: no` instead"

View File

@ -1,4 +1,4 @@
---
features:
minor_changes:
- command module - Added argv option to allow command to be specified as a list vs. a string
(https://github.com/ansible/ansible/issues/19392)

View File

@ -1,2 +1,2 @@
new_features:
minor_changes:
- gem - add ability to specify a custom directory for installing gems (https://github.com/ansible/ansible/pull/38195)

View File

@ -1,34 +0,0 @@
new_features:
- 'New Google Cloud module: gcp_compute_address'
- 'New Google Cloud module: gcp_compute_backend_bucket'
- 'New Google Cloud module: gcp_compute_backend_service'
- 'New Google Cloud module: gcp_compute_firewall'
- 'New Google Cloud module: gcp_compute_forwarding_rule'
- 'New Google Cloud module: gcp_compute_global_address'
- 'New Google Cloud module: gcp_compute_global_forwarding_rule'
- 'New Google Cloud module: gcp_compute_health_check'
- 'New Google Cloud module: gcp_compute_http_health_check'
- 'New Google Cloud module: gcp_compute_https_health_check'
- 'New Google Cloud module: gcp_compute_image'
- 'New Google Cloud module: gcp_compute_instance'
- 'New Google Cloud module: gcp_compute_instance_group'
- 'New Google Cloud module: gcp_compute_instance_group_manager'
- 'New Google Cloud module: gcp_compute_instance_template'
- 'New Google Cloud module: gcp_compute_network'
- 'New Google Cloud module: gcp_compute_route'
- 'New Google Cloud module: gcp_compute_ssl_certificate'
- 'New Google Cloud module: gcp_compute_subnetwork'
- 'New Google Cloud module: gcp_compute_target_http_proxy'
- 'New Google Cloud module: gcp_compute_target_https_proxy'
- 'New Google Cloud module: gcp_compute_target_pool'
- 'New Google Cloud module: gcp_compute_target_ssl_proxy'
- 'New Google Cloud module: gcp_compute_target_tcp_proxy'
- 'New Google Cloud module: gcp_compute_url_map'
- 'New Google Cloud module: gcp_container_cluster'
- 'New Google Cloud module: gcp_container_node_pool'
- 'New Google Cloud module: gcp_dns_managed_zone'
- 'New Google Cloud module: gcp_dns_resource_record_set'
- 'New Google Cloud module: gcp_pubsub_subscription'
- 'New Google Cloud module: gcp_pubsub_topic'
- 'New Google Cloud module: gcp_storage_bucket'
- 'New Google Cloud module: gcp_storage_bucket_access_control'

View File

@ -1,2 +0,0 @@
features:
- onepassword lookup - add lookup plugins onepassword and onepassword_raw to retrieve secrets from 1Password vault (https://github.com/ansible/ansible/pull/37207)

View File

@ -0,0 +1,2 @@
bugfixes:
- synchronize - Ensure the local connection created by synchronize uses _remote_is_local=True, which causes ActionBase to build a local tmpdir (https://github.com/ansible/ansible/pull/40833)

View File

@ -1 +1,7 @@
{}
deprecated_features:
- nxos_igmp_interface module - The oif_prefix and oif_source properties are deprecated. Use the oif_ps parameter with a dictionary of prefix and source to values instead.
minor_changes:
- cloudflare_dns module - Removed restriction from protocol to allow other protocols than tcp and udp to be specified.
- vmware_target_canonical_facts module - The target_id parameter is an optional parameter.
- postgresql_user module - Changed encrypted=yes to be the default. This shouldn't break any current playbooks, the module will just store passwords hashed by default. This change was done because Postgres 10 dropped support for UNENCRYPTED passwords and because all versions since Postgres 7.2 support storing encrypted passwords.

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,3 @@
release_summary: |
| Release Date: 2018-05-30
| `Porting Guide <https://docs.ansible.com/ansible/devel/porting_guides.html>`_

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,3 @@
release_summary: |
| Release Date: 2018-06-05
| `Porting Guide <https://docs.ansible.com/ansible/devel/porting_guides.html>`_

View File

@ -191,8 +191,9 @@ def is_cache_stale(cache_file, cache_expiration_time, refresh=False):
def get_cache_settings(cloud=None):
config_files = cloud_config.CONFIG_FILES + CONFIG_FILES
config = cloud_config.OpenStackConfig(
config_files=cloud_config.CONFIG_FILES + CONFIG_FILES).get_one()
config_files=config_files).get_one(cloud=cloud)
# For inventory-wide caching
cache_expiration_time = config.get_cache_expiration_time()
cache_path = config.get_cache_path()

View File

@ -156,7 +156,7 @@
<div class="DocSiteProduct-logoText">
Ansible
<div class="DocSiteProduct-CurrentVersion" align="right">
devel
2.6
</div>
</div>
</div>

View File

@ -421,51 +421,17 @@ table {
max-width: 100%;
}
.outer-elbow-container {
display: flex;
height: 100%;
flex-direction: row;
}
.elbow-placeholder {
.documentation-table td.elbow-placeholder {
border-left: 1px solid #000;
height: 100%;
border-top: 0px;
width: 30px;
min-width: 30px;
}
.elbow-key {
height: 100%;
padding: 4px;
border-top: 1px solid #000;
flex-grow: 1;
border-left: 1px solid #000;
}
.elbow-blocker {
height: 0;
overflow: hidden;
}
.return-value-column {
height: 1px
}
.return-value-column td {
height: inherit
}
@-moz-document url-prefix() {
.return-value-column td {
height: 100%
}
}
.cell-border {
.documentation-table th, .documentation-table td {
padding: 4px;
border-left: 1px solid #000;
border-top: 1px solid #000;
height: 100%;
}
.documentation-table {

View File

@ -4867,50 +4867,17 @@ table {
}
}
.outer-elbow-container {
display: flex;
height: 100%;
flex-direction: row;
}
.elbow-placeholder {
.documentation-table td.elbow-placeholder {
border-left: 1px solid #000;
height: 100%;
border-top: 0px;
width: 30px;
min-width: 30px;
}
.elbow-key {
height: 100%;
padding: 4px;
border-top: 1px solid #000;
flex-grow: 1;
border-left: 1px solid #000;
}
.elbow-blocker {
height: 0;
overflow: hidden;
}
.return-value-column {
height: 1px
}
.return-value-column td {
height: inherit
}
@-moz-document url-prefix() {
.return-value-column td {
height: 100%
}
}
.cell-border {
.documentation-table th, .documentation-table td {
padding: 4px;
border-left: 1px solid #000;
border-top: 1px solid #000;
height: 100%;
}
.documentation-table {

View File

@ -0,0 +1,15 @@
Sanity Tests » changelog
========================
Basic linting of changelog fragments with yamllint and rstcheck.
One or more of the following sections are required:
- major_changes
- minor_changes
- deprecated_features
- removed_features
- bugfixes
- known_issues
New modules and plugins must not be included in changelog fragments.

View File

@ -4,7 +4,7 @@
EOS Platform Options
***************************************
Arista EOS supports multiple connections. This page offers details on how each connection works in Ansible 2.5 and how to use it.
Arista EOS supports multiple connections. This page offers details on how each connection works in Ansible 2.6 and how to use it.
.. contents:: Topics
@ -41,7 +41,7 @@ Connections Available
For legacy playbooks, EOS still supports ``ansible_connection: local``. We recommend modernizing to use ``ansible_connection: network_cli`` or ``ansible_connection: httpapi`` as soon as possible.
Using CLI in Ansible 2.5
Using CLI in Ansible 2.6
================================================================================
Example CLI ``group_vars/eos.yml``
@ -76,7 +76,7 @@ Example CLI Task
Using eAPI in Ansible 2.5
Using eAPI in Ansible 2.6
================================================================================
Enabling eAPI

View File

@ -4,7 +4,7 @@
IOS Platform Options
***************************************
IOS supports Enable Mode (Privilege Escalation). This page offers details on how to use Enable Mode on IOS in Ansible 2.5.
IOS supports Enable Mode (Privilege Escalation). This page offers details on how to use Enable Mode on IOS in Ansible 2.6.
.. contents:: Topics
@ -34,7 +34,7 @@ Connections Available
For legacy playbooks, IOS still supports ``ansible_connection: local``. We recommend modernizing to use ``ansible_connection: network_cli`` as soon as possible.
Using CLI in Ansible 2.5
Using CLI in Ansible 2.6
================================================================================
Example CLI ``group_vars/ios.yml``

View File

@ -4,7 +4,7 @@
Junos OS Platform Options
***************************************
Juniper Junos OS supports multiple connections. This page offers details on how each connection works in Ansible 2.5 and how to use it.
Juniper Junos OS supports multiple connections. This page offers details on how each connection works in Ansible 2.6 and how to use it.
.. contents:: Topics
@ -34,7 +34,7 @@ Connections Available
For legacy playbooks, Ansible still supports ``ansible_connection=local`` on all JUNOS modules. We recommend modernizing to use ``ansible_connection=netconf`` or ``ansible_connection=network_cli`` as soon as possible.
Using CLI in Ansible 2.5
Using CLI in Ansible 2.6
================================================================================
Example CLI inventory ``[junos:vars]``
@ -65,7 +65,7 @@ Example CLI Task
when: ansible_network_os == 'junos'
Using NETCONF in Ansible 2.5
Using NETCONF in Ansible 2.6
================================================================================
Enabling NETCONF

View File

@ -4,7 +4,7 @@
NXOS Platform Options
***************************************
Cisco NXOS supports multiple connections. This page offers details on how each connection works in Ansible 2.5 and how to use it.
Cisco NXOS supports multiple connections. This page offers details on how each connection works in Ansible 2.6 and how to use it.
.. contents:: Topics
@ -22,6 +22,10 @@ Connections Available
| **Indirect Access** | via a bastion (jump host) | via a web proxy |
+---------------------------+-----------------------------------------------+-----------------------------------------+
| | **Connection Settings** | | ``ansible_connection: network_cli`` | | ``ansible_connection: httpapi`` |
| | | | | | OR |
| | | | | | ``ansible_connection: local`` |
| | | | | | with ``transport: nxapi`` |
| | | | | | in the ``provider`` dictionary |
+---------------------------+-----------------------------------------------+-----------------------------------------+
| | **Enable Mode** | | supported - use ``ansible_become: yes`` | | not supported by NX-API |
| | (Privilege Escalation) | | with ``ansible_become_method: enable`` | | |
@ -32,7 +36,7 @@ Connections Available
For legacy playbooks, NXOS still supports ``ansible_connection: local``. We recommend modernizing to use ``ansible_connection: network_cli`` or ``ansible_connection: httpapi`` as soon as possible.
Using CLI in Ansible 2.5
Using CLI in Ansible 2.6
================================================================================
Example CLI ``group_vars/nxos.yml``
@ -67,7 +71,7 @@ Example CLI Task
Using NX-API in Ansible 2.5
Using NX-API in Ansible 2.6
================================================================================
Enabling NX-API

View File

@ -91,6 +91,11 @@ You will run into errors because Ansible reads name in this context as a keyword
For a full list of keywords see ::ref::`Playbook Keywords`.
Migrating from with_X to loop
-----------------------------
.. include:: ../user_guide/shared_snippets/with2loop.txt
Deprecated
==========

View File

@ -42,6 +42,9 @@ Deprecation notices
The following modules will be removed in Ansible 2.10. Please update your playbooks accordingly.
* ``k8s_raw`` use :ref:`k8s <k8s_module>` instead.
* ``openshift_raw`` use :ref:`k8s <k8s_module>` instead.
* ``openshift_scale`` use :ref:`k8s_scale <k8s_scale_module>` instead.
Noteworthy module changes
-------------------------
@ -62,12 +65,34 @@ 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.
* The ``k8s`` module may not accept resources where the ``api_version`` has been changed to match the shortened version in the Kubernetes Python client. You should now specify the proper full Kubernetes ``api_version`` for a resource.
* The ``k8s`` module can now process multi-document YAML files if they are passed with the ``src`` parameter. It will process each document as a separate resource. Resources provided inline with the ``resource_definition`` parameter must still be a single document.
* The ``k8s`` module will not automatically change ``Project`` creation requests into ``ProjectRequest`` creation requests as the ``openshift_raw`` module did. You must now specify the ``ProjectRequest`` kind explicitly.
* The ``k8s`` module will not automatically remove secrets from the Ansible return values (and by extension the log). In order to prevent secret values in a task from being logged, specify the ``no_log`` parameter on the task block.
* The ``k8s_scale`` module now supports scalable OpenShift objects, such as ``DeploymentConfig``.
Plugins
=======
No notable changes.
Deprecation notices
-------------------
The following modules will be removed in Ansible 2.10. Please update your playbooks accordingly.
* ``openshift`` use ``k8s`` instead.
Noteworthy plugin changes
-------------------------
* The ``k8s`` lookup plugin now supports all Kubernetes resources including those from Custom Resource Definitions and aggregated API servers. This includes all OpenShift resources.
* The ``k8s`` lookup plugin may not accept resources where the ``api_version`` has been changed to match the shortened version in the Kubernetes Python client. You should now specify the proper full Kubernetes ``api_version`` for a resource.
* The ``k8s`` lookup plugin will no longer remove secrets from the Ansible return values (and by extension the log). In order to prevent secret values in a task from being logged, specify the ``no_log`` parameter on the task block.
Porting custom scripts
======================

View File

@ -105,7 +105,7 @@ For example, using the 'nested' lookup, you can combine lists::
priv: "{{ item[1] }}.*:ALL"
append_privs: yes
password: "foo"
loop: "{{ query('nested', [ 'alice', 'bob' ], [ 'clientdb', 'employeedb', 'providerdb' ]) }}"
loop: "{{ ['alice', 'bob'] |product(['clientdb', 'employeedb', 'providerdb'])|list }}"
.. note:: ``with_`` loops are actually a combination of things ``with_`` + ``lookup()``, even ``items`` is a lookup. ``loop`` can be used in the same way as shown above.
@ -121,9 +121,9 @@ In certain situations the ``lookup`` function may not return a list which ``loop
The following invocations are equivalent, using ``wantlist=True`` with ``lookup`` to ensure a return type of a list::
loop: "{{ query('nested', ['alice', 'bob'], ['clientdb', 'employeedb', 'providerdb']) }}"
loop: "{{ query('inventory_hostnames', 'all') }}"
loop: "{{ lookup('nested', ['alice', 'bob'], ['clientdb', 'employeedb', 'providerdb'], wantlist=True) }}"
loop: "{{ lookup('inventory_hostnames', 'all', wantlist=True) }}"
.. _do_until_loops:
@ -330,6 +330,11 @@ If you need to keep track of where you are in a loop, you can use the ``index_va
loop_control:
index_var: my_idx
Migrating from with_X to loop
`````````````````````````````
.. include:: shared_snippets/with2loop.txt
.. seealso::

View File

@ -0,0 +1,193 @@
With the release of Ansible 2.5, the recommended way to perform loops is the use the new ``loop`` keyword instead of ``with_X`` style loops.
In many cases, ``loop`` syntax is better expressed using filters instead of more complex use of ``query`` or ``lookup``.
The following examples will show how to convert many common ``with_`` style loops to ``loop`` and filters.
with_list
---------
``with_list`` is directly replaced by ``loop``.
.. code-block:: yaml+jinja
- name: with_list
debug:
msg: "{{ item }}"
with_list:
- one
- two
- name: with_list -> loop
debug:
msg: "{{ item }}"
loop:
- one
- two
with_items
----------
``with_items`` is replaced by ``loop`` and the ``flatten`` filter.
.. code-block:: yaml+jinja
- name: with_items
debug:
msg: "{{ item }}"
with_items: "{{ items }}"
- name: with_items -> loop
debug:
msg: "{{ item }}"
loop: "{{ items|flatten(levels=1) }}"
with_indexed_items
------------------
``with_indexed_items`` is replaced by ``loop``, the ``flatten`` filter and ``loop_control.index_var``.
.. code-block:: yaml+jinja
- name: with_indexed_items
debug:
msg: "{{ item.0 }} - {{ item.1 }}"
with_indexed_items: "{{ items }}"
- name: with_indexed_items -> loop
debug:
msg: "{{ index }} - {{ item }}"
loop: "{{ items|flatten(levels=1) }}"
loop_control:
index_var: index
with_flattened
--------------
``with_flattened`` is replaced by ``loop`` and the ``flatten`` filter.
.. code-block:: yaml+jinja
- name: with_flattened
debug:
msg: "{{ item }}"
with_flattened: "{{ items }}"
- name: with_flattened -> loop
debug:
msg: "{{ item }}"
loop: "{{ items|flatten }}"
with_together
-------------
``with_together`` is replaced by ``loop`` and the ``zip`` filter.
.. code-block:: yaml+jinja
- name: with_together
debug:
msg: "{{ item.0 }} - {{ item.1 }}"
with_together:
- "{{ list_one }}"
- "{{ list_two }}"
- name: with_together -> loop
debug:
msg: "{{ item.0 }} - {{ item.1 }}"
loop: "{{ list_one|zip(list_two)|list }}"
with_dict
---------
``with_dict`` can be substituted by ``loop`` and either the ``dictsort`` or ``dict2items`` filters.
.. code-block:: yaml+jinja
- name: with_dict
debug:
msg: "{{ item.key }} - {{ item.value }}"
with_dict: "{{ dictionary }}"
- name: with_dict -> loop (option 1)
debug:
msg: "{{ item.key }} - {{ item.value }}"
loop: "{{ dictionary|dict2items }}"
- name: with_dict -> loop (option 2)
debug:
msg: "{{ item.0 }} - {{ item.1 }}"
loop: "{{ dictionary|dictsort }}"
with_sequence
-------------
``with_sequence`` is replaced by ``loop`` and the ``range`` function, and potentially the ``format`` filter.
.. code-block:: yaml+jinja
- name: with_sequence
debug:
msg: "{{ item }}"
with_sequence: start=0 end=4 stride=2 format=testuser%02x
- name: with_sequence -> loop
debug:
msg: "{{ 'testuser%02x' | format(item) }}"
# range is exclusive of the end point
loop: "{{ range(0, 4 + 1, 2)|list }}"
with_subelements
----------------
``with_subelements`` is replaced by ``loop`` and the ``subelements`` filter.
.. code-block:: yaml+jinja
- name: with_subelements
debug:
msg: "{{ item.0.name }} - {{ item.1 }}"
with_subelements:
- "{{ users }}"
- mysql.hosts
- name: with_subelements -> loop
debug:
msg: "{{ item.0.name }} - {{ item.1 }}"
loop: "{{ users|subelements('mysql.hosts') }}"
with_nested/with_cartesian
--------------------------
``with_nested`` and ``with_cartesian`` are replaced by loop and the ``product`` filter.
.. code-block:: yaml+jinja
- name: with_nested
debug:
msg: "{{ item.0 }} - {{ item.1 }}"
with_nested:
- "{{ list_one }}"
- "{{ list_two }}"
- name: with_nested -> loop
debug:
msg: "{{ item.0 }} - {{ item.1 }}"
loop: "{{ list_one|product(list_two)|list }}"
with_random_choice
------------------
``with_random_choice`` is replaced by just use of the ``random`` filter, without need of ``loop``.
.. code-block:: yaml+jinja
- name: with_random_choice
debug:
msg: "{{ item }}"
with_random_choice: "{{ my_list }}"
- name: with_random_choice -> loop (No loop is needed here)
debug:
msg: "{{ my_list|random }}"
tags: random

View File

@ -88,104 +88,107 @@ Parameters
.. raw:: html
<table border=0 cellpadding=0 class="documentation-table">
{# Pre-compute the nesting depth to allocate columns #}
{% set ns = namespace(maxdepth=1) %}
{% for key, value in options|dictsort recursive %}
{% set ns.maxdepth = [loop.depth, ns.maxdepth] | max %}
{% if value.suboptions %}
{% if value.suboptions.items %}
@{ loop(value.suboptions.items()) }@
{% elif value.suboptions[0].items %}
@{ loop(value.suboptions[0].items()) }@
{% endif %}
{% endif %}
{% endfor %}
{# Header of the documentation #}
<tr>
<th class="head"><div class="cell-border">Parameter</div></th>
<th class="head"><div class="cell-border">Choices/<font color="blue">Defaults</font></div></th>
<th colspan="@{ ns.maxdepth }@">Parameter</th>
<th>Choices/<font color="blue">Defaults</font></th>
{% if plugin_type != 'module' %}
<th class="head"><div class="cell-border">Configuration</div></th>
<th>Configuration</th>
{% endif %}
<th class="head" width="100%"><div class="cell-border">Comments</div></th>
<th width="100%">Comments</th>
</tr>
{% for key, value in options|dictsort recursive %}
<tr class="return-value-column">
<tr>
{# indentation based on nesting level #}
{% for i in range(1, loop.depth) %}
<td class="elbow-placeholder"></td>
{% endfor %}
{# parameter name with required and/or introduced label #}
<td>
<div class="outer-elbow-container">
{% for i in range(1, loop.depth) %}
<div class="elbow-placeholder">&nbsp;</div>
{% endfor %}
<div class="elbow-key">
<b>@{ key }@</b>
{% if value.get('required', False) %}<br/><div style="font-size: small; color: red">required</div>{% endif %}
{% if value.version_added %}<br/><div style="font-size: small; color: darkgreen">(added in @{value.version_added}@)</div>{% endif %}
</div>
</div>
<td colspan="@{ ns.maxdepth - loop.depth0 }@">
<b>@{ key }@</b>
{% if value.get('required', False) %}<br/><div style="font-size: small; color: red">required</div>{% endif %}
{% if value.version_added %}<br/><div style="font-size: small; color: darkgreen">(added in @{value.version_added}@)</div>{% endif %}
</td>
{# default / choices #}
<td>
<div class="cell-border">
{# Turn boolean values in 'yes' and 'no' values #}
{% if value.default is sameas true %}
{% set _x = value.update({'default': 'yes'}) %}
{% elif value.default is sameas false %}
{% set _x = value.update({'default': 'no'}) %}
{% endif %}
{% if value.type == 'bool' %}
{% set _x = value.update({'choices': ['no', 'yes']}) %}
{% endif %}
{# Show possible choices and highlight details #}
{% if value.choices %}
<ul><b>Choices:</b>
{% for choice in value.choices %}
{# Turn boolean values in 'yes' and 'no' values #}
{% if choice is sameas true %}
{% set choice = 'yes' %}
{% elif choice is sameas false %}
{% set choice = 'no' %}
{% endif %}
{% if (value.default is string and value.default == choice) or (value.default is iterable and value.default is not string and choice in value.default) %}
<li><div style="color: blue"><b>@{ choice | escape }@</b>&nbsp;&larr;</div></li>
{% else %}
<li>@{ choice | escape }@</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
{# Show default value, when multiple choice or no choices #}
{% if value.default is defined and value.default not in value.choices %}
<b>Default:</b><br/><div style="color: blue">@{ value.default | escape }@</div>
{% endif %}
</div>
{# Turn boolean values in 'yes' and 'no' values #}
{% if value.default is sameas true %}
{% set _x = value.update({'default': 'yes'}) %}
{% elif value.default is sameas false %}
{% set _x = value.update({'default': 'no'}) %}
{% endif %}
{% if value.type == 'bool' %}
{% set _x = value.update({'choices': ['no', 'yes']}) %}
{% endif %}
{# Show possible choices and highlight details #}
{% if value.choices %}
<ul><b>Choices:</b>
{% for choice in value.choices %}
{# Turn boolean values in 'yes' and 'no' values #}
{% if choice is sameas true %}
{% set choice = 'yes' %}
{% elif choice is sameas false %}
{% set choice = 'no' %}
{% endif %}
{% if (value.default is string and value.default == choice) or (value.default is iterable and value.default is not string and choice in value.default) %}
<li><div style="color: blue"><b>@{ choice | escape }@</b>&nbsp;&larr;</div></li>
{% else %}
<li>@{ choice | escape }@</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
{# Show default value, when multiple choice or no choices #}
{% if value.default is defined and value.default not in value.choices %}
<b>Default:</b><br/><div style="color: blue">@{ value.default | escape }@</div>
{% endif %}
</td>
{# configuration #}
{% if plugin_type != 'module' %}
<td>
<div class="cell-border">
{% if 'ini' in value %}
<div> ini entries:
{% for ini in value.ini %}
<p>[@{ ini.section }@ ]<br>@{ ini.key }@ = @{ value.default | default('VALUE') }@</p>
{% endfor %}
</div>
{% endif %}
{% if 'env' in value %}
{% for env in value.env %}
<div>env:@{ env.name }@</div>
{% if 'ini' in value %}
<div> ini entries:
{% for ini in value.ini %}
<p>[@{ ini.section }@ ]<br>@{ ini.key }@ = @{ value.default | default('VALUE') }@</p>
{% endfor %}
{% endif %}
{% if 'vars' in value %}
{% for myvar in value.vars %}
<div>var: @{ myvar.name }@</div>
{% endfor %}
{% endif %}
</div>
</div>
{% endif %}
{% if 'env' in value %}
{% for env in value.env %}
<div>env:@{ env.name }@</div>
{% endfor %}
{% endif %}
{% if 'vars' in value %}
{% for myvar in value.vars %}
<div>var: @{ myvar.name }@</div>
{% endfor %}
{% endif %}
</td>
{% endif %}
{# description #}
<td>
<div class="cell-border">
{% if value.description is string %}
<div>@{ value.description | replace('\n', '\n ') | html_ify }@</div>
{% else %}
{% for desc in value.description %}
<div>@{ desc | replace('\n', '\n ') | html_ify }@</div>
{% endfor %}
{% endif %}
{% if 'aliases' in value and value.aliases %}
<div style="font-size: small; color: darkgreen"><br/>aliases: @{ value.aliases|join(', ') }@</div>
{% endif %}
</div>
{% if value.description is string %}
<div>@{ value.description | replace('\n', '\n ') | html_ify }@</div>
{% else %}
{% for desc in value.description %}
<div>@{ desc | replace('\n', '\n ') | html_ify }@</div>
{% endfor %}
{% endif %}
{% if 'aliases' in value and value.aliases %}
<div style="font-size: small; color: darkgreen"><br/>aliases: @{ value.aliases|join(', ') }@</div>
{% endif %}
</td>
</tr>
{% if value.suboptions %}
@ -242,43 +245,49 @@ Facts returned by this module are added/updated in the ``hostvars`` host facts a
.. raw:: html
<table border=0 cellpadding=0 class="documentation-table">
{# Pre-compute the nesting depth to allocate columns #}
{% set ns = namespace(maxdepth=1) %}
{% for key, value in returnfacts|dictsort recursive %}
{% set ns.maxdepth = [loop.depth, ns.maxdepth] | max %}
{% if value.contains %}
{% if value.contains.items %}
@{ loop(value.contains.items()) }@
{% elif value.contains[0].items %}
@{ loop(value.contains[0].items()) }@
{% endif %}
{% endif %}
{% endfor %}
<tr>
<th class="head"><div class="cell-border">Fact</div></th>
<th class="head"><div class="cell-border">Returned</div></th>
<th class="head" width="100%"><div class="cell-border">Description</div></th>
<th colspan="@{ ns.maxdepth }@">Fact</th>
<th>Returned</th>
<th width="100%">Description</th>
</tr>
{% for key, value in returnfacts|dictsort recursive %}
<tr class="return-value-column">
<td>
<div class="outer-elbow-container">
{% for i in range(1, loop.depth) %}
<div class="elbow-placeholder">&nbsp;</div>
{% endfor %}
<div class="elbow-key">
<b>@{ key }@</b>
<br/><div style="font-size: small; color: red">@{ value.type }@</div>
</div>
</div>
<tr>
{% for i in range(1, loop.depth) %}
<td class="elbow-placeholder"></td>
{% endfor %}
<td colspan="@{ ns.maxdepth - loop.depth0 }@" colspan="@{ ns.maxdepth - loop.depth0 }@">
<b>@{ key }@</b>
<br/><div style="font-size: small; color: red">@{ value.type }@</div>
</td>
<td><div class="cell-border">@{ value.returned | html_ify }@</div></td>
<td>@{ value.returned | html_ify }@</td>
<td>
<div class="cell-border">
{% if value.description is string %}
<div>@{ value.description | html_ify }@
{% if value.description is string %}
<div>@{ value.description | html_ify }@
</div>
{% else %}
{% for desc in value.description %}
<div>@{ desc | html_ify }@
</div>
{% else %}
{% for desc in value.description %}
<div>@{ desc | html_ify }@
</div>
{% endfor %}
{% endif %}
<br/>
{% if value.sample is defined and value.sample %}
<div style="font-size: smaller"><b>Sample:</b></div>
{# TODO: The sample should be escaped, using | escape or | htmlify, but both mess things up beyond repair with dicts #}
<div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">@{ value.sample | replace('\n', '\n ') | html_ify }@</div>
{% endif %}
</div>
{% endfor %}
{% endif %}
<br/>
{% if value.sample is defined and value.sample %}
<div style="font-size: smaller"><b>Sample:</b></div>
{# TODO: The sample should be escaped, using | escape or | htmlify, but both mess things up beyond repair with dicts #}
<div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">@{ value.sample | replace('\n', '\n ') | html_ify }@</div>
{% endif %}
</td>
</tr>
{# ---------------------------------------------------------
@ -308,41 +317,46 @@ Common return values are documented :ref:`here <common_return_values>`, the foll
.. raw:: html
<table border=0 cellpadding=0 class="documentation-table">
{% set ns = namespace(maxdepth=1) %}
{% for key, value in returndocs|dictsort recursive %}
{% set ns.maxdepth = [loop.depth, ns.maxdepth] | max %}
{% if value.contains %}
{% if value.contains.items %}
@{ loop(value.contains.items()) }@
{% elif value.contains[0].items %}
@{ loop(value.contains[0].items()) }@
{% endif %}
{% endif %}
{% endfor %}
<tr>
<th class="head"><div class="cell-border">Key</div></th>
<th class="head"><div class="cell-border">Returned</div></th>
<th class="head" width="100%"><div class="cell-border">Description</div></th>
<th colspan="@{ ns.maxdepth }@">Key</th>
<th>Returned</th>
<th width="100%">Description</th>
</tr>
{% for key, value in returndocs|dictsort recursive %}
<tr class="return-value-column">
<td>
<div class="outer-elbow-container">
{% for i in range(1, loop.depth) %}
<div class="elbow-placeholder">&nbsp;</div>
{% endfor %}
<div class="elbow-key">
<b>@{ key }@</b>
<br/><div style="font-size: small; color: red">@{ value.type }@</div>
</div>
</div>
<tr>
{% for i in range(1, loop.depth) %}
<td class="elbow-placeholder">&nbsp;</td>
{% endfor %}
<td colspan="@{ ns.maxdepth - loop.depth0 }@">
<b>@{ key }@</b>
<br/><div style="font-size: small; color: red">@{ value.type }@</div>
</td>
<td><div class="cell-border">@{ value.returned | html_ify }@</div></td>
<td>@{ value.returned | html_ify }@</td>
<td>
<div class="cell-border">
{% if value.description is string %}
<div>@{ value.description | html_ify |indent(4)}@</div>
{% else %}
{% for desc in value.description %}
<div>@{ desc | html_ify |indent(4)}@</div>
{% endfor %}
{% endif %}
<br/>
{% if value.sample is defined and value.sample %}
<div style="font-size: smaller"><b>Sample:</b></div>
{# TODO: The sample should be escaped, using |escape or |htmlify, but both mess things up beyond repair with dicts #}
<div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">@{ value.sample | replace('\n', '\n ') | html_ify }@</div>
{% endif %}
</div>
{% if value.description is string %}
<div>@{ value.description | html_ify |indent(4)}@</div>
{% else %}
{% for desc in value.description %}
<div>@{ desc | html_ify |indent(4)}@</div>
{% endfor %}
{% endif %}
<br/>
{% if value.sample is defined and value.sample %}
<div style="font-size: smaller"><b>Sample:</b></div>
{# TODO: The sample should be escaped, using |escape or |htmlify, but both mess things up beyond repair with dicts #}
<div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">@{ value.sample | replace('\n', '\n ') | html_ify }@</div>
{% endif %}
</td>
</tr>
{# ---------------------------------------------------------

View File

@ -125,7 +125,7 @@ def boilerplate_module(modfile, args, interpreters, check, destfile):
# default selinux fs list is pass in as _ansible_selinux_special_fs arg
complex_args['_ansible_selinux_special_fs'] = C.DEFAULT_SELINUX_SPECIAL_FS
complex_args['_ansible_tmp'] = C.DEFAULT_LOCAL_TMP
complex_args['_ansible_tmpdir'] = C.DEFAULT_LOCAL_TMP
complex_args['_ansible_keep_remote_files'] = C.DEFAULT_KEEP_REMOTE_FILES
if args.startswith("@"):

View File

@ -180,9 +180,11 @@ class CLI(with_metaclass(ABCMeta, object)):
ver = deprecated[1]['version']
display.deprecated("%s option, %s %s" % (name, why, alt), version=ver)
# warn about typing issues with configuration entries
for unable in C.config.UNABLE:
display.warning("Unable to set correct type for configuration entry: %s" % unable)
# Errors with configuration entries
if C.config.UNABLE:
for unable in C.config.UNABLE:
display.error("Unable to set correct type for configuration entry for %s: %s" % (unable, C.config.UNABLE[unable]))
raise AnsibleError("Invalid configuration settings")
@staticmethod
def split_vault_id(vault_id):

View File

@ -17,6 +17,7 @@ from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import datetime
import json
import os
import textwrap
import traceback
@ -55,6 +56,18 @@ class DocCLI(CLI):
super(DocCLI, self).__init__(args)
self.plugin_list = set()
self.loader_map = {
'cache': cache_loader,
'callback': callback_loader,
'connection': connection_loader,
'lookup': lookup_loader,
'strategy': strategy_loader,
'vars': vars_loader,
'inventory': inventory_loader,
'shell': shell_loader,
'module': module_loader,
}
def parse(self):
self.parser = CLI.base_parser(
@ -72,13 +85,15 @@ class DocCLI(CLI):
help='Show playbook snippet for specified plugin(s)')
self.parser.add_option("-a", "--all", action="store_true", default=False, dest='all_plugins',
help='**For internal testing only** Show documentation for all plugins.')
self.parser.add_option("-j", "--json", action="store_true", default=False, dest='json_dump',
help='**For internal testing only** Dump json metadata for all plugins.')
self.parser.add_option("-t", "--type", action="store", default='module', dest='type', type='choice',
help='Choose which plugin type (defaults to "module")',
choices=C.DOCUMENTABLE_PLUGINS)
super(DocCLI, self).parse()
if [self.options.all_plugins, self.options.list_dir, self.options.list_files, self.options.show_snippet].count(True) > 1:
raise AnsibleOptionsError("Only one of -l, -F, -s or -a can be used at the same time.")
if [self.options.all_plugins, self.options.json_dump, self.options.list_dir, self.options.list_files, self.options.show_snippet].count(True) > 1:
raise AnsibleOptionsError("Only one of -l, -F, -s, -j or -a can be used at the same time.")
display.verbosity = self.options.verbosity
@ -87,26 +102,7 @@ class DocCLI(CLI):
super(DocCLI, self).run()
plugin_type = self.options.type
# choose plugin type
if plugin_type == 'cache':
loader = cache_loader
elif plugin_type == 'callback':
loader = callback_loader
elif plugin_type == 'connection':
loader = connection_loader
elif plugin_type == 'lookup':
loader = lookup_loader
elif plugin_type == 'strategy':
loader = strategy_loader
elif plugin_type == 'vars':
loader = vars_loader
elif plugin_type == 'inventory':
loader = inventory_loader
elif plugin_type == 'shell':
loader = shell_loader
else:
loader = module_loader
loader = self.loader_map.get(plugin_type, self.loader_map['module'])
# add to plugin path from command line
if self.options.module_path:
@ -122,7 +118,7 @@ class DocCLI(CLI):
if self.options.list_files:
paths = loader._get_paths()
for path in paths:
self.find_plugins(path, plugin_type)
self.plugin_list = self.find_plugins(path, plugin_type)
list_text = self.get_plugin_list_filenames(loader)
self.pager(list_text)
@ -132,17 +128,27 @@ class DocCLI(CLI):
if self.options.list_dir:
paths = loader._get_paths()
for path in paths:
self.find_plugins(path, plugin_type)
self.plugin_list = self.find_plugins(path, plugin_type)
self.pager(self.get_plugin_list_text(loader))
return 0
# process all plugins of type
if self.options.all_plugins:
paths = loader._get_paths()
for path in paths:
self.find_plugins(path, plugin_type)
self.args = sorted(set(self.plugin_list))
self.args = self.get_all_plugins_of_type(plugin_type)
# dump plugin metadata as JSON
if self.options.json_dump:
plugin_data = {}
for plugin_type in self.loader_map.keys():
plugin_data[plugin_type] = dict()
plugin_names = self.get_all_plugins_of_type(plugin_type)
for plugin_name in plugin_names:
plugin_data[plugin_type][plugin_name] = self.get_plugin_metadata(plugin_type, plugin_name)
self.pager(json.dumps(plugin_data, sort_keys=True, indent=4))
return 0
if len(self.args) == 0:
raise AnsibleOptionsError("Incorrect options passed")
@ -150,65 +156,122 @@ class DocCLI(CLI):
# process command line list
text = ''
for plugin in self.args:
try:
# if the plugin lives in a non-python file (eg, win_X.ps1), require the corresponding python file for docs
filename = loader.find_plugin(plugin, mod_type='.py', ignore_deprecated=True, check_aliases=True)
if filename is None:
display.warning("%s %s not found in:\n%s\n" % (plugin_type, plugin, search_paths))
continue
if any(filename.endswith(x) for x in C.BLACKLIST_EXTS):
continue
try:
doc, plainexamples, returndocs, metadata = get_docstring(filename, fragment_loader, verbose=(self.options.verbosity > 0))
except Exception:
display.vvv(traceback.format_exc())
display.error("%s %s has a documentation error formatting or is missing documentation." % (plugin_type, plugin), wrap_text=False)
continue
if doc is not None:
# assign from other sections
doc['plainexamples'] = plainexamples
doc['returndocs'] = returndocs
doc['metadata'] = metadata
# generate extra data
if plugin_type == 'module':
# is there corresponding action plugin?
if plugin in action_loader:
doc['action'] = True
else:
doc['action'] = False
doc['filename'] = filename
doc['now_date'] = datetime.date.today().strftime('%Y-%m-%d')
if 'docuri' in doc:
doc['docuri'] = doc[plugin_type].replace('_', '-')
if self.options.show_snippet and plugin_type == 'module':
text += self.get_snippet_text(doc)
else:
text += self.get_man_text(doc)
else:
# this typically means we couldn't even parse the docstring, not just that the YAML is busted,
# probably a quoting issue.
raise AnsibleError("Parsing produced an empty object.")
except Exception as e:
display.vvv(traceback.format_exc())
raise AnsibleError("%s %s missing documentation (or could not parse documentation): %s\n" % (plugin_type, plugin, str(e)))
text += self.format_plugin_doc(plugin, loader, plugin_type, search_paths)
if text:
self.pager(text)
return 0
def get_all_plugins_of_type(self, plugin_type):
loader = self.loader_map[plugin_type]
plugin_list = set()
paths = loader._get_paths()
for path in paths:
plugins_to_add = self.find_plugins(path, plugin_type)
plugin_list.update(plugins_to_add)
return sorted(set(plugin_list))
def get_plugin_metadata(self, plugin_type, plugin_name):
# if the plugin lives in a non-python file (eg, win_X.ps1), require the corresponding python file for docs
loader = self.loader_map[plugin_type]
filename = loader.find_plugin(plugin_name, mod_type='.py', ignore_deprecated=True, check_aliases=True)
if filename is None:
raise AnsibleError("unable to load {0} plugin named {1} ".format(plugin_type, plugin_name))
try:
doc, __, __, __ = get_docstring(filename, fragment_loader, verbose=(self.options.verbosity > 0))
except Exception:
display.vvv(traceback.format_exc())
raise AnsibleError(
"%s %s at %s has a documentation error formatting or is missing documentation." %
(plugin_type, plugin_name, filename), wrap_text=False)
return dict(
name=plugin_name,
namespace=self.namespace_from_plugin_filepath(filename, plugin_name, loader.package_path),
description=doc.get('short_description', "UNKNOWN"),
version_added=doc.get('version_added', "UNKNOWN")
)
def namespace_from_plugin_filepath(self, filepath, plugin_name, basedir):
if not basedir.endswith('/'):
basedir += '/'
rel_path = filepath.replace(basedir, '')
extension_free = os.path.splitext(rel_path)[0]
namespace_only = extension_free.rsplit(plugin_name, 1)[0].strip('/_')
clean_ns = namespace_only.replace('/', '.')
if clean_ns == '':
clean_ns = None
return clean_ns
def format_plugin_doc(self, plugin, loader, plugin_type, search_paths):
text = ''
try:
# if the plugin lives in a non-python file (eg, win_X.ps1), require the corresponding python file for docs
filename = loader.find_plugin(plugin, mod_type='.py', ignore_deprecated=True, check_aliases=True)
if filename is None:
display.warning("%s %s not found in:\n%s\n" % (plugin_type, plugin, search_paths))
return
if any(filename.endswith(x) for x in C.BLACKLIST_EXTS):
return
try:
doc, plainexamples, returndocs, metadata = get_docstring(filename, fragment_loader,
verbose=(self.options.verbosity > 0))
except Exception:
display.vvv(traceback.format_exc())
display.error(
"%s %s has a documentation error formatting or is missing documentation." % (plugin_type, plugin),
wrap_text=False)
return
if doc is not None:
# assign from other sections
doc['plainexamples'] = plainexamples
doc['returndocs'] = returndocs
doc['metadata'] = metadata
# generate extra data
if plugin_type == 'module':
# is there corresponding action plugin?
if plugin in action_loader:
doc['action'] = True
else:
doc['action'] = False
doc['filename'] = filename
doc['now_date'] = datetime.date.today().strftime('%Y-%m-%d')
if 'docuri' in doc:
doc['docuri'] = doc[plugin_type].replace('_', '-')
if self.options.show_snippet and plugin_type == 'module':
text += self.get_snippet_text(doc)
else:
text += self.get_man_text(doc)
return text
else:
# this typically means we couldn't even parse the docstring, not just that the YAML is busted,
# probably a quoting issue.
raise AnsibleError("Parsing produced an empty object.")
except Exception as e:
display.vvv(traceback.format_exc())
raise AnsibleError(
"%s %s missing documentation (or could not parse documentation): %s\n" % (plugin_type, plugin, str(e)))
def find_plugins(self, path, ptype):
display.vvvv("Searching %s for plugins" % path)
plugin_list = set()
if not os.path.exists(path):
display.vvvv("%s does not exist" % path)
return
return plugin_list
bkey = ptype.upper()
for plugin in os.listdir(path):
@ -233,9 +296,11 @@ class DocCLI(CLI):
plugin = plugin.lstrip('_') # remove underscore from deprecated plugins
if plugin not in BLACKLIST.get(bkey, ()):
self.plugin_list.add(plugin)
plugin_list.add(plugin)
display.vvvv("Added %s" % plugin)
return plugin_list
def get_plugin_list_text(self, loader):
columns = display.columns
displace = max(len(x) for x in self.plugin_list)

View File

@ -736,7 +736,7 @@ DEFAULT_LOCAL_TMP:
type: tmppath
DEFAULT_LOG_PATH:
name: Ansible log file path
default: ''
default: ~
description: File to which Ansible will log on the controller. When empty logging is disabled.
env: [{name: ANSIBLE_LOG_PATH}]
ini:

View File

@ -60,7 +60,7 @@ def ensure_type(value, value_type, origin=None):
if value_type in ('boolean', 'bool'):
value = boolean(value, strict=False)
elif value:
elif value is not None:
if value_type in ('integer', 'int'):
value = int(value)
@ -166,7 +166,7 @@ def find_ini_config_file():
class ConfigManager(object):
UNABLE = []
UNABLE = {}
DEPRECATED = []
def __init__(self, conf_file=None, defs_file=None):
@ -281,7 +281,12 @@ class ConfigManager(object):
def get_config_value(self, config, cfile=None, plugin_type=None, plugin_name=None, keys=None, variables=None):
''' wrapper '''
value, _drop = self.get_config_value_and_origin(config, cfile=cfile, plugin_type=plugin_type, plugin_name=plugin_name, keys=keys, variables=variables)
try:
value, _drop = self.get_config_value_and_origin(config, cfile=cfile, plugin_type=plugin_type, plugin_name=plugin_name,
keys=keys, variables=variables)
except Exception as e:
raise AnsibleError("Invalid settings supplied for %s: %s" % (config, to_native(e)))
return value
def get_config_value_and_origin(self, config, cfile=None, plugin_type=None, plugin_name=None, keys=None, variables=None):
@ -343,17 +348,23 @@ class ConfigManager(object):
# set default if we got here w/o a value
if value is None:
value = defs[config].get('default')
origin = 'default'
# skip typing as this is a temlated default that will be resolved later in constants, which has needed vars
if plugin_type is None and isinstance(value, string_types) and (value.startswith('{{') and value.endswith('}}')):
return value, origin
if defs[config].get('required', False):
entry = ''
if plugin_type:
entry += 'plugin_type: %s ' % plugin_type
if plugin_name:
entry += 'plugin: %s ' % plugin_name
entry += 'setting: %s ' % config
raise AnsibleError("No setting was provided for required configuration %s" % (entry))
else:
value = defs[config].get('default')
origin = 'default'
# skip typing as this is a temlated default that will be resolved later in constants, which has needed vars
if plugin_type is None and isinstance(value, string_types) and (value.startswith('{{') and value.endswith('}}')):
return value, origin
# ensure correct type
try:
value = ensure_type(value, defs[config].get('type'), origin=origin)
except Exception as e:
self.UNABLE.append(config)
# ensure correct type, can raise exceptoins on mismatched types
value = ensure_type(value, defs[config].get('type'), origin=origin)
# deal with deprecation of the setting
if 'deprecated' in defs[config] and origin != 'default':
@ -392,7 +403,13 @@ class ConfigManager(object):
raise AnsibleOptionsError("Invalid configuration definition '%s': type is %s" % (to_native(config), type(defs[config])))
# get value and origin
value, origin = self.get_config_value_and_origin(config, configfile)
try:
value, origin = self.get_config_value_and_origin(config, configfile)
except Exception as e:
# when building constants.py we ignore invalid configs
# CLI takes care of warnings once 'display' is loaded
self.UNABLE[config] = to_text(e)
continue
# set the constant
self.data.update_setting(Setting(config, value, origin, defs[config].get('type', 'string')))

View File

@ -84,7 +84,7 @@ BECOME_MISSING_STRINGS = {
'enable': '',
'machinectl': '',
} # FIXME: deal with i18n
BLACKLIST_EXTS = ('.pyc', '.pyo', '.swp', '.bak', '~', '.rpm', '.md', '.txt')
BLACKLIST_EXTS = ('.pyc', '.pyo', '.swp', '.bak', '~', '.rpm', '.md', '.txt', '.rst')
BOOL_TRUE = BOOLEANS_TRUE
CONTROLER_LANG = os.getenv('LANG', 'en_US.UTF-8')
DEFAULT_BECOME_PASS = None
@ -185,6 +185,7 @@ for setting in config.data.get_settings():
pass # not a python data structure
except:
pass # not templatable
value = ensure_type(value, setting.name)
value = ensure_type(value, setting.type)
set_constant(setting.name, value)

View File

@ -195,23 +195,23 @@ AZURE_PKG_VERSIONS = {
},
'ComputeManagementClient': {
'package_name': 'compute',
'expected_version': '2.0.0'
'expected_version': '2.1.0'
},
'ContainerInstanceManagementClient': {
'package_name': 'containerinstance',
'expected_version': '0.3.1'
'expected_version': '0.4.0'
},
'NetworkManagementClient': {
'package_name': 'network',
'expected_version': '1.3.0'
'expected_version': '1.7.1'
},
'ResourceManagementClient': {
'package_name': 'resource',
'expected_version': '1.1.0'
'expected_version': '1.2.2'
},
'DnsManagementClient': {
'package_name': 'dns',
'expected_version': '1.0.1'
'expected_version': '1.2.0'
},
'WebSiteManagementClient': {
'package_name': 'web',
@ -408,8 +408,11 @@ class AzureRMModuleBase(object):
return
expected_version = package_version.get('expected_version')
if Version(client_version) < Version(expected_version):
self.fail("Installed azure-mgmt-{0} client version is {1}. The supported version is {2}. Try "
self.fail("Installed azure-mgmt-{0} client version is {1}. The minimum supported version is {2}. Try "
"`pip install ansible[azure]`".format(client_name, client_version, expected_version))
if Version(client_version) != Version(expected_version):
self.module.warn("Installed azure-mgmt-{0} client version is {1}. The expected version is {2}. Try "
"`pip install ansible[azure]`".format(client_name, client_version, expected_version))
def exec_module(self, **kwargs):
self.fail("Error: {0} failed to implement exec_module method.".format(self.__class__.__name__))
@ -945,6 +948,9 @@ class AzureRMModuleBase(object):
profile_default_version = api_profile_dict.get('default_api_version', None)
if api_version or profile_default_version:
client_kwargs['api_version'] = api_version or profile_default_version
if 'profile' in client_kwargs:
# remove profile; only pass API version if specified
client_kwargs.pop('profile')
client = client_type(**client_kwargs)

View File

@ -28,6 +28,7 @@ from ansible.module_utils.basic import AnsibleModule
try:
import kubernetes
from openshift.dynamic import DynamicClient
from openshift.dynamic.exceptions import ResourceNotFoundError, ResourceNotUniqueError
HAS_K8S_MODULE_HELPER = True
except ImportError:
HAS_K8S_MODULE_HELPER = False
@ -121,13 +122,14 @@ class K8sAnsibleMixin(object):
self._argspec_cache = argument_spec
return self._argspec_cache
def get_api_client(self, **auth):
def get_api_client(self, **auth_params):
auth_args = AUTH_ARG_SPEC.keys()
auth = auth or getattr(self, 'params', {})
auth_params = auth_params or getattr(self, 'params', {})
auth = copy.deepcopy(auth_params)
configuration = kubernetes.client.Configuration()
for key, value in iteritems(auth):
for key, value in iteritems(auth_params):
if key in auth_args and value is not None:
if key == 'api_key':
setattr(configuration, key, {'authorization': "Bearer {0}".format(value)})
@ -137,6 +139,7 @@ class K8sAnsibleMixin(object):
env_value = os.getenv('K8S_AUTH_{0}'.format(key.upper()), None)
if env_value is not None:
setattr(configuration, key, env_value)
auth[key] = env_value
kubernetes.client.Configuration.set_default(configuration)
@ -174,6 +177,18 @@ class K8sAnsibleMixin(object):
return kubernetes.client.ApiClient()
raise
def find_resource(self, kind, api_version, fail=False):
for attribute in ['kind', 'name', 'singular_name']:
try:
return self.client.resources.get(**{'api_version': api_version, attribute: kind})
except (ResourceNotFoundError, ResourceNotUniqueError):
pass
try:
return self.client.resources.get(api_version=api_version, short_names=[kind])
except (ResourceNotFoundError, ResourceNotUniqueError):
if fail:
self.fail(msg='Failed to find exact match for {0}.{1} by [kind, name, singularName, shortNames]'.format(api_version, kind))
def remove_aliases(self):
"""
The helper doesn't know what to do with aliased keys
@ -189,12 +204,12 @@ class K8sAnsibleMixin(object):
result = None
path = os.path.normpath(src)
if not os.path.exists(path):
self.fail_json(msg="Error accessing {0}. Does the file exist?".format(path))
self.fail(msg="Error accessing {0}. Does the file exist?".format(path))
try:
with open(path, 'r') as f:
result = list(yaml.safe_load_all(f))
except (IOError, yaml.YAMLError) as exc:
self.fail_json(msg="Error loading resource_definition: {0}".format(exc))
self.fail(msg="Error loading resource_definition: {0}".format(exc))
return result
@staticmethod
@ -202,16 +217,7 @@ class K8sAnsibleMixin(object):
if not HAS_DICTDIFFER:
return False, []
def get_shared_attrs(o1, o2):
shared_attrs = {}
for k, v in o2.items():
if isinstance(v, dict):
shared_attrs[k] = get_shared_attrs(o1.get(k, {}), v)
else:
shared_attrs[k] = o1.get(k)
return shared_attrs
diffs = list(dictdiffer.diff(new, get_shared_attrs(existing, new)))
diffs = list(dictdiffer.diff(new, existing))
match = len(diffs) == 0
return match, diffs
@ -234,3 +240,6 @@ class KubernetesAnsibleModule(AnsibleModule, K8sAnsibleMixin):
def execute_module(self):
raise NotImplementedError()
def fail(self, msg=None):
self.fail_json(msg=msg)

View File

@ -74,12 +74,9 @@ class KubernetesRawModule(KubernetesAnsibleModule):
if kind.lower().endswith('list'):
search_kind = kind[:-4]
api_version = definition.get('apiVersion')
try:
resource = self.client.resources.get(kind=search_kind, api_version=api_version)
except Exception as e:
self.fail_json(msg='Failed to find resource {0}.{1}: {2}'.format(
api_version, search_kind, e
))
resource = self.find_resource(search_kind, api_version, fail=True)
definition['kind'] = resource.kind
definition['apiVersion'] = resource.group_version
result = self.perform_action(resource, definition)
changed = changed or result['changed']
results.append(result)
@ -151,32 +148,33 @@ class KubernetesRawModule(KubernetesAnsibleModule):
result['method'] = 'create'
return result
match = False
diffs = []
if existing and force:
if not self.check_mode:
try:
k8s_obj = resource.replace(definition, name=name, namespace=namespace)
result['result'] = k8s_obj.to_dict()
k8s_obj = resource.replace(definition, name=name, namespace=namespace).to_dict()
match, diffs = self.diff_objects(existing.to_dict(), k8s_obj)
result['result'] = k8s_obj
except DynamicApiError as exc:
self.fail_json(msg="Failed to replace object: {0}".format(exc.body),
error=exc.status, status=exc.status, reason=exc.reason)
result['changed'] = True
result['changed'] = not match
result['method'] = 'replace'
result['diff'] = diffs
return result
match, diffs = self.diff_objects(existing.to_dict(), definition)
if match:
result['result'] = existing.to_dict()
return result
# Differences exist between the existing obj and requested params
if not self.check_mode:
try:
k8s_obj = resource.patch(definition, name=name, namespace=namespace)
result['result'] = k8s_obj.to_dict()
k8s_obj = resource.patch(definition, name=name, namespace=namespace).to_dict()
match, diffs = self.diff_objects(existing.to_dict(), k8s_obj)
result['result'] = k8s_obj
except DynamicApiError as exc:
self.fail_json(msg="Failed to patch object: {0}".format(exc.body),
error=exc.status, status=exc.status, reason=exc.reason)
result['changed'] = True
result['changed'] = not match
result['method'] = 'patch'
result['diff'] = diffs
return result

View File

@ -52,6 +52,8 @@ class KubernetesAnsibleScaleModule(KubernetesRawModule):
name = definition['metadata']['name']
namespace = definition['metadata'].get('namespace')
api_version = definition['apiVersion']
kind = definition['kind']
current_replicas = self.params.get('current_replicas')
replicas = self.params.get('replicas')
resource_version = self.params.get('resource_version')
@ -62,7 +64,7 @@ class KubernetesAnsibleScaleModule(KubernetesRawModule):
existing_count = None
return_attributes = dict(changed=False, result=dict())
resource = self.client.resources.get(api_version=definition['apiVersion'], kind=definition['kind'])
resource = self.find_resource(kind, api_version, fail=True)
try:
existing = resource.get(name=name, namespace=namespace)

View File

@ -26,10 +26,9 @@
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
import os
from functools import partial
from ansible.module_utils._text import to_native
from ansible.module_utils.six import iteritems
from ansible.module_utils._text import to_text
@ -40,7 +39,15 @@ try:
except ImportError:
HAS_INFOBLOX_CLIENT = False
nios_provider_spec = {
# defining nios constants
NIOS_DNS_VIEW = 'view'
NIOS_NETWORK_VIEW = 'networkview'
NIOS_HOST_RECORD = 'record:host'
NIOS_IPV4_NETWORK = 'network'
NIOS_IPV6_NETWORK = 'ipv6network'
NIOS_ZONE = 'zone_auth'
NIOS_PROVIDER_SPEC = {
'host': dict(),
'username': dict(),
'password': dict(no_log=True),
@ -57,10 +64,8 @@ nios_provider_spec = {
def get_connector(*args, **kwargs):
''' Returns an instance of infoblox_client.connector.Connector
:params args: positional arguments are silently ignored
:params kwargs: dict that is passed to Connector init
:returns: Connector
'''
if not HAS_INFOBLOX_CLIENT:
@ -68,12 +73,11 @@ def get_connector(*args, **kwargs):
'to be installed. It can be installed using the '
'command `pip install infoblox-client`')
if not set(kwargs.keys()).issubset(nios_provider_spec.keys()):
if not set(kwargs.keys()).issubset(NIOS_PROVIDER_SPEC.keys()):
raise Exception('invalid or unsupported keyword argument for connector')
for key, value in iteritems(nios_provider_spec):
for key, value in iteritems(NIOS_PROVIDER_SPEC):
if key not in kwargs:
# apply default values from nios_provider_spec since we cannot just
# apply default values from NIOS_PROVIDER_SPEC since we cannot just
# assume the provider values are coming from AnsibleModule
if 'default' in value:
kwargs[key] = value['default']
@ -89,11 +93,9 @@ def get_connector(*args, **kwargs):
def normalize_extattrs(value):
''' Normalize extattrs field to expected format
The module accepts extattrs as key/value pairs. This method will
transform the key/value pairs into a structure suitable for
sending across WAPI in the format of:
extattrs: {
key: {
value: <value>
@ -105,29 +107,23 @@ def normalize_extattrs(value):
def flatten_extattrs(value):
''' Flatten the key/value struct for extattrs
WAPI returns extattrs field as a dict in form of:
extattrs: {
key: {
value: <value>
}
}
This method will flatten the structure to:
extattrs: {
key: value
}
'''
return dict([(k, v['value']) for k, v in iteritems(value)])
class WapiBase(object):
''' Base class for implementing Infoblox WAPI API '''
provider_spec = {'provider': dict(type='dict', options=nios_provider_spec)}
provider_spec = {'provider': dict(type='dict', options=NIOS_PROVIDER_SPEC)}
def __init__(self, provider):
self.connector = get_connector(**provider)
@ -163,7 +159,6 @@ class WapiInventory(WapiBase):
class WapiModule(WapiBase):
''' Implements WapiBase for executing a NIOS module '''
def __init__(self, module):
self.module = module
provider = module.params['provider']
@ -174,29 +169,29 @@ class WapiModule(WapiBase):
def handle_exception(self, method_name, exc):
''' Handles any exceptions raised
This method will be called if an InfobloxException is raised for
any call to the instance of Connector. This method will then
gracefully fail the module.
any call to the instance of Connector and also, in case of generic
exception. This method will then gracefully fail the module.
:args exc: instance of InfobloxException
'''
self.module.fail_json(
msg=exc.response['text'],
type=exc.response['Error'].split(':')[0],
code=exc.response.get('code'),
operation=method_name
)
if ('text' in exc.response):
self.module.fail_json(
msg=exc.response['text'],
type=exc.response['Error'].split(':')[0],
code=exc.response.get('code'),
operation=method_name
)
else:
self.module.fail_json(msg=to_native(exc))
def run(self, ib_obj_type, ib_spec):
''' Runs the module and performans configuration tasks
:args ib_obj_type: the WAPI object type to operate against
:args ib_spec: the specification for the WAPI object as a dict
:returns: a results dict
'''
update = new_name = None
state = self.module.params['state']
if state not in ('present', 'absent'):
self.module.fail_json(msg='state must be one of `present`, `absent`, got `%s`' % state)
@ -204,10 +199,14 @@ class WapiModule(WapiBase):
result = {'changed': False}
obj_filter = dict([(k, self.module.params[k]) for k, v in iteritems(ib_spec) if v.get('ib_req')])
ib_obj = self.get_object(ib_obj_type, obj_filter.copy(), return_fields=ib_spec.keys())
if ib_obj:
current_object = ib_obj[0]
if('name' in obj_filter):
ib_obj_ref, update, new_name = self.get_object_ref(ib_obj_type, obj_filter, ib_spec)
else:
ib_obj_ref = self.get_object(ib_obj_type, obj_filter.copy(), return_fields=ib_spec.keys())
if ib_obj_ref:
current_object = ib_obj_ref[0]
if 'extattrs' in current_object:
current_object['extattrs'] = flatten_extattrs(current_object['extattrs'])
ref = current_object.pop('_ref')
@ -223,25 +222,28 @@ class WapiModule(WapiBase):
else:
proposed_object[key] = self.module.params[key]
modified = not self.compare_objects(current_object, proposed_object)
# checks if the name's field has been updated
if update and new_name:
proposed_object['name'] = new_name
res = None
modified = not self.compare_objects(current_object, proposed_object)
if 'extattrs' in proposed_object:
proposed_object['extattrs'] = normalize_extattrs(proposed_object['extattrs'])
if state == 'present':
if ref is None:
if not self.module.check_mode:
self.create_object(ib_obj_type, proposed_object)
result['changed'] = True
elif modified:
if 'network_view' in proposed_object:
self.check_if_network_view_exists(proposed_object['network_view'])
proposed_object.pop('network_view')
elif 'view' in proposed_object:
self.check_if_dns_view_exists(proposed_object['view'])
if not self.module.check_mode:
if (ib_obj_type in (NIOS_HOST_RECORD, NIOS_NETWORK_VIEW, NIOS_DNS_VIEW)):
proposed_object = self.on_update(proposed_object, ib_spec)
res = self.update_object(ref, proposed_object)
elif 'network_view' in proposed_object:
proposed_object.pop('network_view')
if not self.module.check_mode and res is None:
proposed_object = self.on_update(proposed_object, ib_spec)
self.update_object(ref, proposed_object)
result['changed'] = True
elif state == 'absent':
@ -252,42 +254,10 @@ class WapiModule(WapiBase):
return result
def check_if_dns_view_exists(self, name, fail_on_missing=True):
''' Checks if the specified DNS view is already configured
:args name: the name of the DNS view to check
:args fail_on_missing: fail the module if the DNS view does not exist
:returns: True if the network_view exists and False if the DNS view
does not exist and fail_on_missing is False
'''
res = self.get_object('view', {'name': name}) is not None
if not res and fail_on_missing:
self.module.fail_json(msg='DNS view %s does not exist, please create '
'it using nios_dns_view first' % name)
return res
def check_if_network_view_exists(self, name, fail_on_missing=True):
''' Checks if the specified network_view is already configured
:args name: the name of the network view to check
:args fail_on_missing: fail the module if the network_view does not exist
:returns: True if the network_view exists and False if the network_view
does not exist and fail_on_missing is False
'''
res = self.get_object('networkview', {'name': name}) is not None
if not res and fail_on_missing:
self.module.fail_json(msg='Network view %s does not exist, please create '
'it using nios_network_view first' % name)
return res
def issubset(self, item, objects):
''' Checks if item is a subset of objects
:args item: the subset item to validate
:args objects: superset list of objects to validate against
:returns: True if item is a subset of one entry in objects otherwise
this method will return None
'''
@ -322,16 +292,45 @@ class WapiModule(WapiBase):
return True
def get_object_ref(self, ib_obj_type, obj_filter, ib_spec):
''' this function gets and returns the current object based on name/old_name passed'''
update = False
old_name = new_name = None
try:
name_obj = self.module._check_type_dict(obj_filter['name'])
old_name = name_obj['old_name']
new_name = name_obj['new_name']
except TypeError:
name = obj_filter['name']
if old_name and new_name:
if (ib_obj_type == NIOS_HOST_RECORD):
test_obj_filter = dict([('name', old_name), ('view', obj_filter['view'])])
else:
test_obj_filter = dict([('name', old_name)])
# get the object reference
ib_obj = self.get_object(ib_obj_type, test_obj_filter, return_fields=ib_spec.keys())
if ib_obj:
obj_filter['name'] = new_name
else:
test_obj_filter['name'] = new_name
ib_obj = self.get_object(ib_obj_type, test_obj_filter, return_fields=ib_spec.keys())
update = True
return ib_obj, update, new_name
if (ib_obj_type == NIOS_HOST_RECORD):
test_obj_filter = dict([('name', name), ('view', obj_filter['view'])])
else:
test_obj_filter = dict([('name', name)])
ib_obj = self.get_object(ib_obj_type, test_obj_filter.copy(), return_fields=ib_spec.keys())
return ib_obj, update, new_name
def on_update(self, proposed_object, ib_spec):
''' Event called before the update is sent to the API endpoing
This method will allow the final proposed object to be changed
and/or keys filtered before it is sent to the API endpoint to
be processed.
:args proposed_object: A dict item that will be encoded and sent
the the API endpoint with the updated data structure
:returns: updated object to be sent to API endpoint
'''
keys = set()
@ -339,5 +338,4 @@ class WapiModule(WapiBase):
update = ib_spec[key].get('update', True)
if not update:
keys.add(key)
return dict([(k, v) for k, v in iteritems(proposed_object) if k not in keys])

View File

@ -37,7 +37,7 @@ class F5Client(F5BaseClient):
if self._client:
return self._client
for x in range(0, 60):
for x in range(0, 10):
try:
result = ManagementRoot(
self.provider['server'],
@ -53,7 +53,7 @@ class F5Client(F5BaseClient):
exc = ex
time.sleep(1)
error = 'Unable to connect to {0} on port {1}.'.format(
self.params['server'], self.params['server_port']
self.provider['server'], self.provider['server_port']
)
if exc is not None:
@ -71,7 +71,6 @@ class F5RestClient(F5BaseClient):
exc = None
if self._client:
return self._client
for x in range(0, 10):
try:
url = "https://{0}:{1}/mgmt/shared/authn/login".format(

View File

@ -29,32 +29,33 @@ except ImportError:
class F5Client(F5BaseClient):
def __init__(self, *args, **kwargs):
super(F5Client, self).__init__(*args, **kwargs)
self.provider = self.merge_provider_params()
@property
def api(self):
exc = None
if self._client:
return self._client
for x in range(0, 3):
for x in range(0, 10):
try:
server = self.params['provider']['server'] or self.params['server']
user = self.params['provider']['user'] or self.params['user']
password = self.params['provider']['password'] or self.params['password']
server_port = self.params['provider']['server_port'] or self.params['server_port'] or 443
validate_certs = self.params['provider']['validate_certs'] or self.params['validate_certs']
result = ManagementRoot(
server,
user,
password,
port=server_port,
verify=validate_certs
self.provider['server'],
self.provider['user'],
self.provider['password'],
port=self.provider['server_port'],
verify=self.provider['validate_certs']
)
self._client = result
return self._client
except Exception as ex:
exc = ex
time.sleep(1)
error = 'Unable to connect to {0} on port {1}.'.format(self.params['server'], self.params['server_port'])
error = 'Unable to connect to {0} on port {1}.'.format(
self.provider['server'], self.provider['server_port']
)
if exc is not None:
error += ' The reported error was "{0}".'.format(str(exc))
raise F5ModuleError(error)

View File

@ -479,3 +479,24 @@ def normalize_interface(name):
proper_interface = name
return proper_interface
def get_interface_type(interface):
"""Gets the type of interface
"""
if interface.upper().startswith('ET'):
return 'ethernet'
elif interface.upper().startswith('VL'):
return 'svi'
elif interface.upper().startswith('LO'):
return 'loopback'
elif interface.upper().startswith('MG'):
return 'management'
elif interface.upper().startswith('MA'):
return 'management'
elif interface.upper().startswith('PO'):
return 'portchannel'
elif interface.upper().startswith('NV'):
return 'nve'
else:
return 'unknown'

View File

@ -21,7 +21,7 @@ author: "Monty Taylor (@emonty)"
description:
- Retrieve an auth token from an OpenStack Cloud
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "openstacksdk"
options:
availability_zone:

View File

@ -29,7 +29,7 @@ notes:
- This module creates a new top-level C(openstack_flavors) fact, which
contains a list of unsorted flavors.
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "openstacksdk"
options:
name:

View File

@ -42,7 +42,7 @@ options:
description:
- Ignored. Present for backwards compatibility
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "openstacksdk"
'''

View File

@ -22,7 +22,7 @@ description:
notes:
- Facts are placed in the C(openstack) variable.
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "openstacksdk"
options:
image:

View File

@ -46,7 +46,7 @@ options:
description:
- Ignored. Present for backwards compatibility
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "openstacksdk"
'''

View File

@ -21,7 +21,7 @@ author: "Ricardo Carrillo Cruz (@rcarrillocruz)"
description:
- Retrieve facts about a one or more OpenStack domains
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "sdk"
options:
name:

View File

@ -35,7 +35,7 @@ options:
- Ignored. Present for backwards compatibility
required: false
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "openstacksdk"
'''

View File

@ -48,7 +48,7 @@ options:
description:
- Ignored. Present for backwards compatibility
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "openstacksdk"
'''

View File

@ -21,7 +21,7 @@ author: "Davide Agnello (@dagnello)"
description:
- Retrieve facts about one or more networks from OpenStack.
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "sdk"
options:
name:

View File

@ -37,7 +37,7 @@ options:
choices: [present, absent]
default: present
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "openstacksdk"
'''

View File

@ -22,7 +22,7 @@ description:
notes:
- Facts are placed in the C(openstack_ports) variable.
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "openstacksdk"
options:
port:

View File

@ -50,7 +50,7 @@ options:
description:
- Ignored. Present for backwards compatibility
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "openstacksdk"
'''

View File

@ -21,7 +21,7 @@ author: "Ricardo Carrillo Cruz (@rcarrillocruz)"
description:
- Retrieve facts about a one or more OpenStack projects
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "openstacksdk"
options:
name:

View File

@ -105,7 +105,7 @@ options:
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "openstacksdk >= 0.13.0"
'''

View File

@ -54,7 +54,7 @@ options:
description:
- Ignored. Present for backwards compatibility
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "openstacksdk"
'''

View File

@ -166,7 +166,7 @@ options:
description:
- Availability zone in which to create the server.
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "openstacksdk"
'''

View File

@ -54,7 +54,7 @@ options:
description:
- Ignored. Present for backwards compatibility
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "openstacksdk"
'''

View File

@ -24,7 +24,7 @@ notes:
- This module creates a new top-level C(openstack_servers) fact, which
contains a list of servers.
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "openstacksdk"
options:
server:

View File

@ -45,7 +45,7 @@ options:
- Ignored. Present for backwards compatibility
required: false
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "openstacksdk"
'''

View File

@ -44,7 +44,7 @@ options:
description:
- Ignored. Present for backwards compatibility
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "openstacksdk"
'''

View File

@ -59,7 +59,7 @@ options:
description:
- Ignored. Present for backwards compatibility
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "openstacksdk"
'''
EXAMPLES = '''

View File

@ -96,7 +96,7 @@ options:
description:
- Ignored. Present for backwards compatibility
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "openstacksdk"
'''

View File

@ -21,7 +21,7 @@ author: "Davide Agnello (@dagnello)"
description:
- Retrieve facts about one or more subnets from OpenStack.
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "openstacksdk"
options:
subnet:

View File

@ -66,7 +66,7 @@ options:
description:
- Ignored. Present for backwards compatibility
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "openstacksdk"
'''

View File

@ -21,7 +21,7 @@ author: "Ricardo Carrillo Cruz (@rcarrillocruz)"
description:
- Retrieve facts about a one or more OpenStack users
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "openstacksdk"
options:
name:

View File

@ -39,7 +39,7 @@ options:
- Ignored. Present for backwards compatibility
required: false
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "openstacksdk"
'''

View File

@ -52,7 +52,7 @@ options:
description:
- Ignored. Present for backwards compatibility
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "openstacksdk"
'''

View File

@ -59,7 +59,7 @@ options:
- Scheduler hints passed to volume API in form of dict
version_added: "2.4"
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "openstacksdk"
'''

View File

@ -52,7 +52,7 @@ options:
description:
- Ignored. Present for backwards compatibility
requirements:
- "python >= 2.6"
- "python >= 2.7"
- "openstacksdk"
'''

View File

@ -555,6 +555,18 @@ class DiskAttachmentsModule(DisksModule):
)
def searchable_attributes(module):
"""
Return all searchable disk attributes passed to module.
"""
attributes = {
'name': module.params.get('name'),
'Storage.name': module.params.get('storage_domain'),
'vm_names': module.params.get('vm_name'),
}
return dict((k, v) for k, v in attributes.items() if v is not None)
def main():
argument_spec = ovirt_full_argument_spec(
state=dict(
@ -616,6 +628,7 @@ def main():
if state in ('present', 'detached', 'attached'):
ret = disks_module.create(
entity=disk,
search_params=searchable_attributes(module),
result_state=otypes.DiskStatus.OK if lun is None else None,
fail_condition=lambda d: d.status == otypes.DiskStatus.ILLEGAL if lun is None else False,
)

View File

@ -422,6 +422,17 @@ def _get_vnic_profile_mappings(module):
return vnicProfileMappings
def searchable_attributes(module):
"""
Return all searchable template attributes passed to module.
"""
attributes = {
'name': module.params.get('name'),
'cluster': module.params.get('cluster'),
}
return dict((k, v) for k, v in attributes.items() if v is not None)
def main():
argument_spec = ovirt_full_argument_spec(
state=dict(
@ -474,6 +485,7 @@ def main():
if state == 'present':
ret = templates_module.create(
result_state=otypes.TemplateStatus.OK,
search_params=searchable_attributes(module),
clone_permissions=module.params['clone_permissions'],
seal=module.params['seal'],
)

View File

@ -1186,8 +1186,9 @@ class VmsModule(BaseModule):
def post_present(self, entity_id):
# After creation of the VM, attach disks and NICs:
entity = self._service.service(entity_id).get()
self.changed = self.__attach_disks(entity)
self.changed = self.__attach_nics(entity)
self.__attach_disks(entity)
self.__attach_nics(entity)
self._attach_cd(entity)
self.changed = self.__attach_numa_nodes(entity)
self.changed = self.__attach_watchdog(entity)
self.changed = self.__attach_graphical_console(entity)
@ -1239,7 +1240,7 @@ class VmsModule(BaseModule):
cd_iso = self.param('cd_iso')
if cd_iso is not None:
vm_service = self._service.service(entity.id)
current = vm_service.get().status == otypes.VmStatus.UP
current = vm_service.get().status == otypes.VmStatus.UP and self.param('state') == 'running'
cdroms_service = vm_service.cdroms_service()
cdrom_device = cdroms_service.list()[0]
cdrom_service = cdroms_service.cdrom_service(cdrom_device.id)
@ -1339,7 +1340,7 @@ class VmsModule(BaseModule):
def __attach_graphical_console(self, entity):
graphical_console = self.param('graphical_console')
if not graphical_console:
return
return False
vm_service = self._service.service(entity.id)
gcs_service = vm_service.graphics_consoles_service()
@ -1450,6 +1451,7 @@ class VmsModule(BaseModule):
)
def __attach_numa_nodes(self, entity):
updated = False
numa_nodes_service = self._service.service(entity.id).numa_nodes_service()
if len(self.param('numa_nodes')) > 0:
@ -1458,10 +1460,11 @@ class VmsModule(BaseModule):
existed_numa_nodes.sort(reverse=len(existed_numa_nodes) > 1 and existed_numa_nodes[1].index > existed_numa_nodes[0].index)
for current_numa_node in existed_numa_nodes:
numa_nodes_service.node_service(current_numa_node.id).remove()
updated = True
for numa_node in self.param('numa_nodes'):
if numa_node is None or numa_node.get('index') is None or numa_node.get('cores') is None or numa_node.get('memory') is None:
return False
continue
numa_nodes_service.add(
otypes.VirtualNumaNode(
@ -1481,8 +1484,9 @@ class VmsModule(BaseModule):
] if numa_node.get('numa_node_pins') is not None else None,
)
)
updated = True
return True
return updated
def __attach_watchdog(self, entity):
watchdogs_service = self._service.service(entity.id).watchdogs_service()
@ -2002,11 +2006,9 @@ def main():
clone=module.params['clone'],
clone_permissions=module.params['clone_permissions'],
)
vms_module.post_present(ret['id'])
if module.params['force']:
ret = vms_module.action(
action='stop',
post_action=vms_module._attach_cd,
action_condition=lambda vm: vm.status != otypes.VmStatus.DOWN,
wait_condition=vms_module.wait_for_down,
)
@ -2014,10 +2016,10 @@ def main():
ret = vms_module.action(
action='shutdown',
pre_action=vms_module._pre_shutdown_action,
post_action=vms_module._attach_cd,
action_condition=lambda vm: vm.status != otypes.VmStatus.DOWN,
wait_condition=vms_module.wait_for_down,
)
vms_module.post_present(ret['id'])
elif state == 'suspended':
vms_module.create(
entity=vm,

View File

@ -47,14 +47,14 @@ requirements:
EXAMPLES = '''
- name: Create a k8s namespace
k8s_raw:
k8s:
name: testing
api_version: v1
kind: Namespace
state: present
- name: Create a Service object from an inline definition
k8s_raw:
k8s:
state: present
definition:
apiVersion: v1
@ -76,12 +76,12 @@ EXAMPLES = '''
port: 8000
- name: Create a Service object by reading the definition from a file
k8s_raw:
k8s:
state: present
src: /testing/service.yml
- name: Get an existing Service object
k8s_raw:
k8s:
api_version: v1
kind: Service
name: web
@ -89,14 +89,14 @@ EXAMPLES = '''
register: web_service
- name: Get a list of all service objects
k8s_raw:
k8s:
api_version: v1
kind: ServiceList
namespace: testing
register: service_list
- name: Remove an existing Service object
k8s_raw:
k8s:
state: absent
api_version: v1
kind: Service
@ -106,17 +106,17 @@ EXAMPLES = '''
# Passing the object definition from a file
- name: Create a Deployment by reading the definition from a local file
k8s_raw:
k8s:
state: present
src: /testing/deployment.yml
- name: Read definition file from the Ansible controller file system
k8s_raw:
k8s:
state: present
definition: "{{ lookup('file', '/testing/deployment.yml') | from_yaml }}"
- name: Read definition file from the Ansible controller file system after Jinja templating
k8s_raw:
k8s:
state: present
definition: "{{ lookup('template', '/testing/deployment.yml') | from_yaml }}"
'''

View File

@ -20,14 +20,16 @@ description:
- Adds and/or removes instances of DNS view objects from
Infoblox NIOS servers. This module manages NIOS C(view) objects
using the Infoblox WAPI interface over REST.
- Updates instances of DNS view object from Infoblox NIOS servers.
requirements:
- infoblox_client
extends_documentation_fragment: nios
options:
name:
description:
- Specifies the name of the DNS view to add and/or remove from the
system configuration based on the setting of the C(state) argument.
- Specifies the fully qualified hostname to add or remove from
the system. User can also update the hostname as it is possible
to pass a dict containing I(new_name), I(old_name). See examples.
required: true
aliases:
- view
@ -73,7 +75,6 @@ EXAMPLES = '''
username: admin
password: admin
connection: local
- name: update the comment for dns view
nios_dns_view:
name: ansible-dns
@ -84,7 +85,6 @@ EXAMPLES = '''
username: admin
password: admin
connection: local
- name: remove the dns view instance
nios_dns_view:
name: ansible-dns
@ -94,12 +94,22 @@ EXAMPLES = '''
username: admin
password: admin
connection: local
- name: update the dns view instance
nios_dns_view:
name: {new_name: ansible-dns-new, old_name: ansible-dns}
state: present
provider:
host: "{{ inventory_hostname_short }}"
username: admin
password: admin
connection: local
'''
RETURN = ''' # '''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.net_tools.nios.api import WapiModule
from ansible.module_utils.net_tools.nios.api import NIOS_DNS_VIEW
def main():
@ -125,7 +135,7 @@ def main():
supports_check_mode=True)
wapi = WapiModule(module)
result = wapi.run('view', ib_spec)
result = wapi.run(NIOS_DNS_VIEW, ib_spec)
module.exit_json(**result)

View File

@ -20,6 +20,7 @@ description:
- Adds and/or removes instances of host record objects from
Infoblox NIOS servers. This module manages NIOS C(record:host) objects
using the Infoblox WAPI interface over REST.
- Updates instances of host record object from Infoblox NIOS servers.
requirements:
- infoblox_client
extends_documentation_fragment: nios
@ -27,7 +28,8 @@ options:
name:
description:
- Specifies the fully qualified hostname to add or remove from
the system
the system. User can also update the hostname as it is possible
to pass a dict containing I(new_name), I(old_name). See examples.
required: true
view:
description:
@ -103,7 +105,6 @@ EXAMPLES = '''
username: admin
password: admin
connection: local
- name: add a comment to an existing host record
nios_host_record:
name: host.ansible.com
@ -116,7 +117,6 @@ EXAMPLES = '''
username: admin
password: admin
connection: local
- name: remove a host record from the system
nios_host_record:
name: host.ansible.com
@ -126,6 +126,17 @@ EXAMPLES = '''
username: admin
password: admin
connection: local
- name: update an ipv4 host record
nios_host_record:
name: {new_name: host-new.ansible.com, old_name: host.ansible.com}
ipv4:
- address: 192.168.10.1
state: present
provider:
host: "{{ inventory_hostname_short }}"
username: admin
password: admin
connection: local
'''
RETURN = ''' # '''
@ -133,19 +144,17 @@ RETURN = ''' # '''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.six import iteritems
from ansible.module_utils.net_tools.nios.api import WapiModule
from ansible.module_utils.net_tools.nios.api import NIOS_HOST_RECORD
def ipaddr(module, key, filtered_keys=None):
''' Transforms the input value into a struct supported by WAPI
This function will transform the input from the playbook into a struct
that is valid for WAPI in the form of:
{
ipv4addr: <value>,
mac: <value>
}
This function does not validate the values are properly formatted or in
the acceptable range, that is left to WAPI.
'''
@ -201,7 +210,7 @@ def main():
supports_check_mode=True)
wapi = WapiModule(module)
result = wapi.run('record:host', ib_spec)
result = wapi.run(NIOS_HOST_RECORD, ib_spec)
module.exit_json(**result)

View File

@ -98,7 +98,6 @@ EXAMPLES = '''
username: admin
password: admin
connection: local
- name: configure a network ipv6
nios_network:
network: fe80::/64
@ -109,7 +108,6 @@ EXAMPLES = '''
username: admin
password: admin
connection: local
- name: set dhcp options for a network ipv4
nios_network:
network: 192.168.10.0/24
@ -123,7 +121,6 @@ EXAMPLES = '''
username: admin
password: admin
connection: local
- name: remove a network ipv4
nios_network:
network: 192.168.10.0/24
@ -141,14 +138,14 @@ from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.six import iteritems
from ansible.module_utils.net_tools.nios.api import WapiModule
from ansible.module_utils.network.common.utils import validate_ip_address, validate_ip_v6_address
from ansible.module_utils.net_tools.nios.api import NIOS_IPV4_NETWORK
from ansible.module_utils.net_tools.nios.api import NIOS_IPV6_NETWORK
def options(module):
''' Transforms the module argument into a valid WAPI struct
This function will transform the options argument into a structure that
is a valid WAPI structure in the format of:
{
name: <value>,
num: <value>,
@ -156,11 +153,9 @@ def options(module):
use_option: <value>,
vendor_class: <value>
}
It will remove any options that are set to None since WAPI will error on
that condition. It will also verify that either `name` or `num` is
set in the structure but does not validate the values are equal.
The remainder of the value validation is performed by WAPI
'''
options = list()
@ -178,9 +173,9 @@ def check_ip_addr_type(ip):
check_ip = ip.split('/')
if validate_ip_address(check_ip[0]):
return 'network'
return NIOS_IPV4_NETWORK
elif validate_ip_v6_address(check_ip[0]):
return 'ipv6network'
return NIOS_IPV6_NETWORK
def main():

View File

@ -20,14 +20,16 @@ description:
- Adds and/or removes instances of network view objects from
Infoblox NIOS servers. This module manages NIOS C(networkview) objects
using the Infoblox WAPI interface over REST.
- Updates instances of network view object from Infoblox NIOS servers.
requirements:
- infoblox_client
extends_documentation_fragment: nios
options:
name:
description:
- Specifies the name of the network view to either add or remove
from the configuration.
- Specifies the fully qualified hostname to add or remove from
the system. User can also update the hostname as it is possible
to pass a dict containing I(new_name), I(old_name). See examples.
required: true
aliases:
- network_view
@ -63,7 +65,6 @@ EXAMPLES = '''
username: admin
password: admin
connection: local
- name: update the comment for network view
nios_network_view:
name: ansible
@ -74,7 +75,6 @@ EXAMPLES = '''
username: admin
password: admin
connection: local
- name: remove the network view
nios_network_view:
name: ansible
@ -84,12 +84,22 @@ EXAMPLES = '''
username: admin
password: admin
connection: local
- name: update a existing network view
nios_network_view:
name: {new_name: ansible-new, old_name: ansible}
state: present
provider:
host: "{{ inventory_hostname_short }}"
username: admin
password: admin
connection: local
'''
RETURN = ''' # '''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.net_tools.nios.api import WapiModule
from ansible.module_utils.net_tools.nios.api import NIOS_NETWORK_VIEW
def main():
@ -97,7 +107,6 @@ def main():
'''
ib_spec = dict(
name=dict(required=True, aliases=['network_view'], ib_req=True),
extattrs=dict(type='dict'),
comment=dict(),
)
@ -114,7 +123,7 @@ def main():
supports_check_mode=True)
wapi = WapiModule(module)
result = wapi.run('networkview', ib_spec)
result = wapi.run(NIOS_NETWORK_VIEW, ib_spec)
module.exit_json(**result)

View File

@ -88,7 +88,6 @@ EXAMPLES = '''
username: admin
password: admin
connection: local
- name: update the comment and ext attributes for an existing zone
nios_zone:
name: ansible.com
@ -101,7 +100,6 @@ EXAMPLES = '''
username: admin
password: admin
connection: local
- name: remove the dns zone
nios_zone:
name: ansible.com
@ -117,6 +115,7 @@ RETURN = ''' # '''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.net_tools.nios.api import WapiModule
from ansible.module_utils.net_tools.nios.api import NIOS_ZONE
def main():
@ -149,7 +148,7 @@ def main():
supports_check_mode=True)
wapi = WapiModule(module)
result = wapi.run('zone_auth', ib_spec)
result = wapi.run(NIOS_ZONE, ib_spec)
module.exit_json(**result)

View File

@ -114,8 +114,8 @@ commands:
import re
from copy import deepcopy
from copy import deepcopy
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.common.utils import remove_default_spec
from ansible.module_utils.network.eos.eos import get_config, load_config
@ -150,19 +150,32 @@ def map_obj_to_commands(updates, module):
del w['state']
if state == 'absent' and w in have:
if dest == 'host':
commands.append('no logging host {}'.format(name))
elif dest:
commands.append('no logging {}'.format(dest))
else:
module.fail_json(msg='dest must be among console, monitor, buffered, host, on')
if dest:
if dest == 'host':
commands.append('no logging host {}'.format(name))
elif dest in DEST_GROUP:
commands.append('no logging {}'.format(dest))
else:
module.fail_json(msg='dest must be among console, monitor, buffered, host, on')
if facility:
commands.append('no logging facility {}'.format(facility))
if state == 'present' and w not in have:
if facility:
commands.append('logging facility {}'.format(facility))
present = False
# Iterate over every dictionary in the 'have' list to check if
# similar configuration for facility exists or not
for entry in have:
if not entry['dest'] and entry['facility'] == facility:
present = True
if not present:
commands.append('logging facility {}'.format(facility))
if dest == 'host':
commands.append('logging host {}'.format(name))
@ -171,7 +184,28 @@ def map_obj_to_commands(updates, module):
commands.append('logging on')
elif dest == 'buffered' and size:
commands.append('logging buffered {}'.format(size))
present = False
# Deals with the following two cases:
# Case 1: logging buffered <size> <level>
# logging buffered <same-size>
#
# Case 2: Same buffered logging configuration
# already exists (i.e., both size &
# level are same)
for entry in have:
if entry['dest'] == 'buffered' and entry['size'] == size:
if not level or entry['level'] == level:
present = True
if not present:
if size and level:
commands.append('logging buffered {} {}'.format(size, level))
else:
commands.append('logging buffered {}'.format(size))
else:
dest_cmd = 'logging {}'.format(dest)
@ -222,11 +256,20 @@ def parse_name(line, dest):
return name
def parse_level(line, dest, module):
def parse_level(line, dest):
level = None
if dest is not 'host':
match = re.search(r'logging {} (\S+)'.format(dest), line, re.M)
# Line for buffer logging entry in running-config is of the form:
# logging buffered <size> <level>
if dest == 'buffered':
match = re.search(r'logging buffered (?:\d+) (\S+)', line, re.M)
else:
match = re.search(r'logging {} (\S+)'.format(dest), line, re.M)
if match:
if match.group(1) in LEVEL_GROUP:
level = match.group(1)
@ -240,19 +283,21 @@ def map_config_to_obj(module):
data = get_config(module, flags=['section logging'])
for line in data.split('\n'):
match = re.search(r'logging (\S+)', line, re.M)
if match:
if match.group(1) in DEST_GROUP:
dest = match.group(1)
else:
pass
dest = None
obj.append({'dest': dest,
'name': parse_name(line, dest),
'size': parse_size(line, dest),
'facility': parse_facility(line),
'level': parse_level(line, dest, module)})
'level': parse_level(line, dest)})
return obj
@ -361,8 +406,8 @@ def main():
if warnings:
result['warnings'] = warnings
want = map_params_to_obj(module, required_if=required_if)
have = map_config_to_obj(module)
want = map_params_to_obj(module, required_if=required_if)
commands = map_obj_to_commands((want, have), module)
result['commands'] = commands
@ -377,5 +422,6 @@ def main():
module.exit_json(**result)
if __name__ == '__main__':
main()

View File

@ -465,6 +465,8 @@ class ApiParameters(Parameters):
def state(self):
if self._values['state'] in ['user-up', 'unchecked', 'fqdn-up-no-addr'] and self._values['session'] in ['user-enabled']:
return 'present'
elif self._values['state'] in ['down', 'up'] and self._values['session'] == 'monitor-enabled':
return 'present'
elif self._values['state'] in ['user-down'] and self._values['session'] in ['user-disabled']:
return 'forced_offline'
else:
@ -520,6 +522,8 @@ class ReportableChanges(Changes):
def state(self):
if self._values['state'] in ['user-up', 'unchecked', 'fqdn-up-no-addr'] and self._values['session'] in ['user-enabled']:
return 'present'
elif self._values['state'] in ['down', 'up'] and self._values['session'] == 'monitor-enabled':
return 'present'
elif self._values['state'] in ['user-down'] and self._values['session'] in ['user-disabled']:
return 'forced_offline'
else:

View File

@ -161,11 +161,13 @@ except ImportError:
class Parameters(AnsibleF5Parameters):
api_map = {
'certKeyChain': 'cert_key_chain'
'certKeyChain': 'cert_key_chain',
'defaultsFrom': 'parent'
}
api_attributes = [
'ciphers', 'certKeyChain'
'ciphers', 'certKeyChain',
'defaultsFrom'
]
returnables = [

View File

@ -214,6 +214,7 @@ class Parameters(AnsibleF5Parameters):
'enableDnssec',
'processXfr',
'enableDnsExpress',
'defaultsFrom'
]
returnables = [

View File

@ -105,7 +105,7 @@ except ImportError:
def hard_timeout(module, want, start):
elapsed = datetime.datetime.utcnow() - start
module.fail_json(
want.msg or "Timeout when waiting for BIG-IP", elapsed=elapsed.seconds
msg=want.msg or "Timeout when waiting for BIG-IP", elapsed=elapsed.seconds
)

View File

@ -228,12 +228,15 @@ class Hardware(FactsBase):
data = self.responses[1]
if data:
processor_line = [l for l in data.splitlines()
if 'Processor' in l].pop()
match = re.findall(r'\s(\d+)\s', processor_line)
if match:
self.facts['memtotal_mb'] = int(match[0]) / 1024
self.facts['memfree_mb'] = int(match[3]) / 1024
if 'Invalid input detected' in data:
warnings.append('Unable to gather memory statistics')
else:
processor_line = [l for l in data.splitlines()
if 'Processor' in l].pop()
match = re.findall(r'\s(\d+)\s', processor_line)
if match:
self.facts['memtotal_mb'] = int(match[0]) / 1024
self.facts['memfree_mb'] = int(match[3]) / 1024
def parse_filesystems(self, data):
return re.findall(r'^Directory of (\S+)/', data, re.M)
@ -376,7 +379,7 @@ class Interfaces(FactsBase):
return match.group(1)
def parse_macaddress(self, data):
match = re.search(r'address is (\S+)', data)
match = re.search(r'Hardware is (?:.*), address is (\S+)', data)
if match:
return match.group(1)
@ -446,6 +449,9 @@ FACT_SUBSETS = dict(
VALID_SUBSETS = frozenset(FACT_SUBSETS.keys())
global warnings
warnings = list()
def main():
"""main entry point for module execution
@ -508,7 +514,6 @@ def main():
key = 'ansible_net_%s' % key
ansible_facts[key] = value
warnings = list()
check_args(module, warnings)
module.exit_json(ansible_facts=ansible_facts, warnings=warnings)

View File

@ -121,7 +121,6 @@ commands:
import re
from copy import deepcopy
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.common.utils import remove_default_spec, validate_ip_address
from ansible.module_utils.network.ios.ios import get_config, load_config
@ -138,6 +137,7 @@ def validate_size(value, module):
def map_obj_to_commands(updates, module, os_version):
dest_group = ('console', 'monitor', 'buffered', 'on')
commands = list()
want, have = updates
for w in want:
@ -149,23 +149,36 @@ def map_obj_to_commands(updates, module, os_version):
state = w['state']
del w['state']
if facility:
w['dest'] = 'facility'
if state == 'absent' and w in have:
if dest == 'host':
if '12.' in os_version:
commands.append('no logging {0}'.format(name))
if dest:
if dest == 'host':
if '12.' in os_version:
commands.append('no logging {0}'.format(name))
else:
commands.append('no logging host {0}'.format(name))
elif dest in dest_group:
commands.append('no logging {0}'.format(dest))
else:
commands.append('no logging host {0}'.format(name))
elif dest:
commands.append('no logging {0}'.format(dest))
else:
module.fail_json(msg='dest must be among console, monitor, buffered, host, on')
module.fail_json(msg='dest must be among console, monitor, buffered, host, on')
if facility:
commands.append('no logging facility {0}'.format(facility))
if state == 'present' and w not in have:
if facility:
commands.append('logging facility {0}'.format(facility))
present = False
for entry in have:
if entry['dest'] == 'facility' and entry['facility'] == facility:
present = True
if not present:
commands.append('logging facility {0}'.format(facility))
if dest == 'host':
if '12.' in os_version:
@ -177,10 +190,17 @@ def map_obj_to_commands(updates, module, os_version):
commands.append('logging on')
elif dest == 'buffered' and size:
if level and level != 'debugging':
commands.append('logging buffered {0} {1}'.format(size, level))
else:
commands.append('logging buffered {0}'.format(size))
present = False
for entry in have:
if entry['dest'] == 'buffered' and entry['size'] == size and entry['level'] == level:
present = True
if not present:
if level and level != 'debugging':
commands.append('logging buffered {0} {1}'.format(size, level))
else:
commands.append('logging buffered {0}'.format(size))
else:
if dest:
@ -205,18 +225,12 @@ def parse_size(line, dest):
size = None
if dest == 'buffered':
match = re.search(r'logging buffered (\S+)', line, re.M)
match = re.search(r'logging buffered(?: (\d+))?(?: [a-z]+)?', line, re.M)
if match:
try:
int_size = int(match.group(1))
except ValueError:
int_size = None
if int_size:
if isinstance(int_size, int):
size = str(match.group(1))
else:
size = str(4096)
if match.group(1) is not None:
size = match.group(1)
else:
size = "4096"
return size
@ -241,15 +255,12 @@ def parse_level(line, dest):
else:
if dest == 'buffered':
match = re.search(r'logging buffered (?:\d+ )([a-z]+)', line, re.M)
match = re.search(r'logging buffered(?: \d+)?(?: ([a-z]+))?', line, re.M)
else:
match = re.search(r'logging {0} (\S+)'.format(dest), line, re.M)
if match:
if match.group(1) in level_group:
level = match.group(1)
else:
level = 'debugging'
if match and match.group(1) in level_group:
level = match.group(1)
else:
level = 'debugging'
@ -293,7 +304,6 @@ def map_config_to_obj(module):
'facility': parse_facility(line, dest),
'level': parse_level(line, dest)
})
return obj
@ -355,7 +365,6 @@ def map_params_to_obj(module, required_if=None):
'level': module.params['level'],
'state': module.params['state']
})
return obj
@ -412,5 +421,6 @@ def main():
module.exit_json(**result)
if __name__ == '__main__':
main()

View File

@ -179,6 +179,7 @@ except ImportError:
from ansible.module_utils.network.nxos.nxos import load_config, run_commands
from ansible.module_utils.network.nxos.nxos import get_capabilities, nxos_argument_spec
from ansible.module_utils.network.nxos.nxos import get_interface_type
from ansible.module_utils.basic import AnsibleModule
@ -209,23 +210,6 @@ def execute_show_command(command, module):
return body
def get_interface_type(interface):
if interface.upper().startswith('ET'):
return 'ethernet'
elif interface.upper().startswith('VL'):
return 'svi'
elif interface.upper().startswith('LO'):
return 'loopback'
elif interface.upper().startswith('MG'):
return 'management'
elif interface.upper().startswith('MA'):
return 'management'
elif interface.upper().startswith('PO'):
return 'portchannel'
else:
return 'unknown'
def is_default(interface, module):
command = 'show run interface {0}'.format(interface)

View File

@ -119,34 +119,10 @@ commands:
from ansible.module_utils.network.nxos.nxos import load_config, run_commands
from ansible.module_utils.network.nxos.nxos import get_capabilities, nxos_argument_spec
from ansible.module_utils.network.nxos.nxos import get_interface_type
from ansible.module_utils.basic import AnsibleModule
def get_interface_type(interface):
"""Gets the type of interface
Args:
interface (str): full name of interface, i.e. Ethernet1/1, loopback10,
port-channel20, vlan20
Returns:
type of interface: ethernet, svi, loopback, management, portchannel,
or unknown
"""
if interface.upper().startswith('ET'):
return 'ethernet'
elif interface.upper().startswith('VL'):
return 'svi'
elif interface.upper().startswith('LO'):
return 'loopback'
elif interface.upper().startswith('MG'):
return 'management'
elif interface.upper().startswith('MA'):
return 'management'
elif interface.upper().startswith('PO'):
return 'portchannel'
else:
return 'unknown'
def get_interface_mode(interface, module):
"""Gets current mode of interface: layer2 or layer3
Args:

View File

@ -197,7 +197,17 @@ def main():
if commands:
if not module.check_mode:
load_config(module, commands)
msgs = load_config(module, commands, True)
if msgs:
for item in msgs:
if item:
if isinstance(item, dict):
err_str = item['clierror']
else:
err_str = item
if 'more than 40 lines' in err_str or 'buffer overflowed' in err_str:
load_config(module, commands)
result['changed'] = True
module.exit_json(**result)

View File

@ -148,6 +148,7 @@ commands:
from ansible.module_utils.network.nxos.nxos import load_config, run_commands
from ansible.module_utils.network.nxos.nxos import get_capabilities, nxos_argument_spec
from ansible.module_utils.network.nxos.nxos import get_interface_type
from ansible.module_utils.basic import AnsibleModule
@ -159,21 +160,6 @@ PARAM_TO_DEFAULT_KEYMAP = {
}
def execute_show_command(command, module):
device_info = get_capabilities(module)
network_api = device_info.get('network_api', 'nxapi')
if network_api == 'cliconf':
command += ' | json'
cmds = [command]
body = run_commands(module, cmds)
elif network_api == 'nxapi':
cmds = [command]
body = run_commands(module, cmds)
return body
def apply_key_map(key_map, table):
new_dict = {}
for key in table:
@ -187,29 +173,12 @@ def apply_key_map(key_map, table):
return new_dict
def get_interface_type(interface):
if interface.upper().startswith('ET'):
return 'ethernet'
elif interface.upper().startswith('VL'):
return 'svi'
elif interface.upper().startswith('LO'):
return 'loopback'
elif interface.upper().startswith('MG'):
return 'management'
elif interface.upper().startswith('MA'):
return 'management'
elif interface.upper().startswith('PO'):
return 'portchannel'
else:
return 'unknown'
def get_interface_mode(interface, intf_type, module):
command = 'show interface {0}'.format(interface)
command = 'show interface {0} | json'.format(interface)
interface = {}
mode = 'unknown'
try:
body = execute_show_command(command, module)[0]
body = run_commands(module, [command])[0]
except IndexError:
return None
@ -224,7 +193,7 @@ def get_interface_mode(interface, intf_type, module):
def get_hsrp_group(group, interface, module):
command = 'show hsrp group {0} all'.format(group)
command = 'show hsrp group {0} all | json'.format(group)
hsrp = {}
hsrp_key = {
@ -240,9 +209,9 @@ def get_hsrp_group(group, interface, module):
}
try:
body = execute_show_command(command, module)[0]
body = run_commands(module, [command])[0]
hsrp_table = body['TABLE_grp_detail']['ROW_grp_detail']
except (AttributeError, IndexError, TypeError):
except (AttributeError, IndexError, TypeError, KeyError):
return {}
if isinstance(hsrp_table, dict):
@ -365,7 +334,7 @@ def is_default(interface, module):
command = 'show run interface {0}'.format(interface)
try:
body = execute_show_command(command, module)[0]
body = run_commands(module, [command], check_rc=False)[0]
if 'invalid' in body.lower():
return 'DNE'
else:

View File

@ -191,6 +191,7 @@ changed:
from ansible.module_utils.network.nxos.nxos import get_config, load_config, run_commands
from ansible.module_utils.network.nxos.nxos import nxos_argument_spec, check_args
from ansible.module_utils.network.nxos.nxos import get_interface_type
from ansible.module_utils.basic import AnsibleModule
import re
@ -227,23 +228,6 @@ def get_interface_mode(interface, intf_type, module):
return mode
def get_interface_type(interface):
if interface.upper().startswith('ET'):
return 'ethernet'
elif interface.upper().startswith('VL'):
return 'svi'
elif interface.upper().startswith('LO'):
return 'loopback'
elif interface.upper().startswith('MG'):
return 'management'
elif interface.upper().startswith('MA'):
return 'management'
elif interface.upper().startswith('PO'):
return 'portchannel'
else:
return 'unknown'
def apply_key_map(key_map, table):
new_dict = {}
for key, value in table.items():

View File

@ -35,13 +35,9 @@ notes:
- N9k 7.0(3)I4(6), 7.0(3)I5(3), 7.0(3)I6(1), 7.0(3)I7(1), 7.0(3)F2(2), 7.0(3)F3(2)
- N3k 6.0(2)A8(6), 6.0(2)A8(8), 7.0(3)I6(1), 7.0(3)I7(1)
- N7k 7.3(0)D1(1), 8.0(1), 8.2(1)
- This module executes longer then the default ansible timeout value and
will generate errors unless the module timeout parameter is set to a
value of 500 seconds or higher.
The example time is sufficent for most upgrades but this can be
tuned higher based on specific upgrade time requirements.
The module will exit with a failure message if the timer is
not set to 500 seconds or higher.
- This module requires both the ANSIBLE_PERSISTENT_CONNECT_TIMEOUT and
ANSIBLE_PERSISTENT_COMMAND_TIMEOUT timers to be set to 600 seconds or higher.
The module will exit if the timers are not set properly.
- Do not include full file paths, just the name of the file(s) stored on
the top level flash directory.
- This module attempts to install the software immediately,
@ -81,7 +77,6 @@ EXAMPLES = '''
nxos_install_os:
system_image_file: nxos.7.0.3.I6.1.bin
issu: desired
provider: "{{ connection | combine({'timeout': 500}) }}"
- name: Wait for device to come back up with new image
wait_for:
@ -129,25 +124,6 @@ from ansible.module_utils.network.nxos.nxos import nxos_argument_spec, check_arg
from ansible.module_utils.basic import AnsibleModule
def check_ansible_timer(module):
'''Check Ansible Timer Values'''
msg = "The 'timeout' provider param value for this module to execute\n"
msg = msg + 'properly is too low.\n'
msg = msg + 'Upgrades can take a long time so the value needs to be set\n'
msg = msg + 'to the recommended value of 500 seconds or higher in the\n'
msg = msg + 'ansible playbook for the nxos_install_os module.\n'
msg = msg + '\n'
msg = msg + 'provider: "{{ connection | combine({\'timeout\': 500}) }}"'
data = module.params.get('provider')
timer_low = False
if data.get('timeout') is None:
timer_low = True
if data.get('timeout') is not None and data.get('timeout') < 500:
timer_low = True
if timer_low:
module.fail_json(msg=msg.split('\n'))
# Output options are 'text' or 'json'
def execute_show_command(module, command, output='text'):
cmds = [{
@ -276,6 +252,9 @@ def parse_show_install(data):
if re.search(r'Install has been successful', x):
ud['upgrade_succeeded'] = True
break
if re.search(r'Switching over onto standby', x):
ud['upgrade_succeeded'] = True
break
# We get these messages when the upgrade is non-disruptive and
# we loose connection with the switchover but far enough along that
@ -539,10 +518,6 @@ def main():
warnings = list()
check_args(module, warnings)
# This module will error out if the Ansible task timeout value is not
# tuned high enough.
check_ansible_timer(module)
# Get system_image_file(sif), kickstart_image_file(kif) and
# issu settings from module params.
sif = module.params['system_image_file']

View File

@ -203,6 +203,7 @@ from copy import deepcopy
from ansible.module_utils.network.nxos.nxos import load_config, run_commands
from ansible.module_utils.network.nxos.nxos import nxos_argument_spec, normalize_interface
from ansible.module_utils.network.nxos.nxos import get_interface_type
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.common.utils import conditional, remove_default_spec
@ -231,27 +232,6 @@ def search_obj_in_list(name, lst):
return None
def get_interface_type(interface):
"""Gets the type of interface
"""
if interface.upper().startswith('ET'):
return 'ethernet'
elif interface.upper().startswith('VL'):
return 'svi'
elif interface.upper().startswith('LO'):
return 'loopback'
elif interface.upper().startswith('MG'):
return 'management'
elif interface.upper().startswith('MA'):
return 'management'
elif interface.upper().startswith('PO'):
return 'portchannel'
elif interface.upper().startswith('NV'):
return 'nve'
else:
return 'unknown'
def get_interfaces_dict(module):
"""Gets all active interfaces on a given switch
"""
@ -287,7 +267,7 @@ def get_vlan_interface_attributes(name, intf_type, module):
command = 'show run interface {0} all'.format(name)
try:
body = execute_show_command(command, module)[0]
except IndexError:
except (IndexError, TypeError):
return None
if body:
command_list = body.split('\n')
@ -524,7 +504,7 @@ def map_config_to_obj(want, module):
if body:
try:
interface_table = body['TABLE_interface']['ROW_interface']
except KeyError:
except (KeyError, TypeError):
return list()
if interface_table:
@ -619,11 +599,12 @@ def check_declarative_intent_params(module, want):
return
cmd = [{'command': 'show interface {0}'.format(w['name']), 'output': 'text'}]
output = run_commands(module, cmd, check_rc=False)
if output:
out = output[0]
else:
try:
out = run_commands(module, cmd, check_rc=False)[0]
except (AttributeError, IndexError, TypeError):
out = ''
if want_tx_rate:
match = re.search(r'output rate (\d+)', out, re.M)
have_tx_rate = None

View File

@ -120,36 +120,11 @@ import re
from copy import deepcopy
from ansible.module_utils.network.nxos.nxos import get_config, load_config, run_commands
from ansible.module_utils.network.nxos.nxos import nxos_argument_spec
from ansible.module_utils.network.nxos.nxos import nxos_argument_spec, get_interface_type
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.common.utils import remove_default_spec
def get_interface_type(name):
"""Gets the type of interface
Args:
interface (str): full name of interface, i.e. Ethernet1/1, loopback10,
port-channel20, vlan20
Returns:
type of interface: ethernet, svi, loopback, management, portchannel,
or unknown
"""
if name.upper().startswith('ET'):
return 'ethernet'
elif name.upper().startswith('VL'):
return 'svi'
elif name.upper().startswith('LO'):
return 'loopback'
elif name.upper().startswith('MG'):
return 'management'
elif name.upper().startswith('MA'):
return 'management'
elif name.upper().startswith('PO'):
return 'portchannel'
else:
return 'unknown'
def get_interface_mode(name, module):
"""Gets current mode of interface: layer2 or layer3
Args:

View File

@ -133,22 +133,6 @@ from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.common.utils import remove_default_spec
def execute_show_command(command, module):
device_info = get_capabilities(module)
network_api = device_info.get('network_api', 'nxapi')
if network_api == 'cliconf':
if 'show port-channel summary' in command:
command += ' | json'
cmds = [command]
body = run_commands(module, cmds)
elif network_api == 'nxapi':
cmds = [command]
body = run_commands(module, cmds)
return body
def search_obj_in_list(group, lst):
for o in lst:
if o['group'] == group:
@ -337,7 +321,7 @@ def parse_channel_options(module, output, channel):
def map_config_to_obj(module):
objs = list()
output = execute_show_command('show port-channel summary', module)[0]
output = run_commands(module, ['show port-channel summary | json'])[0]
if not output:
return list()

View File

@ -135,6 +135,7 @@ import re
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.nxos.nxos import get_config, load_config, run_commands
from ansible.module_utils.network.nxos.nxos import nxos_argument_spec, check_args
from ansible.module_utils.network.nxos.nxos import get_interface_type
from ansible.module_utils.six import string_types
@ -200,23 +201,6 @@ def local_existing(gexisting):
return gexisting, jp_bidir, isauth
def get_interface_type(interface):
if interface.upper().startswith('ET'):
return 'ethernet'
elif interface.upper().startswith('VL'):
return 'svi'
elif interface.upper().startswith('LO'):
return 'loopback'
elif interface.upper().startswith('MG'):
return 'management'
elif interface.upper().startswith('MA'):
return 'management'
elif interface.upper().startswith('PO'):
return 'portchannel'
else:
return 'unknown'
def get_interface_mode(interface, intf_type, module):
mode = 'unknown'
command = 'show interface {0}'.format(interface)

View File

@ -289,12 +289,6 @@ def invoke(name, *args, **kwargs):
return func(*args, **kwargs)
def get_snapshot(module):
command = 'show snapshot dump {0}'.format(module.params['snapshot_name'])
body = execute_show_command(command, module)[0]
return body
def write_on_file(content, filename, module):
path = module.params['path']
if path[-1] != '/':
@ -373,10 +367,10 @@ def main():
result['changed'] = True
if action == 'create' and module.params['path'] and module.params['save_snapshot_locally']:
command = 'show snapshot | include {}'.format(module.params['snapshot_name'])
command = 'show snapshot dump {} | json'.format(module.params['snapshot_name'])
content = execute_show_command(command, module)[0]
if content:
write_on_file(content, module.params['snapshot_name'], module)
write_on_file(str(content), module.params['snapshot_name'], module)
module.exit_json(**result)

Some files were not shown because too many files have changed in this diff Show More