Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0a6c643e8e | |||
| ddb60ce345 | |||
| 14acce0b44 | |||
| 9823432ec3 | |||
| 25d52697af | |||
| 121139b755 | |||
| 73b5c997f4 | |||
| 74a56f0ca1 | |||
| e24743ff19 | |||
| 773fdeb6f0 | |||
| e8d8fa4e1a | |||
| 1ab973c3d3 | |||
| fb3f0a53d9 | |||
| 205b66ee7c | |||
| eedf5d6c6d | |||
| 8d345c7cd6 | |||
| 08aeeefa90 | |||
| 7d3ce99a6f | |||
| 4536fcc722 | |||
| cfc67ce177 | |||
| 6e7bdb04ee | |||
| 8f2c34fc69 | |||
| 741e09f984 | |||
| 65d96bf354 | |||
| cad351328d | |||
| ca3c0dfaea | |||
| 42661ba459 | |||
| 7f50d11806 | |||
| 1ea5790f56 | |||
| a3178a1153 | |||
| 079a1c3d06 | |||
| 9bb260b543 | |||
| 707f069fe8 | |||
| 87a163064f | |||
| a2ca2e2a25 | |||
| 0c907779a4 | |||
| b184e73b89 | |||
| 848327a6b5 | |||
| 592c6877b5 | |||
| 58f018e367 | |||
| 828d878bf9 | |||
| b97a47ac2c | |||
| a018a1e6d4 | |||
| 882f249f1d | |||
| 3f83e7e473 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -103,3 +103,4 @@ htmlcov/
|
||||
# ansible-test coverage results
|
||||
test/units/.coverage.*
|
||||
/test/integration/cloud-config-azure.yml
|
||||
changelogs/.plugin-cache.yaml
|
||||
|
||||
6
Makefile
6
Makefile
@ -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
280
changelogs/.changes.yaml
Normal 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'
|
||||
@ -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
|
||||
|
||||
@ -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']
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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'
|
||||
@ -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)
|
||||
2
changelogs/fragments/synchronize_remote_is_local.yaml
Normal file
2
changelogs/fragments/synchronize_remote_is_local.yaml
Normal 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)
|
||||
@ -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.
|
||||
|
||||
1
changelogs/fragments/v2.6.0a2_catchup.yaml
Normal file
1
changelogs/fragments/v2.6.0a2_catchup.yaml
Normal file
@ -0,0 +1 @@
|
||||
{}
|
||||
3
changelogs/fragments/v2.6.0a2_summary.yaml
Normal file
3
changelogs/fragments/v2.6.0a2_summary.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
release_summary: |
|
||||
| Release Date: 2018-05-30
|
||||
| `Porting Guide <https://docs.ansible.com/ansible/devel/porting_guides.html>`_
|
||||
1
changelogs/fragments/v2.6.0rc1_catchup.yaml
Normal file
1
changelogs/fragments/v2.6.0rc1_catchup.yaml
Normal file
@ -0,0 +1 @@
|
||||
{}
|
||||
3
changelogs/fragments/v2.6.0rc1_summary.yaml
Normal file
3
changelogs/fragments/v2.6.0rc1_summary.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
release_summary: |
|
||||
| Release Date: 2018-06-05
|
||||
| `Porting Guide <https://docs.ansible.com/ansible/devel/porting_guides.html>`_
|
||||
@ -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()
|
||||
|
||||
@ -156,7 +156,7 @@
|
||||
<div class="DocSiteProduct-logoText">
|
||||
Ansible
|
||||
<div class="DocSiteProduct-CurrentVersion" align="right">
|
||||
devel
|
||||
2.6
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
15
docs/docsite/rst/dev_guide/testing/sanity/changelog.rst
Normal file
15
docs/docsite/rst/dev_guide/testing/sanity/changelog.rst
Normal 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.
|
||||
@ -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
|
||||
|
||||
@ -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``
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
==========
|
||||
|
||||
@ -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
|
||||
======================
|
||||
|
||||
@ -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::
|
||||
|
||||
|
||||
193
docs/docsite/rst/user_guide/shared_snippets/with2loop.txt
Normal file
193
docs/docsite/rst/user_guide/shared_snippets/with2loop.txt
Normal 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
|
||||
294
docs/templates/plugin.rst.j2
vendored
294
docs/templates/plugin.rst.j2
vendored
@ -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"> </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> ←</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> ←</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"> </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"> </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 }@">
|
||||
<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>
|
||||
{# ---------------------------------------------------------
|
||||
|
||||
@ -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("@"):
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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')))
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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])
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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'
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -42,7 +42,7 @@ options:
|
||||
description:
|
||||
- Ignored. Present for backwards compatibility
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python >= 2.7"
|
||||
- "openstacksdk"
|
||||
'''
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@ description:
|
||||
notes:
|
||||
- Facts are placed in the C(openstack) variable.
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python >= 2.7"
|
||||
- "openstacksdk"
|
||||
options:
|
||||
image:
|
||||
|
||||
@ -46,7 +46,7 @@ options:
|
||||
description:
|
||||
- Ignored. Present for backwards compatibility
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python >= 2.7"
|
||||
- "openstacksdk"
|
||||
'''
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -35,7 +35,7 @@ options:
|
||||
- Ignored. Present for backwards compatibility
|
||||
required: false
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python >= 2.7"
|
||||
- "openstacksdk"
|
||||
'''
|
||||
|
||||
|
||||
@ -48,7 +48,7 @@ options:
|
||||
description:
|
||||
- Ignored. Present for backwards compatibility
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python >= 2.7"
|
||||
- "openstacksdk"
|
||||
'''
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -37,7 +37,7 @@ options:
|
||||
choices: [present, absent]
|
||||
default: present
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python >= 2.7"
|
||||
- "openstacksdk"
|
||||
'''
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -50,7 +50,7 @@ options:
|
||||
description:
|
||||
- Ignored. Present for backwards compatibility
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python >= 2.7"
|
||||
- "openstacksdk"
|
||||
'''
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -105,7 +105,7 @@ options:
|
||||
|
||||
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python >= 2.7"
|
||||
- "openstacksdk >= 0.13.0"
|
||||
'''
|
||||
|
||||
|
||||
@ -54,7 +54,7 @@ options:
|
||||
description:
|
||||
- Ignored. Present for backwards compatibility
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python >= 2.7"
|
||||
- "openstacksdk"
|
||||
'''
|
||||
|
||||
|
||||
@ -166,7 +166,7 @@ options:
|
||||
description:
|
||||
- Availability zone in which to create the server.
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python >= 2.7"
|
||||
- "openstacksdk"
|
||||
'''
|
||||
|
||||
|
||||
@ -54,7 +54,7 @@ options:
|
||||
description:
|
||||
- Ignored. Present for backwards compatibility
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python >= 2.7"
|
||||
- "openstacksdk"
|
||||
'''
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -45,7 +45,7 @@ options:
|
||||
- Ignored. Present for backwards compatibility
|
||||
required: false
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python >= 2.7"
|
||||
- "openstacksdk"
|
||||
'''
|
||||
|
||||
|
||||
@ -44,7 +44,7 @@ options:
|
||||
description:
|
||||
- Ignored. Present for backwards compatibility
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python >= 2.7"
|
||||
- "openstacksdk"
|
||||
'''
|
||||
|
||||
|
||||
@ -59,7 +59,7 @@ options:
|
||||
description:
|
||||
- Ignored. Present for backwards compatibility
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python >= 2.7"
|
||||
- "openstacksdk"
|
||||
'''
|
||||
EXAMPLES = '''
|
||||
|
||||
@ -96,7 +96,7 @@ options:
|
||||
description:
|
||||
- Ignored. Present for backwards compatibility
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python >= 2.7"
|
||||
- "openstacksdk"
|
||||
'''
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -66,7 +66,7 @@ options:
|
||||
description:
|
||||
- Ignored. Present for backwards compatibility
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python >= 2.7"
|
||||
- "openstacksdk"
|
||||
'''
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -39,7 +39,7 @@ options:
|
||||
- Ignored. Present for backwards compatibility
|
||||
required: false
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python >= 2.7"
|
||||
- "openstacksdk"
|
||||
'''
|
||||
|
||||
|
||||
@ -52,7 +52,7 @@ options:
|
||||
description:
|
||||
- Ignored. Present for backwards compatibility
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python >= 2.7"
|
||||
- "openstacksdk"
|
||||
'''
|
||||
|
||||
|
||||
@ -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"
|
||||
'''
|
||||
|
||||
|
||||
@ -52,7 +52,7 @@ options:
|
||||
description:
|
||||
- Ignored. Present for backwards compatibility
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- "python >= 2.7"
|
||||
- "openstacksdk"
|
||||
'''
|
||||
|
||||
|
||||
@ -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,
|
||||
)
|
||||
|
||||
@ -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'],
|
||||
)
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 }}"
|
||||
'''
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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():
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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 = [
|
||||
|
||||
@ -214,6 +214,7 @@ class Parameters(AnsibleF5Parameters):
|
||||
'enableDnssec',
|
||||
'processXfr',
|
||||
'enableDnsExpress',
|
||||
'defaultsFrom'
|
||||
]
|
||||
|
||||
returnables = [
|
||||
|
||||
@ -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
|
||||
)
|
||||
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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():
|
||||
|
||||
@ -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']
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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()
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
Reference in New Issue
Block a user