Skip to content

Commit cbd6a7b

Browse files
Merge pull request #557 from netscaler/NSNETAUTO-1005_SupportingBGP
NSNETAUTO-1005 supporting dynamic protocols -BGP
2 parents 700874f + d98141a commit cbd6a7b

File tree

12 files changed

+535
-54
lines changed

12 files changed

+535
-54
lines changed

Makefile

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
fmt:
2-
autoflake plugins/modules/*.py
3-
autoflake plugins/module_utils/*.py
4-
autoflake --recursive tests/
5-
autoflake tools/migrationtool/*py
2+
autoflake plugins/modules/*.py
3+
autoflake plugins/module_utils/*.py
4+
autoflake --recursive tests/
5+
autoflake tools/migrationtool/*.py
66

7-
black plugins/modules/*.py
8-
black plugins/module_utils/*.py
9-
black tests/
10-
black tools/migrationtool/*.py
7+
black plugins/modules/*.py
8+
black plugins/module_utils/*.py
9+
black tests/
10+
black tools/migrationtool/*.py
1111

12-
isort plugins/modules/*.py
13-
isort plugins/module_utils/*.py
14-
isort tests/
15-
isort tools/migrationtool/*.py
12+
isort plugins/modules/*.py
13+
isort plugins/module_utils/*.py
14+
isort tests/
15+
isort tools/migrationtool/*.py
1616

17-
yamlfmt $(shell find . -name '*.yml' -o -name '*.yaml')
17+
yamlfmt $(shell find . -name '*.yml' -o -name '*.yaml')
1818

1919
install:
2020
ansible-galaxy collection install . --force
@@ -63,7 +63,7 @@ galaxy_importer: build
6363
# skip the playbook which contains "password" in the file name
6464
run_examples:
6565
@for playbook in examples/*.yaml; do \
66-
if [[ $$playbook == *"password"* || $$playbook == *"login"* || $$playbook == *"logout"* || $$playbook == *"route"* || $$playbook == locationfile.yaml || $$playbook == nsip6.yaml || $$playbook == hanode.yaml ]]; then \
66+
if [[ $$playbook == *"password"* || $$playbook == *"login"* || $$playbook == *"logout"* || $$playbook == *"route"* || $$playbook == *"locationfile.yaml"* || $$playbook == *"nsip6.yaml"* || $$playbook == *"hanode.yaml"* ]]; then \
6767
continue; \
6868
fi; \
6969
echo "Running $$playbook"; \

examples/bgprouter.yaml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
- name: Sample bgprouter playbook
3+
hosts: localhost
4+
gather_facts: false
5+
tasks:
6+
- name: Configure bgprouter
7+
delegate_to: localhost
8+
netscaler.adc.bgprouter:
9+
state: present
10+
11+
localAS: 122
12+
routerId: "2.2.2.2"
13+
afParams:
14+
- addressFamily: "ipv4"
15+
redistribute:
16+
- protocol: "static"
17+
routeMap: "test"
18+
- addressFamily: "ipv6"
19+
neighbor:
20+
- address: "44.1.1.33"
21+
remoteAS: 300
22+
ASOriginationInterval: 11
23+
advertisementInterval: 34
24+
updateSource: "vlan101"
25+
singlehopBfd: false
26+
multihopBfd: false
27+
afParams:
28+
- addressFamily: "ipv4"
29+
routeMap:
30+
- name: "test"
31+
direction: "out"
32+
- addressFamily: "ipv6"

meta/runtime.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -981,3 +981,4 @@ action_groups:
981981
- sslechconfig.py
982982
- sslhpkekey.py
983983
- sslprofile_sslechconfig_binding.py
984+
- bgprouter

plugins/module_utils/client.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@
1515
from ansible.module_utils.six.moves.urllib.parse import quote
1616
from ansible.module_utils.urls import fetch_url
1717

18-
from .constants import HTTP_SUCCESS_CODES
18+
from .constants import (
19+
DYNAMIC_PROTOCOLS,
20+
DYNAMIC_PROTOCOLS_ALIAS,
21+
HTTP_SUCCESS_CODES,
22+
)
1923
from .decorators import trace
2024
from .logger import log
2125

@@ -98,6 +102,9 @@ def url_builder(
98102
filter = filter if filter is not None else {}
99103

100104
# Construct basic URL
105+
if resource in DYNAMIC_PROTOCOLS:
106+
resource = "routerDynamicRouting/" + DYNAMIC_PROTOCOLS_ALIAS[resource]
107+
101108
url = "%s://%s/%s/%s" % (
102109
self._module.params["nitro_protocol"],
103110
self._module.params["nsip"],
@@ -235,6 +242,7 @@ def post(self, post_data, resource, action=None):
235242
def put(self, put_data, resource=None, id=None):
236243
url = self.url_builder(resource, id=id)
237244
data = self._module.jsonify(put_data)
245+
238246
return self.send("PUT", url, data)
239247

240248
@trace

plugins/module_utils/common.py

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import re
1111

1212
from .constants import (
13+
DYNAMIC_PROTOCOLS,
14+
DYNAMIC_PROTOCOLS_ALIAS,
1315
GLOBAL_BINDING_ARG_LIST,
1416
HTTP_RESOURCE_ALREADY_EXISTS,
1517
HTTP_RESOURCE_NOT_FOUND,
@@ -94,16 +96,29 @@ def get_resource(client, resource_name, resource_id=None, resource_module_params
9496
args=get_args,
9597
)
9698
else:
97-
status_code, response_body = client.get(
98-
resource=resource_name,
99-
id=resource_id,
100-
args=get_args,
101-
)
99+
if resource_name in DYNAMIC_PROTOCOLS:
100+
new_resource_name = (
101+
"routerDynamicRouting/" + DYNAMIC_PROTOCOLS_ALIAS[resource_name]
102+
)
103+
status_code, response_body = client.get(
104+
resource=new_resource_name,
105+
id=resource_id,
106+
args=get_args,
107+
)
108+
else:
109+
status_code, response_body = client.get(
110+
resource=resource_name,
111+
id=resource_id,
112+
args=get_args,
113+
)
102114
if status_code in {HTTP_RESOURCE_NOT_FOUND}:
103115
return False, []
104116
if status_code in HTTP_SUCCESS_CODES:
105117
# for zero bindings and some resources, the response_body will be {'errorcode': 0, 'message': 'Done', 'severity': 'NONE'}
106-
if resource_name not in response_body:
118+
if (
119+
resource_name not in response_body
120+
and resource_name not in DYNAMIC_PROTOCOLS
121+
):
107122
if resource_name == "sslcipher":
108123
resource_primary_key = NITRO_RESOURCE_MAP[resource_name]["primary_key"]
109124
return True, [
@@ -112,7 +127,12 @@ def get_resource(client, resource_name, resource_id=None, resource_module_params
112127

113128
return False, []
114129
# `update-only` resources return a dict instead of a list.
115-
return_response = response_body[resource_name]
130+
if resource_name in DYNAMIC_PROTOCOLS:
131+
return_response = response_body.get("routerDynamicRouting", {}).get(
132+
DYNAMIC_PROTOCOLS_ALIAS[resource_name], {}
133+
)
134+
else:
135+
return_response = response_body[resource_name]
116136
# FIXME: NITRO-BUG: for some resources like `policypatset_pattern_binding`, NITRO returns keys with uppercase. eg: `String` for `string`.
117137
# So, we are converting the keys to lowercase.
118138
# 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=
242262
key, resource_name, action.upper()
243263
)
244264
)
245-
246265
return True, None, post_data
247266

248267

@@ -281,7 +300,11 @@ def create_resource(client, resource_name, resource_module_params, action=None):
281300
if not ok:
282301
return False, err
283302

284-
post_data = {resource_name: post_data}
303+
if resource_name in DYNAMIC_PROTOCOLS:
304+
post_data = {DYNAMIC_PROTOCOLS_ALIAS[resource_name]: post_data}
305+
post_data = {"routerDynamicRouting": post_data}
306+
else:
307+
post_data = {resource_name: post_data}
285308
status_code, response_body = client.post(
286309
post_data=post_data,
287310
resource=resource_name,
@@ -355,7 +378,11 @@ def update_resource(client, resource_name, resource_module_params):
355378
if not ok:
356379
return False, err
357380

358-
put_data = {resource_name: put_payload}
381+
if resource_name in DYNAMIC_PROTOCOLS:
382+
put_data = {DYNAMIC_PROTOCOLS_ALIAS[resource_name]: put_payload}
383+
put_data = {"routerDynamicRouting": put_data}
384+
else:
385+
put_data = {resource_name: put_payload}
359386

360387
status_code, response_body = client.put(
361388
put_data=put_data,

plugins/module_utils/constants.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,28 @@
151151
"sslkeyfile",
152152
"systementitydata",
153153
]
154+
155+
# Dynamic Protocol list
156+
DYNAMIC_PROTOCOLS = [
157+
"accesslist",
158+
"bfdinterface",
159+
"iproute",
160+
"ospf6interface",
161+
"routemap",
162+
"ospf6router",
163+
"bgprouter",
164+
"ospfrouter",
165+
"ospfinterface",
166+
]
167+
168+
DYNAMIC_PROTOCOLS_ALIAS = {
169+
"accesslist": "accessList",
170+
"bfdinterface": "bfdInterface",
171+
"iproute": "ipRoute",
172+
"ospf6interface": "ospf6Interface",
173+
"routemap": "routeMap",
174+
"ospf6router": "ospf6Router",
175+
"bgprouter": "bgpRouter",
176+
"ospfrouter": "ospfRouter",
177+
"ospfinterface": "ospfInterface",
178+
}

plugins/module_utils/module_executor.py

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -305,17 +305,30 @@ def _add_nitro_attributes_aliases(self, payload):
305305

306306
@trace
307307
def _filter_resource_module_params(self):
308+
def filter_values(value):
309+
if isinstance(value, dict):
310+
cleaned_dict = {}
311+
for k, v in value.items():
312+
cleaned_v = filter_values(v)
313+
if cleaned_v is not None:
314+
cleaned_dict[k] = cleaned_v
315+
return cleaned_dict if cleaned_dict else None
316+
elif isinstance(value, list):
317+
cleaned_list = []
318+
for item in value:
319+
cleaned_item = filter_values(item)
320+
if cleaned_item is not None:
321+
cleaned_list.append(cleaned_item)
322+
return cleaned_list if cleaned_list else None
323+
else:
324+
return value if value is not None else None
325+
308326
log("DEBUG: self.module.params: %s" % self.module.params)
309327
for k, v in self.module.params.items():
310-
if (not k.endswith("_binding")) and (
311-
k
312-
in NITRO_RESOURCE_MAP[self.resource_name]["readwrite_arguments"].keys()
313-
):
314-
# self.module.params is a dict of key:value pairs. If an attribute is not
315-
# defined in the playbook, it's value will be None. So, filter out those attributes.
316-
# Also, filter out attributes ending with `_binding` as they are handled separately
317-
if v is not None:
318-
self.resource_module_params[k] = v
328+
if not k.endswith("_binding") and k in NITRO_RESOURCE_MAP[self.resource_name]["readwrite_arguments"]:
329+
cleaned_value = filter_values(v)
330+
if cleaned_value is not None:
331+
self.resource_module_params[k] = cleaned_value
319332

320333
if self.resource_name in NITRO_ATTRIBUTES_ALIASES:
321334
self.resource_module_params = self._add_nitro_attributes_aliases(
@@ -500,7 +513,7 @@ def create_or_update(self):
500513
errorcode = None
501514
message = None
502515
err_str = str(err)
503-
regex_string = re.search(r'status_code:\s*(\d+)', err_str)
516+
regex_string = re.search(r"status_code:\s*(\d+)", err_str)
504517
if regex_string:
505518
status_code = int(regex_string.group(1))
506519
regex_string = re.search(r"'errorcode':\s*(\d+)", err_str)
@@ -510,9 +523,9 @@ def create_or_update(self):
510523
if regex_string:
511524
message = regex_string.group(1)
512525
if not (
513-
status_code == 599 and
514-
errorcode == 1065 and
515-
message == "Internal error while adding HSM key."
526+
status_code == 599
527+
and errorcode == 1065
528+
and message == "Internal error while adding HSM key."
516529
):
517530
self.return_failure(err)
518531

@@ -580,15 +593,21 @@ def create_or_update(self):
580593
"INFO: Resource %s:%s exists and is different. Will be REMOVED and ADDED."
581594
% (self.resource_name, self.resource_id)
582595
)
583-
if self.resource_name == "systemfile":
584-
# If the systemfile is present, we will delete it and add it again
585-
self.delete()
586-
ok, err = create_resource(
587-
self.client, self.resource_name, self.resource_module_params
588-
)
589-
if not ok:
590-
self.return_failure(err)
591-
596+
# If the systemfile is present, we will delete it and add it again
597+
self.delete()
598+
ok, err = create_resource(
599+
self.client, self.resource_name, self.resource_module_params
600+
)
601+
if not ok:
602+
self.return_failure(err)
603+
elif self.resource_name == "location":
604+
# TODO: primary composite key needs to be added.
605+
# location resource has composite primary key. 1.ipfrom 2.ipto
606+
ok, err = create_resource(
607+
self.client, self.resource_name, self.resource_module_params
608+
)
609+
if not ok:
610+
self.return_failure(err)
592611
elif self.resource_name.endswith("_binding"):
593612
# Generally bindings are not updated. They are removed and added again.
594613
log(
@@ -707,7 +726,7 @@ def delete(self):
707726
errorcode = None
708727
message = None
709728
err_str = str(err)
710-
regex_string = re.search(r'status_code:\s*(\d+)', err_str)
729+
regex_string = re.search(r"status_code:\s*(\d+)", err_str)
711730
if regex_string:
712731
status_code = int(regex_string.group(1))
713732
regex_string = re.search(r"'errorcode':\s*(\d+)", err_str)
@@ -717,9 +736,9 @@ def delete(self):
717736
if regex_string:
718737
message = regex_string.group(1)
719738
if not (
720-
status_code == 599 and
721-
errorcode == 1065 and
722-
message == "Internal error while adding HSM key."
739+
status_code == 599
740+
and errorcode == 1065
741+
and message == "Internal error while adding HSM key."
723742
):
724743
self.return_failure(err)
725744
else:

0 commit comments

Comments
 (0)