Skip to content

Commit

Permalink
[ignore] Added support to query using uuid
Browse files Browse the repository at this point in the history
  • Loading branch information
sajagana authored and lhercot committed Oct 3, 2024
1 parent 42125b6 commit 528f4b2
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 43 deletions.
39 changes: 9 additions & 30 deletions plugins/module_utils/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
__metaclass__ = type

from ansible_collections.cisco.mso.plugins.module_utils.constants import TEMPLATE_TYPES
from ansible_collections.cisco.mso.plugins.module_utils.utils import generate_api_endpoint
from collections import namedtuple

KVPair = namedtuple("KVPair", "key value")
Expand Down Expand Up @@ -144,16 +145,12 @@ def get_vlan_pool_name(self, vlan_pool_uuid):
def get_route_map(self, attr_name, tenant_id, tenant_name, route_map, route_map_objects):
"""
Retrieves the details of a specific route map object based on the provided attributes.
Args:
attr_name: The attribute name for error messaging. -> Str
tenant_id: The ID of the tenant. -> Str
tenant_name: The name of the tenant. -> Str
route_map: The name of the route map. -> Str
route_map_objects: The list of route map objects to search from. -> List
Returns:
The details of the route map object if found, otherwise an empty dictionary. -> Dict
:param attr_name: The attribute name for error messaging. -> Str
:param tenant_id: The ID of the tenant. -> Str
:param tenant_name: The name of the tenant. -> Str
:param route_map: The name of the route map. -> Str
:param route_map_objects: The list of route map objects to search from. -> List
:return: The details of the route map object if found, otherwise an empty dictionary. -> Dict
"""
if route_map and tenant_id and route_map_objects:
route_map_object = self.get_object_from_list(
Expand All @@ -175,8 +172,8 @@ def get_vrf_object(self, vrf_dict, tenant_id, templates_objects_path):
:param templates_objects_path: Path to the templates objects. -> Str
:return: VRF object if found, otherwise fail with an error message. -> Dict
"""
vrf_params = {"type": "vrf", "tenant-id": tenant_id, "include-common": "true"}
vrf_path = self.generate_api_endpoint(templates_objects_path, **vrf_params)

vrf_path = generate_api_endpoint(templates_objects_path, **{"type": "vrf", "tenant-id": tenant_id, "include-common": "true"})
vrf_objects = self.mso.query_objs(vrf_path)
vrf_kv_list = [
KVPair("name", vrf_dict.get("name")),
Expand All @@ -191,21 +188,3 @@ def get_vrf_object(self, vrf_dict, tenant_id, templates_objects_path):
return vrf_object[0]
else:
self.mso.fail_json(msg="Provided VRF {0} not found.".format(vrf_dict.get("name")))

@staticmethod
def generate_api_endpoint(path, **kwargs):
"""
Generates an API endpoint with query strings based on the provided keyword arguments.
:param path: The base URL of the API endpoint. -> Str
:param kwargs: Keyword arguments representing query parameters. -> Dict
:return: A string representing the full API endpoint with query parameters. -> Str
"""
if not kwargs:
return path

query_strings = ["{0}={1}".format(key, value) for key, value in kwargs.items()]
query_string = "&".join(query_strings)
full_url = "{0}?{1}".format(path, query_string)

return full_url
28 changes: 28 additions & 0 deletions plugins/module_utils/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-

# Copyright: (c) 2024, Sabari Jaganathan (@sajagana) <sajagana@cisco.com>
# 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


def generate_api_endpoint(path, **kwargs):
"""
Generates an API endpoint with query strings based on the provided keyword arguments.
:param path: The base URL of the API endpoint. -> Str
:param kwargs: Keyword arguments representing query parameters. -> Dict
:return: A string representing the full API endpoint with query parameters. -> Str
"""
# if not kwargs:
# return path

# query_strings = ["{0}={1}".format(key, value) for key, value in kwargs.items()]
# query_string = "&".join(query_strings)
# full_url = "{0}?{1}".format(path, query_string)

# return full_url

return path if not kwargs else "{0}?{1}".format(path, "&".join(["{0}={1}".format(key, value) for key, value in kwargs.items()]))
37 changes: 30 additions & 7 deletions plugins/modules/ndo_l3out_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,16 @@
state: disabled
state: "present"
- name: Query a L3Out object with uuid
cisco.mso.ndo_l3out_template:
host: mso_host
username: admin
password: SomeSecretPassword
l3out_template: l3out_template
uuid: "{{ query_l3out_name.current.uuid }}"
state: "query"
register: query_l3out_name
- name: Query all L3Out objects
cisco.mso.ndo_l3out_template:
host: mso_host
Expand All @@ -374,6 +384,15 @@
l3out_template: l3out_template
name: "l3out_1"
state: "absent"
- name: Delete a L3Out object with uuid
cisco.mso.ndo_l3out_template:
host: mso_host
username: admin
password: SomeSecretPassword
l3out_template: l3out_template
uuid: "{{ query_l3out_name.current.uuid }}"
state: "absent"
"""

RETURN = r"""
Expand All @@ -384,6 +403,7 @@
from ansible_collections.cisco.mso.plugins.module_utils.mso import MSOModule, mso_argument_spec
from ansible_collections.cisco.mso.plugins.module_utils.template import MSOTemplate, KVPair
from ansible_collections.cisco.mso.plugins.module_utils.constants import TARGET_DSCP_MAP, ORIGINATE_DEFAULT_ROUTE, L3OUT_ROUTING_PROTOCOLS
from ansible_collections.cisco.mso.plugins.module_utils.utils import generate_api_endpoint


def get_routing_protocol(existing_protocol, ospf_state, bgp_state):
Expand Down Expand Up @@ -454,8 +474,8 @@ def main():
argument_spec=argument_spec,
supports_check_mode=True,
required_if=[
["state", "absent", ["name"]],
["state", "present", ["name"]],
["state", "absent", ["name", "uuid"], True],
["state", "present", ["name", "uuid"], True],
],
)

Expand Down Expand Up @@ -490,7 +510,7 @@ def main():
mso.exit_json()
elif state == "query" and not (name or uuid):
mso.existing = l3outs
elif l3outs and name:
elif l3outs and (name or uuid):
object_description = "L3Out"

if uuid:
Expand All @@ -505,18 +525,21 @@ def main():
ops = []

if state == "present":
if uuid and not mso.existing:
mso.fail_json(msg="L3Out with the uuid: '{0}' not found".format(uuid))

templates_objects_path = "templates/objects"
route_map_params = {"type": "routeMap", "tenant-id": tenant_id}
route_map_path = l3out_template_object.generate_api_endpoint(templates_objects_path, **route_map_params)
route_map_path = generate_api_endpoint(templates_objects_path, **route_map_params)
route_map_objects = mso.query_objs(route_map_path)

vrf_ref = None
if vrf_dict:
vrf_object = l3out_template_object.get_vrf_object(vrf_dict, tenant_id, templates_objects_path)
if pim and vrf_object.details.get("l3MCast") is False:
mso.fail_json(
msg="Invalid configuration in L3Out '{0}', 'PIM' cannot be enabled while using the VRF '{1}' with L3 Multicast disabled".format(
name, vrf_dict.get("name")
msg="Invalid configuration in L3Out {0}, 'PIM' cannot be enabled while using the VRF '{1}' with L3 Multicast disabled".format(
"UUID: {0}".format(uuid) if uuid else "Name: {0}".format(name), vrf_dict.get("name")
)
)
vrf_ref = vrf_object.details.get("uuid")
Expand Down Expand Up @@ -671,7 +694,7 @@ def main():
proposed_payload = copy.deepcopy(match.details)
l3out_attrs_path = "/l3outTemplate/l3outs/{0}".format(match.index)

if name is not None and mso.existing.get("name") != name:
if name is not [None, ""] and mso.existing.get("name") != name:
ops.append(dict(op="replace", path=l3out_attrs_path + "/name", value=name))
proposed_payload["name"] = name

Expand Down
88 changes: 82 additions & 6 deletions tests/integration/targets/ndo_l3out_template/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1386,6 +1386,30 @@
- update_l3out_3_bgp_min_config.previous.uuid is defined
- update_l3out_3_bgp_min_config.previous.vrfRef != ""

- name: Query a L3Out with name
cisco.mso.ndo_l3out_template:
<<: *mso_info
l3out_template: '{{ ansible_l3out_template | default("ansible_test") }}'
name: "l3out_2"
state: query
register: query_l3out_with_name

- name: Query a L3Out with uuid
cisco.mso.ndo_l3out_template:
<<: *mso_info
l3out_template: '{{ ansible_l3out_template | default("ansible_test") }}'
uuid: "{{ query_l3out_with_name.current.uuid }}"
state: query
register: query_l3out_with_uuid

- name: Assertion check for query l3out with name and uuid
ansible.builtin.assert:
that:
- query_l3out_with_name is not changed
- query_l3out_with_uuid is not changed
- query_l3out_with_name.current.name == query_l3out_with_uuid.current.name == "l3out_2"
- query_l3out_with_name.current.uuid == query_l3out_with_uuid.current.uuid

- name: Delete l3out_2 object - check_mode
cisco.mso.ndo_l3out_template: &cm_l3out_2_absent
<<: *mso_info
Expand Down Expand Up @@ -1428,6 +1452,27 @@
- nm_l3out_2_absent_again.previous == {}

# Negative test part begins
- name: Update l3out_3 object name using invalid uuid
cisco.mso.ndo_l3out_template:
<<: *mso_info
l3out_template: '{{ ansible_l3out_template | default("ansible_test") }}'
name: "l3out_3_nt"
uuid: "invalid_uuid"
l3_domain: ans_l3_domain
vrf:
name: "VRF1"
schema: '{{ ansible_schema | default("ansible_test") }}'
template: "Template1"
state: "present"
register: update_with_invalid_uuid
ignore_errors: true

- name: Assertion check for update l3out_3 object name using invalid uuid
ansible.builtin.assert:
that:
- update_with_invalid_uuid is not changed
- update_with_invalid_uuid.msg == "L3Out with the uuid{{':'}} 'invalid_uuid' not found"

- name: Update l3out_3 object without ospf - id and type
cisco.mso.ndo_l3out_template:
<<: *mso_info
Expand Down Expand Up @@ -1469,9 +1514,28 @@
ansible.builtin.assert:
that:
- enable_pim_with_invalid_vrf is not changed
- enable_pim_with_invalid_vrf.current == {}
- enable_pim_with_invalid_vrf.msg == "Invalid configuration in L3Out 'l3out_nt', 'PIM' cannot be enabled while using the VRF 'VRF1' with L3 Multicast disabled"
- enable_pim_with_invalid_vrf.previous == {}
- enable_pim_with_invalid_vrf.msg == "Invalid configuration in L3Out Name{{':'}} l3out_nt, 'PIM' cannot be enabled while using the VRF 'VRF1' with L3 Multicast disabled"

- name: Update l3out_3 object to enable with L3 Multicast disabled vrf
cisco.mso.ndo_l3out_template:
<<: *mso_info
l3out_template: '{{ ansible_l3out_template | default("ansible_test") }}'
uuid: "{{ update_l3out_3_bgp_min_config.current.uuid }}"
l3_domain: ans_l3_domain
vrf:
name: "VRF1"
schema: '{{ ansible_schema | default("ansible_test") }}'
template: "Template1"
pim: true
state: "present"
register: update_l3out_3_pim_nt
ignore_errors: true

- name: Assertion check for update l3out_3 object to enable with L3 Multicast disabled vrf
ansible.builtin.assert:
that:
- update_l3out_3_pim_nt is not changed
- update_l3out_3_pim_nt.msg is match("Invalid configuration in L3Out UUID.+, 'PIM' cannot be enabled while using the VRF 'VRF1' with L3 Multicast disabled")

- name: Create L3Out object without ospf area_id
cisco.mso.ndo_l3out_template:
Expand Down Expand Up @@ -1509,10 +1573,7 @@
ansible.builtin.assert:
that:
- create_without_vrf is not changed
- create_without_vrf.current == {}
- create_without_vrf.msg == "The O(vrf) is required during the creation."
- create_without_vrf.previous == {}

# Negative test part ends

- name: Query all L3Out objects
Expand All @@ -1528,6 +1589,21 @@
- query_all_l3outs is not changed
- query_all_l3outs.current | length == 2

- name: Delete L3Out using uuid
cisco.mso.ndo_l3out_template:
<<: *mso_info
l3out_template: '{{ ansible_l3out_template | default("ansible_test") }}'
uuid: "{{ update_l3out_3_bgp_min_config.current.uuid }}"
state: "absent"
register: absent_with_uuid

- name: Assertion check for delete L3Out using uuid
ansible.builtin.assert:
that:
- absent_with_uuid is changed
- absent_with_uuid.current == {}
- absent_with_uuid.previous.name == "l3out_3"

# Cleanup Part
- name: Remove l3out tenant template
cisco.mso.ndo_template:
Expand Down

0 comments on commit 528f4b2

Please sign in to comment.