Aci build url (#33017)

* ACI Buid URL: Refactor how ACI modules dynamically build proper URL.

* Remove MIM URl from update

* fix missing comma
This commit is contained in:
Jacob McGill 2017-11-22 10:07:39 -05:00 committed by John R Barker
parent e50f931cf3
commit 429af90027
32 changed files with 732 additions and 185 deletions

View file

@ -61,6 +61,7 @@ aci_argument_spec = dict(
validate_certs=dict(type='bool', default=True),
)
'''
URL_MAPPING = dict(
action_rule=dict(aci_class='rtctrlAttrP', mo='attr-', key='name'),
aep=dict(aci_class='infraAttEntityP', mo='infra/attentp-', key='name'),
@ -97,6 +98,7 @@ URL_MAPPING = dict(
tenant_span_src_grp_dst_grp=dict(aci_class='spanSpanLbl', mo='spanlbl-', key='name'),
vrf=dict(aci_class='fvCtx', mo='ctx-', key='name'),
)
'''
def aci_response_error(result):
@ -284,16 +286,16 @@ class ACIModule(object):
"""
This method is used to retrieve the appropriate URL path and filter_string to make the request to the APIC.
:param root_class: Type str.
The top-level class naming parameter per the modules (EX: tenant).
:param sublass_1: Type str.
The second-level class naming parameter per the modules (EX: bd).
:param sublass_2: Type str.
The third-level class naming parameter per the modules (EX: gateway).
:param sublass_3: Type str.
The fourth-level class naming parameter per the modules.
:param child_classes: Type tuple.
The list of child classes that the module supports along with the object.
:param root_class: The top-level class dictionary containing aci_class, aci_rn, filter_target, and module_object keys.
:param sublass_1: The second-level class dictionary containing aci_class, aci_rn, filter_target, and module_object keys.
:param sublass_2: The third-level class dictionary containing aci_class, aci_rn, filter_target, and module_object keys.
:param sublass_3: The fourth-level class dictionary containing aci_class, aci_rn, filter_target, and module_object keys.
:param child_classes: The list of child classes that the module supports along with the object.
:type root_class: dict
:type subclass_1: dict
:type subclass_2: dict
:type subclass_3: dict
:type child_classes: list
:return: The path and filter_string needed to build the full URL.
"""
if child_classes is None:
@ -314,18 +316,16 @@ class ACIModule(object):
self.result['url'] = '{}://{}/{}'.format(self.module.params['protocol'], self.module.params['hostname'], path)
self.result['filter_string'] = filter_string
def _construct_url_1(self, obj_class, child_includes):
def _construct_url_1(self, obj, child_includes):
"""
This method is used by get_url when the object is the top-level class.
"""
obj = self.module.params.get(obj_class)
obj_dict = URL_MAPPING[obj_class]
obj_class = obj_dict['aci_class']
obj_mo = obj_dict['mo']
obj_class = obj['aci_class']
obj_rn = obj['aci_rn']
# State is present or absent
if self.module.params['state'] != 'query':
path = 'api/mo/uni/{}[{}].json'.format(obj_mo, obj)
path = 'api/mo/uni/{}.json'.format(obj_rn)
filter_string = '?rsp-prop-include=config-only' + child_includes
# Query for all objects of the module's class
elif obj is None:
@ -333,7 +333,7 @@ class ACIModule(object):
filter_string = ''
# Query for a specific object in the module's class
else:
path = 'api/mo/uni/{}[{}].json'.format(obj_mo, obj)
path = 'api/mo/uni/{}.json'.format(obj_rn)
filter_string = ''
# Append child_includes to filter_string if filter string is empty
@ -342,19 +342,16 @@ class ACIModule(object):
return path, filter_string
def _construct_url_2(self, parent_class, obj_class, child_includes):
def _construct_url_2(self, parent, obj, child_includes):
"""
This method is used by get_url when the object is the second-level class.
"""
parent = self.module.params.get(parent_class)
parent_dict = URL_MAPPING[parent_class]
parent_class = parent_dict['aci_class']
parent_mo = parent_dict['mo']
obj = self.module.params.get(obj_class)
obj_dict = URL_MAPPING[obj_class]
obj_class = obj_dict['aci_class']
obj_mo = obj_dict['mo']
obj_key = obj_dict['key']
parent_rn = parent['aci_rn']
parent_obj = parent['module_object']
obj_class = obj['aci_class']
obj_rn = obj['aci_rn']
obj_filter = obj['filter_target']
obj = obj['module_object']
if not child_includes:
self_child_includes = '?rsp-subtree=full&rsp-subtree-class=' + obj_class
@ -363,26 +360,26 @@ class ACIModule(object):
# State is present or absent
if self.module.params['state'] != 'query':
path = 'api/mo/uni/{}[{}]/{}[{}].json'.format(parent_mo, parent, obj_mo, obj)
path = 'api/mo/uni/{}/{}.json'.format(parent_rn, obj_rn)
filter_string = '?rsp-prop-include=config-only' + child_includes
# Query for all objects of the module's class
elif obj is None and parent is None:
elif obj is None and parent_obj is None:
path = 'api/class/{}.json'.format(obj_class)
filter_string = ''
# Queries when parent object is provided
elif parent is not None:
elif parent_obj is not None:
# Query for specific object in the module's class
if obj is not None:
path = 'api/mo/uni/{}[{}]/{}[{}].json'.format(parent_mo, parent, obj_mo, obj)
path = 'api/mo/uni/{}/{}.json'.format(parent_rn, obj_rn)
filter_string = ''
# Query for all object's of the module's class that belong to a specific parent object
else:
path = 'api/mo/uni/{}[{}].json'.format(parent_mo, parent)
path = 'api/mo/uni/{}.json'.format(parent_rn)
filter_string = self_child_includes
# Query for all objects of the module's class that match the provided ID value
else:
path = 'api/class/{}.json'.format(obj_class)
filter_string = '?query-target-filter=eq({}.{}, \"{}\")'.format(obj_class, obj_key, obj) + child_includes
filter_string = '?query-target-filter=eq{}'.format(obj_filter) + child_includes
# Append child_includes to filter_string if filter string is empty
if child_includes is not None and filter_string == '':
@ -390,24 +387,20 @@ class ACIModule(object):
return path, filter_string
def _construct_url_3(self, root_class, parent_class, obj_class, child_includes):
def _construct_url_3(self, root, parent, obj, child_includes):
"""
This method is used by get_url when the object is the third-level class.
"""
root = self.module.params.get(root_class)
root_dict = URL_MAPPING[root_class]
root_class = root_dict['aci_class']
root_mo = root_dict['mo']
parent = self.module.params.get(parent_class)
parent_dict = URL_MAPPING[parent_class]
parent_class = parent_dict['aci_class']
parent_mo = parent_dict['mo']
parent_key = parent_dict['key']
obj = self.module.params.get(obj_class)
obj_dict = URL_MAPPING[obj_class]
obj_class = obj_dict['aci_class']
obj_mo = obj_dict['mo']
obj_key = obj_dict['key']
root_rn = root['aci_rn']
root_obj = root['module_object']
parent_class = parent['aci_class']
parent_rn = parent['aci_rn']
parent_filter = parent['filter_target']
parent_obj = parent['module_object']
obj_class = obj['aci_class']
obj_rn = obj['aci_rn']
obj_filter = obj['filter_target']
obj = obj['module_object']
if not child_includes:
self_child_includes = '&rsp-subtree=full&rsp-subtree-class=' + obj_class
@ -421,49 +414,49 @@ class ACIModule(object):
# State is ablsent or present
if self.module.params['state'] != 'query':
path = 'api/mo/uni/{}[{}]/{}[{}]/{}[{}].json'.format(root_mo, root, parent_mo, parent, obj_mo, obj)
path = 'api/mo/uni/{}/{}/{}.json'.format(root_rn, parent_rn, obj_rn)
filter_string = '?rsp-prop-include=config-only' + child_includes
# Query for all objects of the module's class
elif obj is None and parent is None and root is None:
elif obj is None and parent_obj is None and root_obj is None:
path = 'api/class/{}.json'.format(obj_class)
filter_string = ''
# Queries when root object is provided
elif root is not None:
elif root_obj is not None:
# Queries when parent object is provided
if parent is not None:
if parent_obj is not None:
# Query for a specific object of the module's class
if obj is not None:
path = 'api/mo/uni/{}[{}]/{}[{}]/{}[{}].json'.format(root_mo, root, parent_mo, parent, obj_mo, obj)
path = 'api/mo/uni/{}/{}/{}.json'.format(root_rn, parent_rn, obj_rn)
filter_string = ''
# Query for all objects of the module's class that belong to a specific parent object
else:
path = 'api/mo/uni/{}[{}]/{}[{}].json'.format(root_mo, root, parent_mo, parent)
path = 'api/mo/uni/{}/{}.json'.format(root_rn, parent_rn)
filter_string = self_child_includes.replace('&', '?', 1)
# Query for all objects of the module's class that match the provided ID value and belong to a specefic root object
elif obj is not None:
path = 'api/mo/uni/{}[{}].json'.format(root_mo, root)
filter_string = '?rsp-subtree-filter=eq({}.{}, \"{}\"){}'.format(obj_class, obj_key, obj, self_child_includes)
path = 'api/mo/uni/{}.json'.format(root_rn)
filter_string = '?rsp-subtree-filter=eq{}{}'.format(obj_filter, self_child_includes)
# Query for all objects of the module's class that belong to a specific root object
else:
path = 'api/mo/uni/{}[{}].json'.format(root_mo, root)
path = 'api/mo/uni/{}.json'.format(root_rn)
filter_string = '?' + parent_self_child_includes
# Queries when parent object is provided but root object is not provided
elif parent is not None:
elif parent_obj is not None:
# Query for all objects of the module's class that belong to any parent class
# matching the provided ID values for both object and parent object
if obj is not None:
path = 'api/class/{}.json'.format(parent_class)
filter_string = '?query-target-filter=eq({}.{}, \"{}\"){}&rsp-subtree-filter=eq({}.{}, \"{}\")'.format(
parent_class, parent_key, parent, self_child_includes, obj_class, obj_key, obj)
filter_string = '?query-target-filter=eq{}{}&rsp-subtree-filter=eq{}'.format(
parent_filter, self_child_includes, obj_filter)
# Query for all objects of the module's class that belong to any parent class
# matching the provided ID value for the parent object
else:
path = 'api/class/{}.json'.format(parent_class)
filter_string = '?query-target-filter=eq({}.{}, \"{}\"){}'.format(parent_class, parent_key, parent, self_child_includes)
filter_string = '?query-target-filter=eq{}{}'.format(parent_filter, self_child_includes)
# Query for all objects of the module's class matching the provided ID value of the object
else:
path = 'api/class/{}.json'.format(obj_class)
filter_string = '?query-target-filter=eq({}.{}, \"{}\")'.format(obj_class, obj_key, obj) + child_includes
filter_string = '?query-target-filter=eq{}'.format(obj_filter) + child_includes
# append child_includes to filter_string if filter string is empty
if child_includes is not None and filter_string == '':
@ -471,33 +464,30 @@ class ACIModule(object):
return path, filter_string
def _construct_url_4(self, root_class, sec_class, parent_class, obj_class, child_includes):
def _construct_url_4(self, root, sec, parent, obj, child_includes):
"""
This method is used by get_url when the object is the third-level class.
"""
root = self.module.params.get(root_class)
root_dict = URL_MAPPING[root_class]
root_class = root_dict['aci_class']
root_mo = root_dict['mo']
sec = self.module.params.get(sec_class)
sec_dict = URL_MAPPING[sec_class]
sec_class = sec_dict['aci_class']
sec_mo = sec_dict['mo']
# sec_key = sec_dict['key']
parent = self.module.params.get(parent_class)
parent_dict = URL_MAPPING[parent_class]
parent_class = parent_dict['aci_class']
parent_mo = parent_dict['mo']
# parent_key = parent_dict['key']
obj = self.module.params.get(obj_class)
obj_dict = URL_MAPPING[obj_class]
obj_class = obj_dict['aci_class']
obj_mo = obj_dict['mo']
# obj_key = obj_dict['key']
# root_class = root['aci_class']
root_rn = root['aci_rn']
# root_filter = root['filter_target']
# root_obj = root['module_object']
# sec_class = sec['aci_class']
sec_rn = sec['aci_rn']
# sec_filter = sec['filter_target']
# sec_obj = sec['module_object']
# parent_class = parent['aci_class']
parent_rn = parent['aci_rn']
# parent_filter = parent['filter_target']
# parent_obj = parent['module_object']
obj_class = obj['aci_class']
obj_rn = obj['aci_rn']
# obj_filter = obj['filter_target']
# obj = obj['module_object']
# State is ablsent or present
if self.module.params['state'] != 'query':
path = 'api/mo/uni/{}[{}]/{}[{}]/{}[{}]/{}[{}].json'.format(root_mo, root, sec_mo, sec, parent_mo, parent, obj_mo, obj)
path = 'api/mo/uni/{}/{}/{}/{}.json'.format(root_rn, sec_rn, parent_rn, obj_rn)
filter_string = '?rsp-prop-include=config-only' + child_includes
else:
path = 'api/class/{}.json'.format(obj_class)

View file

@ -108,7 +108,14 @@ def main():
state = module.params['state']
aci = ACIModule(module)
aci.construct_url(root_class="aep")
aci.construct_url(
root_class=dict(
aci_class='infraAttEntityP',
aci_rn='infra/attentp-{}'.format(aep),
filter_target='(infraAttEntityP.name, "{}")'.format(aep),
module_object=aep,
),
)
aci.get_existing()
if state == 'present':

View file

@ -120,9 +120,24 @@ def main():
ap = module.params['ap']
description = module.params['description']
state = module.params['state']
tenant = module.params['tenant']
aci = ACIModule(module)
aci.construct_url(root_class='tenant', subclass_1='ap')
aci.construct_url(
root_class=dict(
aci_class='fvTenant',
aci_rn='tn-{}'.format(tenant),
filter_target='(fvTenant.name, "{}")'.format(tenant),
module_object=tenant,
),
subclass_1=dict(
aci_class='fvAp',
aci_rn='ap-{}'.format(ap),
filter_target='(fvAp.name, "{}")'.format(ap),
module_object=ap,
),
)
aci.get_existing()
if state == 'present':

View file

@ -270,6 +270,7 @@ def main():
limit_ip_learn = module.params['limit_ip_learn']
multi_dest = module.params['multi_dest']
state = module.params['state']
tenant = module.params['tenant']
vrf = module.params['vrf']
# Give warning when fvSubnet parameters are passed as those have been moved to the aci_subnet module
@ -278,7 +279,22 @@ def main():
The new modules still supports 'gateway_ip' and 'subnet_mask' along with more features"]
aci = ACIModule(module)
aci.construct_url(root_class="tenant", subclass_1="bd", child_classes=['fvRsCtx', 'fvRsIgmpsn', 'fvRsBDToNdP', 'fvRsBdToEpRet'])
aci.construct_url(
root_class=dict(
aci_class='fvTenant',
aci_rn='tn-{}'.format(tenant),
filter_target='(fvTenant.name, "{}")'.format(tenant),
module_object=tenant,
),
subclass_1=dict(
aci_class='fvBD',
aci_rn='BD-{}'.format(bd),
filter_target='(fvBD.name, "{}")'.format(bd),
module_object=bd,
),
child_classes=['fvRsCtx', 'fvRsIgmpsn', 'fvRsBDToNdP', 'fvRsBdToEpRet'],
)
aci.get_existing()
if state == 'present':

View file

@ -71,20 +71,26 @@ options:
- The L3 Out that contains the assocated Route Profile.
scope:
description:
- Determines if scope of the Subnet.
- The private option only allows communication with hosts in the same VRF.
- The public option allows the Subnet to be advertised outside of the ACI Fabric, and allows communication with
- Determines the scope of the Subnet.
- The C(private) option only allows communication with hosts in the same VRF.
- The C(public) option allows the Subnet to be advertised outside of the ACI Fabric, and allows communication with
hosts in other VRFs.
- The shared option limits communication to hosts in either the same VRF or the shared VRF.
- The value is a list of options, C(private) and C(public) are mutually exclusive, but both can be used with C(shared).
- The APIC defaults new Subnets to C(private).
choices: [ private, public, shared ]
choices:
- private
- public
- shared
- [ private, shared ]
- [ public, shared ]
default: private
subnet_control:
description:
- Determines the Subnet's Control State.
- The querier_ip option is used to treat the gateway_ip as an IGMP querier source IP.
- The nd_ra option is used to treate the gateway_ip address as a Neighbor Discovery Router Advertisement Prefix.
- The no_gw option is used to remove default gateway functionality from the gateway address.
- The C(querier_ip) option is used to treat the gateway_ip as an IGMP querier source IP.
- The C(nd_ra) option is used to treate the gateway_ip address as a Neighbor Discovery Router Advertisement Prefix.
- The C(no_gw) option is used to remove default gateway functionality from the gateway address.
- The APIC defaults new Subnets to C(nd_ra).
choices: [ nd_ra, no_gw, querier_ip, unspecified ]
default: nd_ra
@ -98,7 +104,97 @@ options:
aliases: [ tenant_name ]
'''
EXAMPLES = r''' # '''
EXAMPLES = r'''
- name: create a tenant
aci_tenant:
hostname: apic
username: admin
password: SomeSecretPassword
tenant: production
- name: create a bridge domain
aci_bd:
hostname: apic
username: admin
password: SomeSecretPassword
tenant: production
bd: database
- name: create a subnet
aci_bd_subnet:
hostname: apic
username: admin
password: SomeSecretPassword
tenant: production
bd: database
gateway: 10.1.1.1
mask: 24
- name: create a subnet with options
aci_bd_subnet:
hostname: apic
username: admin
password: SomeSecretPassword
tenant: production
bd: database
subnet_name: sql
gateway: 10.1.2.1
mask: 23
description: SQL Servers
scope: public
route_profile_l3_out: corp
route_profile: corp_route_profile
- name: update a subnets scope to private and shared
aci_bd_subnet:
hostname: apic
username: admin
password: SomeSecretPassword
tenant: production
bd: database
gateway: 10.1.1.1
mask: 24
scope: [private, shared]
- name: get all subnets
aci_bd_subnet:
hostname: apic
username: admin
password: SomeSecretPassword
state: query
- name: get all subnets of specific gateway in specified tenant
aci_bd_subnet:
hostname: apic
username: admin
password: SomeSecretPassword
state: query
tenant: production
gateway: 10.1.1.1
mask: 24
- name: get specific subnet
aci_bd_subnet:
hostname: apic
username: admin
password: SomeSecretPassword
state: query
tenant: production
bd: database
gateway: 10.1.1.1
mask: 24
- name: delete a subnet
aci_bd_subnet:
hostname: apic
username: admin
password: SomeSecretPassword
state: absent
tenant: production
bd: database
gateway: 10.1.1.1
mask: 24
'''
RETURN = r''' # '''
@ -122,7 +218,10 @@ def main():
preferred=dict(type='str', choices=['no', 'yes']),
route_profile=dict(type='str'),
route_profile_l3_out=dict(type='str'),
scope=dict(type='str', choices=['private', 'public', 'shared']),
scope=dict(
type='list',
choices=[['private'], ['public'], ['shared'], ['private', 'shared'], ['shared', 'private'], ['public', 'shared'], ['shared', 'public']],
),
subnet_control=dict(type='str', choices=['nd_ra', 'no_gw', 'querier_ip', 'unspecified']),
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
tenant=dict(type='str', aliases=['tenant_name']),
@ -141,32 +240,56 @@ def main():
description = module.params['description']
enable_vip = module.params['enable_vip']
tenant = module.params['tenant']
bd = module.params['bd']
gateway = module.params['gateway']
mask = module.params['mask']
if mask is not None and mask not in range(0, 129):
# TODO: split checkes between IPv4 and IPv6 Addresses
module.fail_json(msg='Valid Subnet Masks are 0 to 32 for IPv4 Addresses and 0 to 128 for IPv6 addresses')
if gateway is not None:
gateway = '{}/{}'.format(gateway, str(mask))
subnet_name = module.params['subnet_name']
nd_prefix_policy = module.params['nd_prefix_policy']
preferred = module.params['preferred']
route_profile = module.params['route_profile']
route_profile_l3_out = module.params['route_profile_l3_out']
scope = module.params['scope']
if scope:
if len(scope) == 1:
scope = scope[0]
elif 'public' in scope:
scope = 'public,shared'
else:
scope = 'private,shared'
state = module.params['state']
subnet_control = module.params['subnet_control']
if subnet_control:
subnet_control = SUBNET_CONTROL_MAPPING[subnet_control]
# Construct gateway_addr and add to module.params for constructing URL
if gateway is not None and mask is not None:
gateway_addr = '{}/{}'.format(gateway, str(mask))
module.params['gateway_addr'] = gateway_addr
aci = ACIModule(module)
aci.construct_url(
root_class='tenant', subclass_1='bd', subclass_2='gateway_addr',
root_class=dict(
aci_class='fvTenant',
aci_rn='tn-{}'.format(tenant),
filter_target='(fvTenant.name, \"{}\")'.format(tenant),
module_object=tenant,
),
subclass_1=dict(
aci_class='fvBD',
aci_rn='BD-{}'.format(bd),
filter_target='(fvBD.name, \"{}\")'.format(bd),
module_object=bd,
),
subclass_2=dict(
aci_class='fvSubnet',
aci_rn='subnet-[{}]'.format(gateway),
filter_target='(fvSubnet.ip, \"{}\")'.format(gateway),
module_object=gateway,
),
child_classes=['fvRsBDSubnetToProfile', 'fvRsNdPfxPol'],
)
aci.get_existing()
if state == 'present':
@ -176,7 +299,7 @@ def main():
class_config=dict(
ctrl=subnet_control,
descr=description,
ip=gateway_addr,
ip=gateway,
name=subnet_name,
preferred=preferred,
scope=scope,
@ -197,9 +320,6 @@ def main():
elif state == 'absent':
aci.delete_config()
# Remove gateway_addr used to form URL from module.params
module.params.pop("gateway_addr", None)
module.exit_json(**aci.result)

View file

@ -79,14 +79,33 @@ def main():
],
)
bd = module.params['bd']
l3out = module.params['l3out']
state = module.params['state']
# Add bd_l3out key to module.params for building the URL
module.params['bd_l3out'] = l3out
tenant = module.params['tenant']
aci = ACIModule(module)
aci.construct_url(root_class='tenant', subclass_1='bd', subclass_2='bd_l3out')
aci.construct_url(
root_class=dict(
aci_class='fvTenant',
aci_rn='tn-{}'.format(tenant),
filter_target='(fvTenant.name, "{}")'.format(tenant),
module_object=tenant,
),
subclass_1=dict(
aci_class='fvBD',
aci_rn='BD-{}'.format(bd),
filter_target='(fvBD.name, "{}")'.format(bd),
module_object=bd,
),
subclass_2=dict(
aci_class='fvRsBDToOut',
aci_rn='rsBDToOut-{}'.format(l3out),
filter_target='(fvRsBDToOut.tnL3extOutName, "{}")'.format(l3out),
module_object=l3out,
),
)
aci.get_existing()
if state == 'present':
@ -105,9 +124,6 @@ def main():
elif state == 'absent':
aci.delete_config()
# Remove bd_l3out key used for URL building from module.params
module.params.pop('bd_l3out')
module.exit_json(**aci.result)

