1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

ACI: Implement aci.boolean() to return an ACI boolean (#35610)

* ACI: Implement aci.boolean() to return an ACI boolean

A boolean value in ACI is not always standardized to yes/no.
Sometimes we have active/inactive, or enabled/disabled
Whereas the interface we want is a true YAML boolean.

We did not modify enabled/disabled values at this time.
I first want to determine if this implementation is acceptable.

* Support enabled/disabled as well, with deprecation messages

* Fix typo

* Fix PEP8 issue

* Ensure the aci object exists before using it

* Add comment to ensure this gets fixed in v2.9

* Fix typo
This commit is contained in:
Dag Wieers 2018-02-02 18:54:48 +01:00 committed by GitHub
parent 2646ea9b86
commit 3dfede5642
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 96 additions and 88 deletions

View file

@ -36,6 +36,7 @@ import json
import os
from copy import deepcopy
from ansible.module_utils.parsing.convert_bool import boolean
from ansible.module_utils.urls import fetch_url
from ansible.module_utils._text import to_bytes
@ -188,6 +189,27 @@ class ACIModule(object):
else:
self.module.fail_json(msg="Either parameter 'password' or 'private_key' is required for authentication")
def boolean(self, value, true='yes', false='no'):
''' Return an acceptable value back '''
if value is None:
return None
elif value is True:
return true
elif value is False:
return false
elif boolean(value) is True: # When type=raw, this supports Ansible booleans
return true
elif boolean(value) is False: # When type=raw, this supports Ansible booleans
return false
elif value == true: # When type=raw, this supports the original boolean values
self.module.deprecate("Boolean value '%s' is no longer valid, please use 'yes' as a boolean value." % value, '2.9')
return true
elif value == false: # When type=raw, this supports the original boolean values
self.module.deprecate("Boolean value '%s' is no longer valid, please use 'no' as a boolean value." % value, '2.9')
return false
else: # When type=raw, escalate back to user
self.module.fail_json(msg="Boolean value '%s' is an invalid ACI boolean value.")
def iso8601_format(self, dt):
''' Return an ACI-compatible ISO8601 formatted time: 2123-12-12T00:00:00.000+00:00 '''
try:

View file

@ -184,12 +184,9 @@ def main():
aci = ACIModule(module)
if module.params['enabled'] is True:
enabled = 'active'
elif module.params['enabled'] is False:
enabled = 'inactive'
else:
enabled = None
aaa_password_update_required = aci.boolean(module.params['aaa_password_update_required'])
enabled = aci.boolean(module.params['enabled'], 'active', 'inactive')
expires = aci.boolean(module.params['expires'])
expiration = module.params['expiration']
if expiration is not None and expiration != 'never':
@ -198,18 +195,6 @@ def main():
except Exception as e:
module.fail_json(msg="Failed to parse date format '%s', %s" % (module.params['expiration'], e))
expires = module.params['expires']
if expires is True:
expires = 'yes'
elif expires is False:
expires = 'no'
aaa_password_update_required = module.params['aaa_password_update_required']
if aaa_password_update_required is True:
aaa_password_update_required = 'yes'
elif aaa_password_update_required is False:
aaa_password_update_required = 'no'
aci.construct_url(
root_class=dict(
aci_class='aaaUser',

View file

@ -30,7 +30,7 @@ options:
- Determines if the Bridge Domain should flood ARP traffic.
- The APIC defaults new Bridge Domains to C(no).
type: bool
default: no
default: 'no'
bd:
description:
- The name of the Bridge Domain.
@ -49,20 +49,20 @@ options:
- Determines if PIM is enabled
- The APIC defaults new Bridge Domains to C(no).
type: bool
default: no
default: 'no'
enable_routing:
description:
- Determines if IP forwarding should be allowed.
- The APIC defaults new Bridge Domains to C(yes).
type: bool
default: yes
default: 'yes'
endpoint_clear:
description:
- Clears all End Points in all Leaves when C(yes).
- The APIC defaults new Bridge Domains to C(no).
- The value is not reset to disabled once End Points have been cleared; that requires a second task.
type: bool
default: no
default: 'no'
endpoint_move_detect:
description:
- Determines if GARP should be enabled to detect when End Points move.
@ -109,7 +109,7 @@ options:
- Determines if the BD should limit IP learning to only subnets owned by the Bridge Domain.
- The APIC defaults new Bridge Domains to C(yes).
type: bool
default: yes
default: 'yes'
mac_address:
description:
- The MAC Address to assign to the C(bd) instead of using the default.
@ -145,7 +145,7 @@ EXAMPLES = r'''
host: "{{ inventory_hostname }}"
username: "{{ username }}"
password: "{{ password }}"
validate_certs: false
validate_certs: no
state: present
tenant: prod
bd: web_servers
@ -157,7 +157,7 @@ EXAMPLES = r'''
host: "{{ inventory_hostname }}"
username: "{{ username }}"
password: "{{ password }}"
validate_certs: false
validate_certs: no
state: present
tenant: prod
bd: storage
@ -170,7 +170,7 @@ EXAMPLES = r'''
host: "{{ inventory_hostname }}"
username: "{{ username }}"
password: "{{ password }}"
validate_certs: true
validate_certs: yes
state: present
tenant: prod
bd: web_servers
@ -182,7 +182,7 @@ EXAMPLES = r'''
host: "{{ inventory_hostname }}"
username: "{{ username }}"
password: "{{ password }}"
validate_certs: true
validate_certs: yes
state: query
- name: Query a Bridge Domain
@ -190,7 +190,7 @@ EXAMPLES = r'''
host: "{{ inventory_hostname }}"
username: "{{ username }}"
password: "{{ password }}"
validate_certs: true
validate_certs: yes
state: query
tenant: prod
bd: web_servers
@ -200,7 +200,7 @@ EXAMPLES = r'''
host: "{{ inventory_hostname }}"
username: "{{ username }}"
password: "{{ password }}"
validate_certs: true
validate_certs: yes
state: absent
tenant: prod
bd: web_servers
@ -215,22 +215,22 @@ from ansible.module_utils.basic import AnsibleModule
def main():
argument_spec = aci_argument_spec()
argument_spec.update(
arp_flooding=dict(choices=['no', 'yes']),
arp_flooding=dict(type='bool'),
bd=dict(type='str', aliases=['bd_name', 'name']),
bd_type=dict(type='str', choices=['ethernet', 'fc']),
description=dict(type='str'),
enable_multicast=dict(type='str', choices=['no', 'yes']),
enable_routing=dict(type='str', choices=['no', 'yes']),
endpoint_clear=dict(type='str', choices=['no', 'yes']),
enable_multicast=dict(type='bool'),
enable_routing=dict(type='bool'),
endpoint_clear=dict(type='bool'),
endpoint_move_detect=dict(type='str', choices=['default', 'garp']),
endpoint_retention_action=dict(type='str', choices=['inherit', 'resolve']),
endpoint_retention_policy=dict(type='str'),
igmp_snoop_policy=dict(type='str'),
ip_learning=dict(type='str', choices=['no', 'yes']),
ip_learning=dict(type='bool'),
ipv6_nd_policy=dict(type='str'),
l2_unknown_unicast=dict(choices=['proxy', 'flood']),
l3_unknown_multicast=dict(choices=['flood', 'opt-flood']),
limit_ip_learn=dict(type='str', choices=['no', 'yes']),
limit_ip_learn=dict(type='bool'),
mac_address=dict(type='str', aliases=['mac']),
multi_dest=dict(choices=['bd-flood', 'drop', 'encap-flood']),
state=dict(choices=['absent', 'present', 'query'], type='str', default='present'),
@ -252,16 +252,18 @@ def main():
],
)
arp_flooding = module.params['arp_flooding']
aci = ACIModule(module)
arp_flooding = aci.boolean(module.params['arp_flooding'])
bd = module.params['bd']
bd_type = module.params['bd_type']
if bd_type == 'ethernet':
# ethernet type is represented as regular, but that is not clear to the users
bd_type = 'regular'
description = module.params['description']
enable_multicast = module.params['enable_multicast']
enable_routing = module.params['enable_routing']
endpoint_clear = module.params['endpoint_clear']
enable_multicast = aci.boolean(module.params['enable_multicast'])
enable_routing = aci.boolean(module.params['enable_routing'])
endpoint_clear = aci.boolean(module.params['endpoint_clear'])
endpoint_move_detect = module.params['endpoint_move_detect']
if endpoint_move_detect == 'default':
# the ACI default setting is an empty string, but that is not a good input value
@ -269,11 +271,11 @@ def main():
endpoint_retention_action = module.params['endpoint_retention_action']
endpoint_retention_policy = module.params['endpoint_retention_policy']
igmp_snoop_policy = module.params['igmp_snoop_policy']
ip_learning = module.params['ip_learning']
ip_learning = aci.boolean(module.params['ip_learning'])
ipv6_nd_policy = module.params['ipv6_nd_policy']
l2_unknown_unicast = module.params['l2_unknown_unicast']
l3_unknown_multicast = module.params['l3_unknown_multicast']
limit_ip_learn = module.params['limit_ip_learn']
limit_ip_learn = aci.boolean(module.params['limit_ip_learn'])
mac_address = module.params['mac_address']
multi_dest = module.params['multi_dest']
state = module.params['state']
@ -285,7 +287,6 @@ def main():
module._warnings = ["The support for managing Subnets has been moved to its own module, aci_subnet. \
The new modules still supports 'gateway_ip' and 'subnet_mask' along with more features"]
aci = ACIModule(module)
aci.construct_url(
root_class=dict(
aci_class='fvTenant',

View file

@ -39,8 +39,8 @@ options:
description:
- Determines if the Subnet should be treated as a VIP; used when the BD is extended to multiple sites.
- The APIC defaults new Subnets to C(no).
choices: [ no, yes ]
default: no
type: bool
default: 'no'
gateway:
description:
- The IPv4 or IPv6 gateway address for the Subnet.
@ -59,8 +59,8 @@ options:
- Determines if the Subnet is preferred over all available Subnets. Only one Subnet per Address Family (IPv4/IPv6).
can be preferred in the Bridge Domain.
- The APIC defaults new Subnets to C(no).
choices: [ no, yes ]
default: no
type: bool
default: 'no'
route_profile:
description:
- The Route Profile to the associate with the Subnet.
@ -215,12 +215,12 @@ def main():
argument_spec.update(
bd=dict(type='str', aliases=['bd_name']),
description=dict(type='str', aliases=['descr']),
enable_vip=dict(type='str', choices=['no', 'yes']),
enable_vip=dict(type='bool'),
gateway=dict(type='str', aliases=['gateway_ip']),
mask=dict(type='int', aliases=['subnet_mask']),
subnet_name=dict(type='str', aliases=['name']),
nd_prefix_policy=dict(type='str'),
preferred=dict(type='str', choices=['no', 'yes']),
preferred=dict(type='bool'),
route_profile=dict(type='str'),
route_profile_l3_out=dict(type='str'),
scope=dict(
@ -244,8 +244,10 @@ def main():
],
)
aci = ACIModule(module)
description = module.params['description']
enable_vip = module.params['enable_vip']
enable_vip = aci.boolean(module.params['enable_vip'])
tenant = module.params['tenant']
bd = module.params['bd']
gateway = module.params['gateway']
@ -257,7 +259,7 @@ def main():
gateway = '{0}/{1}'.format(gateway, str(mask))
subnet_name = module.params['subnet_name']
nd_prefix_policy = module.params['nd_prefix_policy']
preferred = module.params['preferred']
preferred = aci.boolean(module.params['preferred'])
route_profile = module.params['route_profile']
route_profile_l3_out = module.params['route_profile_l3_out']
scope = module.params['scope']
@ -273,7 +275,6 @@ def main():
if subnet_control:
subnet_control = SUBNET_CONTROL_MAPPING[subnet_control]
aci = ACIModule(module)
aci.construct_url(
root_class=dict(
aci_class='fvTenant',

View file

@ -168,21 +168,17 @@ def main():
],
)
aci = ACIModule(module)
description = module.params['description']
export_policy = module.params['export_policy']
fail_on_decrypt = module.params['fail_on_decrypt']
if fail_on_decrypt is True:
fail_on_decrypt = 'yes'
elif fail_on_decrypt is False:
fail_on_decrypt = 'no'
fail_on_decrypt = aci.boolean(module.params['fail_on_decrypt'])
import_mode = module.params['import_mode']
import_policy = module.params['import_policy']
import_type = module.params['import_type']
snapshot = module.params['snapshot']
state = module.params['state']
aci = ACIModule(module)
if state == 'rollback':
if snapshot.startswith('run-'):
snapshot = snapshot.replace('run-', '', 1)

View file

@ -46,7 +46,7 @@ options:
description:
- Determines if secure information should be included in the backup.
- The APIC defaults new Export Policies to C(yes).
choices: [ 'no', 'yes' ]
type: bool
default: 'yes'
max_count:
description:
@ -114,7 +114,7 @@ def main():
description=dict(type='str', aliases=['descr']),
export_policy=dict(type='str', aliases=['name']),
format=dict(type='str', choices=['json', 'xml']),
include_secure=dict(type='str', choices=['no', 'yes']),
include_secure=dict(type='bool'),
max_count=dict(type='int'),
snapshot=dict(type='str'),
state=dict(type='str', choices=['absent', 'present', 'query'], default='present'),
@ -129,10 +129,12 @@ def main():
],
)
aci = ACIModule(module)
description = module.params['description']
export_policy = module.params['export_policy']
file_format = module.params['format']
include_secure = module.params['include_secure']
include_secure = aci.boolean(module.params['include_secure'])
max_count = module.params['max_count']
if max_count is not None:
if max_count in range(1, 11):
@ -144,8 +146,6 @@ def main():
snapshot = 'run-' + snapshot
state = module.params['state']
aci = ACIModule(module)
if state == 'present':
aci.construct_url(
root_class=dict(

View file

@ -42,8 +42,8 @@ options:
- Determines if the APIC should reverse the src and dst ports to allow the
return traffic back, since ACI is stateless filter.
- The APIC defaults new Contract Subjects to C(yes).
choices: [ yes, no ]
default: yes
type: bool
default: 'yes'
priority:
description:
- The QoS class.
@ -143,7 +143,7 @@ def main():
subject=dict(type='str', aliases=['contract_subject', 'name', 'subject_name']),
tenant=dict(type='str', aliases=['tenant_name']),
priority=dict(type='str', choices=['unspecified', 'level1', 'level2', 'level3']),
reverse_filter=dict(type='str', choices=['yes', 'no']),
reverse_filter=dict(type='bool'),
dscp=dict(type='str', aliases=['target']),
description=dict(type='str', aliases=['descr']),
consumer_match=dict(type='str', choices=['all', 'at_least_one', 'at_most_one', 'none']),
@ -164,9 +164,11 @@ def main():
],
)
aci = ACIModule(module)
subject = module.params['subject']
priority = module.params['priority']
reverse_filter = module.params['reverse_filter']
reverse_filter = aci.boolean(module.params['reverse_filter'])
contract = module.params['contract']
dscp = module.params['dscp']
description = module.params['description']
@ -184,7 +186,6 @@ def main():
if directive is not None or filter_name is not None:
module.fail_json(msg="Managing Contract Subjects to Filter bindings has been moved to module 'aci_subject_bind_filter'")
aci = ACIModule(module)
aci.construct_url(
root_class=dict(
aci_class='fvTenant',

View file

@ -68,9 +68,9 @@ options:
netflow:
description:
- Determines if netflow should be enabled.
- The APIC defaults new EPG to Domain binings to C(disabled).
choices: [ disabled, enabled ]
default: disabled
- The APIC defaults new EPG to Domain binings to C(no).
type: bool
default: 'no'
primary_encap:
description:
- Determines the primary VLAN ID when using useg.
@ -129,7 +129,7 @@ def main():
encap=dict(type='int'),
encap_mode=dict(type='str', choices=['auto', 'vlan', 'vxlan']),
epg=dict(type='str', aliases=['name', 'epg_name']),
netflow=dict(type='str', choices=['disabled', 'enabled']),
netflow=dict(type='raw'), # Turn into a boolean in v2.9
primary_encap=dict(type='int'),
resolution_immediacy=dict(type='str', choices=['immediate', 'lazy', 'pre-provision']),
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
@ -149,6 +149,8 @@ def main():
],
)
aci = ACIModule(module)
allow_useg = module.params['allow_useg']
ap = module.params['ap']
deploy_immediacy = module.params['deploy_immediacy']
@ -163,7 +165,7 @@ def main():
module.fail_json(msg='Valid VLAN assigments are from 1 to 4096')
encap_mode = module.params['encap_mode']
epg = module.params['epg']
netflow = module.params['netflow']
netflow = aci.boolean(module.params['netflow'], 'enabled', 'disabled')
primary_encap = module.params['primary_encap']
if primary_encap is not None:
if primary_encap in range(1, 4097):
@ -185,7 +187,6 @@ def main():
else:
epg_domain = None
aci = ACIModule(module)
aci.construct_url(
root_class=dict(
aci_class='fvTenant',

View file

@ -151,7 +151,7 @@ def main():
icmp6_msg_type=dict(type='str', choices=VALID_ICMP6_TYPES),
ip_protocol=dict(choices=VALID_IP_PROTOCOLS, type='str'),
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
stateful=dict(type='str', choices=['no', 'yes']),
stateful=dict(type='bool'),
tenant=dict(type="str", aliases=['tenant_name']),
)
@ -164,6 +164,8 @@ def main():
],
)
aci = ACIModule(module)
arp_flag = module.params['arp_flag']
if arp_flag is not None:
arp_flag = ARP_FLAG_MAPPING[arp_flag]
@ -188,7 +190,7 @@ def main():
icmp6_msg_type = ICMP6_MAPPING[icmp6_msg_type]
ip_protocol = module.params['ip_protocol']
state = module.params['state']
stateful = module.params['stateful']
stateful = aci.boolean(module.params['stateful'])
tenant = module.params['tenant']
# validate that dst_port is not passed with dst_start or dst_end
@ -198,7 +200,6 @@ def main():
dst_end = dst_port
dst_start = dst_port
aci = ACIModule(module)
aci.construct_url(
root_class=dict(
aci_class='fvTenant',

View file

@ -83,7 +83,7 @@ def main():
description=dict(type='str', aliases=['descr']),
vlan_scope=dict(type='str', choices=['global', 'portlocal']), # No default provided on purpose
qinq=dict(type='str', choices=['core', 'disabled', 'edge']),
vepa=dict(type='str', choices=['disabled', 'enabled']),
vepa=dict(type='raw'), # Turn into a boolean in v2.9
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
method=dict(type='str', choices=['delete', 'get', 'post'], aliases=['action'], removed_in_version='2.6'), # Deprecated starting from v2.6
protocol=dict(type='str', removed_in_version='2.6'), # Deprecated in v2.6
@ -103,7 +103,7 @@ def main():
qinq = module.params['qinq']
if qinq is not None:
qinq = QINQ_MAPPING[qinq]
vepa = module.params['vepa']
vepa = aci.boolean(module.params['vepa'], 'enabled', 'disabled')
description = module.params['description']
state = module.params['state']

View file

@ -77,8 +77,8 @@ def main():
argument_spec.update(
lldp_policy=dict(type='str', require=False, aliases=['name']),
description=dict(type='str', aliases=['descr']),
receive_state=dict(type='str', choices=['disabled', 'enabled']),
transmit_state=dict(type='str', choices=['disabled', 'enabled']),
receive_state=dict(type='raw'), # Turn into a boolean in v2.9
transmit_state=dict(type='raw'), # Turn into a boolean in v2.9
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
method=dict(type='str', choices=['delete', 'get', 'post'], aliases=['action'], removed_in_version='2.6'), # Deprecated starting from v2.6
protocol=dict(type='str', removed_in_version='2.6'), # Deprecated in v2.6
@ -95,8 +95,8 @@ def main():
lldp_policy = module.params['lldp_policy']
description = module.params['description']
receive_state = module.params['receive_state']
transmit_state = module.params['transmit_state']
receive_state = aci.boolean(module.params['receive_state'], 'enabled', 'disabled')
transmit_state = aci.boolean(module.params['transmit_state'], 'enabled', 'disabled')
state = module.params['state']
aci = ACIModule(module)

View file

@ -69,7 +69,7 @@ def main():
argument_spec.update(
mcp=dict(type='str', required=False, aliases=['mcp_interface', 'name']), # Not required for querying all objects
description=dict(type='str', aliases=['descr']),
admin_state=dict(type='str', choices=['disabled', 'enabled']),
admin_state=dict(type='raw'), # Turn into a boolean in v2.9
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
method=dict(type='str', choices=['delete', 'get', 'post'], aliases=['action'], removed_in_version='2.6'), # Deprecated starting from v2.6
protocol=dict(type='str', removed_in_version='2.6'), # Deprecated in v2.6
@ -86,7 +86,7 @@ def main():
mcp = module.params['mcp']
description = module.params['description']
admin_state = module.params['admin_state']
admin_state = aci.boolean(module.params['admin_state'], 'enabled', 'disabled')
state = module.params['state']
aci = ACIModule(module)

View file

@ -77,7 +77,7 @@ from ansible.module_utils.basic import AnsibleModule
def main():
argument_spec = aci_argument_spec()
argument_spec.update(
admin_state=dict(type='str', choices=['enabled', 'disabled']),
admin_state=dict(type='raw'), # Turn into a boolean in v2.9
description=dict(type='str', aliases=['descr']),
dst_group=dict(type='str'),
src_group=dict(type='str', required=False, aliases=['name']), # Not required for querying all objects
@ -96,7 +96,7 @@ def main():
],
)
admin_state = module.params['admin_state']
admin_state = aci.boolean(module.params['admin_state'], 'enabled', 'disabled')
description = module.params['description']
dst_group = module.params['dst_group']
src_group = module.params['src_group']

View file

@ -128,7 +128,7 @@ def main():
description=dict(type='str', aliases=['descr']),
pool=dict(type='str', aliases=['pool_name']),
pool_allocation_mode=dict(type='str', aliases=['pool_mode'], choices=['dynamic', 'static']),
block_name=dict(type='str', aliases=["name"]),
block_name=dict(type='str', aliases=['name']),
block_end=dict(type='int', aliases=['end']),
block_start=dict(type='int', aliases=["start"]),
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),