Skip to content

Nsnetauto 1005 bgprouter #534

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
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
22 changes: 22 additions & 0 deletions examples/bgpRouter.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
- name: BGP routing
hosts: localhost
gather_facts: false
tasks:
- name: Configure BGP routing
delegate_to: localhost
netscaler.adc.bgpRouter:
state: present
localAS: 10
afParams.addressFamily: "ipv4"
afParams.redistribute.protocol: "kernel"
routerId: "10.102.201.219"
neighbor.ASOriginationInterval: 15
neighbor.address: "2.2.12.30"
neighbor.advertisementInterval: 30
neighbor.afParams.addressFamily: "ipv4"
neighbor.holdTimerConfig: 90
neighbor.keepaliveTimerConfig: 30
neighbor.multihopBfd: "False"
neighbor.remoteAS: 100
neighbor.singlehopBfd: "False"
1 change: 1 addition & 0 deletions meta/runtime.yml
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,7 @@ action_groups:
- route
- route6
- routerdynamicrouting
- bgpRouter
- rsskeytype
- save_config
- server
Expand Down
26 changes: 19 additions & 7 deletions plugins/module_utils/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
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 (
HTTP_SUCCESS_CODES,
NESTED_POST_DATA_RESOURCES,
)
from .decorators import trace
from .logger import log

Expand Down Expand Up @@ -98,12 +101,21 @@ def url_builder(
filter = filter if filter is not None else {}

# Construct basic URL
url = "%s://%s/%s/%s" % (
self._module.params["nitro_protocol"],
self._module.params["nsip"],
self.api_path,
resource,
)
if resource in NESTED_POST_DATA_RESOURCES:
url = "%s://%s/%s/%s/%s" % (
self._module.params["nitro_protocol"],
self._module.params["nsip"],
self.api_path,
"routerDynamicRouting",
resource,
)
else:
url = "%s://%s/%s/%s" % (
self._module.params["nitro_protocol"],
self._module.params["nsip"],
self.api_path,
resource,
)

# Append resource id
if id:
Expand Down
98 changes: 68 additions & 30 deletions plugins/module_utils/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@
HTTP_RESOURCE_ALREADY_EXISTS,
HTTP_RESOURCE_NOT_FOUND,
HTTP_SUCCESS_CODES,
NESTED_POST_DATA_RESOURCES,
)
from .decorators import trace
from .logger import log
from .nitro_resource_map import NITRO_RESOURCE_MAP


@trace
def get_netscaler_version(client):
is_exist, response = get_resource(client, "nsversion")
Expand Down Expand Up @@ -83,15 +83,25 @@ 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 NESTED_POST_DATA_RESOURCES:
status_code, response_body = client.get(
resource="routerDynamicRouting/%s" % 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 in NESTED_POST_DATA_RESOURCES:
if "routerDynamicRouting" in response_body:
response_body = response_body["routerDynamicRouting"]
if resource_name not in response_body:
if resource_name == "sslcipher":
resource_primary_key = NITRO_RESOURCE_MAP[resource_name]["primary_key"]
Expand All @@ -100,6 +110,7 @@ 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]
# FIXME: NITRO-BUG: for some resources like `policypatset_pattern_binding`, NITRO returns keys with uppercase. eg: `String` for `string`.
Expand All @@ -125,6 +136,7 @@ def get_resource(client, resource_name, resource_id=None, resource_module_params
resource_name, resource_module_params, return_response
)
return (True, return_response)
log("pass 3 ")
return False, []


Expand Down Expand Up @@ -178,7 +190,6 @@ def is_resource_exists(client, resource_name, resource_module_params):
)
return is_exists


