Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions Makefile
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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"; \
Expand Down
32 changes: 32 additions & 0 deletions examples/bgprouter.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
- 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"
1 change: 1 addition & 0 deletions meta/runtime.yml
Original file line number Diff line number Diff line change
Expand Up @@ -981,3 +981,4 @@ action_groups:
- sslechconfig.py
- sslhpkekey.py
- sslprofile_sslechconfig_binding.py
- bgprouter
10 changes: 9 additions & 1 deletion plugins/module_utils/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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"],
Expand Down Expand Up @@ -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
Expand Down
47 changes: 37 additions & 10 deletions plugins/module_utils/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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, [
Expand All @@ -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.
Expand Down Expand Up @@ -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


Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
25 changes: 25 additions & 0 deletions plugins/module_utils/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,28 @@
"sslkeyfile",
"systementitydata",
]

# Dynamic Protocol list
DYNAMIC_PROTOCOLS = [
"accesslist",
"bfdinterface",
"iproute",
"ospf6interface",
"routemap",
"ospf6router",
"bgprouter",
"ospfrouter",
"ospfinterface",
]

DYNAMIC_PROTOCOLS_ALIAS = {
"accesslist": "accessList",
"bfdinterface": "bfdInterface",
"iproute": "ipRoute",
"ospf6interface": "ospf6Interface",
"routemap": "routeMap",
"ospf6router": "ospf6Router",
"bgprouter": "bgpRouter",
"ospfrouter": "ospfRouter",
"ospfinterface": "ospfInterface",
}
71 changes: 45 additions & 26 deletions plugins/module_utils/module_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,17 +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:
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(
Expand Down Expand Up @@ -500,7 +513,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)
Expand All @@ -510,9 +523,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)

Expand Down Expand Up @@ -580,15 +593,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(
Expand Down Expand Up @@ -707,7 +726,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)
Expand All @@ -717,9 +736,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:
Expand Down
Loading