View file

@ -196,7 +196,15 @@ def main():
filename = 'ce2_{0}-{1}'.format(export_policy, snapshot)
aci.construct_url(root_class="import_policy")
aci.construct_url(
root_class=dict(
aci_class='configImportP',
aci_rn='fabric/configimp-{}'.format(import_policy),
filter_target='(configImportP.name, "{}")'.format(import_policy),
module_object=import_policy,
),
)
aci.get_existing()
# Filter out module parameters with null values

View file

@ -147,13 +147,20 @@ def main():
snapshot = module.params['snapshot']
if snapshot is not None and not snapshot.startswith('run-'):
snapshot = 'run-' + snapshot
module.params['snapshot'] = snapshot
state = module.params['state']
aci = ACIModule(module)
if state == 'present':
aci.construct_url(root_class='export_policy')
aci.construct_url(
root_class=dict(
aci_class='configExportP',
aci_rn='fabric/configexp-{}'.format(export_policy),
filter_target='(configExportP.name, "{}")'.format(export_policy),
module_object=export_policy,
),
)
aci.get_existing()
# Filter out module params with null values
@ -176,13 +183,25 @@ def main():
aci.post_config()
else:
# Add snapshot_container to module.params to build URL
# Prefix the proper url to export_policy
if export_policy is not None:
module.params['snapshot_container'] = 'uni/fabric/configexp-{}'.format(module.params['export_policy'])
else:
module.params['snapshot_container'] = None
export_policy = 'uni/fabric/configexp-{}'.format(export_policy)
aci.construct_url(
root_class=dict(
aci_class='configSnapshotCont',
aci_rn='backupst/snapshots-[{}]'.format(export_policy),
filter_target='(configSnapshotCont.name, "{}")'.format(export_policy),
module_object=export_policy,
),
subclass_1=dict(
aci_class='configSnapshot',
aci_rn='snapshot-{}'.format(snapshot),
filter_target='(configSnapshot.name, "{}")'.format(snapshot),
module_object=snapshot,
),
)
aci.construct_url(root_class='snapshot_container', subclass_1='snapshot')
aci.get_existing()
if state == 'absent':
@ -201,9 +220,6 @@ def main():
# Mark Snapshot for Deletion
aci.post_config()
# Remove snapshot used to build URL from module.params
module.params.pop('snapshot_container')
module.exit_json(**aci.result)