@trace
def _check_create_resource_params(resource_name, resource_module_params, action=None):
post_data = {}
Expand Down Expand Up @@ -210,27 +221,49 @@ def _check_create_resource_params(resource_name, resource_module_params, action=
return False, msg, None
else:
# TODO: Should we allow non-add keys for the resource? OR should we error out if any non-add key is passed?
for key in resource_module_params.keys():
if not action:
if key in resource_add_keys:
post_data[key] = resource_module_params[key]
elif resource_name == "service" and key == "ipaddress":
post_data["ip"] = resource_module_params[key]
else:
log(
"WARNING: Key `{}` is not allowed for the resource `{}` for CREATE operation. Skipping the key for the operation".format(
key, resource_name
if resource_name in NESTED_POST_DATA_RESOURCES:
post_data = {"routerDynamicRouting": {resource_name: {}}}
resource_add_keys = NITRO_RESOURCE_MAP[resource_name]["add_payload_keys"]

for key in resource_module_params.keys():
if key in resource_add_keys:
keylist = key.split(".")
current_dict = post_data["routerDynamicRouting"][resource_name]
for i, k in enumerate(keylist):
if i == len(keylist) - 1:
current_dict[k] = resource_module_params[key]
else:
if k not in current_dict:
current_dict[k] = {}
current_dict = current_dict[k]
else:
log(
"WARNING: Key `{}` is not allowed for the resource `{}` for CREATE operation. Skipping the key for the operation".format(
key, resource_name
)
)
else:
for key in resource_module_params.keys():
if not action:
if key in resource_add_keys:
post_data[key] = resource_module_params[key]
elif resource_name == "service" and key == "ipaddress":
post_data["ip"] = resource_module_params[key]
else:
log(
"WARNING: Key `{}` is not allowed for the resource `{}` for CREATE operation. Skipping the key for the operation".format(
key, resource_name
)
)
)
else:
if key in resource_action_keys:
post_data[key] = resource_module_params[key]
else:
log(
"WARNING: Key `{}` is not allowed for the resource `{}` for `{}` action. Skipping the key for the operation".format(
key, resource_name, action.upper()
if key in resource_action_keys:
post_data[key] = resource_module_params[key]
else:
log(
"WARNING: Key `{}` is not allowed for the resource `{}` for `{}` action. Skipping the key for the operation".format(
key, resource_name, action.upper()
)
)
)

return True, None, post_data

Expand All @@ -240,10 +273,10 @@ def create_resource_with_action(client, resource_name, resource_module_params, a
ok, err, post_data = _check_create_resource_params(
resource_name, resource_module_params, action=action
)
if resource_name not in NESTED_POST_DATA_RESOURCES:
post_data = {resource_name: post_data}
if not ok:
return False, err

post_data = {resource_name: post_data}
status_code, response_body = client.post(
post_data=post_data,
resource=resource_name,
Expand All @@ -270,7 +303,10 @@ def create_resource(client, resource_name, resource_module_params, action=None):
if not ok:
return False, err

post_data = {resource_name: post_data}
# For nested post data resources, post_data is already properly structured
if resource_name not in NESTED_POST_DATA_RESOURCES:
post_data = {resource_name: post_data}

status_code, response_body = client.post(
post_data=post_data,
resource=resource_name,
Expand Down Expand Up @@ -343,8 +379,10 @@ 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 NESTED_POST_DATA_RESOURCES:
put_data = {"routerDynamicRouting": {resource_name: {}}}
else:
put_data = {resource_name: put_payload}

status_code, response_body = client.put(
put_data=put_data,
Expand Down
2 changes: 2 additions & 0 deletions plugins/module_utils/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
"sslcertkey": {"password"},
}

NESTED_POST_DATA_RESOURCES = ["bgpRouter"]

# NITRO accepts some attributes with a name and responsds with a different name in its GET reponse.
# Eg: For "gslbservice" resource, NITRO expects "ip" in POST request
# but expects "ipaddress" in PUT payload and returns "ipaddress" in GET response.
Expand Down
1 change: 1 addition & 0 deletions plugins/module_utils/module_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
HTTP_RESOURCE_ALREADY_EXISTS,
NETSCALER_COMMON_ARGUMENTS,
NITRO_ATTRIBUTES_ALIASES,
NESTED_POST_DATA_RESOURCES,
)
from .decorators import trace
from .logger import log, loglines
Expand Down
92 changes: 92 additions & 0 deletions plugins/module_utils/nitro_resource_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -16532,6 +16532,98 @@
"singleton": False,
"update_payload_keys": [],
},
"bgpRouter": {
"_supported_operations": ["add", "delete", "get"],
"action_payload_keys": {
"create": [],
"force": [],
"import": [],
"link": [],
"switch": [],
"unlink": [],
"unset": ["commandstring"],
},
"add_payload_keys": [
"afParams.addressFamily",
"afParams.redistribute.protocol",
"afParams.redistribute.routeMap",
"localAS",
"neighbor.ASOriginationInterval",
"neighbor.address",
"neighbor.advertisementInterval",
"neighbor.afParams.activate",
"neighbor.afParams.addressFamily",
"neighbor.afParams.routeMap.direction",
"neighbor.afParams.routeMap.name",
"neighbor.connectTimer",
"neighbor.holdTimerConfig",
"neighbor.keepaliveTimerConfig",
"neighbor.md5Password",
"neighbor.multihopBfd",
"neighbor.remoteAS",
"neighbor.singlehopBfd",
"neighbor.updateSource",
"routerId",
],
"bindings": [],
"bindprimary_key": "",
"delete_arg_keys": ["localAS"],
"disable_payload_keys": [],
"enable_payload_keys": [],
"get_arg_keys": [],
"immutable_keys": ["localAS"],
"password_keys": [],
"primary_key": "",
"primary_key_composite": [],
"readwrite_arguments": {
"afParams.addressFamily": {
"choices": ["ipv4", "ipv6"],
"no_log": False,
"type": "str",
},
"afParams.redistribute.protocol": {
"choices": [
"connected",
"intranet",
"isis",
"kernel",
"ospf",
"rip",
"static",
],
"no_log": False,
"type": "str",
},
"afParams.redistribute.routeMap": {"no_log": False, "type": "str"},
"localAS": {"no_log": False, "type": "int"},
"neighbor.ASOriginationInterval": {"no_log": False, "type": "int"},
"neighbor.address": {"no_log": False, "type": "str"},
"neighbor.advertisementInterval": {"no_log": False, "type": "int"},
"neighbor.afParams.activate": {"no_log": False, "type": "bool"},
"neighbor.afParams.addressFamily": {
"choices": ["ipv4", "ipv6"],
"no_log": False,
"type": "str",
},
"neighbor.afParams.routeMap.direction": {
"choices": ["in", "out"],
"no_log": False,
"type": "str",
},
"neighbor.afParams.routeMap.name": {"no_log": False, "type": "str"},
"neighbor.connectTimer": {"no_log": False, "type": "int"},
"neighbor.holdTimerConfig": {"no_log": False, "type": "int"},
"neighbor.keepaliveTimerConfig": {"no_log": False, "type": "int"},
"neighbor.md5Password": {"no_log": True, "type": "str"},
"neighbor.multihopBfd": {"no_log": False, "type": "bool"},
"neighbor.remoteAS": {"no_log": False, "type": "int"},
"neighbor.singlehopBfd": {"no_log": False, "type": "bool"},
"neighbor.updateSource": {"no_log": False, "type": "str"},
"routerId": {"no_log": False, "type": "str"},
},
"singleton": False,
"update_payload_keys": [],
},
"botglobal_botpolicy_binding": {
"_supported_operations": ["add", "count", "delete", "get"],
"action_payload_keys": {
Expand Down
Loading
Loading