Skip to content

Commit a700b92

Browse files
Enhancement: Added query_params to default arg_spec (#222)
1 parent 646cef4 commit a700b92

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+415
-19
lines changed

plugins/module_utils/netbox_circuits.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ def run(self):
4040
application = self._find_app(self.endpoint)
4141
nb_app = getattr(self.nb, application)
4242
nb_endpoint = getattr(nb_app, self.endpoint)
43+
user_query_params = self.module.params.get("query_params")
4344

4445
data = self.data
4546

@@ -60,7 +61,9 @@ def run(self):
6061
if not data.get("slug"):
6162
data["slug"] = self._to_slug(name)
6263

63-
object_query_params = self._build_query_params(endpoint_name, data)
64+
object_query_params = self._build_query_params(
65+
endpoint_name, data, user_query_params
66+
)
6467
self.nb_object = self._nb_endpoint_get(nb_endpoint, object_query_params, name)
6568

6669
if self.state == "present":

plugins/module_utils/netbox_dcim.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ def run(self):
6262
application = self._find_app(self.endpoint)
6363
nb_app = getattr(self.nb, application)
6464
nb_endpoint = getattr(nb_app, self.endpoint)
65+
user_query_params = self.module.params.get("query_params")
6566

6667
data = self.data
6768

@@ -81,7 +82,9 @@ def run(self):
8182
if data.get("color"):
8283
data["color"] = data["color"].lower()
8384

84-
object_query_params = self._build_query_params(endpoint_name, data)
85+
object_query_params = self._build_query_params(
86+
endpoint_name, data, user_query_params
87+
)
8588
self.nb_object = self._nb_endpoint_get(nb_endpoint, object_query_params, name)
8689

8790
# This is logic to handle interfaces on a VC

plugins/module_utils/netbox_extras.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ def run(self):
2929
application = self._find_app(self.endpoint)
3030
nb_app = getattr(self.nb, application)
3131
nb_endpoint = getattr(nb_app, self.endpoint)
32+
user_query_params = self.module.params.get("query_params")
3233

3334
data = self.data
3435

@@ -37,7 +38,9 @@ def run(self):
3738

3839
data["slug"] = self._to_slug(name)
3940

40-
object_query_params = self._build_query_params(endpoint_name, data)
41+
object_query_params = self._build_query_params(
42+
endpoint_name, data, user_query_params
43+
)
4144
self.nb_object = self._nb_endpoint_get(nb_endpoint, object_query_params, name)
4245

4346
if self.state == "present":

plugins/module_utils/netbox_ipam.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ def run(self):
137137
application = self._find_app(self.endpoint)
138138
nb_app = getattr(self.nb, application)
139139
nb_endpoint = getattr(nb_app, self.endpoint)
140+
user_query_params = self.module.params.get("query_params")
140141

141142
data = self.data
142143

@@ -161,7 +162,9 @@ def run(self):
161162
else:
162163
first_available = False
163164

164-
object_query_params = self._build_query_params(endpoint_name, data)
165+
object_query_params = self._build_query_params(
166+
endpoint_name, data, user_query_params
167+
)
165168
if data.get("prefix") and self.endpoint == "ip_addresses":
166169
object_query_params = self._build_query_params("prefix", data)
167170
self.nb_object = self._nb_endpoint_get(

plugins/module_utils/netbox_secrets.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ def run(self):
2929
application = self._find_app(self.endpoint)
3030
nb_app = getattr(self.nb, application)
3131
nb_endpoint = getattr(nb_app, self.endpoint)
32+
user_query_params = self.module.params.get("query_params")
3233

3334
data = self.data
3435

@@ -37,7 +38,9 @@ def run(self):
3738

3839
data["slug"] = self._to_slug(name)
3940

40-
object_query_params = self._build_query_params(endpoint_name, data)
41+
object_query_params = self._build_query_params(
42+
endpoint_name, data, user_query_params
43+
)
4144
self.nb_object = self._nb_endpoint_get(nb_endpoint, object_query_params, name)
4245

4346
if self.state == "present":

plugins/module_utils/netbox_tenancy.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def run(self):
3636
application = self._find_app(self.endpoint)
3737
nb_app = getattr(self.nb, application)
3838
nb_endpoint = getattr(nb_app, self.endpoint)
39+
user_query_params = self.module.params.get("query_params")
3940

4041
data = self.data
4142

@@ -49,7 +50,9 @@ def run(self):
4950
if not data.get("slug"):
5051
data["slug"] = self._to_slug(name)
5152

52-
object_query_params = self._build_query_params(endpoint_name, data)
53+
object_query_params = self._build_query_params(
54+
endpoint_name, data, user_query_params
55+
)
5356
self.nb_object = self._nb_endpoint_get(nb_endpoint, object_query_params, name)
5457

5558
if self.state == "present":

plugins/module_utils/netbox_utils.py

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,17 @@
1010
# Import necessary packages
1111
import traceback
1212
import re
13+
import json
1314
from itertools import chain
15+
1416
from ansible_collections.ansible.netcommon.plugins.module_utils.compat import ipaddress
1517

16-
# from ansible.module_utils._text import to_text
1718
from ansible.module_utils.common.text.converters import to_text
1819

19-
# from ._text import to_native
2020
from ansible.module_utils._text import to_native
2121
from ansible.module_utils.common.collections import is_iterable
2222
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
23+
from ansible.module_utils.urls import open_url
2324

2425
PYNETBOX_IMP_ERR = None
2526
try:
@@ -303,6 +304,7 @@
303304
netbox_url=dict(type="str", required=True),
304305
netbox_token=dict(type="str", required=True, no_log=True),
305306
state=dict(required=False, default="present", choices=["present", "absent"]),
307+
query_params=dict(required=False, type="list"),
306308
validate_certs=dict(type="raw", default=True),
307309
)
308310

@@ -322,6 +324,7 @@ def __init__(self, module, endpoint, nb_client=None):
322324
self.check_mode = self.module.check_mode
323325
self.endpoint = endpoint
324326
self.version = None
327+
query_params = self.module.params.get("query_params")
325328

326329
if not HAS_PYNETBOX:
327330
self.module.fail_json(
@@ -338,11 +341,14 @@ def __init__(self, module, endpoint, nb_client=None):
338341
else:
339342
self.nb = nb_client
340343

344+
# if self.module.params.get("query_params"):
345+
# self._validate_query_params(self.module.params["query_params"])
346+
341347
# These methods will normalize the regular data
342348
cleaned_data = self._remove_arg_spec_default(module.params["data"])
343349
norm_data = self._normalize_data(cleaned_data)
344350
choices_data = self._change_choices_id(self.endpoint, norm_data)
345-
data = self._find_ids(choices_data)
351+
data = self._find_ids(choices_data, query_params)
346352
self.data = self._convert_identical_keys(data)
347353

348354
def _connect_netbox_api(self, url, token, ssl_verify):
@@ -372,6 +378,46 @@ def _nb_endpoint_get(self, nb_endpoint, query_params, search_item):
372378

373379
return response
374380

381+
def _validate_query_params(self, query_params):
382+
"""
383+
Validate query_params that are passed in by users to make sure
384+
they're valid and return error if they're not valid.
385+
"""
386+
invalid_query_params = []
387+
388+
app = self._find_app(self.endpoint)
389+
nb_app = getattr(self.nb, app)
390+
nb_endpoint = getattr(nb_app, self.endpoint)
391+
# Fetch the OpenAPI spec to perform validation against
392+
base_url = self.nb.base_url
393+
junk, endpoint_url = nb_endpoint.url.split(base_url)
394+
response = open_url(base_url + "/docs/?format=openapi")
395+
try:
396+
raw_data = to_text(response.read(), errors="surrogate_or_strict")
397+
except UnicodeError:
398+
self._handle_errors(
399+
msg="Incorrect encoding of fetched payload from NetBox API."
400+
)
401+
402+
try:
403+
openapi = json.loads(raw_data)
404+
except ValueError:
405+
self._handle_errors(msg="Incorrect JSON payload returned: %s" % raw_data)
406+
407+
valid_query_params = openapi["paths"][endpoint_url + "/"]["get"]["parameters"]
408+
409+
# Loop over passed in params and add to invalid_query_params and then fail if non-empty
410+
for param in query_params:
411+
if param not in valid_query_params:
412+
invalid_query_params.append(param)
413+
414+
if invalid_query_params:
415+
self._handle_errors(
416+
"The following query_params are invalid: {0}".format(
417+
", ".join(invalid_query_params)
418+
)
419+
)
420+
375421
def _handle_errors(self, msg):
376422
"""
377423
Returns message and changed = False
@@ -438,7 +484,9 @@ def _get_query_param_id(self, match, data):
438484
else:
439485
return data
440486

441-
def _build_query_params(self, parent, module_data, child=None):
487+
def _build_query_params(
488+
self, parent, module_data, user_query_params=None, child=None
489+
):
442490
"""
443491
:returns dict(query_dict): Returns a query dictionary built using mappings to dynamically
444492
build available query params for Netbox endpoints
@@ -449,7 +497,10 @@ def _build_query_params(self, parent, module_data, child=None):
449497
to build the appropriate `query_dict` for the parent
450498
"""
451499
query_dict = dict()
452-
query_params = ALLOWED_QUERY_PARAMS.get(parent)
500+
if user_query_params:
501+
query_params = set(user_query_params)
502+
else:
503+
query_params = ALLOWED_QUERY_PARAMS.get(parent)
453504

454505
if child:
455506
matches = query_params.intersection(set(child.keys()))
@@ -540,7 +591,7 @@ def _find_app(self, endpoint):
540591
nb_app = k
541592
return nb_app
542593

543-
def _find_ids(self, data):
594+
def _find_ids(self, data, user_query_params):
544595
"""Will find the IDs of all user specified data if resolvable
545596
:returns data (dict): Returns the updated dict with the IDs of user specified data
546597
:params data (dict): User defined data passed into the module
@@ -557,22 +608,24 @@ def _find_ids(self, data):
557608
if k == "interface" and v.get("virtual_machine"):
558609
nb_app = getattr(self.nb, "virtualization")
559610
nb_endpoint = getattr(nb_app, endpoint)
560-
query_params = self._build_query_params(k, data, v)
611+
query_params = self._build_query_params(k, data, child=v)
561612
query_id = self._nb_endpoint_get(nb_endpoint, query_params, k)
562613

563614
elif isinstance(v, list):
564615
id_list = list()
565616
for list_item in v:
566617
norm_data = self._normalize_data(list_item)
567-
temp_dict = self._build_query_params(k, data, norm_data)
618+
temp_dict = self._build_query_params(k, data, child=norm_data)
568619
query_id = self._nb_endpoint_get(nb_endpoint, temp_dict, k)
569620
if query_id:
570621
id_list.append(query_id.id)
571622
else:
572623
self._handle_errors(msg="%s not found" % (list_item))
573624
else:
574625
if k in ["lag"]:
575-
query_params = self._build_query_params(k, data)
626+
query_params = self._build_query_params(
627+
k, data, user_query_params
628+
)
576629
else:
577630
query_params = {QUERY_TYPES.get(k, "q"): search}
578631
query_id = self._nb_endpoint_get(nb_endpoint, query_params, k)

plugins/module_utils/netbox_virtualization.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ def run(self):
4646
application = self._find_app(self.endpoint)
4747
nb_app = getattr(self.nb, application)
4848
nb_endpoint = getattr(nb_app, self.endpoint)
49+
user_query_params = self.module.params.get("query_params")
4950

5051
data = self.data
5152

@@ -59,7 +60,9 @@ def run(self):
5960
if not data.get("slug"):
6061
data["slug"] = self._to_slug(name)
6162

62-
object_query_params = self._build_query_params(endpoint_name, data)
63+
object_query_params = self._build_query_params(
64+
endpoint_name, data, user_query_params
65+
)
6366
self.nb_object = self._nb_endpoint_get(nb_endpoint, object_query_params, name)
6467

6568
if self.state == "present":

plugins/modules/netbox_aggregate.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,13 @@
8080
choices: [ present, absent ]
8181
default: present
8282
type: str
83+
query_params:
84+
description:
85+
- This can be used to override the specified values in ALLOWED_QUERY_PARAMS that is defined
86+
- in plugins/module_utils/netbox_utils.py and provides control to users on what may make
87+
- an object unique in their environment.
88+
required: false
89+
type: list
8390
validate_certs:
8491
description:
8592
- "If C(no), SSL certificates will not be validated. This should only be used on personally controlled sites using self-signed certificates."

plugins/modules/netbox_circuit.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,13 @@
105105
choices: [ absent, present ]
106106
default: present
107107
type: str
108+
query_params:
109+
description:
110+
- This can be used to override the specified values in ALLOWED_QUERY_PARAMS that is defined
111+
- in plugins/module_utils/netbox_utils.py and provides control to users on what may make
112+
- an object unique in their environment.
113+
required: false
114+
type: list
108115
validate_certs:
109116
description:
110117
- If C(no), SSL certificates will not be validated. This should only be used on personally controlled sites using self-signed certificates.

0 commit comments

Comments
 (0)