View file

@ -123,9 +123,24 @@ def main():
priority = module.params['priority']
dscp = module.params['dscp']
state = module.params['state']
tenant = module.params['tenant']
aci = ACIModule(module)
aci.construct_url(root_class='tenant', subclass_1='contract')
aci.construct_url(
root_class=dict(
aci_class='fvTenant',
aci_rn='tn-{}'.format(tenant),
filter_target='(fvTenant.name, "{}")'.format(tenant),
module_object=tenant,
),
subclass_1=dict(
aci_class='vzBrCP',
aci_rn='brc-{}'.format(contract),
filter_target='(vzBrCP.name, "{}")'.format(contract),
module_object=contract,
),
)
aci.get_existing()
if state == 'present':

View file

@ -136,6 +136,8 @@ RETURN = r'''
from ansible.module_utils.aci import ACIModule, aci_argument_spec
from ansible.module_utils.basic import AnsibleModule
MATCH_MAPPING = dict(all='All', at_least_one='AtleastOne', at_most_one='AtmostOne', none='None')
def main():
argument_spec = aci_argument_spec
@ -167,19 +169,45 @@ def main():
subject = module.params['subject']
priority = module.params['priority']
reverse_filter = module.params['reverse_filter']
contract = module.params['contract']
dscp = module.params['dscp']
description = module.params['description']
filter_name = module.params['filter']
directive = module.params['directive']
consumer_match = module.params['consumer_match']
if consumer_match is not None:
consumer_match = MATCH_MAPPING[consumer_match]
provider_match = module.params['provider_match']
if provider_match is not None:
provider_match = MATCH_MAPPING[provider_match]
state = module.params['state']
tenant = module.params['tenant']
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 M(aci_subject_bind_filter)')
aci = ACIModule(module)
aci.construct_url(root_class='tenant', subclass_1='contract', subclass_2='subject')
aci.construct_url(
root_class=dict(
aci_class='fvTenant',
aci_rn='tn-{}'.format(tenant),
filter_target='(fvTenant.name, "{}")'.format(tenant),
module_object=tenant,
),
subclass_1=dict(
aci_class='vzBrCP',
aci_rn='brc-{}'.format(contract),
filter_target='(vzBrCP.name, "{}")'.format(contract),
module_object=contract,
),
subclass_2=dict(
aci_class='vzSubj',
aci_rn='subj-{}'.format(subject),
filter_target='(vzSubj.name, "{}")'.format(subject),
module_object=subject,
),
)
aci.get_existing()
if state == 'present':

View file

@ -103,11 +103,11 @@ def main():
],
)
# contract = module.params['contract']
contract = module.params['contract']
filter_name = module.params['filter']
log = module.params['log']
# subject = module.params['subject']
# tenant = module.params['tenant']
subject = module.params['subject']
tenant = module.params['tenant']
state = module.params['state']
# Add subject_filter key to modul.params for building the URL
@ -118,7 +118,33 @@ def main():
log = ''
aci = ACIModule(module)
aci.construct_url(root_class='tenant', subclass_1='contract', subclass_2='subject', subclass_3='subject_filter')
aci.construct_url(
root_class=dict(
aci_class='fvTenant',
aci_rn='tn-{}'.format(tenant),
filter_target='(fvTenant.name, "{}")'.format(tenant),
module_object=tenant,
),
subclass_1=dict(
aci_class='vzBrCP',
aci_rn='brc-{}'.format(contract),
filter_target='(vzBrCP.name, "{}")'.format(contract),
module_object=contract,
),
subclass_2=dict(
aci_class='vzSubj',
aci_rn='subj-{}'.format(subject),
filter_target='(vzSubj.name, "{}")'.format(subject),
module_object=subject,
),
subclass_3=dict(
aci_class='vzRsSubjFiltAtt',
aci_rn='rssubjFiltAtt-{}'.format(filter_name),
filter_target='(vzRsSubjFiltAtt.tnVzFilterName, "{}")'.format(filter_name),
module_object=filter_name,
),
)
aci.get_existing()
if state == 'present':

View file

@ -193,9 +193,32 @@ def main():
intra_epg_isolation = module.params['intra_epg_isolation']
fwd_control = module.params['fwd_control']
state = module.params['state']
tenant = module.params['tenant']
ap = module.params['ap']
aci = ACIModule(module)
aci.construct_url(root_class="tenant", subclass_1="ap", subclass_2="epg", child_classes=['fvRsBd'])
aci.construct_url(
root_class=dict(
aci_class='fvTenant',
aci_rn='tn-{}'.format(tenant),
filter_target='(fvTenant.name, "{}")'.format(tenant),
module_object=tenant,
),
subclass_1=dict(
aci_class='fvAp',
aci_rn='ap-{}'.format(ap),
filter_target='(fvAp.name, "{}")'.format(ap),
module_object=ap,
),
subclass_2=dict(
aci_class='fvAEPg',
aci_rn='epg-{}'.format(epg),
filter_target='(fvAEPg.name, "{}")'.format(epg),
module_object=epg,
),
child_classes=['fvRsBd'],
)
aci.get_existing()
if state == 'present':

View file

@ -93,9 +93,24 @@ def main():
monitoring_policy = module.params['monitoring_policy']
description = module.params['description']
state = module.params['state']
tenant = module.params['tenant']
aci = ACIModule(module)
aci.construct_url(root_class='tenant', subclass_1='monitoring_policy')
aci.construct_url(
root_class=dict(
aci_class='fvTenant',
aci_rn='tn-{}'.format(tenant),
filter_target='(fvTenant.name, "{}")'.format(tenant),
module_object=tenant,
),
subclass_1=dict(
aci_class='monEPGPol',
aci_rn='monepg-{}'.format(monitoring_policy),
filter_target='(monEPGPol.name, "{}")'.format(monitoring_policy),
module_object=monitoring_policy,
),
)
aci.get_existing()
if state == 'present':

View file

@ -79,7 +79,7 @@ RETURN = r''' # '''
from ansible.module_utils.aci import ACIModule, aci_argument_spec
from ansible.module_utils.basic import AnsibleModule
ACI_CLASS_MAPPING = {"consumer": "fvRsCons", "provider": "fvRsProv"}
ACI_CLASS_MAPPING = {"consumer": {"class": "fvRsCons", "rn": "rscons-"}, "provider": {"class": "fvRsProv", "rn": "rsprov-"}}
PROVIDER_MATCH_MAPPING = {"all": "All", "at_least_one": "AtleastOne", "at_most_one": "AtmostOne", "none": "None"}
@ -106,22 +106,51 @@ def main():
],
)
ap = module.params['ap']
contract = module.params['contract']
contract_type = module.params['contract_type']
aci_class = ACI_CLASS_MAPPING[contract_type]
epg = module.params['epg']
priority = module.params['priority']
provider_match = module.params['provider_match']
if provider_match is not None:
provider_match = PROVIDER_MATCH_MAPPING[provider_match]
state = module.params['state']
tenant = module.params['tenant']
aci_class = ACI_CLASS_MAPPING[contract_type]["class"]
aci_rn = ACI_CLASS_MAPPING[contract_type]["rn"]
if contract_type == "consumer" and provider_match is not None:
module.fail_json(msg="the 'provider_match' is only configurable for Provided Contracts")
# Construct contract_class key and add to module.params for building URL
contract_class = 'epg_' + contract_type
module.params[contract_class] = contract
aci = ACIModule(module)
aci.construct_url(root_class='tenant', subclass_1='ap', subclass_2='epg', subclass_3=contract_class)
aci.construct_url(
root_class=dict(
aci_class='fvTenant',
aci_rn='tn-{}'.format(tenant),
filter_target='(fvTenant.name, "{}")'.format(tenant),
module_object=tenant,
),
subclass_1=dict(
aci_class='fvAp',
aci_rn='ap-{}'.format(ap),
filter_target='(fvAp.name, "{}")'.format(ap),
module_object=ap,
),
subclass_2=dict(
aci_class='fvAEPg',
aci_rn='epg-{}'.format(epg),
filter_target='(fvAEPg.name, "{}")'.format(epg),
module_object=epg,
),
subclass_3=dict(
aci_class=aci_class,
aci_rn='{}{}'.format(aci_rn, contract),
filter_target='({}.tnVzBrCPName, "{}'.format(aci_class, contract),
module_object=contract,
),
)
aci.get_existing()
if state == 'present':
@ -144,9 +173,6 @@ def main():
elif state == 'absent':
aci.delete_config()
# Remove contract_class that is used to build URL from module.params
module.params.pop(contract_class)
module.exit_json(**aci.result)

