Skip to content

Addition of L4l7 device modules (DCNE-334) #739

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
May 23, 2025
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
5fd5812
[minor_change] Addition of module aci_l4l7_device and its test file
timcragg Apr 10, 2025
c57b3e3
[minor_change] Addition of module aci_l4l7_concrete_device and its te…
timcragg Apr 10, 2025
15983a0
[ignore] Removed aci_ip_sla_monitoring_policy module and its test file
timcragg Apr 10, 2025
19da4e0
[ignore] Addition of concrete device modules and their test files
timcragg Apr 10, 2025
e1e3b45
[ignore] Modified description of attributes in concrete device modules
timcragg Apr 11, 2025
434f044
[ignore] Modified aci_l4l7_device modules to add appropriate aliases
shrsr Apr 22, 2025
ef3845b
[ignore] Added accidentally removed test file
shrsr Apr 22, 2025
7909d0b
[ignore] Modified description in aci_l4l7_concrete_interface_attach
shrsr Apr 22, 2025
72b9cdc
[ignore] Modified required_if in aci_l4l7_device
shrsr Apr 22, 2025
967d062
[ignore] Modified the title in modules examples of aci_l4l7_concrete_…
shrsr Apr 24, 2025
23e123d
[ignore] Addition of plugin routing in runtime.yml to redirect module…
shrsr Apr 29, 2025
a040efa
[ignore] Addition of empty line in runtime.yml
shrsr Apr 29, 2025
86981de
[ignore] Changes to plugin routing in runtime.yml to redirect modules…
shrsr Apr 29, 2025
881fbf0
[ignore] Changes to plugin routing in runtime.yml to redirect modules…
shrsr May 1, 2025
9a70b06
[ignore] Made changes to aliases of aci_l4l7 modules
shrsr May 7, 2025
0bd3e88
[ignore] Applied black formatting to aci_l4l7_concrete_interface_atta…
shrsr May 7, 2025
614c89f
[ignore] Added namespace and collection to see also note in docs of l…
shrsr May 13, 2025
7223fe7
[ignore] Changed description of docs in aci_l4l7_device
shrsr May 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
294 changes: 294 additions & 0 deletions plugins/modules/aci_l4l7_concrete_device.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright: (c) 2025, Tim Cragg (@timcragg)
# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import absolute_import, division, print_function

__metaclass__ = type

ANSIBLE_METADATA = {"metadata_version": "1.1", "status": ["preview"], "supported_by": "community"}

DOCUMENTATION = r"""
---
module: aci_l4l7_concrete_device
short_description: Manage L4-L7 Concrete Devices (vns:CDev)
description:
- Manage Layer 4 to Layer 7 (L4-L7) Concrete Devices.
options:
tenant:
description:
- The name of an existing tenant.
type: str
aliases: [ tenant_name ]
device:
description:
- The name of the logical device (vns:lDevVip) the concrete device is attached to.
type: str
aliases: [ device_name, logical_device_name ]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it not better to call it logical_device and add device as an alias? It would be less confusing since we have logical device and concrete device but this is just a suggestion

Suggested change
device:
description:
- The name of the logical device (vns:lDevVip) the concrete device is attached to.
type: str
aliases: [ device_name, logical_device_name ]
logical_device:
description:
- The name of the logical device (vns:lDevVip) the concrete device is attached to.
type: str
aliases: [ device, device_name, logical_device_name ]

If you do make the change, it should be applied to every module where logical device is referenced

name:
description:
- The name of the concrete device.
type: str
aliases: [ concrete_device, concrete_device_name ]
vcenter_name:
description:
- The name of the vCenter hosting the L4-L7 device.
type: str
vm_name:
description:
- The VM name within the vCenter for the L4-L7 device.
type: str
state:
description:
- Use C(present) or C(absent) for adding or removing.
- Use C(query) for listing an object or multiple objects.
type: str
choices: [ absent, present, query ]
default: present
extends_documentation_fragment:
- cisco.aci.aci
- cisco.aci.annotation

notes:
- The I(tenant) and I(device) must exist before using this module in your playbook.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this use O for option instead.

Suggested change
- The I(tenant) and I(device) must exist before using this module in your playbook.
- The O(tenant) and O(logical_device) must exist before using this module in your playbook.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment was already made by @gmicol in a related PR. In ansible ACI collection we've been using I() for Input in the other modules. I believe that this usage should be consistent within this collection.

The M(cisco.aci.aci_tenant) and M(cisco.aci.aci_l4l7_device) modules can be used for this.
seealso:
- module: aci_l4l7_device
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we reference the module for the tenant here as well?

Suggested change
seealso:
- module: aci_l4l7_device
seealso:
- module: aci_tenant
- module: aci_l4l7_device

If so it should be applied it to every module where it was missed

- name: APIC Management Information Model reference
description: More information about the internal APIC class B(vns:CDev)
link: https://developer.cisco.com/docs/apic-mim-ref/
author:
- Tim Cragg (@timcragg)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

your name in modules

"""

