From 1b3f9a60681a60ad90bcf9bfe73afb5834b466dc Mon Sep 17 00:00:00 2001 From: Shiva Shankar Vaddepally Date: Tue, 14 Oct 2025 13:42:01 +0000 Subject: [PATCH 1/9] NSNETAUTO-1005 supporting dynamic routing protocols Signed-off-by: Shiva Shankar Vaddepally --- Makefile | 28 +-- examples/accesslist.yaml | 14 ++ examples/bfdinterface.yaml | 13 + examples/iproute.yaml | 12 + examples/location.yaml | 13 +- meta/runtime.yml | 3 + plugins/module_utils/client.py | 10 +- plugins/module_utils/common.py | 47 +++- plugins/module_utils/constants.py | 19 ++ plugins/module_utils/module_executor.py | 40 ++-- plugins/module_utils/nitro_resource_map.py | 265 +++++++++++++++++++++ plugins/modules/accesslist.py | 122 ++++++++++ plugins/modules/bfdinterface.py | 123 ++++++++++ plugins/modules/iproute.py | 145 +++++++++++ tests/sanity/ignore-2.15.txt | 5 +- tests/sanity/ignore-2.16.txt | 5 +- tests/sanity/ignore-2.17.txt | 5 +- 17 files changed, 820 insertions(+), 49 deletions(-) create mode 100644 examples/accesslist.yaml create mode 100644 examples/bfdinterface.yaml create mode 100644 examples/iproute.yaml create mode 100644 plugins/modules/accesslist.py create mode 100644 plugins/modules/bfdinterface.py create mode 100644 plugins/modules/iproute.py diff --git a/Makefile b/Makefile index 6b996b5c8..0ad34bae2 100644 --- a/Makefile +++ b/Makefile @@ -1,20 +1,20 @@ fmt: - autoflake plugins/modules/*.py - autoflake plugins/module_utils/*.py - autoflake --recursive tests/ - autoflake tools/migrationtool/*py + autoflake plugins/modules/*.py + autoflake plugins/module_utils/*.py + autoflake --recursive tests/ + autoflake tools/migrationtool/*.py - black plugins/modules/*.py - black plugins/module_utils/*.py - black tests/ - black tools/migrationtool/*.py + black plugins/modules/*.py + black plugins/module_utils/*.py + black tests/ + black tools/migrationtool/*.py - isort plugins/modules/*.py - isort plugins/module_utils/*.py - isort tests/ - isort tools/migrationtool/*.py + isort plugins/modules/*.py + isort plugins/module_utils/*.py + isort tests/ + isort tools/migrationtool/*.py - yamlfmt $(shell find . -name '*.yml' -o -name '*.yaml') + yamlfmt $(shell find . -name '*.yml' -o -name '*.yaml') install: ansible-galaxy collection install . --force @@ -63,7 +63,7 @@ galaxy_importer: build # skip the playbook which contains "password" in the file name run_examples: @for playbook in examples/*.yaml; do \ - if [[ $$playbook == *"password"* || $$playbook == *"login"* || $$playbook == *"logout"* || $$playbook == *"route"* || $$playbook == locationfile.yaml || $$playbook == nsip6.yaml || $$playbook == hanode.yaml ]]; then \ + if [[ $$playbook == *"password"* || $$playbook == *"login"* || $$playbook == *"logout"* || $$playbook == *"route"* || $$playbook == *"locationfile.yaml"* || $$playbook == *"nsip6.yaml"* || $$playbook == *"hanode.yaml"* ]]; then \ continue; \ fi; \ echo "Running $$playbook"; \ diff --git a/examples/accesslist.yaml b/examples/accesslist.yaml new file mode 100644 index 000000000..97de23996 --- /dev/null +++ b/examples/accesslist.yaml @@ -0,0 +1,14 @@ +--- +- name: Configure accessslist + hosts: localhost + tasks: + - name: Create accesslist + delegate_to: localhost + netscaler.adc.accesslist: + state: present + id: 1 + remark: "Allow all traffic" + rules: + - action: permit + address: 2.1.1.21 + wildcard: "0.0.0.255" diff --git a/examples/bfdinterface.yaml b/examples/bfdinterface.yaml new file mode 100644 index 000000000..f6577fbb3 --- /dev/null +++ b/examples/bfdinterface.yaml @@ -0,0 +1,13 @@ +--- +- name: Configure bfdinterface + hosts: localhost + tasks: + - name: Create bfdinterface + delegate_to: localhost + netscaler.adc.bfdinterface: + state: present + name: vlan0 + passive: true + interval: 752 + minrx: 501 + multiplier: 3 diff --git a/examples/iproute.yaml b/examples/iproute.yaml new file mode 100644 index 000000000..19a449778 --- /dev/null +++ b/examples/iproute.yaml @@ -0,0 +1,12 @@ +--- +- name: Configure bfdinterface + hosts: localhost + tasks: + - name: Create bfdinterface + delegate_to: localhost + netscaler.adc.iproute: + state: present + addressFamily: ipv4 + prefix: 33.1.1.0 + prefixLength: 24 + nextHop: 10.106.210.24 diff --git a/examples/location.yaml b/examples/location.yaml index 48ad13e86..1b6f289d9 100644 --- a/examples/location.yaml +++ b/examples/location.yaml @@ -1,12 +1,17 @@ --- - name: Sample location playbook - hosts: demo_netscalers + hosts: localhost gather_facts: false tasks: - name: Configure location delegate_to: localhost netscaler.adc.location: state: present - ipfrom: 1.1.1.1 - ipto: 2.2.2.2 - preferredlocation: '*.US.*' + nsip: 10.106.210.21 + nitro_user: nsroot + nitro_pass: notnsroot + nitro_protocol: http + validate_certs: false + ipfrom: "170.173.217.0" + ipto: "170.173.222.255" + preferredlocation: "prov.WA.sdc.*.*.*" diff --git a/meta/runtime.yml b/meta/runtime.yml index 73be80cff..3e2df3da2 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -981,3 +981,6 @@ action_groups: - sslechconfig.py - sslhpkekey.py - sslprofile_sslechconfig_binding.py + - accesslist + - bfdinterface + - iproute diff --git a/plugins/module_utils/client.py b/plugins/module_utils/client.py index 4038f0b32..39a9c05aa 100644 --- a/plugins/module_utils/client.py +++ b/plugins/module_utils/client.py @@ -15,7 +15,11 @@ from ansible.module_utils.six.moves.urllib.parse import quote from ansible.module_utils.urls import fetch_url -from .constants import HTTP_SUCCESS_CODES +from .constants import ( + DYNAMIC_PROTOCOLS, + DYNAMIC_PROTOCOLS_ALIAS, + HTTP_SUCCESS_CODES, +) from .decorators import trace from .logger import log @@ -98,6 +102,9 @@ def url_builder( filter = filter if filter is not None else {} # Construct basic URL + if resource in DYNAMIC_PROTOCOLS: + resource = "routerDynamicRouting/" + DYNAMIC_PROTOCOLS_ALIAS[resource] + url = "%s://%s/%s/%s" % ( self._module.params["nitro_protocol"], self._module.params["nsip"], @@ -235,6 +242,7 @@ def post(self, post_data, resource, action=None): def put(self, put_data, resource=None, id=None): url = self.url_builder(resource, id=id) data = self._module.jsonify(put_data) + return self.send("PUT", url, data) @trace diff --git a/plugins/module_utils/common.py b/plugins/module_utils/common.py index 7312f652c..03d7b1ce0 100644 --- a/plugins/module_utils/common.py +++ b/plugins/module_utils/common.py @@ -10,6 +10,8 @@ import re from .constants import ( + DYNAMIC_PROTOCOLS, + DYNAMIC_PROTOCOLS_ALIAS, GLOBAL_BINDING_ARG_LIST, HTTP_RESOURCE_ALREADY_EXISTS, HTTP_RESOURCE_NOT_FOUND, @@ -94,16 +96,29 @@ def get_resource(client, resource_name, resource_id=None, resource_module_params args=get_args, ) else: - status_code, response_body = client.get( - resource=resource_name, - id=resource_id, - args=get_args, - ) + if resource_name in DYNAMIC_PROTOCOLS: + new_resource_name = ( + "routerDynamicRouting/" + DYNAMIC_PROTOCOLS_ALIAS[resource_name] + ) + status_code, response_body = client.get( + resource=new_resource_name, + id=resource_id, + args=get_args, + ) + else: + status_code, response_body = client.get( + resource=resource_name, + id=resource_id, + args=get_args, + ) if status_code in {HTTP_RESOURCE_NOT_FOUND}: return False, [] if status_code in HTTP_SUCCESS_CODES: # for zero bindings and some resources, the response_body will be {'errorcode': 0, 'message': 'Done', 'severity': 'NONE'} - if resource_name not in response_body: + if ( + resource_name not in response_body + and resource_name not in DYNAMIC_PROTOCOLS + ): if resource_name == "sslcipher": resource_primary_key = NITRO_RESOURCE_MAP[resource_name]["primary_key"] return True, [ @@ -112,7 +127,12 @@ def get_resource(client, resource_name, resource_id=None, resource_module_params return False, [] # `update-only` resources return a dict instead of a list. - return_response = response_body[resource_name] + if resource_name in DYNAMIC_PROTOCOLS: + return_response = response_body.get("routerDynamicRouting", {}).get( + DYNAMIC_PROTOCOLS_ALIAS[resource_name], {} + ) + else: + return_response = response_body[resource_name] # FIXME: NITRO-BUG: for some resources like `policypatset_pattern_binding`, NITRO returns keys with uppercase. eg: `String` for `string`. # So, we are converting the keys to lowercase. # except for `ping` and `traceroute`, all the othe resources returns a keys with lowercase. @@ -242,7 +262,6 @@ def _check_create_resource_params(resource_name, resource_module_params, action= key, resource_name, action.upper() ) ) - return True, None, post_data @@ -281,7 +300,11 @@ def create_resource(client, resource_name, resource_module_params, action=None): if not ok: return False, err - post_data = {resource_name: post_data} + if resource_name in DYNAMIC_PROTOCOLS: + post_data = {DYNAMIC_PROTOCOLS_ALIAS[resource_name]: post_data} + post_data = {"routerDynamicRouting": post_data} + else: + post_data = {resource_name: post_data} status_code, response_body = client.post( post_data=post_data, resource=resource_name, @@ -355,7 +378,11 @@ def update_resource(client, resource_name, resource_module_params): if not ok: return False, err - put_data = {resource_name: put_payload} + if resource_name in DYNAMIC_PROTOCOLS: + put_data = {DYNAMIC_PROTOCOLS_ALIAS[resource_name]: put_payload} + put_data = {"routerDynamicRouting": put_data} + else: + put_data = {resource_name: put_payload} status_code, response_body = client.put( put_data=put_data, diff --git a/plugins/module_utils/constants.py b/plugins/module_utils/constants.py index ce019cb89..9aacf5c15 100644 --- a/plugins/module_utils/constants.py +++ b/plugins/module_utils/constants.py @@ -151,3 +151,22 @@ "sslkeyfile", "systementitydata", ] + +# Dynamic Protocol list +DYNAMIC_PROTOCOLS = [ + "accesslist", + "bfdinterface", + "iproute", + "ospf6interface", +] + +DYNAMIC_PROTOCOLS_ALIAS = { + "accesslist": "accessList", + "accessList": "accesslist", + "bfdinterface": "bfdInterface", + "bfdInterface": "bfdinterface", + "iproute": "ipRoute", + "ipRoute": "iproute", + "ospf6interface": "ospf6Interface", + "ospf6Interface": "ospf6interface", +} diff --git a/plugins/module_utils/module_executor.py b/plugins/module_utils/module_executor.py index 48e36ea5e..f1f7b915a 100644 --- a/plugins/module_utils/module_executor.py +++ b/plugins/module_utils/module_executor.py @@ -500,7 +500,7 @@ def create_or_update(self): errorcode = None message = None err_str = str(err) - regex_string = re.search(r'status_code:\s*(\d+)', err_str) + regex_string = re.search(r"status_code:\s*(\d+)", err_str) if regex_string: status_code = int(regex_string.group(1)) regex_string = re.search(r"'errorcode':\s*(\d+)", err_str) @@ -510,9 +510,9 @@ def create_or_update(self): if regex_string: message = regex_string.group(1) if not ( - status_code == 599 and - errorcode == 1065 and - message == "Internal error while adding HSM key." + status_code == 599 + and errorcode == 1065 + and message == "Internal error while adding HSM key." ): self.return_failure(err) @@ -580,15 +580,21 @@ def create_or_update(self): "INFO: Resource %s:%s exists and is different. Will be REMOVED and ADDED." % (self.resource_name, self.resource_id) ) - if self.resource_name == "systemfile": - # If the systemfile is present, we will delete it and add it again - self.delete() - ok, err = create_resource( - self.client, self.resource_name, self.resource_module_params - ) - if not ok: - self.return_failure(err) - + # If the systemfile is present, we will delete it and add it again + self.delete() + ok, err = create_resource( + self.client, self.resource_name, self.resource_module_params + ) + if not ok: + self.return_failure(err) + elif self.resource_name == "location": + # TODO: primary composite key needs to be added. + # location resource has composite primary key. 1.ipfrom 2.ipto + ok, err = create_resource( + self.client, self.resource_name, self.resource_module_params + ) + if not ok: + self.return_failure(err) elif self.resource_name.endswith("_binding"): # Generally bindings are not updated. They are removed and added again. log( @@ -707,7 +713,7 @@ def delete(self): errorcode = None message = None err_str = str(err) - regex_string = re.search(r'status_code:\s*(\d+)', err_str) + regex_string = re.search(r"status_code:\s*(\d+)", err_str) if regex_string: status_code = int(regex_string.group(1)) regex_string = re.search(r"'errorcode':\s*(\d+)", err_str) @@ -717,9 +723,9 @@ def delete(self): if regex_string: message = regex_string.group(1) if not ( - status_code == 599 and - errorcode == 1065 and - message == "Internal error while adding HSM key." + status_code == 599 + and errorcode == 1065 + and message == "Internal error while adding HSM key." ): self.return_failure(err) else: diff --git a/plugins/module_utils/nitro_resource_map.py b/plugins/module_utils/nitro_resource_map.py index 0024ae8a4..7e03e0c19 100644 --- a/plugins/module_utils/nitro_resource_map.py +++ b/plugins/module_utils/nitro_resource_map.py @@ -2351,6 +2351,45 @@ "singleton": False, "update_payload_keys": [], }, + "accesslist": { + "_supported_operations": ["get", "unset", "update", "delete", "add"], + "action_payload_keys": { + "apply": [], + "create": [], + "force": [], + "import": [], + "link": [], + "switch": [], + "unlink": [], + "unset": [], + }, + "add_payload_keys": ["id", "remark", "rules"], + "bindings": [], + "bindprimary_key": "", + "delete_arg_keys": ["id"], + "disable_payload_keys": [], + "enable_payload_keys": [], + "get_arg_keys": ["id"], + "immutable_keys": [], + "password_keys": [], + "primary_key": "", + "primary_key_composite": [], + "readwrite_arguments": { + "id": {"no_log": False, "type": "str"}, + "remark": {"no_log": False, "type": "str"}, + "rules": { + "type": "list", + "elements": "dict", + "options": { + "action": {"no_log": False, "type": "str"}, + "address": {"no_log": False, "type": "str"}, + "wildcard": {"no_log": False, "type": "str"}, + }, + }, + }, + "singleton": True, + "update_payload_keys": ["rules"], + }, "admparameter": { "_supported_operations": ["get", "unset", "update"], "action_payload_keys": { @@ -16532,6 +16571,39 @@ "singleton": False, "update_payload_keys": [], }, + "bfdinterface": { + "_supported_operations": ["get", "add", "unset"], + "action_payload_keys": { + "apply": [], + "create": [], + "force": [], + "import": [], + "link": [], + "switch": [], + "unlink": [], + "unset": ["name", "passive"], + }, + "add_payload_keys": ["interval", "minrx", "name", "multiplier", "passive"], + "bindings": [], + "bindprimary_key": "", + "delete_arg_keys": [], + "disable_payload_keys": [], + "enable_payload_keys": [], + "get_arg_keys": [], + "immutable_keys": [], + "password_keys": [], + "primary_key": "", + "primary_key_composite": [], + "update_payload_keys": ["name", "interval", "minrx", "multiplier", "passive"], + "readwrite_arguments": { + "interval": {"no_log": False, "type": "int"}, + "minrx": {"no_log": False, "type": "int"}, + "name": {"no_log": False, "type": "str"}, + "multiplier": {"no_log": False, "type": "int"}, + "passive": {"no_log": False, "type": "bool"}, + }, + "singleton": False, + }, "botglobal_botpolicy_binding": { "_supported_operations": ["add", "count", "delete", "get"], "action_payload_keys": { @@ -31157,6 +31229,89 @@ "useclientsourceipv6", ], }, + "iproute": { + "_supported_operations": [ + "add", + "delete", + "get", + ], + "action_payload_keys": { + "apply": [], + "create": [], + "force": [], + "import": [], + "link": [], + "switch": [], + "unlink": [], + "unset": [], + }, + "add_payload_keys": [ + "ipRoute", + "addressFamily", + "distance", + "interface", + "nextHop", + "prefix", + "prefixLength", + ], + "bindings": [], + "bindprimary_key": "", + "delete_arg_keys": [ + "prefix", + "addressFamily", + "nextHop", + "interface", + "prefixLength", + ], + "disable_payload_keys": [], + "enable_payload_keys": [], + "get_arg_keys": ["prefixLength", "prefix", "addressFamily"], + "immutable_keys": [], + "password_keys": [], + "primary_key": "", + "primary_key_composite": [], + "readwrite_arguments": { + "addressFamily": { + "no_log": False, + "type": "str", + "choices": ["ipv4", "ipv6"], + }, + "distance": { + "no_log": False, + "type": "int", + }, + "interface": { + "no_log": False, + "type": "str", + }, + "isBest": { + "no_log": False, + "type": "bool", + }, + "metric": { + "no_log": False, + "type": "str", + }, + "nextHop": { + "no_log": False, + "type": "str", + }, + "prefix": { + "no_log": False, + "type": "str", + }, + "prefixLength": { + "no_log": False, + "type": "int", + }, + "type": { + "no_log": False, + "type": "str", + }, + }, + "singleton": False, + "update_payload_keys": [], + }, "ipsecalgprofile": { "_supported_operations": [ "add", @@ -46585,6 +46740,116 @@ "singleton": True, "update_payload_keys": [], }, + "ospf6interface": { + "_supported_operations": [ + "add", + "get", + "unset", + "update", + ], + "action_payload_keys": { + "apply": [], + "create": [], + "force": [], + "import": [], + "link": [], + "switch": [], + "unlink": [], + "unset": [ + "areaId", + "cost", + "deadInterval", + "helloInterval", + "instanceId", + "name", + "networkType", + "priority", + "retransmitInterval", + "tagId", + "transmitDelay", + ], + }, + "add_payload_keys": [], + "bindings": [], + "bindprimary_key": "", + "delete_arg_keys": [], + "disable_payload_keys": [], + "enable_payload_keys": [], + "get_arg_keys": ["name"], + "immutable_keys": [], + "password_keys": [], + "primary_key": "", + "primary_key_composite": [], + "readwrite_arguments": { + "areaId": { + "no_log": False, + "type": "int", + "description": "OSPFv3 area ID (0-4294967295)", + }, + "cost": { + "no_log": False, + "type": "int", + "description": "Interface cost (1-65535)", + }, + "deadInterval": { + "no_log": False, + "type": "int", + "description": "Neighbor dead interval (1-65535)", + }, + "helloInterval": { + "no_log": False, + "type": "int", + "description": "HELLO packet interval (1-65535)", + }, + "instanceId": { + "no_log": False, + "type": "int", + "description": "Instance ID (0-255)", + }, + "name": {"no_log": False, "type": "str", "description": "Interface name"}, + "networkType": { + "no_log": False, + "type": "str", + "choices": [ + "broadcast", + "non-broadcast", + "point-to-multipoint", + "point-to-point", + ], + "description": "Network type", + }, + "priority": { + "no_log": False, + "type": "int", + "description": "Router priority (0-255)", + }, + "retransmitInterval": { + "no_log": False, + "type": "int", + "description": "Retransmit interval (1-65535)", + }, + "tagId": {"no_log": False, "type": "int", "description": "OSPFv3 Tag"}, + "transmitDelay": { + "no_log": False, + "type": "int", + "description": "Transmit delay (1-65535)", + }, + }, + "singleton": False, + "update_payload_keys": [ + "areaId", + "cost", + "deadInterval", + "helloInterval", + "instanceId", + "name", + "networkType", + "priority", + "retransmitInterval", + "tagId", + "transmitDelay", + ], + }, "onlinkipv6prefix": { "_supported_operations": [ "add", diff --git a/plugins/modules/accesslist.py b/plugins/modules/accesslist.py new file mode 100644 index 000000000..aeeba00ef --- /dev/null +++ b/plugins/modules/accesslist.py @@ -0,0 +1,122 @@ +#!/usr/bin/python + +# -*- coding: utf-8 -*- + +# Copyright (c) 2025 Cloud Software Group, Inc. +# MIT License (see LICENSE or https://opensource.org/licenses/MIT) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "community", +} +DOCUMENTATION = r""" +module: accesslist +short_description: Manage accesslist configuration on Citrix ADC (NetScaler) devices +description: + - Manage Accesslist configuration on Citrix ADC (NetScaler) devices. +version_added: 2.10.0 +author: + - Shiva Shankar Vaddepally (@shivashankar-vaddepally) +options: + state: + type: str + choices: ["absent", "present", "unset"] + default: present + description: + - The state of the resource on the NetScaler ADC node. + - When C(present), the resource will be added or updated. + - When C(absent), the resource will be deleted. + - When C(unset), the resource will be unset. + id: + type: str + description: + - Standard access list number in the range <0-99> or a ZebOS access-list name. + + remark: + type: str + description: + - Access list entry comment. + + rules: + type: list + elements: dict + description: + - List of rule parameters for the access list entry. + suboptions: + action: + type: str + description: + - Allow or deny if traffic matches the rule. + address: + type: str + description: + - Address to match. + wildcard: + type: str + description: + - Wildcard mask to apply to the address. + + remove_non_updatable_params: + type: str + choices: ["yes", "no"] + default: "no" + description: + - Remove non-updatable parameters from the configuration. + +extends_documentation_fragment: netscaler.adc.netscaler_adc +""" + +EXAMPLES = r""" +""" +RETURN = r""" +--- +changed: + description: Indicates if any change is made by the module + returned: always + type: bool + sample: true +diff: + description: Dictionary of before and after changes + returned: always + type: dict + sample: {'before': {'key1': 'xyz'}, 'after': {'key2': 'pqr'}, 'prepared': 'changes + done'} +diff_list: + description: List of differences between the actual configured object and the configuration + specified in the module + returned: when changed + type: list + sample: ["Attribute `key1` differs. Desired: () XYZ. Existing: () PQR"] +failed: + description: Indicates if the module failed or not + returned: always + type: bool + sample: false +loglines: + description: list of logged messages by the module + returned: always + type: list + sample: ['message 1', 'message 2'] + +""" +import os + +from ..module_utils.module_executor import ModuleExecutor + +RESOURCE_NAME = os.path.basename(__file__).replace(".py", "") + + +def main(): + executor = ModuleExecutor(RESOURCE_NAME) + executor.main() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/bfdinterface.py b/plugins/modules/bfdinterface.py new file mode 100644 index 000000000..01a512498 --- /dev/null +++ b/plugins/modules/bfdinterface.py @@ -0,0 +1,123 @@ +#!/usr/bin/python + +# -*- coding: utf-8 -*- + +# Copyright (c) 2025 Cloud Software Group, Inc. +# MIT License (see LICENSE or https://opensource.org/licenses/MIT) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "community", +} +DOCUMENTATION = r""" +module: bfdinterface +short_description: Manage bfdinterface configuration on Citrix ADC (NetScaler) devices +description: + - Manage bfdInterface configuration on Citrix ADC (NetScaler) devices. +version_added: 2.10.0 +author: + - Shiva Shankar Vaddepally (@shivashankar-vaddepally) +options: + state: + type: str + choices: ["present", "unset"] + default: present + description: + - The state of the resource on the NetScaler ADC node. + - When C(present), the resource will be added or updated. + interval: + type: int + description: + - Transmit interval for BFD packets. + - Minimum value is 100. + - Maximum value is 30000. + + minrx: + type: int + description: + - Minimum receive interval for BFD packets. + - Minimum value is 100. + - Maximum value is 30000. + + multiplier: + type: int + description: + - Multiplier for BFD packets. + - Minimum value is 1. + - Maximum value is 20. + + name: + type: str + description: + - Name of the interface. + + passive: + type: bool + description: + - Make BFD session passive. + - Read-write. + + remove_non_updatable_params: + type: str + choices: ["yes", "no"] + default: "no" + description: + - Remove non-updatable parameters from the configuration. + +extends_documentation_fragment: netscaler.adc.netscaler_adc +""" + +EXAMPLES = r""" +""" +RETURN = r""" +--- +changed: + description: Indicates if any change is made by the module + returned: always + type: bool + sample: true +diff: + description: Dictionary of before and after changes + returned: always + type: dict + sample: {'before': {'key1': 'xyz'}, 'after': {'key2': 'pqr'}, 'prepared': 'changes + done'} +diff_list: + description: List of differences between the actual configured object and the configuration + specified in the module + returned: when changed + type: list + sample: ["Attribute `key1` differs. Desired: () XYZ. Existing: () PQR"] +failed: + description: Indicates if the module failed or not + returned: always + type: bool + sample: false +loglines: + description: list of logged messages by the module + returned: always + type: list + sample: ['message 1', 'message 2'] + +""" +import os + +from ..module_utils.module_executor import ModuleExecutor + +RESOURCE_NAME = os.path.basename(__file__).replace(".py", "") + + +def main(): + executor = ModuleExecutor(RESOURCE_NAME) + executor.main() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/iproute.py b/plugins/modules/iproute.py new file mode 100644 index 000000000..4c7b6e72b --- /dev/null +++ b/plugins/modules/iproute.py @@ -0,0 +1,145 @@ +#!/usr/bin/python + +# -*- coding: utf-8 -*- + +# Copyright (c) 2025 Cloud Software Group, Inc. +# MIT License (see LICENSE or https://opensource.org/licenses/MIT) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "community", +} +DOCUMENTATION = r""" +module: iproute +short_description: Manage iproute configuration on Citrix ADC (NetScaler) devices +description: + - Manage ipRoute configuration on Citrix ADC (NetScaler) devices. +version_added: 2.10.0 +author: + - Shiva Shankar Vaddepally (@shivashankar-vaddepally) +options: + state: + type: str + choices: ["present", "absent"] + default: present + description: + - The state of the resource on the NetScaler ADC node. + - When C(present), the resource will be added or updated. + - When C(absent), the resource will be deleted. + addressFamily: + type: str + choices: + - ipv4 + - ipv6 + description: + - The address family of the route. + - Possible values are ipv4 or ipv6. + + distance: + type: int + description: + - Distance value for this route. + - Minimum value is 1. + - Maximum value is 255. + + interface: + type: str + description: + - IP gateway interface name or pseudo interface Null. + + isBest: + type: bool + description: + - Indicates if this is the best route for the prefix. + + metric: + type: str + description: + - IP destination prefix. + + nextHop: + type: str + description: + - IP gateway address. + + prefix: + type: str + description: + - IP destination prefix. + + prefixLength: + type: int + description: + - IP destination prefix length. + - Minimum value is 0. + - Maximum value is 128. + + type: + type: str + description: + - IP route protocol type. + + remove_non_updatable_params: + type: str + choices: ["yes", "no"] + default: "no" + description: + - Remove non-updatable parameters from the configuration. + +extends_documentation_fragment: netscaler.adc.netscaler_adc +""" + +EXAMPLES = r""" +""" +RETURN = r""" +--- +changed: + description: Indicates if any change is made by the module + returned: always + type: bool + sample: true +diff: + description: Dictionary of before and after changes + returned: always + type: dict + sample: {'before': {'key1': 'xyz'}, 'after': {'key2': 'pqr'}, 'prepared': 'changes + done'} +diff_list: + description: List of differences between the actual configured object and the configuration + specified in the module + returned: when changed + type: list + sample: ["Attribute `key1` differs. Desired: () XYZ. Existing: () PQR"] +failed: + description: Indicates if the module failed or not + returned: always + type: bool + sample: false +loglines: + description: list of logged messages by the module + returned: always + type: list + sample: ['message 1', 'message 2'] + +""" +import os + +from ..module_utils.module_executor import ModuleExecutor + +RESOURCE_NAME = os.path.basename(__file__).replace(".py", "") + + +def main(): + executor = ModuleExecutor(RESOURCE_NAME) + executor.main() + + +if __name__ == "__main__": + main() diff --git a/tests/sanity/ignore-2.15.txt b/tests/sanity/ignore-2.15.txt index cf19e3b41..47e679f6c 100644 --- a/tests/sanity/ignore-2.15.txt +++ b/tests/sanity/ignore-2.15.txt @@ -947,4 +947,7 @@ plugins/modules/nslaslicense.py validate-modules:missing-gplv3-license # We use plugins/modules/sslechconfig.py validate-modules:missing-gplv3-license # We use MIT license plugins/modules/sslhpkekey.py validate-modules:missing-gplv3-license # We use MIT license plugins/modules/sslprofile_sslechconfig_binding.py validate-modules:missing-gplv3-license # We use MIT license -plugins/modules/authenticationprotecteduseraction.py validate-modules:missing-gplv3-license # We use MIT license \ No newline at end of file +plugins/modules/authenticationprotecteduseraction.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/accesslist.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/bfdinterface.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/iproute.py validate-modules:missing-gplv3-license # We use MIT license \ No newline at end of file diff --git a/tests/sanity/ignore-2.16.txt b/tests/sanity/ignore-2.16.txt index cf19e3b41..47e679f6c 100644 --- a/tests/sanity/ignore-2.16.txt +++ b/tests/sanity/ignore-2.16.txt @@ -947,4 +947,7 @@ plugins/modules/nslaslicense.py validate-modules:missing-gplv3-license # We use plugins/modules/sslechconfig.py validate-modules:missing-gplv3-license # We use MIT license plugins/modules/sslhpkekey.py validate-modules:missing-gplv3-license # We use MIT license plugins/modules/sslprofile_sslechconfig_binding.py validate-modules:missing-gplv3-license # We use MIT license -plugins/modules/authenticationprotecteduseraction.py validate-modules:missing-gplv3-license # We use MIT license \ No newline at end of file +plugins/modules/authenticationprotecteduseraction.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/accesslist.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/bfdinterface.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/iproute.py validate-modules:missing-gplv3-license # We use MIT license \ No newline at end of file diff --git a/tests/sanity/ignore-2.17.txt b/tests/sanity/ignore-2.17.txt index cf19e3b41..47e679f6c 100644 --- a/tests/sanity/ignore-2.17.txt +++ b/tests/sanity/ignore-2.17.txt @@ -947,4 +947,7 @@ plugins/modules/nslaslicense.py validate-modules:missing-gplv3-license # We use plugins/modules/sslechconfig.py validate-modules:missing-gplv3-license # We use MIT license plugins/modules/sslhpkekey.py validate-modules:missing-gplv3-license # We use MIT license plugins/modules/sslprofile_sslechconfig_binding.py validate-modules:missing-gplv3-license # We use MIT license -plugins/modules/authenticationprotecteduseraction.py validate-modules:missing-gplv3-license # We use MIT license \ No newline at end of file +plugins/modules/authenticationprotecteduseraction.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/accesslist.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/bfdinterface.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/iproute.py validate-modules:missing-gplv3-license # We use MIT license \ No newline at end of file From e9d98111125634f31c6b0b4415f0b746b69c67f1 Mon Sep 17 00:00:00 2001 From: Shiva Shankar Vaddepally Date: Tue, 14 Oct 2025 17:21:00 +0000 Subject: [PATCH 2/9] supporting ospf6interface, ospf6router, routermap Signed-off-by: Shiva Shankar Vaddepally --- examples/location.yaml | 5 - examples/ospf6interface.yaml | 12 ++ examples/ospf6router.yaml | 13 ++ examples/routemap.yaml | 15 ++ meta/runtime.yml | 3 + plugins/module_utils/constants.py | 6 + plugins/module_utils/module_executor.py | 19 ++- plugins/module_utils/nitro_resource_map.py | 115 +++++++++++++ plugins/modules/ospf6interface.py | 169 +++++++++++++++++++ plugins/modules/ospf6router.py | 145 ++++++++++++++++ plugins/modules/routemap.py | 184 +++++++++++++++++++++ tests/sanity/ignore-2.15.txt | 5 +- tests/sanity/ignore-2.16.txt | 5 +- tests/sanity/ignore-2.17.txt | 5 +- 14 files changed, 692 insertions(+), 9 deletions(-) create mode 100644 examples/ospf6interface.yaml create mode 100644 examples/ospf6router.yaml create mode 100644 examples/routemap.yaml create mode 100644 plugins/modules/ospf6interface.py create mode 100644 plugins/modules/ospf6router.py create mode 100644 plugins/modules/routemap.py diff --git a/examples/location.yaml b/examples/location.yaml index 1b6f289d9..19f02c58d 100644 --- a/examples/location.yaml +++ b/examples/location.yaml @@ -7,11 +7,6 @@ delegate_to: localhost netscaler.adc.location: state: present - nsip: 10.106.210.21 - nitro_user: nsroot - nitro_pass: notnsroot - nitro_protocol: http - validate_certs: false ipfrom: "170.173.217.0" ipto: "170.173.222.255" preferredlocation: "prov.WA.sdc.*.*.*" diff --git a/examples/ospf6interface.yaml b/examples/ospf6interface.yaml new file mode 100644 index 000000000..88ebfc5a2 --- /dev/null +++ b/examples/ospf6interface.yaml @@ -0,0 +1,12 @@ +--- +- name: Configure ospf6interface + hosts: localhost + tasks: + - name: Create ospf6interface + delegate_to: localhost + netscaler.adc.ospf6interface: + state: present + name: vlan0 + areaId: 0 + tagId: "22" + instanceId: 0 diff --git a/examples/ospf6router.yaml b/examples/ospf6router.yaml new file mode 100644 index 000000000..5c2101d4d --- /dev/null +++ b/examples/ospf6router.yaml @@ -0,0 +1,13 @@ +--- +- name: Sample ospf6router playbook + hosts: localhost + gather_facts: false + tasks: + - name: Configure ospf6router + delegate_to: localhost + netscaler.adc.ospf6router: + state: present + tagId: 11 + routerId: 1.1.1.1 + afParams: + - addressFamily: ipv6 diff --git a/examples/routemap.yaml b/examples/routemap.yaml new file mode 100644 index 000000000..fc10413a6 --- /dev/null +++ b/examples/routemap.yaml @@ -0,0 +1,15 @@ +--- +- name: Sample routeMap playbook + hosts: localhost + gather_facts: false + tasks: + - name: Configure routeMap + delegate_to: localhost + netscaler.adc.routemap: + state: present + name: test + rules: + - action: permit + sequence: 10 + setMetric: 300 + matchAsPath: "300" diff --git a/meta/runtime.yml b/meta/runtime.yml index 3e2df3da2..b5b497ae2 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -984,3 +984,6 @@ action_groups: - accesslist - bfdinterface - iproute + - routemap + - ospf6interface + - ospf6router diff --git a/plugins/module_utils/constants.py b/plugins/module_utils/constants.py index 9aacf5c15..ea1c94921 100644 --- a/plugins/module_utils/constants.py +++ b/plugins/module_utils/constants.py @@ -158,6 +158,8 @@ "bfdinterface", "iproute", "ospf6interface", + "routemap", + "ospf6router", ] DYNAMIC_PROTOCOLS_ALIAS = { @@ -169,4 +171,8 @@ "ipRoute": "iproute", "ospf6interface": "ospf6Interface", "ospf6Interface": "ospf6interface", + "routemap": "routeMap", + "routeMap": "routemap", + "ospf6router": "ospf6Router", + "ospf6Router": "ospf6router", } diff --git a/plugins/module_utils/module_executor.py b/plugins/module_utils/module_executor.py index f1f7b915a..c667893d3 100644 --- a/plugins/module_utils/module_executor.py +++ b/plugins/module_utils/module_executor.py @@ -315,7 +315,24 @@ def _filter_resource_module_params(self): # defined in the playbook, it's value will be None. So, filter out those attributes. # Also, filter out attributes ending with `_binding` as they are handled separately if v is not None: - self.resource_module_params[k] = v + if isinstance(v, list): + filtered_list = [] + for listitem in v: + if isinstance(listitem, dict): + filtered_dict = { + key: value + for key, value in listitem.items() + if value is not None + } + if filtered_dict: + filtered_list.append(filtered_dict) + else: + if listitem is not None: + filtered_list.append(listitem) + if filtered_list: + self.resource_module_params[k] = filtered_list + else: + self.resource_module_params[k] = v if self.resource_name in NITRO_ATTRIBUTES_ALIASES: self.resource_module_params = self._add_nitro_attributes_aliases( diff --git a/plugins/module_utils/nitro_resource_map.py b/plugins/module_utils/nitro_resource_map.py index 7e03e0c19..ed1112ee0 100644 --- a/plugins/module_utils/nitro_resource_map.py +++ b/plugins/module_utils/nitro_resource_map.py @@ -46850,6 +46850,65 @@ "transmitDelay", ], }, + "ospf6router": { + "_supported_operations": [ + "add", + "get", + "unset", + "update", + "delete", + ], + "action_payload_keys": { + "apply": [], + "create": [], + "force": [], + "import": [], + "link": [], + "switch": [], + "unlink": [], + "unset": ["afParams", "passiveInterface", "routerId", "tagId"], + }, + "add_payload_keys": ["afParams", "passiveInterface", "routerId", "tagId"], + "bindings": [], + "bindprimary_key": "", + "delete_arg_keys": ["tagId"], + "disable_payload_keys": [], + "enable_payload_keys": [], + "get_arg_keys": ["tagId"], + "immutable_keys": [], + "password_keys": [], + "primary_key": "", + "primary_key_composite": [], + "readwrite_arguments": { + "afParams": { + "type": "list", + "elements": "dict", + "options": { + "addressFamily": {"no_log": False, "type": "str"}, + "redistribute": { + "type": "list", + "elements": "dict", + "options": { + "metric": {"no_log": False, "type": "int"}, + "metricType": {"no_log": False, "type": "int"}, + "protocol": {"no_log": False, "type": "str"}, + "routeMap": {"no_log": False, "type": "str"}, + }, + }, + }, + }, + "passiveInterface": {"no_log": False, "type": "list", "elements": "str"}, + "routerId": {"no_log": False, "type": "str"}, + "tagId": {"no_log": False, "type": "str"}, + }, + "singleton": False, + "update_payload_keys": [ + "afParams", + "passiveInterface", + "routerId", + "tagId", + ], + }, "onlinkipv6prefix": { "_supported_operations": [ "add", @@ -50131,6 +50190,62 @@ "weight", ], }, + "routemap": { + "_supported_operations": [ + "add", + "delete", + "get", + "unset", + "update", + ], + "action_payload_keys": { + "apply": [], + "create": [], + "force": [], + "import": [], + "link": [], + "switch": [], + "unlink": [], + "unset": ["name", "rules"], + }, + "add_payload_keys": ["name", "rules"], + "bindings": [], + "bindprimary_key": "", + "delete_arg_keys": ["name"], + "disable_payload_keys": [], + "enable_payload_keys": [], + "get_arg_keys": ["name"], + "immutable_keys": [], + "password_keys": [], + "primary_key": "", + "primary_key_composite": [], + "readwrite_arguments": { + "name": {"no_log": False, "type": "str"}, + "rules": { + "type": "list", + "elements": "dict", + "options": { + "action": {"no_log": False, "type": "str"}, + "localPreference": {"no_log": False, "type": "int"}, + "matchAsPath": {"no_log": False, "type": "str"}, + "matchCommunity": {"no_log": False, "type": "str"}, + "matchIpAddress": {"no_log": False, "type": "str"}, + "matchIpNextHop": {"no_log": False, "type": "str"}, + "matchMetric": {"no_log": False, "type": "int"}, + "matchRouteType": {"no_log": False, "type": "str"}, + "sequence": {"no_log": False, "type": "int"}, + "setAsPath": {"no_log": False, "type": "str"}, + "setCommunity": {"no_log": False, "type": "str"}, + "setIpNextHop": {"no_log": False, "type": "str"}, + "setMetric": {"no_log": False, "type": "int"}, + "setMetricType": {"no_log": False, "type": "str"}, + "weight": {"no_log": False, "type": "int"}, + }, + }, + }, + "singleton": False, + "update_payload_keys": ["rules"], + }, "routerdynamicrouting": { "_supported_operations": [ "add", diff --git a/plugins/modules/ospf6interface.py b/plugins/modules/ospf6interface.py new file mode 100644 index 000000000..f3106aab1 --- /dev/null +++ b/plugins/modules/ospf6interface.py @@ -0,0 +1,169 @@ +#!/usr/bin/python + +# -*- coding: utf-8 -*- + +# Copyright (c) 2025 Cloud Software Group, Inc. +# MIT License (see LICENSE or https://opensource.org/licenses/MIT) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "community", +} +DOCUMENTATION = r""" +module: ospf6interface +short_description: Manage ospf6interface configuration on Citrix ADC (NetScaler) devices +description: + - Manage ospf6interface configuration on Citrix ADC (NetScaler) devices. +version_added: 2.10.0 +author: + - Shiva Shankar Vaddepally (@shivashankar-vaddepally) +options: + state: + type: str + choices: ["present", "absent"] + default: present + description: + - The state of the resource on the NetScaler ADC node. + - When C(present), the resource will be added or updated. + - When C(absent), the resource will be deleted. + + areaId: + type: int + description: + - Area on which OSPFv3 is running. + - Minimum value: 0 + - Maximum value: 4294967295 + + cost: + type: int + description: + - Interface cost. + - Minimum value: 1 + - Maximum value: 65535 + + deadInterval: + type: int + description: + - Interval after which a neighbor is declared dead. + - Minimum value: 1 + - Maximum value: 65535 + + helloInterval: + type: int + description: + - Time between HELLO packets. + - Minimum value: 1 + - Maximum value: 65535 + + instanceId: + type: int + description: + - Interface Instance Id - <0-31> for v6, <64-95> for v4. + - Minimum value: 0 + - Maximum value: 255 + + name: + type: str + description: + - Name of the interface. + + networkType: + type: str + choices: + - broadcast + - non-broadcast + - point-to-multipoint + - point-to-point + description: + - Network type. + + priority: + type: int + description: + - Router priority. + - Minimum value: 0 + - Maximum value: 255 + + retransmitInterval: + type: int + description: + - Time between retransmitting lost link state advertisements. + - Minimum value: 1 + - Maximum value: 65535 + + tagId: + type: int + description: + - OSPFv3 Tag. + + transmitDelay: + type: int + description: + - Link state transmit delay. + - Minimum value: 1 + - Maximum value: 65535 + + remove_non_updatable_params: + type: str + choices: ["yes", "no"] + default: "no" + description: + - Remove non-updatable parameters from the configuration. + +extends_documentation_fragment: netscaler.adc.netscaler_adc +""" + +EXAMPLES = r""" +""" +RETURN = r""" +--- +changed: + description: Indicates if any change is made by the module + returned: always + type: bool + sample: true +diff: + description: Dictionary of before and after changes + returned: always + type: dict + sample: {'before': {'key1': 'xyz'}, 'after': {'key2': 'pqr'}, 'prepared': 'changes + done'} +diff_list: + description: List of differences between the actual configured object and the configuration + specified in the module + returned: when changed + type: list + sample: ["Attribute `key1` differs. Desired: () XYZ. Existing: () PQR"] +failed: + description: Indicates if the module failed or not + returned: always + type: bool + sample: false +loglines: + description: list of logged messages by the module + returned: always + type: list + sample: ['message 1', 'message 2'] + +""" +import os + +from ..module_utils.module_executor import ModuleExecutor + +RESOURCE_NAME = os.path.basename(__file__).replace(".py", "") + + +def main(): + executor = ModuleExecutor(RESOURCE_NAME) + executor.main() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/ospf6router.py b/plugins/modules/ospf6router.py new file mode 100644 index 000000000..0979a1510 --- /dev/null +++ b/plugins/modules/ospf6router.py @@ -0,0 +1,145 @@ +#!/usr/bin/python + +# -*- coding: utf-8 -*- + +# Copyright (c) 2025 Cloud Software Group, Inc. +# MIT License (see LICENSE or https://opensource.org/licenses/MIT) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "community", +} + +DOCUMENTATION = r""" +--- +module: ospf6router +short_description: Configuration for ospf6router resource. +description: Configuration for ospf6router resource. +version_added: 2.0.0 +author: + - Sumanth Lingappa (@sumanth-lingappa) + - Shiva Shankar Vaddepally (@shivashankar-vaddepally) +options: + state: + type: str + choices: + - present + - unset + - absent + default: present + description: + - The state of the resource being configured by the module on the NetScaler ADC node. + - When C(present), the resource will be added/updated configured according to the module's parameters. + - When C(unset), the resource will be unset on the NetScaler ADC node. + remove_non_updatable_params: + type: str + choices: + - 'yes' + - 'no' + default: 'no' + description: + - When given yes, the module will remove any parameters that are not updatable in the resource. + - If no, the module will return error if any non-updatable parameters are provided. + afParams: + type: dict + description: + - Address family-specific parameters for OSPFv3. + suboptions: + addressFamily: + type: str + description: + - Address family for OSPFv3 + redistribute: + type: dict + description: + - Redistribution settings for OSPFv3. + suboptions: + metric: + type: int + description: + - Metric value used for redistributed routes. + metricType: + type: int + description: + - Metric type used in redistribution. + protocol: + type: str + description: + - Protocol being redistributed (e.g., connected, static). + routeMap: + type: str + description: + - Route map applied to redistributed routes. + passiveInterface: + type: list + elements: str + description: + - List of interfaces that are passive (OSPF doesn't form adjacencies). + routerId: + type: str + description: + - Router ID for OSPFv3 instance. + tagId: + type: str + description: + - Optional route tag for redistributed routes. +extends_documentation_fragment: netscaler.adc.netscaler_adc +""" + +EXAMPLES = r""" +""" + +RETURN = r""" +--- +changed: + description: Indicates if any change is made by the module + returned: always + type: bool + sample: true +diff: + description: Dictionary of before and after changes + returned: always + type: dict + sample: {'before': {'key1': 'xyz'}, 'after': {'key2': 'pqr'}, 'prepared': 'changes + done'} +diff_list: + description: List of differences between the actual configured object and the configuration + specified in the module + returned: when changed + type: list + sample: ["Attribute `key1` differs. Desired: () XYZ. Existing: () PQR"] +failed: + description: Indicates if the module failed or not + returned: always + type: bool + sample: false +loglines: + description: list of logged messages by the module + returned: always + type: list + sample: ['message 1', 'message 2'] + +""" + + +import os + +from ..module_utils.module_executor import ModuleExecutor + +RESOURCE_NAME = os.path.basename(__file__).replace(".py", "") + + +def main(): + executor = ModuleExecutor(RESOURCE_NAME) + executor.main() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/routemap.py b/plugins/modules/routemap.py new file mode 100644 index 000000000..fa1c86384 --- /dev/null +++ b/plugins/modules/routemap.py @@ -0,0 +1,184 @@ +#!/usr/bin/python + +# -*- coding: utf-8 -*- + +# Copyright (c) 2025 Cloud Software Group, Inc. +# MIT License (see LICENSE or https://opensource.org/licenses/MIT) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "community", +} +DOCUMENTATION = r""" +module: routemap +short_description: Manage routemap configuration on Citrix ADC (NetScaler) devices +description: + - Manage routemap configuration on Citrix ADC (NetScaler) devices. +version_added: 2.10.0 +author: + - Shiva Shankar Vaddepally (@shivashankar-vaddepally) +options: + state: + type: str + choices: ["present", "absent", "unset", "get", "delete"] + default: present + description: + - The state of the resource on the NetScaler ADC node. + - When C(present), the resource will be added or updated. + - When C(absent), the resource will be deleted. + - When C(unset), the resource will be unset. + - When C(get), retrieve the resource. + - When C(delete), delete the resource. + + name: + type: str + description: + - Route map tag. + + rules: + type: list + elements: dict + description: + - List of rule parameters for the route map entry. + suboptions: + action: + type: str + choices: ["permit", "deny"] + description: + - Specifies if the route-map denies or permits the operations. + + localPreference: + type: int + description: + - Set the BGP local preference path attribute. + + matchAsPath: + type: str + description: + - Match the BGP AS-path list. + + matchCommunity: + type: str + description: + - Match BGP community list. + + matchIpAddress: + type: str + description: + - Match IP address of route. IP access-list number in the range <1-99> or access-list name. + + matchIpNextHop: + type: str + description: + - Match next-hop address of route. IP access-list number in the range <1-99> or access-list name. + + matchMetric: + type: int + description: + - Match values from routing table. Match metric of route. + + matchRouteType: + type: str + choices: ["type-1", "type-2"] + description: + - Match OSPF external routes of type 1 or type 2 metrics. + + sequence: + type: int + description: + - Sequence to insert to/delete from existing route-map entry. + + setAsPath: + type: str + description: + - Set the prepend string for a BGP AS-path attribute. + + setCommunity: + type: str + description: + - Set the BGP community attribute. + + setIpNextHop: + type: str + description: + - Set next hop address of a route. + + setMetric: + type: int + description: + - Set metric value for destination routing protocol. + + setMetricType: + type: str + choices: ["type-1", "type-2"] + description: + - Set type of metric for destination routing protocol. OSPF external type 1 metric or OSPF external type 2 metric. + + weight: + type: int + description: + - Set BGP weight for routing table. + + remove_non_updatable_params: + type: str + choices: ["yes", "no"] + default: "no" + description: + - Remove non-updatable parameters from the configuration. + +extends_documentation_fragment: netscaler.adc.netscaler_adc +""" +EXAMPLES = r""" +""" +RETURN = r""" +--- +changed: + description: Indicates if any change is made by the module + returned: always + type: bool + sample: true +diff: + description: Dictionary of before and after changes + returned: always + type: dict + sample: {'before': {'key1': 'xyz'}, 'after': {'key2': 'pqr'}, 'prepared': 'changes + done'} +diff_list: + description: List of differences between the actual configured object and the configuration + specified in the module + returned: when changed + type: list + sample: ["Attribute `key1` differs. Desired: () XYZ. Existing: () PQR"] +failed: + description: Indicates if the module failed or not + returned: always + type: bool + sample: false +loglines: + description: list of logged messages by the module + returned: always + type: list + sample: ['message 1', 'message 2'] + +""" +import os + +from ..module_utils.module_executor import ModuleExecutor + +RESOURCE_NAME = os.path.basename(__file__).replace(".py", "") + + +def main(): + executor = ModuleExecutor(RESOURCE_NAME) + executor.main() + + +if __name__ == "__main__": + main() diff --git a/tests/sanity/ignore-2.15.txt b/tests/sanity/ignore-2.15.txt index 47e679f6c..39b2ad3b0 100644 --- a/tests/sanity/ignore-2.15.txt +++ b/tests/sanity/ignore-2.15.txt @@ -950,4 +950,7 @@ plugins/modules/sslprofile_sslechconfig_binding.py validate-modules:missing-gplv plugins/modules/authenticationprotecteduseraction.py validate-modules:missing-gplv3-license # We use MIT license plugins/modules/accesslist.py validate-modules:missing-gplv3-license # We use MIT license plugins/modules/bfdinterface.py validate-modules:missing-gplv3-license # We use MIT license -plugins/modules/iproute.py validate-modules:missing-gplv3-license # We use MIT license \ No newline at end of file +plugins/modules/iproute.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/routemap.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/ospf6interface.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/ospf6router.py validate-modules:missing-gplv3-license # We use MIT license \ No newline at end of file diff --git a/tests/sanity/ignore-2.16.txt b/tests/sanity/ignore-2.16.txt index 47e679f6c..39b2ad3b0 100644 --- a/tests/sanity/ignore-2.16.txt +++ b/tests/sanity/ignore-2.16.txt @@ -950,4 +950,7 @@ plugins/modules/sslprofile_sslechconfig_binding.py validate-modules:missing-gplv plugins/modules/authenticationprotecteduseraction.py validate-modules:missing-gplv3-license # We use MIT license plugins/modules/accesslist.py validate-modules:missing-gplv3-license # We use MIT license plugins/modules/bfdinterface.py validate-modules:missing-gplv3-license # We use MIT license -plugins/modules/iproute.py validate-modules:missing-gplv3-license # We use MIT license \ No newline at end of file +plugins/modules/iproute.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/routemap.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/ospf6interface.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/ospf6router.py validate-modules:missing-gplv3-license # We use MIT license \ No newline at end of file diff --git a/tests/sanity/ignore-2.17.txt b/tests/sanity/ignore-2.17.txt index 47e679f6c..39b2ad3b0 100644 --- a/tests/sanity/ignore-2.17.txt +++ b/tests/sanity/ignore-2.17.txt @@ -950,4 +950,7 @@ plugins/modules/sslprofile_sslechconfig_binding.py validate-modules:missing-gplv plugins/modules/authenticationprotecteduseraction.py validate-modules:missing-gplv3-license # We use MIT license plugins/modules/accesslist.py validate-modules:missing-gplv3-license # We use MIT license plugins/modules/bfdinterface.py validate-modules:missing-gplv3-license # We use MIT license -plugins/modules/iproute.py validate-modules:missing-gplv3-license # We use MIT license \ No newline at end of file +plugins/modules/iproute.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/routemap.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/ospf6interface.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/ospf6router.py validate-modules:missing-gplv3-license # We use MIT license \ No newline at end of file From 03c81a5e36662e7889628c1e66ceccea93efdc40 Mon Sep 17 00:00:00 2001 From: Shiva Shankar Vaddepally Date: Wed, 15 Oct 2025 12:07:25 +0000 Subject: [PATCH 3/9] supporting ospfinterface, ospfrouter, bgprouter Signed-off-by: Shiva Shankar Vaddepally --- examples/bgprouter.yaml | 36 +++ examples/ospfinterface.yaml | 20 ++ examples/ospfrouter.yaml | 18 ++ meta/runtime.yml | 3 + plugins/module_utils/constants.py | 12 +- plugins/module_utils/nitro_resource_map.py | 251 +++++++++++++++++++++ plugins/modules/bgprouter.py | 231 +++++++++++++++++++ plugins/modules/ospfinterface.py | 163 +++++++++++++ plugins/modules/ospfrouter.py | 122 ++++++++++ tests/sanity/ignore-2.15.txt | 15 +- tests/sanity/ignore-2.16.txt | 15 +- tests/sanity/ignore-2.17.txt | 15 +- 12 files changed, 877 insertions(+), 24 deletions(-) create mode 100644 examples/bgprouter.yaml create mode 100644 examples/ospfinterface.yaml create mode 100644 examples/ospfrouter.yaml create mode 100644 plugins/modules/bgprouter.py create mode 100644 plugins/modules/ospfinterface.py create mode 100644 plugins/modules/ospfrouter.py diff --git a/examples/bgprouter.yaml b/examples/bgprouter.yaml new file mode 100644 index 000000000..b7c172dc8 --- /dev/null +++ b/examples/bgprouter.yaml @@ -0,0 +1,36 @@ +--- +- name: Sample bgprouter playbook + hosts: localhost + gather_facts: false + tasks: + - name: Configure bgprouter + delegate_to: localhost + netscaler.adc.bgprouter: + state: present + localAS: 100 + routerId: 2.2.2.2 + afParams: + - addressFamily: ipv4 + redistribute: + - protocol: static + routeMap: test + - addressFamily: ipv6 + neighbor: + - address: 44.1.1.33 + remoteAS: 300 + ASOriginationInterval: 15 + advertisementInterval: 30 + updateSource: vlan101 + holdTimer: 90 + keepaliveTimer: 30 + state: Connect + singlehopBfd: false + multihopBfd: false + afParams: + - addressFamily: ipv4 + activate: true + routeMap: + - name: test + direction: out + - addressFamily: ipv6 + activate: false diff --git a/examples/ospfinterface.yaml b/examples/ospfinterface.yaml new file mode 100644 index 000000000..0f7033773 --- /dev/null +++ b/examples/ospfinterface.yaml @@ -0,0 +1,20 @@ +--- +- name: Sample ospfinterface playbook + hosts: localhost + gather_facts: false + tasks: + - name: Configure ospfinterface + delegate_to: localhost + netscaler.adc.ospfinterface: + state: present + name: vlan100 + helloInterval: 10 + deadInterval: 40 + cost: 10 + priority: 1 + mtu: 1500 + networkType: broadcast + authType: null + retransmitInterval: 5 + transmitDelay: 1 + bfd: false diff --git a/examples/ospfrouter.yaml b/examples/ospfrouter.yaml new file mode 100644 index 000000000..70411c4b6 --- /dev/null +++ b/examples/ospfrouter.yaml @@ -0,0 +1,18 @@ +--- +- name: Sample routeMap playbook + hosts: localhost + gather_facts: false + tasks: + - name: Configure routeMap + delegate_to: localhost + netscaler.adc.ospfrouter: + state: present + nsip: 10.106.210.21 + nitro_user: nsroot + nitro_pass: notnsroot + validate_certs: false + nitro_protocol: http + tagId: "11" + routerId: "1.1.1.1" + afParams: + - addressFamily: "ipv6" diff --git a/meta/runtime.yml b/meta/runtime.yml index b5b497ae2..428ba8a36 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -987,3 +987,6 @@ action_groups: - routemap - ospf6interface - ospf6router + - ospfrouter + - ospfinterface + - bgprouter diff --git a/plugins/module_utils/constants.py b/plugins/module_utils/constants.py index ea1c94921..eb95a5e4b 100644 --- a/plugins/module_utils/constants.py +++ b/plugins/module_utils/constants.py @@ -160,19 +160,19 @@ "ospf6interface", "routemap", "ospf6router", + "bgprouter", + "ospfrouter", + "ospfinterface", ] DYNAMIC_PROTOCOLS_ALIAS = { "accesslist": "accessList", - "accessList": "accesslist", "bfdinterface": "bfdInterface", - "bfdInterface": "bfdinterface", "iproute": "ipRoute", - "ipRoute": "iproute", "ospf6interface": "ospf6Interface", - "ospf6Interface": "ospf6interface", "routemap": "routeMap", - "routeMap": "routemap", "ospf6router": "ospf6Router", - "ospf6Router": "ospf6router", + "bgprouter": "bgpRouter", + "ospfrouter": "ospfRouter", + "ospfinterface": "ospfInterface", } diff --git a/plugins/module_utils/nitro_resource_map.py b/plugins/module_utils/nitro_resource_map.py index ed1112ee0..8f12fae1d 100644 --- a/plugins/module_utils/nitro_resource_map.py +++ b/plugins/module_utils/nitro_resource_map.py @@ -16571,6 +16571,103 @@ "singleton": False, "update_payload_keys": [], }, + "bgprouter": { + "_supported_operations": ["get", "add", "unset", "update", "delete"], + "action_payload_keys": { + "apply": [], + "create": [], + "force": [], + "import": [], + "link": [], + "switch": [], + "unlink": [], + "unset": ["afParams", "localAS", "neighbor", "routerId"], + }, + "add_payload_keys": ["afParams", "localAS", "neighbor", "routerId"], + "bindings": [], + "bindprimary_key": "", + "delete_arg_keys": ["localAS"], + "disable_payload_keys": [], + "enable_payload_keys": [], + "get_arg_keys": [], + "immutable_keys": [], + "password_keys": [], + "primary_key": "", + "primary_key_composite": [], + "update_payload_keys": ["afParams", "localAS", "neighbor", "routerId"], + "readwrite_argument": { + "localAS": {"no_log": False, "type": "int"}, + "routerId": {"no_log": False, "type": "str"}, + "afParams": { + "type": "list", + "elements": "dict", + "options": { + "addressFamily": { + "no_log": False, + "type": "str", + "choices": ["ipv4", "ipv6"], + }, + "redistribute": { + "type": "list", + "elements": "dict", + "options": { + "protocol": { + "no_log": False, + "type": "str", + "choices": [ + "kernel", + "connected", + "static", + "rip", + "ospf", + "isis", + "intranet", + ], + }, + "routeMap": {"no_log": False, "type": "str"}, + }, + }, + }, + }, + "neighbor": { + "type": "list", + "elements": "dict", + "options": { + "ASOriginationInterval": {"no_log": False, "type": "int"}, + "address": {"no_log": False, "type": "str"}, + "advertisementInterval": {"no_log": False, "type": "int"}, + "afParams": { + "type": "list", + "elements": "dict", + "options": { + "activate": {"no_log": False, "type": "bool"}, + "addressFamily": {"no_log": False, "type": "str"}, + "routeMap": { + "type": "dict", + "options": { + "direction": { + "no_log": False, + "type": "str", + "choices": ["in", "out"], + }, + "name": {"no_log": False, "type": "str"}, + }, + }, + }, + }, + "connectTimer": {"no_log": False, "type": "int"}, + "holdTimerConfig": {"no_log": False, "type": "int"}, + "keepaliveTimerConfig": {"no_log": False, "type": "int"}, + "md5Password": {"no_log": True, "type": "str"}, + "multihopBfd": {"no_log": False, "type": "bool"}, + "remoteAS": {"no_log": False, "type": "int"}, + "singlehopBfd": {"no_log": False, "type": "bool"}, + "updateSource": {"no_log": False, "type": "str"}, + }, + }, + }, + "singleton": False, + }, "bfdinterface": { "_supported_operations": ["get", "add", "unset"], "action_payload_keys": { @@ -46909,6 +47006,160 @@ "tagId", ], }, + "ospfinterface": { + "_supported_operations": [ + "add", + "get", + "unset", + "update", + ], + "action_payload_keys": { + "apply": [], + "create": [], + "force": [], + "import": [], + "link": [], + "switch": [], + "unlink": [], + "unset": [ + "networks", + "passiveInterface", + "processId", + "redistribute", + "routerId", + ], + }, + "add_payload_keys": [ + "networks", + "passiveInterface", + "processId", + "redistribute", + "routerId", + ], + "bindings": [], + "bindprimary_key": "", + "delete_arg_keys": ["processId"], + "disable_payload_keys": [], + "enable_payload_keys": [], + "get_arg_keys": ["processId"], + "immutable_keys": [], + "password_keys": [], + "primary_key": "", + "primary_key_composite": [], + "readwrite_arguments": { + "authKey": {"no_log": False, "type": "str"}, + "authType": { + "no_log": False, + "type": "str", + "choices": ["null", "simple", "message-digest"], + }, + "bfd": {"no_log": False, "type": "bool"}, + "cost": {"no_log": False, "type": "int"}, + "deadInterval": {"no_log": False, "type": "int"}, + "helloInterval": {"no_log": False, "type": "int"}, + "mtu": {"no_log": False, "type": "int"}, + "name": {"no_log": False, "type": "str"}, + "networkType": {"no_log": False, "type": "str"}, + "priority": {"no_log": False, "type": "int"}, + "retransmitInterval": {"no_log": False, "type": "int"}, + "transmitDelay": {"no_log": False, "type": "int"}, + }, + "singleton": False, + "update_payload_keys": [ + "networks", + "passiveInterface", + "processId", + "redistribute", + "routerId", + ], + }, + "ospfrouter": { + "_supported_operations": [ + "add", + "get", + "unset", + "update", + "delete", + ], + "action_payload_keys": { + "apply": [], + "create": [], + "force": [], + "import": [], + "link": [], + "switch": [], + "unlink": [], + "unset": [ + "networks", + "passiveInterface", + "processId", + "redistribute", + "routerId", + ], + }, + "add_payload_keys": [ + "networks", + "passiveInterface", + "processId", + "redistribute", + "routerId", + ], + "bindings": [], + "bindprimary_key": "", + "delete_arg_keys": ["processId"], + "disable_payload_keys": [], + "enable_payload_keys": [], + "get_arg_keys": ["processId"], + "immutable_keys": [], + "password_keys": [], + "primary_key": "", + "primary_key_composite": [], + "readwrite_arguments": { + "networks": { + "type": "dict", + "options": { + "area": {"no_log": False, "type": "int"}, + "ipaddress": {"no_log": False, "type": "str"}, + "netmask": {"no_log": False, "type": "int"}, + }, + }, + "passiveInterface": {"no_log": False, "type": "list", "elements": "str"}, + "processId": {"no_log": False, "type": "int"}, + "redistribute": { + "type": "dict", + "options": { + "metric": {"no_log": False, "type": "int"}, + "metricType": {"no_log": False, "type": "int"}, + "ospfProcessId": {"no_log": False, "type": "int"}, + "protocol": { + "no_log": False, + "type": "str", + "choices": [ + "bgp", + "connected", + "intranet", + "isis", + "kernel", + "ospf", + "rip", + "static", + ], + }, + "routeMap": {"no_log": False, "type": "str"}, + "tag": {"no_log": False, "type": "int"}, + }, + }, + "routerId": {"no_log": False, "type": "str"}, + }, + "singleton": True, + "update_payload_keys": [ + "networks", + "passiveInterface", + "processId", + "redistribute", + "routerId", + ], + }, "onlinkipv6prefix": { "_supported_operations": [ "add", diff --git a/plugins/modules/bgprouter.py b/plugins/modules/bgprouter.py new file mode 100644 index 000000000..067271227 --- /dev/null +++ b/plugins/modules/bgprouter.py @@ -0,0 +1,231 @@ +#!/usr/bin/python + +# -*- coding: utf-8 -*- + +# Copyright (c) 2025 Cloud Software Group, Inc. +# MIT License (see LICENSE or https://opensource.org/licenses/MIT) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "community", +} + +DOCUMENTATION = r""" +--- +module: bgprouter +short_description: Configuration for BGP Router resource. +description: Configuration for BGP Router resource. +version_added: 2.0.0 +author: + - Sumanth Lingappa (@sumanth-lingappa) + - Shiva Shankar Vaddepally (@shivashankar-vaddepally) +options: + state: + type: str + choices: + - present + - absent + - unset + default: present + description: + - The state of the resource being configured by the module on the NetScaler ADC node. + - When C(present), the resource will be added/updated configured according to the module's parameters. + - When C(absent), the resource will be deleted from the NetScaler ADC node. + - When C(unset), the resource will be unset on the NetScaler ADC node. + remove_non_updatable_params: + type: str + choices: + - 'yes' + - 'no' + default: 'no' + description: + - When given yes, the module will remove any parameters that are not updatable in the resource. + - If no, the module will return error if any non-updatable parameters are provided. + localAS: + type: int + description: + - Autonomous system number for the BGP router. + routerId: + type: str + description: + - Router identifier for the BGP routing instance. + afParams: + type: list + elements: dict + description: + - Address family parameters for the BGP router. + suboptions: + addressFamily: + type: str + description: + - Address family of the routes. + choices: + - ipv4 + - ipv6 + redistribute: + type: list + elements: dict + description: + - Redistribution settings per protocol. + suboptions: + protocol: + type: str + description: + - The protocol from which routes need to be redistributed. + choices: + - kernel + - connected + - static + - rip + - ospf + - isis + - intranet + routeMap: + type: str + description: + - Route map reference for redistribution. + neighbor: + type: dict + description: + - Neighbor router configuration. + suboptions: + ASOriginationInterval: + type: int + description: + - Minimum interval between sending AS-origination routing updates. + address: + type: str + description: + - The IPv4 or IPv6 address of the neighboring router. + advertisementInterval: + type: int + description: + - Minimum interval between sending BGP routing updates. + afParams: + type: list + elements: dict + description: + - Address family parameters for the neighbor. + suboptions: + activate: + type: bool + description: + - Enable the address family for the neighbor. + addressFamily: + type: str + description: + - Address family identifier. + choices: + - ipv4 + - ipv6 + routeMap: + type: list + elements: dict + description: + - Route maps applied to neighbor routes. + suboptions: + direction: + type: str + description: + - Apply the route-map to incoming or outgoing routes. + choices: + - in + - out + name: + type: str + description: + - Name of the route-map. + connectTimer: + type: int + description: + - Time interval (in seconds) for the ConnectRetry timer. + holdTimerConfig: + type: int + description: + - Configured hold time for the neighbor. + keepaliveTimerConfig: + type: int + description: + - Configured keepalive time for the neighbor. + md5Password: + type: str + description: + - MD5 password for the neighbor. + multihopBfd: + type: bool + description: + - Enable BFD for multihop sessions. + remoteAS: + type: int + description: + - AS number of the neighbor. + singlehopBfd: + type: bool + description: + - Enable BFD on this neighbor. + updateSource: + type: str + description: + - Source of routing updates. + +extends_documentation_fragment: netscaler.adc.netscaler_adc + +""" + +EXAMPLES = r""" +""" + +RETURN = r""" +--- +changed: + description: Indicates if any change is made by the module + returned: always + type: bool + sample: true +diff: + description: Dictionary of before and after changes + returned: always + type: dict + sample: {'before': {'key1': 'xyz'}, 'after': {'key2': 'pqr'}, 'prepared': 'changes + done'} +diff_list: + description: List of differences between the actual configured object and the configuration + specified in the module + returned: when changed + type: list + sample: ["Attribute `key1` differs. Desired: () XYZ. Existing: () PQR"] +failed: + description: Indicates if the module failed or not + returned: always + type: bool + sample: false +loglines: + description: list of logged messages by the module + returned: always + type: list + sample: ['message 1', 'message 2'] + +""" + + +import os + +from ..module_utils.module_executor import ModuleExecutor + +RESOURCE_NAME = os.path.basename(__file__).replace(".py", "") + + +def main(): + executor = ModuleExecutor(RESOURCE_NAME) + executor.main() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/ospfinterface.py b/plugins/modules/ospfinterface.py new file mode 100644 index 000000000..7a0075b5a --- /dev/null +++ b/plugins/modules/ospfinterface.py @@ -0,0 +1,163 @@ +#!/usr/bin/python + +# -*- coding: utf-8 -*- + +# Copyright (c) 2025 Cloud Software Group, Inc. +# MIT License (see LICENSE or https://opensource.org/licenses/MIT) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "community", +} + +DOCUMENTATION = r""" +module: ospfinterface +short_description: Configuration for OSPF Interface resource. +description: Configuration for OSPF Interface resource. +version_added: 2.0.0 +author: + - Sumanth Lingappa (@sumanth-lingappa) + - Shiva Shankar Vaddepally (@shivashankar-vaddepally) +options: + state: + type: str + choices: + - present + - absent + - enabled + - disabled + - unset + - renamed + default: present + description: + - The state of the resource being configured by the module on the NetScaler ADC node. + - When C(present), the resource will be added/updated configured according to the module's parameters. + - When C(absent), the resource will be deleted from the NetScaler ADC node. + - When C(enabled), the resource will be enabled on the NetScaler ADC node. + - When C(disabled), the resource will be disabled on the NetScaler ADC node. + - When C(unset), the resource will be unset on the NetScaler ADC node. + - When C(renamed), the resource will be renamed on the NetScaler ADC node. + remove_non_updatable_params: + type: str + choices: + - 'yes' + - 'no' + default: 'no' + description: + - When given yes, the module will remove any parameters that are not updatable in the resource. + - If no, the module will return error if any non-updatable parameters are provided. + + authKey: + type: str + description: + - Authentication password (key) for the OSPF interface. + authType: + type: str + choices: + - null + - simple + - message-digest + description: + - Authentication type on the OSPF interface. + bfd: + type: bool + description: + - Enable or disable Bidirectional Forwarding Detection (BFD) on the interface. + cost: + type: int + description: + - Cost metric of the OSPF interface. + deadInterval: + type: int + description: + - Time interval (in seconds) after which a router is declared down if no Hello packets are received. + helloInterval: + type: int + description: + - Time interval (in seconds) between sending Hello packets on the interface. + mtu: + type: int + description: + - Maximum Transmission Unit size on the interface. + name: + type: str + description: + - Name of the OSPF interface. + networkType: + type: str + description: + - Type of the OSPF network. + priority: + type: int + description: + - Router priority used in the designated router election process. + retransmitInterval: + type: int + description: + - Time interval (in seconds) between retransmissions of Link State Advertisements (LSAs). + transmitDelay: + type: int + description: + - Delay in seconds on the interface before transmitting LSAs. + +extends_documentation_fragment: netscaler.adc.netscaler_adc + +""" + +EXAMPLES = r""" +""" + +RETURN = r""" +--- +changed: + description: Indicates if any change is made by the module + returned: always + type: bool + sample: true +diff: + description: Dictionary of before and after changes + returned: always + type: dict + sample: {'before': {'key1': 'xyz'}, 'after': {'key2': 'pqr'}, 'prepared': 'changes + done'} +diff_list: + description: List of differences between the actual configured object and the configuration + specified in the module + returned: when changed + type: list + sample: ["Attribute `key1` differs. Desired: () XYZ. Existing: () PQR"] +failed: + description: Indicates if the module failed or not + returned: always + type: bool + sample: false +loglines: + description: list of logged messages by the module + returned: always + type: list + sample: ['message 1', 'message 2'] + +""" + + +import os + +from ..module_utils.module_executor import ModuleExecutor + +RESOURCE_NAME = os.path.basename(__file__).replace(".py", "") + + +def main(): + executor = ModuleExecutor(RESOURCE_NAME) + executor.main() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/ospfrouter.py b/plugins/modules/ospfrouter.py new file mode 100644 index 000000000..860ee72f6 --- /dev/null +++ b/plugins/modules/ospfrouter.py @@ -0,0 +1,122 @@ +#!/usr/bin/python + +# -*- coding: utf-8 -*- + +# Copyright (c) 2025 Cloud Software Group, Inc. +# MIT License (see LICENSE or https://opensource.org/licenses/MIT) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "community", +} + +DOCUMENTATION = r""" +--- +module: nsweblogparam +short_description: Configuration for Web log parameters resource. +description: Configuration for Web log parameters resource. +version_added: 2.0.0 +author: + - Sumanth Lingappa (@sumanth-lingappa) + - Shiva Shankar Vaddepally (@shivashankar-vaddepally) +options: + state: + choices: + - present + - unset + default: present + description: + - The state of the resource being configured by the module on the NetScaler + ADC node. + - When C(present), the resource will be added/updated configured according to + the module's parameters. + - When C(unset), the resource will be unset on the NetScaler ADC node. + type: str + remove_non_updatable_params: + choices: + - 'yes' + - 'no' + default: 'no' + description: + - When given yes, the module will remove any parameters that are not updatable + in the resource. + - If no, the module will return error if any non-updatable parameters are provided. + type: str + buffersizemb: + type: int + description: + - Buffer size, in MB, allocated for log transaction data on the system. The + maximum value is limited to the memory available on the system. + customreqhdrs: + type: list + description: + - Name(s) of HTTP request headers whose values should be exported by the Web + Logging feature. + elements: str + customrsphdrs: + type: list + description: + - Name(s) of HTTP response headers whose values should be exported by the Web + Logging feature. + elements: str +extends_documentation_fragment: netscaler.adc.netscaler_adc + +""" + +EXAMPLES = r""" +""" + +RETURN = r""" +--- +changed: + description: Indicates if any change is made by the module + returned: always + type: bool + sample: true +diff: + description: Dictionary of before and after changes + returned: always + type: dict + sample: {'before': {'key1': 'xyz'}, 'after': {'key2': 'pqr'}, 'prepared': 'changes + done'} +diff_list: + description: List of differences between the actual configured object and the configuration + specified in the module + returned: when changed + type: list + sample: ["Attribute `key1` differs. Desired: () XYZ. Existing: () PQR"] +failed: + description: Indicates if the module failed or not + returned: always + type: bool + sample: false +loglines: + description: list of logged messages by the module + returned: always + type: list + sample: ['message 1', 'message 2'] + +""" + + +import os + +from ..module_utils.module_executor import ModuleExecutor + +RESOURCE_NAME = os.path.basename(__file__).replace(".py", "") + + +def main(): + executor = ModuleExecutor(RESOURCE_NAME) + executor.main() + + +if __name__ == "__main__": + main() diff --git a/tests/sanity/ignore-2.15.txt b/tests/sanity/ignore-2.15.txt index 39b2ad3b0..9311ac21f 100644 --- a/tests/sanity/ignore-2.15.txt +++ b/tests/sanity/ignore-2.15.txt @@ -948,9 +948,12 @@ plugins/modules/sslechconfig.py validate-modules:missing-gplv3-license # We use plugins/modules/sslhpkekey.py validate-modules:missing-gplv3-license # We use MIT license plugins/modules/sslprofile_sslechconfig_binding.py validate-modules:missing-gplv3-license # We use MIT license plugins/modules/authenticationprotecteduseraction.py validate-modules:missing-gplv3-license # We use MIT license -plugins/modules/accesslist.py validate-modules:missing-gplv3-license # We use MIT license -plugins/modules/bfdinterface.py validate-modules:missing-gplv3-license # We use MIT license -plugins/modules/iproute.py validate-modules:missing-gplv3-license # We use MIT license -plugins/modules/routemap.py validate-modules:missing-gplv3-license # We use MIT license -plugins/modules/ospf6interface.py validate-modules:missing-gplv3-license # We use MIT license -plugins/modules/ospf6router.py validate-modules:missing-gplv3-license # We use MIT license \ No newline at end of file +plugins/modules/accesslist.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/bfdinterface.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/iproute.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/routemap.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/ospf6interface.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/ospf6router.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/ospfrouter.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/ospfinterface.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/bgprouter.py validate-modules:missing-gplv3-license # We use MIT license \ No newline at end of file diff --git a/tests/sanity/ignore-2.16.txt b/tests/sanity/ignore-2.16.txt index 39b2ad3b0..9311ac21f 100644 --- a/tests/sanity/ignore-2.16.txt +++ b/tests/sanity/ignore-2.16.txt @@ -948,9 +948,12 @@ plugins/modules/sslechconfig.py validate-modules:missing-gplv3-license # We use plugins/modules/sslhpkekey.py validate-modules:missing-gplv3-license # We use MIT license plugins/modules/sslprofile_sslechconfig_binding.py validate-modules:missing-gplv3-license # We use MIT license plugins/modules/authenticationprotecteduseraction.py validate-modules:missing-gplv3-license # We use MIT license -plugins/modules/accesslist.py validate-modules:missing-gplv3-license # We use MIT license -plugins/modules/bfdinterface.py validate-modules:missing-gplv3-license # We use MIT license -plugins/modules/iproute.py validate-modules:missing-gplv3-license # We use MIT license -plugins/modules/routemap.py validate-modules:missing-gplv3-license # We use MIT license -plugins/modules/ospf6interface.py validate-modules:missing-gplv3-license # We use MIT license -plugins/modules/ospf6router.py validate-modules:missing-gplv3-license # We use MIT license \ No newline at end of file +plugins/modules/accesslist.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/bfdinterface.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/iproute.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/routemap.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/ospf6interface.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/ospf6router.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/ospfrouter.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/ospfinterface.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/bgprouter.py validate-modules:missing-gplv3-license # We use MIT license \ No newline at end of file diff --git a/tests/sanity/ignore-2.17.txt b/tests/sanity/ignore-2.17.txt index 39b2ad3b0..9311ac21f 100644 --- a/tests/sanity/ignore-2.17.txt +++ b/tests/sanity/ignore-2.17.txt @@ -948,9 +948,12 @@ plugins/modules/sslechconfig.py validate-modules:missing-gplv3-license # We use plugins/modules/sslhpkekey.py validate-modules:missing-gplv3-license # We use MIT license plugins/modules/sslprofile_sslechconfig_binding.py validate-modules:missing-gplv3-license # We use MIT license plugins/modules/authenticationprotecteduseraction.py validate-modules:missing-gplv3-license # We use MIT license -plugins/modules/accesslist.py validate-modules:missing-gplv3-license # We use MIT license -plugins/modules/bfdinterface.py validate-modules:missing-gplv3-license # We use MIT license -plugins/modules/iproute.py validate-modules:missing-gplv3-license # We use MIT license -plugins/modules/routemap.py validate-modules:missing-gplv3-license # We use MIT license -plugins/modules/ospf6interface.py validate-modules:missing-gplv3-license # We use MIT license -plugins/modules/ospf6router.py validate-modules:missing-gplv3-license # We use MIT license \ No newline at end of file +plugins/modules/accesslist.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/bfdinterface.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/iproute.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/routemap.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/ospf6interface.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/ospf6router.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/ospfrouter.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/ospfinterface.py validate-modules:missing-gplv3-license # We use MIT license +plugins/modules/bgprouter.py validate-modules:missing-gplv3-license # We use MIT license \ No newline at end of file From 0f854403b78f60b9ad8bc80bfefe6c5e420a9318 Mon Sep 17 00:00:00 2001 From: Shiva Shankar Vaddepally Date: Thu, 16 Oct 2025 05:17:40 +0000 Subject: [PATCH 4/9] solving pip issues Signed-off-by: Shiva Shankar Vaddepally --- plugins/module_utils/nitro_resource_map.py | 21 ++++++++------------- plugins/modules/bgprouter.py | 6 +++--- plugins/modules/ospf6interface.py | 20 ++------------------ plugins/modules/ospf6router.py | 6 ++++-- plugins/modules/ospfinterface.py | 10 +--------- plugins/modules/ospfrouter.py | 8 +++++--- plugins/modules/routemap.py | 2 +- 7 files changed, 24 insertions(+), 49 deletions(-) diff --git a/plugins/module_utils/nitro_resource_map.py b/plugins/module_utils/nitro_resource_map.py index 8f12fae1d..9a655f0db 100644 --- a/plugins/module_utils/nitro_resource_map.py +++ b/plugins/module_utils/nitro_resource_map.py @@ -16595,7 +16595,7 @@ "primary_key": "", "primary_key_composite": [], "update_payload_keys": ["afParams", "localAS", "neighbor", "routerId"], - "readwrite_argument": { + "readwrite_arguments": { "localAS": {"no_log": False, "type": "int"}, "routerId": {"no_log": False, "type": "str"}, "afParams": { @@ -16641,7 +16641,11 @@ "elements": "dict", "options": { "activate": {"no_log": False, "type": "bool"}, - "addressFamily": {"no_log": False, "type": "str"}, + "addressFamily": { + "no_log": False, + "type": "str", + "choices": ["ipv4", "ipv6"], + }, "routeMap": { "type": "dict", "options": { @@ -46881,29 +46885,24 @@ "areaId": { "no_log": False, "type": "int", - "description": "OSPFv3 area ID (0-4294967295)", }, "cost": { "no_log": False, "type": "int", - "description": "Interface cost (1-65535)", }, "deadInterval": { "no_log": False, "type": "int", - "description": "Neighbor dead interval (1-65535)", }, "helloInterval": { "no_log": False, "type": "int", - "description": "HELLO packet interval (1-65535)", }, "instanceId": { "no_log": False, "type": "int", - "description": "Instance ID (0-255)", }, - "name": {"no_log": False, "type": "str", "description": "Interface name"}, + "name": {"no_log": False, "type": "str"}, "networkType": { "no_log": False, "type": "str", @@ -46913,23 +46912,19 @@ "point-to-multipoint", "point-to-point", ], - "description": "Network type", }, "priority": { "no_log": False, "type": "int", - "description": "Router priority (0-255)", }, "retransmitInterval": { "no_log": False, "type": "int", - "description": "Retransmit interval (1-65535)", }, - "tagId": {"no_log": False, "type": "int", "description": "OSPFv3 Tag"}, + "tagId": {"no_log": False, "type": "int"}, "transmitDelay": { "no_log": False, "type": "int", - "description": "Transmit delay (1-65535)", }, }, "singleton": False, diff --git a/plugins/modules/bgprouter.py b/plugins/modules/bgprouter.py index 067271227..7b491aad4 100644 --- a/plugins/modules/bgprouter.py +++ b/plugins/modules/bgprouter.py @@ -91,7 +91,8 @@ description: - Route map reference for redistribution. neighbor: - type: dict + type: list + elements: dict description: - Neighbor router configuration. suboptions: @@ -125,8 +126,7 @@ - ipv4 - ipv6 routeMap: - type: list - elements: dict + type: dict description: - Route maps applied to neighbor routes. suboptions: diff --git a/plugins/modules/ospf6interface.py b/plugins/modules/ospf6interface.py index f3106aab1..e85d3b988 100644 --- a/plugins/modules/ospf6interface.py +++ b/plugins/modules/ospf6interface.py @@ -26,47 +26,37 @@ options: state: type: str - choices: ["present", "absent"] + choices: ["present", "unset"] default: present description: - The state of the resource on the NetScaler ADC node. - When C(present), the resource will be added or updated. - - When C(absent), the resource will be deleted. + - When C(unset), the resource will be unset. areaId: type: int description: - Area on which OSPFv3 is running. - - Minimum value: 0 - - Maximum value: 4294967295 cost: type: int description: - Interface cost. - - Minimum value: 1 - - Maximum value: 65535 deadInterval: type: int description: - Interval after which a neighbor is declared dead. - - Minimum value: 1 - - Maximum value: 65535 helloInterval: type: int description: - Time between HELLO packets. - - Minimum value: 1 - - Maximum value: 65535 instanceId: type: int description: - Interface Instance Id - <0-31> for v6, <64-95> for v4. - - Minimum value: 0 - - Maximum value: 255 name: type: str @@ -87,15 +77,11 @@ type: int description: - Router priority. - - Minimum value: 0 - - Maximum value: 255 retransmitInterval: type: int description: - Time between retransmitting lost link state advertisements. - - Minimum value: 1 - - Maximum value: 65535 tagId: type: int @@ -106,8 +92,6 @@ type: int description: - Link state transmit delay. - - Minimum value: 1 - - Maximum value: 65535 remove_non_updatable_params: type: str diff --git a/plugins/modules/ospf6router.py b/plugins/modules/ospf6router.py index 0979a1510..a16cce31b 100644 --- a/plugins/modules/ospf6router.py +++ b/plugins/modules/ospf6router.py @@ -47,7 +47,8 @@ - When given yes, the module will remove any parameters that are not updatable in the resource. - If no, the module will return error if any non-updatable parameters are provided. afParams: - type: dict + type: list + elements: dict description: - Address family-specific parameters for OSPFv3. suboptions: @@ -56,7 +57,8 @@ description: - Address family for OSPFv3 redistribute: - type: dict + type: list + elements: dict description: - Redistribution settings for OSPFv3. suboptions: diff --git a/plugins/modules/ospfinterface.py b/plugins/modules/ospfinterface.py index 7a0075b5a..274f158c4 100644 --- a/plugins/modules/ospfinterface.py +++ b/plugins/modules/ospfinterface.py @@ -29,20 +29,12 @@ type: str choices: - present - - absent - - enabled - - disabled - unset - - renamed default: present description: - The state of the resource being configured by the module on the NetScaler ADC node. - When C(present), the resource will be added/updated configured according to the module's parameters. - - When C(absent), the resource will be deleted from the NetScaler ADC node. - - When C(enabled), the resource will be enabled on the NetScaler ADC node. - - When C(disabled), the resource will be disabled on the NetScaler ADC node. - When C(unset), the resource will be unset on the NetScaler ADC node. - - When C(renamed), the resource will be renamed on the NetScaler ADC node. remove_non_updatable_params: type: str choices: @@ -60,7 +52,7 @@ authType: type: str choices: - - null + - "null" - simple - message-digest description: diff --git a/plugins/modules/ospfrouter.py b/plugins/modules/ospfrouter.py index 860ee72f6..e413a062d 100644 --- a/plugins/modules/ospfrouter.py +++ b/plugins/modules/ospfrouter.py @@ -18,9 +18,9 @@ DOCUMENTATION = r""" --- -module: nsweblogparam -short_description: Configuration for Web log parameters resource. -description: Configuration for Web log parameters resource. +module: ospfrouter +short_description: Configuration for OSPF Router resource. +description: Configuration for OSPF Router resource. version_added: 2.0.0 author: - Sumanth Lingappa (@sumanth-lingappa) @@ -29,6 +29,7 @@ state: choices: - present + - absent - unset default: present description: @@ -36,6 +37,7 @@ ADC node. - When C(present), the resource will be added/updated configured according to the module's parameters. + - When C(absent), the resource will be deleted from the NetScaler ADC node. - When C(unset), the resource will be unset on the NetScaler ADC node. type: str remove_non_updatable_params: diff --git a/plugins/modules/routemap.py b/plugins/modules/routemap.py index fa1c86384..91d6974be 100644 --- a/plugins/modules/routemap.py +++ b/plugins/modules/routemap.py @@ -26,7 +26,7 @@ options: state: type: str - choices: ["present", "absent", "unset", "get", "delete"] + choices: ["present", "absent", "unset", "get"] default: present description: - The state of the resource on the NetScaler ADC node. From 6cf38c58c4ded5e946dcb87581b98e0d6550c26a Mon Sep 17 00:00:00 2001 From: Shiva Shankar Vaddepally Date: Thu, 16 Oct 2025 10:17:46 +0000 Subject: [PATCH 5/9] supporting bgp and adding examples to modules Signed-off-by: Shiva Shankar Vaddepally --- examples/bgprouter.yaml | 34 ++++----- examples/ospf6interface.yaml | 3 +- examples/ospf6router.yaml | 6 +- examples/ospfinterface.yaml | 2 +- examples/ospfrouter.yaml | 22 +++--- examples/routemap.yaml | 3 +- plugins/module_utils/module_executor.py | 48 ++++++------ plugins/module_utils/nitro_resource_map.py | 52 +++++++++---- plugins/modules/accesslist.py | 14 ++++ plugins/modules/bfdinterface.py | 13 ++++ plugins/modules/bgprouter.py | 49 ++++++++---- plugins/modules/iproute.py | 12 +++ plugins/modules/ospf6interface.py | 16 +++- plugins/modules/ospf6router.py | 15 ++++ plugins/modules/ospfinterface.py | 22 ++++++ plugins/modules/ospfrouter.py | 86 +++++++++++++++++++--- plugins/modules/routemap.py | 15 ++++ 17 files changed, 312 insertions(+), 100 deletions(-) diff --git a/examples/bgprouter.yaml b/examples/bgprouter.yaml index b7c172dc8..c85f3fa9d 100644 --- a/examples/bgprouter.yaml +++ b/examples/bgprouter.yaml @@ -7,30 +7,26 @@ delegate_to: localhost netscaler.adc.bgprouter: state: present - localAS: 100 - routerId: 2.2.2.2 + + localAS: 122 + routerId: "2.2.2.2" afParams: - - addressFamily: ipv4 + - addressFamily: "ipv4" redistribute: - - protocol: static - routeMap: test - - addressFamily: ipv6 + - protocol: "static" + routeMap: "test" + - addressFamily: "ipv6" neighbor: - - address: 44.1.1.33 + - address: "44.1.1.33" remoteAS: 300 - ASOriginationInterval: 15 - advertisementInterval: 30 - updateSource: vlan101 - holdTimer: 90 - keepaliveTimer: 30 - state: Connect + ASOriginationInterval: 11 + advertisementInterval: 34 + updateSource: "vlan101" singlehopBfd: false multihopBfd: false afParams: - - addressFamily: ipv4 - activate: true + - addressFamily: "ipv4" routeMap: - - name: test - direction: out - - addressFamily: ipv6 - activate: false + - name: "test" + direction: "out" + - addressFamily: "ipv6" diff --git a/examples/ospf6interface.yaml b/examples/ospf6interface.yaml index 88ebfc5a2..db15c5c6b 100644 --- a/examples/ospf6interface.yaml +++ b/examples/ospf6interface.yaml @@ -6,7 +6,8 @@ delegate_to: localhost netscaler.adc.ospf6interface: state: present - name: vlan0 + + name: "vlan10" areaId: 0 tagId: "22" instanceId: 0 diff --git a/examples/ospf6router.yaml b/examples/ospf6router.yaml index 5c2101d4d..ed106ed1d 100644 --- a/examples/ospf6router.yaml +++ b/examples/ospf6router.yaml @@ -6,8 +6,10 @@ - name: Configure ospf6router delegate_to: localhost netscaler.adc.ospf6router: - state: present + state: unset tagId: 11 - routerId: 1.1.1.1 afParams: - addressFamily: ipv6 + redistribute: + - protocol: static + metric: 111 diff --git a/examples/ospfinterface.yaml b/examples/ospfinterface.yaml index 0f7033773..150c939ed 100644 --- a/examples/ospfinterface.yaml +++ b/examples/ospfinterface.yaml @@ -14,7 +14,7 @@ priority: 1 mtu: 1500 networkType: broadcast - authType: null + authType: "null" retransmitInterval: 5 transmitDelay: 1 bfd: false diff --git a/examples/ospfrouter.yaml b/examples/ospfrouter.yaml index 70411c4b6..70813cca2 100644 --- a/examples/ospfrouter.yaml +++ b/examples/ospfrouter.yaml @@ -1,18 +1,20 @@ --- -- name: Sample routeMap playbook +- name: Sample ospfrouter playbook hosts: localhost gather_facts: false tasks: - - name: Configure routeMap + - name: Configure ospfrouter delegate_to: localhost netscaler.adc.ospfrouter: state: present - nsip: 10.106.210.21 - nitro_user: nsroot - nitro_pass: notnsroot - validate_certs: false - nitro_protocol: http - tagId: "11" + + processId: 1 routerId: "1.1.1.1" - afParams: - - addressFamily: "ipv6" + passiveInterface: + - vlan22 + redistribute: + - protocol: connected + networks: + - ipaddress: "33.1.2.5" + netmask: 25 + area: 1 diff --git a/examples/routemap.yaml b/examples/routemap.yaml index fc10413a6..94020d305 100644 --- a/examples/routemap.yaml +++ b/examples/routemap.yaml @@ -7,9 +7,10 @@ delegate_to: localhost netscaler.adc.routemap: state: present + name: test rules: - action: permit sequence: 10 - setMetric: 300 + setMetric: 105 matchAsPath: "300" diff --git a/plugins/module_utils/module_executor.py b/plugins/module_utils/module_executor.py index c667893d3..9e1df85a8 100644 --- a/plugins/module_utils/module_executor.py +++ b/plugins/module_utils/module_executor.py @@ -305,34 +305,30 @@ def _add_nitro_attributes_aliases(self, payload): @trace def _filter_resource_module_params(self): + def filter_values(value): + if isinstance(value, dict): + cleaned_dict = {} + for k, v in value.items(): + cleaned_v = filter_values(v) + if cleaned_v is not None: + cleaned_dict[k] = cleaned_v + return cleaned_dict if cleaned_dict else None + elif isinstance(value, list): + cleaned_list = [] + for item in value: + cleaned_item = filter_values(item) + if cleaned_item is not None: + cleaned_list.append(cleaned_item) + return cleaned_list if cleaned_list else None + else: + return value if value is not None else None + log("DEBUG: self.module.params: %s" % self.module.params) for k, v in self.module.params.items(): - if (not k.endswith("_binding")) and ( - k - in NITRO_RESOURCE_MAP[self.resource_name]["readwrite_arguments"].keys() - ): - # self.module.params is a dict of key:value pairs. If an attribute is not - # defined in the playbook, it's value will be None. So, filter out those attributes. - # Also, filter out attributes ending with `_binding` as they are handled separately - if v is not None: - if isinstance(v, list): - filtered_list = [] - for listitem in v: - if isinstance(listitem, dict): - filtered_dict = { - key: value - for key, value in listitem.items() - if value is not None - } - if filtered_dict: - filtered_list.append(filtered_dict) - else: - if listitem is not None: - filtered_list.append(listitem) - if filtered_list: - self.resource_module_params[k] = filtered_list - else: - self.resource_module_params[k] = v + if not k.endswith("_binding") and k in NITRO_RESOURCE_MAP[self.resource_name]["readwrite_arguments"]: + cleaned_value = filter_values(v) + if cleaned_value is not None: + self.resource_module_params[k] = cleaned_value if self.resource_name in NITRO_ATTRIBUTES_ALIASES: self.resource_module_params = self._add_nitro_attributes_aliases( diff --git a/plugins/module_utils/nitro_resource_map.py b/plugins/module_utils/nitro_resource_map.py index 9a655f0db..f44adaa1b 100644 --- a/plugins/module_utils/nitro_resource_map.py +++ b/plugins/module_utils/nitro_resource_map.py @@ -16647,7 +16647,8 @@ "choices": ["ipv4", "ipv6"], }, "routeMap": { - "type": "dict", + "type": "list", + "elements": "dict", "options": { "direction": { "no_log": False, @@ -16666,6 +16667,7 @@ "multihopBfd": {"no_log": False, "type": "bool"}, "remoteAS": {"no_log": False, "type": "int"}, "singlehopBfd": {"no_log": False, "type": "bool"}, + "state": {"no_log": False, "type": "str"}, "updateSource": {"no_log": False, "type": "str"}, }, }, @@ -46870,7 +46872,19 @@ "transmitDelay", ], }, - "add_payload_keys": [], + "add_payload_keys": [ + "areaId", + "cost", + "deadInterval", + "helloInterval", + "instanceId", + "name", + "networkType", + "priority", + "retransmitInterval", + "tagId", + "transmitDelay", + ], "bindings": [], "bindprimary_key": "", "delete_arg_keys": [], @@ -46921,7 +46935,7 @@ "no_log": False, "type": "int", }, - "tagId": {"no_log": False, "type": "int"}, + "tagId": {"no_log": False, "type": "str"}, "transmitDelay": { "no_log": False, "type": "int", @@ -46997,7 +47011,6 @@ "update_payload_keys": [ "afParams", "passiveInterface", - "routerId", "tagId", ], }, @@ -47025,11 +47038,18 @@ ], }, "add_payload_keys": [ - "networks", - "passiveInterface", - "processId", - "redistribute", - "routerId", + "authKey", + "authType", + "bfd", + "cost", + "deadInterval", + "helloInterval", + "mtu", + "name", + "networkType", + "priority", + "retransmitInterval", + "transmitDelay" ], "bindings": [], "bindprimary_key": "", @@ -47054,7 +47074,11 @@ "helloInterval": {"no_log": False, "type": "int"}, "mtu": {"no_log": False, "type": "int"}, "name": {"no_log": False, "type": "str"}, - "networkType": {"no_log": False, "type": "str"}, + "networkType": { + "no_log": False, + "type": "str", + "choices": ["broadcast", "non-broadcast", "point-to-multipoint", "point-to-point"], + }, "priority": {"no_log": False, "type": "int"}, "retransmitInterval": {"no_log": False, "type": "int"}, "transmitDelay": {"no_log": False, "type": "int"}, @@ -47111,7 +47135,8 @@ "primary_key_composite": [], "readwrite_arguments": { "networks": { - "type": "dict", + "type": "list", + "elements": "dict", "options": { "area": {"no_log": False, "type": "int"}, "ipaddress": {"no_log": False, "type": "str"}, @@ -47121,7 +47146,8 @@ "passiveInterface": {"no_log": False, "type": "list", "elements": "str"}, "processId": {"no_log": False, "type": "int"}, "redistribute": { - "type": "dict", + "type": "list", + "elements": "dict", "options": { "metric": {"no_log": False, "type": "int"}, "metricType": {"no_log": False, "type": "int"}, @@ -50490,7 +50516,7 @@ }, }, "singleton": False, - "update_payload_keys": ["rules"], + "update_payload_keys": ["name", "rules"], }, "routerdynamicrouting": { "_supported_operations": [ diff --git a/plugins/modules/accesslist.py b/plugins/modules/accesslist.py index aeeba00ef..badeceb23 100644 --- a/plugins/modules/accesslist.py +++ b/plugins/modules/accesslist.py @@ -73,6 +73,20 @@ """ EXAMPLES = r""" +--- +- name: Configure accessslist + hosts: localhost + tasks: + - name: Create accesslist + delegate_to: localhost + netscaler.adc.accesslist: + state: present + id: 1 + remark: "Allow all traffic" + rules: + - action: permit + address: 2.1.1.21 + wildcard: "0.0.0.255" """ RETURN = r""" --- diff --git a/plugins/modules/bfdinterface.py b/plugins/modules/bfdinterface.py index 01a512498..6adb1a979 100644 --- a/plugins/modules/bfdinterface.py +++ b/plugins/modules/bfdinterface.py @@ -74,6 +74,19 @@ """ EXAMPLES = r""" +--- +- name: Configure bfdinterface + hosts: localhost + tasks: + - name: Create bfdinterface + delegate_to: localhost + netscaler.adc.bfdinterface: + state: present + name: vlan0 + passive: true + interval: 752 + minrx: 501 + multiplier: 3 """ RETURN = r""" --- diff --git a/plugins/modules/bgprouter.py b/plugins/modules/bgprouter.py index 7b491aad4..59e5fdfb5 100644 --- a/plugins/modules/bgprouter.py +++ b/plugins/modules/bgprouter.py @@ -126,21 +126,10 @@ - ipv4 - ipv6 routeMap: - type: dict + type: list description: - - Route maps applied to neighbor routes. - suboptions: - direction: - type: str - description: - - Apply the route-map to incoming or outgoing routes. - choices: - - in - - out - name: - type: str - description: - - Name of the route-map. + - Route map configuration. + elements: dict connectTimer: type: int description: @@ -179,6 +168,38 @@ """ EXAMPLES = r""" +--- +- name: Sample bgprouter playbook + hosts: localhost + gather_facts: false + tasks: + - name: Configure bgprouter + delegate_to: localhost + netscaler.adc.bgprouter: + state: present + + localAS: 122 + routerId: "2.2.2.2" + afParams: + - addressFamily: "ipv4" + redistribute: + - protocol: "static" + routeMap: "test" + - addressFamily: "ipv6" + neighbor: + - address: "44.1.1.33" + remoteAS: 300 + ASOriginationInterval: 11 + advertisementInterval: 34 + updateSource: "vlan101" + singlehopBfd: false + multihopBfd: false + afParams: + - addressFamily: "ipv4" + routeMap: + - name: "test" + direction: "out" + - addressFamily: "ipv6" """ RETURN = r""" diff --git a/plugins/modules/iproute.py b/plugins/modules/iproute.py index 4c7b6e72b..ce438e6cf 100644 --- a/plugins/modules/iproute.py +++ b/plugins/modules/iproute.py @@ -96,6 +96,18 @@ """ EXAMPLES = r""" +--- +- name: Configure bfdinterface + hosts: localhost + tasks: + - name: Create bfdinterface + delegate_to: localhost + netscaler.adc.iproute: + state: present + addressFamily: ipv4 + prefix: 33.1.1.0 + prefixLength: 24 + nextHop: 10.106.210.24 """ RETURN = r""" --- diff --git a/plugins/modules/ospf6interface.py b/plugins/modules/ospf6interface.py index e85d3b988..c9d5340ab 100644 --- a/plugins/modules/ospf6interface.py +++ b/plugins/modules/ospf6interface.py @@ -84,9 +84,9 @@ - Time between retransmitting lost link state advertisements. tagId: - type: int + type: str description: - - OSPFv3 Tag. + - Tag identifier. transmitDelay: type: int @@ -104,6 +104,18 @@ """ EXAMPLES = r""" +--- +- name: Configure ospf6interface + hosts: localhost + tasks: + - name: Create ospf6interface + delegate_to: localhost + netscaler.adc.ospf6interface: + state: present + name: "vlan10" + areaId: 0 + tagId: "22" + instanceId: 0 """ RETURN = r""" --- diff --git a/plugins/modules/ospf6router.py b/plugins/modules/ospf6router.py index a16cce31b..4cc099c3c 100644 --- a/plugins/modules/ospf6router.py +++ b/plugins/modules/ospf6router.py @@ -95,6 +95,21 @@ """ EXAMPLES = r""" +--- +- name: Sample ospf6router playbook + hosts: localhost + gather_facts: false + tasks: + - name: Configure ospf6router + delegate_to: localhost + netscaler.adc.ospf6router: + state: unset + tagId: 11 + afParams: + - addressFamily: ipv6 + redistribute: + - protocol: static + metric: 111 """ RETURN = r""" diff --git a/plugins/modules/ospfinterface.py b/plugins/modules/ospfinterface.py index 274f158c4..49b6259e2 100644 --- a/plugins/modules/ospfinterface.py +++ b/plugins/modules/ospfinterface.py @@ -83,6 +83,7 @@ - Name of the OSPF interface. networkType: type: str + choices: ['broadcast', 'non-broadcast', 'point-to-multipoint', 'point-to-point'] description: - Type of the OSPF network. priority: @@ -103,6 +104,27 @@ """ EXAMPLES = r""" +--- +- name: Sample ospfinterface playbook + hosts: localhost + gather_facts: false + tasks: + - name: Configure ospfinterface + delegate_to: localhost + netscaler.adc.ospfinterface: + state: present + + name: vlan100 + helloInterval: 10 + deadInterval: 40 + cost: 10 + priority: 1 + mtu: 1500 + networkType: broadcast + authType: "null" + retransmitInterval: 5 + transmitDelay: 1 + bfd: false """ RETURN = r""" diff --git a/plugins/modules/ospfrouter.py b/plugins/modules/ospfrouter.py index e413a062d..be34631bf 100644 --- a/plugins/modules/ospfrouter.py +++ b/plugins/modules/ospfrouter.py @@ -50,28 +50,92 @@ in the resource. - If no, the module will return error if any non-updatable parameters are provided. type: str - buffersizemb: + routerId: + description: + - Router ID for the OSPF process + type: str + processId: + description: + - OSPF process ID type: int + networks: description: - - Buffer size, in MB, allocated for log transaction data on the system. The - maximum value is limited to the memory available on the system. - customreqhdrs: + - Network configurations for OSPF type: list + elements: dict + suboptions: + ipaddress: + description: + - IP address of the network + type: str + netmask: + description: + - Network mask + type: int + area: + description: + - OSPF area ID + type: int + passiveInterface: description: - - Name(s) of HTTP request headers whose values should be exported by the Web - Logging feature. - elements: str - customrsphdrs: + - List of passive interfaces type: list - description: - - Name(s) of HTTP response headers whose values should be exported by the Web - Logging feature. elements: str + redistribute: + description: + - Route redistribution configuration + type: list + elements: dict + suboptions: + protocol: + description: + - Protocol to redistribute + type: str + choices: ['bgp', 'connected', 'intranet', 'isis', 'kernel', 'ospf', 'rip', 'static'] + metric: + description: + - Metric for redistributed routes + type: int + metricType: + description: + - Metric type for redistributed routes + type: int + routeMap: + description: + - Route map for redistribution + type: str + tag: + description: + - Tag for redistributed routes + type: int + ospfProcessId: + description: + - OSPF process ID for redistribution + type: int extends_documentation_fragment: netscaler.adc.netscaler_adc """ EXAMPLES = r""" +--- +- name: Sample ospfrouter playbook + hosts: localhost + gather_facts: false + tasks: + - name: Configure ospfrouter + delegate_to: localhost + netscaler.adc.ospfrouter: + state: present + processId: 1 + routerId: "1.1.1.1" + passiveInterface: + - vlan22 + redistribute: + - protocol: connected + networks: + - ipaddress: "33.1.2.5" + netmask: 25 + area: 1 """ RETURN = r""" diff --git a/plugins/modules/routemap.py b/plugins/modules/routemap.py index 91d6974be..7e45d23bc 100644 --- a/plugins/modules/routemap.py +++ b/plugins/modules/routemap.py @@ -135,6 +135,21 @@ extends_documentation_fragment: netscaler.adc.netscaler_adc """ EXAMPLES = r""" +--- +- name: Sample routeMap playbook + hosts: localhost + gather_facts: false + tasks: + - name: Configure routeMap + delegate_to: localhost + netscaler.adc.routemap: + state: present + name: test + rules: + - action: permit + sequence: 10 + setMetric: 105 + matchAsPath: "300" """ RETURN = r""" --- From e4c4887ca216e34db1bc1c066507b1fa8330a4c3 Mon Sep 17 00:00:00 2001 From: Shiva Shankar Vaddepally Date: Thu, 16 Oct 2025 12:46:28 +0000 Subject: [PATCH 6/9] solving pip issues Signed-off-by: Shiva Shankar Vaddepally --- plugins/module_utils/nitro_resource_map.py | 3 +-- plugins/modules/accesslist.py | 3 +-- plugins/modules/bfdinterface.py | 2 +- plugins/modules/bgprouter.py | 14 +++++++++++++- plugins/modules/ospf6interface.py | 3 +-- plugins/modules/routemap.py | 8 +++----- 6 files changed, 20 insertions(+), 13 deletions(-) diff --git a/plugins/module_utils/nitro_resource_map.py b/plugins/module_utils/nitro_resource_map.py index f44adaa1b..44708ca98 100644 --- a/plugins/module_utils/nitro_resource_map.py +++ b/plugins/module_utils/nitro_resource_map.py @@ -16667,7 +16667,6 @@ "multihopBfd": {"no_log": False, "type": "bool"}, "remoteAS": {"no_log": False, "type": "int"}, "singlehopBfd": {"no_log": False, "type": "bool"}, - "state": {"no_log": False, "type": "str"}, "updateSource": {"no_log": False, "type": "str"}, }, }, @@ -50497,7 +50496,7 @@ "type": "list", "elements": "dict", "options": { - "action": {"no_log": False, "type": "str"}, + "action": {"no_log": False, "type": "str", "choices": ["permit", "deny"]}, "localPreference": {"no_log": False, "type": "int"}, "matchAsPath": {"no_log": False, "type": "str"}, "matchCommunity": {"no_log": False, "type": "str"}, diff --git a/plugins/modules/accesslist.py b/plugins/modules/accesslist.py index badeceb23..013c3b4d7 100644 --- a/plugins/modules/accesslist.py +++ b/plugins/modules/accesslist.py @@ -26,13 +26,12 @@ options: state: type: str - choices: ["absent", "present", "unset"] + choices: ["absent", "present"] default: present description: - The state of the resource on the NetScaler ADC node. - When C(present), the resource will be added or updated. - When C(absent), the resource will be deleted. - - When C(unset), the resource will be unset. id: type: str description: diff --git a/plugins/modules/bfdinterface.py b/plugins/modules/bfdinterface.py index 6adb1a979..bfb4284a2 100644 --- a/plugins/modules/bfdinterface.py +++ b/plugins/modules/bfdinterface.py @@ -26,7 +26,7 @@ options: state: type: str - choices: ["present", "unset"] + choices: ["present"] default: present description: - The state of the resource on the NetScaler ADC node. diff --git a/plugins/modules/bgprouter.py b/plugins/modules/bgprouter.py index 59e5fdfb5..6e3aa8ffe 100644 --- a/plugins/modules/bgprouter.py +++ b/plugins/modules/bgprouter.py @@ -27,7 +27,6 @@ - Shiva Shankar Vaddepally (@shivashankar-vaddepally) options: state: - type: str choices: - present - absent @@ -38,6 +37,7 @@ - When C(present), the resource will be added/updated configured according to the module's parameters. - When C(absent), the resource will be deleted from the NetScaler ADC node. - When C(unset), the resource will be unset on the NetScaler ADC node. + type: str remove_non_updatable_params: type: str choices: @@ -130,6 +130,18 @@ description: - Route map configuration. elements: dict + suboptions: + name: + type: str + description: + - Name of the route map. + direction: + type: str + description: + - Direction for the route map. + choices: + - in + - out connectTimer: type: int description: diff --git a/plugins/modules/ospf6interface.py b/plugins/modules/ospf6interface.py index c9d5340ab..8518f145b 100644 --- a/plugins/modules/ospf6interface.py +++ b/plugins/modules/ospf6interface.py @@ -26,12 +26,11 @@ options: state: type: str - choices: ["present", "unset"] + choices: ["present"] default: present description: - The state of the resource on the NetScaler ADC node. - When C(present), the resource will be added or updated. - - When C(unset), the resource will be unset. areaId: type: int diff --git a/plugins/modules/routemap.py b/plugins/modules/routemap.py index 7e45d23bc..0cea40304 100644 --- a/plugins/modules/routemap.py +++ b/plugins/modules/routemap.py @@ -26,14 +26,12 @@ options: state: type: str - choices: ["present", "absent", "unset", "get"] + choices: ["present", "absent"] default: present description: - The state of the resource on the NetScaler ADC node. - When C(present), the resource will be added or updated. - When C(absent), the resource will be deleted. - - When C(unset), the resource will be unset. - - When C(get), retrieve the resource. - When C(delete), delete the resource. name: @@ -85,7 +83,7 @@ matchRouteType: type: str - choices: ["type-1", "type-2"] + choices: [] description: - Match OSPF external routes of type 1 or type 2 metrics. @@ -116,7 +114,7 @@ setMetricType: type: str - choices: ["type-1", "type-2"] + choices: [] description: - Set type of metric for destination routing protocol. OSPF external type 1 metric or OSPF external type 2 metric. From 93d41d132c56ac2c474042fbc4038a2c6e55c824 Mon Sep 17 00:00:00 2001 From: Shiva Shankar Vaddepally Date: Thu, 16 Oct 2025 14:31:08 +0000 Subject: [PATCH 7/9] solving pip issues Signed-off-by: Shiva Shankar Vaddepally --- plugins/modules/accesslist.py | 3 ++- plugins/modules/bfdinterface.py | 3 ++- plugins/modules/bgprouter.py | 2 +- plugins/modules/ospf6interface.py | 3 ++- plugins/modules/routemap.py | 5 +++-- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/plugins/modules/accesslist.py b/plugins/modules/accesslist.py index 013c3b4d7..badeceb23 100644 --- a/plugins/modules/accesslist.py +++ b/plugins/modules/accesslist.py @@ -26,12 +26,13 @@ options: state: type: str - choices: ["absent", "present"] + choices: ["absent", "present", "unset"] default: present description: - The state of the resource on the NetScaler ADC node. - When C(present), the resource will be added or updated. - When C(absent), the resource will be deleted. + - When C(unset), the resource will be unset. id: type: str description: diff --git a/plugins/modules/bfdinterface.py b/plugins/modules/bfdinterface.py index bfb4284a2..dedcf43d9 100644 --- a/plugins/modules/bfdinterface.py +++ b/plugins/modules/bfdinterface.py @@ -26,11 +26,12 @@ options: state: type: str - choices: ["present"] + choices: ["present", "unset"] default: present description: - The state of the resource on the NetScaler ADC node. - When C(present), the resource will be added or updated. + - When C(unset), the resource will be unset. interval: type: int description: diff --git a/plugins/modules/bgprouter.py b/plugins/modules/bgprouter.py index 6e3aa8ffe..162c2fedb 100644 --- a/plugins/modules/bgprouter.py +++ b/plugins/modules/bgprouter.py @@ -27,6 +27,7 @@ - Shiva Shankar Vaddepally (@shivashankar-vaddepally) options: state: + type: str choices: - present - absent @@ -37,7 +38,6 @@ - When C(present), the resource will be added/updated configured according to the module's parameters. - When C(absent), the resource will be deleted from the NetScaler ADC node. - When C(unset), the resource will be unset on the NetScaler ADC node. - type: str remove_non_updatable_params: type: str choices: diff --git a/plugins/modules/ospf6interface.py b/plugins/modules/ospf6interface.py index 8518f145b..c9d5340ab 100644 --- a/plugins/modules/ospf6interface.py +++ b/plugins/modules/ospf6interface.py @@ -26,11 +26,12 @@ options: state: type: str - choices: ["present"] + choices: ["present", "unset"] default: present description: - The state of the resource on the NetScaler ADC node. - When C(present), the resource will be added or updated. + - When C(unset), the resource will be unset. areaId: type: int diff --git a/plugins/modules/routemap.py b/plugins/modules/routemap.py index 0cea40304..4ac4d97df 100644 --- a/plugins/modules/routemap.py +++ b/plugins/modules/routemap.py @@ -26,13 +26,14 @@ options: state: type: str - choices: ["present", "absent"] + choices: ["present", "absent", "unset] default: present description: - The state of the resource on the NetScaler ADC node. - When C(present), the resource will be added or updated. - When C(absent), the resource will be deleted. - - When C(delete), delete the resource. + - When C(unset), the resource will be unset. + name: type: str From 59c17cfc00e949d1f54f7d1d4baa2ffc77f2c597 Mon Sep 17 00:00:00 2001 From: Shiva Shankar Vaddepally Date: Thu, 16 Oct 2025 16:49:59 +0000 Subject: [PATCH 8/9] resolving pep issues Signed-off-by: Shiva Shankar Vaddepally --- plugins/module_utils/nitro_resource_map.py | 2 +- plugins/modules/routemap.py | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/module_utils/nitro_resource_map.py b/plugins/module_utils/nitro_resource_map.py index 44708ca98..e837b2c47 100644 --- a/plugins/module_utils/nitro_resource_map.py +++ b/plugins/module_utils/nitro_resource_map.py @@ -50496,7 +50496,7 @@ "type": "list", "elements": "dict", "options": { - "action": {"no_log": False, "type": "str", "choices": ["permit", "deny"]}, + "action": {"no_log": False, "type": "str"}, "localPreference": {"no_log": False, "type": "int"}, "matchAsPath": {"no_log": False, "type": "str"}, "matchCommunity": {"no_log": False, "type": "str"}, diff --git a/plugins/modules/routemap.py b/plugins/modules/routemap.py index 4ac4d97df..779653a49 100644 --- a/plugins/modules/routemap.py +++ b/plugins/modules/routemap.py @@ -26,14 +26,13 @@ options: state: type: str - choices: ["present", "absent", "unset] + choices: ["unset", "absent", "present"] default: present description: - The state of the resource on the NetScaler ADC node. - When C(present), the resource will be added or updated. - When C(absent), the resource will be deleted. - When C(unset), the resource will be unset. - name: type: str @@ -48,7 +47,6 @@ suboptions: action: type: str - choices: ["permit", "deny"] description: - Specifies if the route-map denies or permits the operations. From f37469fb6382de9814cd836f4262aaaba1c9fb77 Mon Sep 17 00:00:00 2001 From: Shiva Shankar Vaddepally <86876400+shivashankar-vaddepally@users.noreply.github.com> Date: Fri, 17 Oct 2025 23:43:04 +0530 Subject: [PATCH 9/9] Delete examples/location.yaml --- examples/location.yaml | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 examples/location.yaml diff --git a/examples/location.yaml b/examples/location.yaml deleted file mode 100644 index 19f02c58d..000000000 --- a/examples/location.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- -- name: Sample location playbook - hosts: localhost - gather_facts: false - tasks: - - name: Configure location - delegate_to: localhost - netscaler.adc.location: - state: present - ipfrom: "170.173.217.0" - ipto: "170.173.222.255" - preferredlocation: "prov.WA.sdc.*.*.*"