View file

@ -143,6 +143,7 @@ def main():
)
allow_useg = module.params['allow_useg']
ap = module.params['ap']
deploy_immediacy = module.params['deploy_immediacy']
domain = module.params['domain']
domain_type = module.params['domain_type']
@ -154,6 +155,7 @@ def main():
else:
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']
primary_encap = module.params['primary_encap']
if primary_encap is not None:
@ -163,18 +165,47 @@ def main():
module.fail_json(msg='Valid VLAN assigments are from 1 to 4096')
resolution_immediacy = module.params['resolution_immediacy']
state = module.params['state']
tenant = module.params['tenant']
if domain_type == 'phys' and vm_provider is not None:
module.fail_json(msg="Domain type 'phys' cannot have a 'vm_provider'")
# Compile the full domain and add it to module.params for URL building
# Compile the full domain for URL building
if domain_type == 'vmm':
module.params["epg_domain"] = VM_PROVIDER_MAPPING[vm_provider] + domain
epg_domain = '{}{}'.format(VM_PROVIDER_MAPPING[vm_provider], domain)
elif domain_type is not None:
module.params["epg_domain"] = 'uni/phys-' + domain
epg_domain = 'uni/phys-{}'.format(domain)
else:
epg_domain = None
aci = ACIModule(module)
aci.construct_url(root_class="tenant", subclass_1="ap", subclass_2="epg", subclass_3="epg_domain")
aci.construct_url(
root_class=dict(
aci_class='fvTenant',
aci_rn='tn-{}'.format(tenant),
filter_target='(fvTenant.name, "{}")'.format(tenant),
module_object=tenant,
),
subclass_1=dict(
aci_class='fvAp',
aci_rn='ap-{}'.format(ap),
filter_target='(fvAp.name, "{}")'.format(ap),
module_object=ap,
),
subclass_2=dict(
aci_class='fvAEPg',
aci_rn='epg-{}'.format(epg),
filter_target='(fvTenant.name, "{}")'.format(epg),
module_object=epg,
),
subclass_3=dict(
aci_class='fvRsDomAtt',
aci_rn='rsdomAtt-[{}]'.format(epg_domain),
filter_target='(fvRsDomAtt.tDn, "{}")'.format(epg_domain),
module_object=epg_domain,
),
)
aci.get_existing()
if state == 'present':
@ -201,9 +232,6 @@ def main():
elif state == 'absent':
aci.delete_config()
# Pop the epg_domain key that was added for URL building
module.params.pop("epg_domain")
module.exit_json(**aci.result)