EXAMPLES = r"""
- name: Add a new concrete device
cisco.aci.aci_l4l7_concrete_device:
host: apic
username: admin
password: SomeSecretPassword
tenant: my_tenant
device: my_device
concrete_device: my_concrete_device
state: present
delegate_to: localhost

- name: Query a concrete device
cisco.aci.aci_l4l7_concrete_device:
host: apic
username: admin
password: SomeSecretPassword
tenant: my_tenant
device: my_device
concrete_device: my_concrete_device
state: query
delegate_to: localhost
register: query_result

- name: Query all concrete devices
cisco.aci.aci_l4l7_concrete_device:
host: apic
username: admin
password: SomeSecretPassword
state: query
delegate_to: localhost
register: query_result

- name: Delete a concrete device
cisco.aci.aci_l4l7_concrete_device:
host: apic
username: admin
password: SomeSecretPassword
tenant: my_tenant
device: my_device
concrete_device: my_concrete_device
state: absent
delegate_to: localhost
"""

RETURN = r"""
current:
description: The existing configuration from the APIC after the module has finished
returned: success
type: list
sample:
[
{
"fvTenant": {
"attributes": {
"descr": "Production environment",
"dn": "uni/tn-production",
"name": "production",
"nameAlias": "",
"ownerKey": "",
"ownerTag": ""
}
}
}
]
error:
description: The error information as returned from the APIC
returned: failure
type: dict
sample:
{
"code": "122",
"text": "unknown managed object class foo"
}
raw:
description: The raw output returned by the APIC REST API (xml or json)
returned: parse error
type: str
sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>'
sent:
description: The actual/minimal configuration pushed to the APIC
returned: info
type: list
sample:
{
"fvTenant": {
"attributes": {
"descr": "Production environment"
}
}
}
previous:
description: The original configuration from the APIC before the module has started
returned: info
type: list
sample:
[
{
"fvTenant": {
"attributes": {
"descr": "Production",
"dn": "uni/tn-production",
"name": "production",
"nameAlias": "",
"ownerKey": "",
"ownerTag": ""
}
}
}
]
proposed:
description: The assembled configuration from the user-provided parameters
returned: info
type: dict
sample:
{
"fvTenant": {
"attributes": {
"descr": "Production environment",
"name": "production"
}
}
}
filter_string:
description: The filter string used for the request
returned: failure or debug
type: str
sample: ?rsp-prop-include=config-only
method:
description: The HTTP method used for the request to the APIC
returned: failure or debug
type: str
sample: POST
response:
description: The HTTP response from the APIC
returned: failure or debug
type: str
sample: OK (30 bytes)
status:
description: The HTTP status from the APIC
returned: failure or debug
type: int
sample: 200
url:
description: The HTTP url used for the request to the APIC
returned: failure or debug
type: str
sample: https://10.11.12.13/api/mo/uni/tn-production.json
"""


from ansible.module_utils.basic import AnsibleModule
from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec


def main():
argument_spec = aci_argument_spec()
argument_spec.update(aci_annotation_spec())
argument_spec.update(
tenant=dict(type="str", aliases=["tenant_name"]),
device=dict(type="str", aliases=["device_name", "logical_device_name"]),
name=dict(type="str", aliases=["concrete_device", "concrete_device_name"]),
vcenter_name=dict(type="str"),
vm_name=dict(type="str"),
state=dict(type="str", default="present", choices=["absent", "present", "query"]),
)

module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True,
required_if=[
["state", "absent", ["tenant", "device", "name"]],
["state", "present", ["tenant", "device", "name"]],
],
)

tenant = module.params.get("tenant")
state = module.params.get("state")
device = module.params.get("device")
name = module.params.get("name")
vcenter_name = module.params.get("vcenter_name")
vm_name = module.params.get("vm_name")

aci = ACIModule(module)

aci.construct_url(
root_class=dict(
aci_class="fvTenant",
aci_rn="tn-{0}".format(tenant),
module_object=tenant,
target_filter={"name": tenant},
),
subclass_1=dict(
aci_class="vnsLDevVip",
aci_rn="lDevVip-{0}".format(device),
module_object=device,
target_filter={"name": device},
),
subclass_2=dict(
aci_class="vnsCDev",
aci_rn="cDev-{0}".format(name),
module_object=name,
target_filter={"name": name},
),
)

aci.get_existing()

if state == "present":
aci.payload(
aci_class="vnsCDev",
class_config=dict(
name=name,
vcenterName=vcenter_name,
vmName=vm_name,
),
)
aci.get_diff(aci_class="vnsCDev")

aci.post_config()

elif state == "absent":
aci.delete_config()

aci.exit_json()


if __name__ == "__main__":
main()
Loading