View file

@ -121,9 +121,24 @@ def main():
filter_name = module.params['filter']
description = module.params['description']
state = module.params['state']
tenant = module.params['tenant']
aci = ACIModule(module)
aci.construct_url(root_class="tenant", subclass_1="filter")
aci.construct_url(
root_class=dict(
aci_class='fvTenant',
aci_rn='tn-{}'.format(tenant),
filter_target='(fvTenant.name, "{}")'.format(tenant),
module_object=tenant,
),
subclass_1=dict(
aci_class='vzFilter',
aci_rn='flt-{}'.format(filter_name),
filter_target='(vzFilter.name, "{}")'.format(filter_name),
module_object=filter_name,
),
)
aci.get_existing()
if state == 'present':

View file

@ -184,6 +184,7 @@ def main():
dst_start = FILTER_PORT_MAPPING[dst_start]
entry = module.params['entry']
ether_type = module.params['ether_type']
filter_name = module.params['filter']
icmp_msg_type = module.params['icmp_msg_type']
if icmp_msg_type is not None:
icmp_msg_type = ICMP_MAPPING[icmp_msg_type]
@ -193,6 +194,7 @@ def main():
ip_protocol = module.params['ip_protocol']
state = module.params['state']
stateful = module.params['stateful']
tenant = module.params['tenant']
# validate that dst_port is not passed with dst_start or dst_end
if dst_port is not None and (dst_end is not None or dst_start is not None):
@ -202,7 +204,27 @@ def main():
dst_start = dst_port
aci = ACIModule(module)
aci.construct_url(root_class='tenant', subclass_1='filter', subclass_2='entry')
aci.construct_url(
root_class=dict(
aci_class='fvTenant',
aci_rn='tn-{}'.format(tenant),
filter_target='(fvTenant.name, "{}")'.format(tenant),
module_object=tenant,
),
subclass_1=dict(
aci_class='vzFilter',
aci_rn='flt-{}'.format(filter_name),
filter_target='(vzFilter.name, "{}")'.format(filter_name),
module_object=filter_name,
),
subclass_2=dict(
aci_class='vzEntry',
aci_rn='e-{}'.format(entry),
filter_target='(vzEntry.name, "{}")'.format(entry),
module_object=entry
),
)
aci.get_existing()
if state == 'present':

View file

@ -93,7 +93,15 @@ def main():
state = module.params['state']
aci = ACIModule(module)
aci.construct_url(root_class='fc_policy')
aci.construct_url(
root_class=dict(
aci_class='fcIfPol',
aci_rn='infra/fcIfPol-{}'.format(fc_policy),
filter_target='(fcIfPol.name, "{}")'.format(fc_policy),
module_object=fc_policy,
),
)
aci.get_existing()
if state == 'present':

View file

@ -111,7 +111,15 @@ def main():
state = module.params['state']
aci = ACIModule(module)
aci.construct_url(root_class='l2_policy')
aci.construct_url(
root_class=dict(
aci_class='l2IfPol',
aci_rn='infra/l2IfP-{}'.format(l2_policy),
filter_target='(l2IfPol.name, "{}")'.format(l2_policy),
module_object=l2_policy,
),
)
aci.get_existing()
if state == 'present':

View file

@ -102,7 +102,15 @@ def main():
state = module.params['state']
aci = ACIModule(module)
aci.construct_url(root_class='lldp_policy')
aci.construct_url(
root_class=dict(
aci_class='lldpIfPol',
aci_rn='infra/lldpIfP-{}'.format(lldp_policy),
filter_target='(lldpIfPol.name, "{}")'.format(lldp_policy),
module_object=lldp_policy,
),
)
aci.get_existing()
if state == 'present':

View file

@ -93,7 +93,15 @@ def main():
state = module.params['state']
aci = ACIModule(module)
aci.construct_url(root_class='mcp')
aci.construct_url(
root_class=dict(
aci_class='mcpIfPol',
aci_rn='infra/mcpIfP-{}'.format(mcp),
filter_target='(mcpIfPol.name, "{}")'.format(mcp),
module_object=mcp,
),
)
aci.get_existing()
if state == 'present':

View file

@ -178,7 +178,15 @@ def main():
ctrl = ",".join(ctrl)
aci = ACIModule(module)
aci.construct_url(root_class='port_channel')
aci.construct_url(
root_class=dict(
aci_class='lacpLagPol',
aci_rn='infra/lacplagp-{}'.format(port_channel),
filter_target='(lacpLagPol.name, "{}")'.format(port_channel),
module_object=port_channel,
),
)
aci.get_existing()
if state == 'present':

View file

@ -94,7 +94,15 @@ def main():
state = module.params['state']
aci = ACIModule(module)
aci.construct_url(root_class='port_security')
aci.construct_url(
root_class=dict(
aci_class='l2PortSecurityPol',
aci_rn='infra/portsecurityP-{}'.format(port_security),
filter_target='(l2PortSecurityPol.name, "{}")'.format(port_security),
module_object=port_security,
),
)
aci.get_existing()
if state == 'present':

View file

@ -100,9 +100,24 @@ def main():
description = module.params['description']
tag = module.params['tag']
state = module.params['state']
tenant = module.params['tenant']
aci = ACIModule(module)
aci.construct_url(root_class='tenant', subclass_1='rtp')
aci.construct_url(
root_class=dict(
aci_class='fvTenant',
aci_rn='tn-{}'.format(tenant),
filter_target='(fvTenant.name, "{}")'.format(tenant),
module_object=tenant,
),
subclass_1=dict(
aci_class='l3extRouteTagPol',
aci_rn='rttag-{}'.format(rtp),
filter_target='(l3extRouteTagPol.name, "{}")'.format(rtp),
module_object=rtp,
),
)
aci.get_existing()
if state == 'present':

View file

@ -101,9 +101,24 @@ def main():
description = module.params['description']
scope = module.params['scope']
state = module.params['state']
tenant = module.params['tenant']
aci = ACIModule(module)
aci.construct_url(root_class='tenant', subclass_1='taboo_contract')
aci.construct_url(
root_class=dict(
aci_class='fvTenant',
aci_rn='tn-{}'.format(tenant),
filter_target='(fvTenant.name, "{}")'.format(tenant),
module_object=tenant,
),
subclass_1=dict(
aci_class='vzTaboo',
aci_rn='taboo-{}'.format(taboo_contract),
filter_target='(vzTaboo.name, "{}")'.format(taboo_contract),
module_object=taboo_contract,
),
)
aci.get_existing()
if state == 'present':

View file

@ -109,7 +109,14 @@ def main():
state = module.params['state']
aci = ACIModule(module)
aci.construct_url(root_class="tenant")
aci.construct_url(
root_class=dict(
aci_class='fvTenant',
aci_rn='tn-{}'.format(tenant),
filter_target='(fvTenant.name, "{}")'.format(tenant),
module_object=tenant,
),
)
aci.get_existing()
if state == 'present':

View file

@ -91,9 +91,24 @@ def main():
action_rule = module.params['action_rule']
description = module.params['description']
state = module.params['state']
tenant = module.params['tenant']
aci = ACIModule(module)
aci.construct_url(root_class='tenant', subclass_1='action_rule')
aci.construct_url(
root_class=dict(
aci_class='fvTenant',
aci_rn='tn-{}'.format(tenant),
filter_target='(fvTenant.name, "{}")'.format(tenant),
module_object=tenant,
),
subclass_1=dict(
aci_class='rtctrlAttrP',
aci_rn='attr-{}'.format(action_rule),
filter_target='(rtctrlAttrP.name, "{}")'.format(action_rule),
module_object=action_rule,
),
)
aci.get_existing()
if state == 'present':

View file

@ -185,9 +185,24 @@ def main():
if remote_ep_interval == 0:
remote_ep_interval = "infinite"
state = module.params['state']
tenant = module.params['tenant']
aci = ACIModule(module)
aci.construct_url(root_class='tenant', subclass_1='epr_policy')
aci.construct_url(
root_class=dict(
aci_class='fvTenant',
aci_rn='tn-{}'.format(tenant),
filter_target='(fvTenant.name, "{}")'.format(tenant),
module_object=tenant,
),
subclass_1=dict(
aci_class='fvEpRetPol',
aci_rn='epRPol-{}'.format(epr_policy),
filter_target='(fvEpRetPol.name, "{}")'.format(epr_policy),
module_object=epr_policy,
),
)
aci.get_existing()
if state == 'present':

View file

@ -93,12 +93,24 @@ def main():
dst_group = module.params['dst_group']
description = module.params['description']
state = module.params['state']
# Add tenant_span_dst_grp to module.params for URL building
module.params['tenant_span_dst_grp'] = dst_group
tenant = module.params['tenant']
aci = ACIModule(module)
aci.construct_url(root_class='tenant', subclass_1='tenant_span_dst_grp')
aci.construct_url(
root_class=dict(
aci_class='fvTenant',
aci_rn='tn-{}'.format(tenant),
filter_target='(fvTenant.name, "{}")'.format(tenant),
module_object=tenant,
),
subclass_1=dict(
aci_class='spanDestGrp',
aci_rn='destgrp-{}'.format(dst_group),
filter_target='(spanDestGrp.name, "{}")'.format(dst_group),
module_object=dst_group,
),
)
aci.get_existing()
if state == 'present':
@ -120,9 +132,6 @@ def main():
elif state == 'absent':
aci.delete_config()
# Remove tenant_span_dst_grp that was used to build URL from module.params
module.params.pop('tenant_span_dst_grp')
module.exit_json(**aci.result)

View file

@ -104,12 +104,25 @@ def main():
dst_group = module.params['dst_group']
src_group = module.params['src_group']
state = module.params['state']
# Add tenant_span_dst_grp to module.params for URL building
module.params['tenant_span_src_grp'] = src_group
tenant = module.params['tenant']
aci = ACIModule(module)
aci.construct_url(root_class='tenant', subclass_1='tenant_span_src_grp', child_classes=['spanSpanLbl'])
aci.construct_url(
root_class=dict(
aci_class='fvTenant',
aci_rn='tn-{}'.format(tenant),
filter_target='(fvTenant.name, "{}")'.format(tenant),
module_object=tenant,
),
subclass_1=dict(
aci_class='spanSrcGrp',
aci_rn='srcgrp-{}'.format(src_group),
filter_target='(spanSrcGrp.name, "{}")'.format(src_group),
module_object=src_group,
),
child_classes=['spanSpanLbl'],
)
aci.get_existing()
if state == 'present':
@ -133,9 +146,6 @@ def main():
elif state == 'absent':
aci.delete_config()
# Remove tenant_span_src_grp that was used to build URL from module.params
module.params.pop('tenant_span_src_grp')
module.exit_json(**aci.result)

View file

@ -95,13 +95,30 @@ def main():
dst_group = module.params['dst_group']
src_group = module.params['src_group']
state = module.params['state']
# Add tenant_span_src_grp and tenant_span_src_grp_dst_grp to module.params for URL building
module.params['tenant_span_src_grp'] = src_group
module.params['tenant_span_src_grp_dst_grp'] = dst_group
tenant = module.params['tenant']
aci = ACIModule(module)
aci.construct_url(root_class='tenant', subclass_1='tenant_span_src_grp', subclass_2='tenant_span_src_grp_dst_grp')
aci.construct_url(
root_class=dict(
aci_class='fvTenant',
aci_rn='tn-{}'.format(tenant),
filter_target='(fvTenant.name, "{}")'.format(tenant),
module_object=tenant,
),
subclass_1=dict(
aci_class='spanSrcGrp',
aci_rn='srcgrp-{}'.format(src_group),
filter_target='(spanSrcGrp.name, "{}")'.format(src_group),
module_object=src_group,
),
subclass_2=dict(
aci_class='spanSpanLbl',
aci_rn='spanlbl-{}'.format(dst_group),
filter_target='(spanSpanLbl.name, "{}")'.format(dst_group),
module_object=dst_group,
),
)
aci.get_existing()
if state == 'present':
@ -123,10 +140,6 @@ def main():
elif state == 'absent':
aci.delete_config()
# Remove tenant_span_src_grp and tenant_span_src_grp_dst_grp that was used to build URL from module.params
module.params.pop('tenant_span_src_grp')
module.params.pop('tenant_span_src_grp_dst_grp')
module.exit_json(**aci.result)

View file

@ -128,10 +128,24 @@ def main():
policy_control_direction = module.params['policy_control_direction']
policy_control_preference = module.params['policy_control_preference']
state = module.params['state']
tenant = module.params['tenant']
vrf = module.params['vrf']
aci = ACIModule(module)
aci.construct_url(root_class="tenant", subclass_1="vrf")
aci.construct_url(
root_class=dict(
aci_class='fvTenant',
aci_rn='tn-{}'.format(tenant),
filter_target='(fvTenant.name, "{}")'.format(tenant),
module_object=tenant,
),
subclass_1=dict(
aci_class='fvCtx',
aci_rn='ctx-{}'.format(vrf),
filter_target='(fvCtx.name, "{}")'.format(vrf),
module_object=vrf,
),
)
aci.get_existing()
if state == 'present':