Skip to content

Commit 50f8260

Browse files
fail task on errors
1 parent f362638 commit 50f8260

27 files changed

+310
-135
lines changed

.travis.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ env:
1313

1414
matrix:
1515
include:
16-
- name: "Python 3.6 - Netbox 2.5 - Ansible 2.9RC4"
16+
- name: "Python 3.6 - Netbox 2.5 - Ansible 2.9"
1717
python: 3.6
18-
env: PYTHON_VER=3.6 VERSION=v2.5 ANSIBLE_VER=2.9.0rc4
18+
env: PYTHON_VER=3.6 VERSION=v2.5 ANSIBLE_VER=2.9.0
1919
install:
2020
- cd ..
2121
# Setup netbox container for integration testing
@@ -31,7 +31,7 @@ matrix:
3131

3232
- name: "Python 3.6 - Netbox Latest - Ansible Devel"
3333
python: 3.6
34-
env: PYTHON_VER=3.6
34+
env: PYTHON_VER=3.6 VERSION=v2.6.6
3535
install:
3636
- cd ..
3737
# Setup netbox container for integration testing

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
- [#9](https://github.com/FragmentedPacket/netbox_modules/issues/9) - Changed role to prefix_role in netbox_prefix.py
77
- [#9](https://github.com/FragmentedPacket/netbox_modules/issues/9) - Changed group to tenant_group in netbox_tenant.py
88
- [#9](https://github.com/FragmentedPacket/netbox_modules/issues/9) - Renamed netbox_interface to netbox_device_interface
9+
- [#24](https://github.com/FragmentedPacket/netbox_modules/issues/24) - Module failures when required fields arent provided
910

1011
### New Modules / Plugins
1112
- [#9](https://github.com/FragmentedPacket/netbox_modules/issues/9) - Added netbox_device_role
@@ -30,4 +31,4 @@
3031
### Bug Fixes
3132

3233
### Enhancements
33-
- [#10](https://github.com/FragmentedPacket/netbox_modules/issues/10) - Add primary_ip4/6 to netbox_ip_address
34+
- [#10](https://github.com/FragmentedPacket/netbox_modules/issues/10) - Add primary_ip4/6 to netbox_ip_address

plugins/module_utils/netbox_utils.py

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
import traceback
1010
from ansible.module_utils.compat import ipaddress
1111
from ansible.module_utils._text import to_text
12+
13+
# from ._text import to_native
14+
from ansible.module_utils._text import to_native
15+
from ansible.module_utils.common.collections import is_iterable
1216
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
1317

1418
PYNETBOX_IMP_ERR = None
@@ -379,7 +383,7 @@ def _handle_errors(self, msg):
379383
:params msg (str): Message indicating why there is no change
380384
"""
381385
if msg:
382-
self.module.exit_json(msg=msg, changed=False)
386+
self.module.fail_json(msg=msg, changed=False)
383387

384388
def _build_diff(self, before=None, after=None):
385389
"""Builds diff of before and after changes"""
@@ -692,3 +696,117 @@ def run(self):
692696
Must be implemented in subclasses
693697
"""
694698
raise NotImplementedError
699+
700+
701+
class NetboxAnsibleModule(AnsibleModule):
702+
"""
703+
Creating this due to needing to override some functionality to provide required_together, required_if
704+
and will be able to override more in the future.
705+
This is due to the Netbox modules having the module arguments within a key in the argument spec, using suboptions rather than
706+
having all module arguments within the regular argument spec.
707+
708+
Didn't want to change that functionality of the Netbox modules as its disruptive and we're required to send a specific payload
709+
to the Netbox API
710+
"""
711+
712+
def __init__(
713+
self,
714+
argument_spec,
715+
bypass_checks=False,
716+
no_log=False,
717+
check_invalid_arguments=None,
718+
mutually_exclusive=None,
719+
required_together=None,
720+
required_one_of=None,
721+
add_file_common_args=False,
722+
supports_check_mode=False,
723+
required_if=None,
724+
required_by=None,
725+
):
726+
super().__init__(
727+
argument_spec,
728+
bypass_checks,
729+
no_log,
730+
check_invalid_arguments,
731+
mutually_exclusive,
732+
required_together,
733+
required_one_of,
734+
add_file_common_args,
735+
supports_check_mode,
736+
required_if,
737+
required_by,
738+
)
739+
740+
def _check_required_if(self, spec, param=None):
741+
""" ensure that parameters which conditionally required are present """
742+
if spec is None:
743+
return
744+
if param is None:
745+
param = self.params
746+
747+
try:
748+
self.check_required_if(spec, param)
749+
except TypeError as e:
750+
msg = to_native(e)
751+
if self._options_context:
752+
msg += " found in %s" % " -> ".join(self._options_context)
753+
self.fail_json(msg=msg)
754+
755+
def check_required_if(self, requirements, module_parameters):
756+
results = []
757+
if requirements is None:
758+
return results
759+
760+
for req in requirements:
761+
missing = {}
762+
missing["missing"] = []
763+
max_missing_count = 0
764+
is_one_of = False
765+
if len(req) == 4:
766+
key, val, requirements, is_one_of = req
767+
else:
768+
key, val, requirements = req
769+
770+
# is_one_of is True at least one requirement should be
771+
# present, else all requirements should be present.
772+
if is_one_of:
773+
max_missing_count = len(requirements)
774+
missing["requires"] = "any"
775+
else:
776+
missing["requires"] = "all"
777+
778+
if key in module_parameters and module_parameters[key] == val:
779+
for check in requirements:
780+
count = self.count_terms(check, module_parameters["data"])
781+
if count == 0:
782+
missing["missing"].append(check)
783+
if len(missing["missing"]) and len(missing["missing"]) >= max_missing_count:
784+
missing["parameter"] = key
785+
missing["value"] = val
786+
missing["requirements"] = requirements
787+
results.append(missing)
788+
789+
if results:
790+
for missing in results:
791+
msg = "%s is %s but %s of the following are missing: %s" % (
792+
missing["parameter"],
793+
missing["value"],
794+
missing["requires"],
795+
", ".join(missing["missing"]),
796+
)
797+
raise TypeError(to_native(msg))
798+
799+
return results
800+
801+
def count_terms(self, terms, module_parameters):
802+
"""Count the number of occurrences of a key in a given dictionary
803+
:arg terms: String or iterable of values to check
804+
:arg module_parameters: Dictionary of module parameters
805+
:returns: An integer that is the number of occurrences of the terms values
806+
in the provided dictionary.
807+
"""
808+
809+
if not is_iterable(terms):
810+
terms = [terms]
811+
812+
return len(set(terms).intersection(module_parameters))

plugins/modules/netbox_aggregate.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,11 @@
4343
prefix:
4444
description:
4545
- The aggregate prefix
46+
required: true
4647
rir:
4748
description:
4849
- The RIR the aggregate will be assigned to
50+
required:true
4951
date_added:
5052
description:
5153
- Date added, format: YYYY-MM-DD
@@ -120,7 +122,9 @@
120122
type: str
121123
"""
122124

123-
from ansible.module_utils.basic import AnsibleModule
125+
from ansible_collections.fragmentedpacket.netbox_modules.plugins.module_utils.netbox_utils import (
126+
NetboxAnsibleModule,
127+
)
124128
from ansible_collections.fragmentedpacket.netbox_modules.plugins.module_utils.netbox_ipam import (
125129
NetboxIpamModule,
126130
NB_AGGREGATES,
@@ -139,7 +143,11 @@ def main():
139143
validate_certs=dict(type="bool", default=True),
140144
)
141145

142-
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
146+
required_if = [("state", "present", ["prefix"]), ("state", "absent", ["prefix"])]
147+
148+
module = NetboxAnsibleModule(
149+
argument_spec=argument_spec, supports_check_mode=True, required_if=required_if
150+
)
143151

144152
netbox_aggregate = NetboxIpamModule(module, NB_AGGREGATES)
145153
netbox_aggregate.run()

plugins/modules/netbox_device.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,9 @@
179179
type: str
180180
"""
181181

182-
from ansible.module_utils.basic import AnsibleModule
182+
from ansible_collections.fragmentedpacket.netbox_modules.plugins.module_utils.netbox_utils import (
183+
NetboxAnsibleModule,
184+
)
183185
from ansible_collections.fragmentedpacket.netbox_modules.plugins.module_utils.netbox_dcim import (
184186
NetboxDcimModule,
185187
NB_DEVICES,
@@ -197,12 +199,11 @@ def main():
197199
state=dict(required=False, default="present", choices=["present", "absent"]),
198200
validate_certs=dict(type="bool", default=True),
199201
)
202+
required_if = [("state", "present", ["name"]), ("state", "absent", ["name"])]
200203

201-
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
202-
203-
# Fail if device name is not given
204-
if not module.params["data"].get("name"):
205-
module.fail_json(msg="missing name")
204+
module = NetboxAnsibleModule(
205+
argument_spec=argument_spec, supports_check_mode=True, required_if=required_if
206+
)
206207

207208
netbox_device = NetboxDcimModule(module, NB_DEVICES)
208209
netbox_device.run()

plugins/modules/netbox_device_bay.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,9 @@
118118
type: str
119119
"""
120120

121-
from ansible.module_utils.basic import AnsibleModule
121+
from ansible_collections.fragmentedpacket.netbox_modules.plugins.module_utils.netbox_utils import (
122+
NetboxAnsibleModule,
123+
)
122124
from ansible_collections.fragmentedpacket.netbox_modules.plugins.module_utils.netbox_dcim import (
123125
NetboxDcimModule,
124126
NB_DEVICE_BAYS,
@@ -136,12 +138,11 @@ def main():
136138
state=dict(required=False, default="present", choices=["present", "absent"]),
137139
validate_certs=dict(type="bool", default=True),
138140
)
141+
required_if = [("state", "present", ["name"]), ("state", "absent", ["name"])]
139142

140-
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
141-
142-
# Fail if device_bay name is not given
143-
if not module.params["data"].get("name"):
144-
module.fail_json(msg="missing name")
143+
module = NetboxAnsibleModule(
144+
argument_spec=argument_spec, supports_check_mode=True, required_if=required_if
145+
)
145146

146147
netbox_device_bay = NetboxDcimModule(module, NB_DEVICE_BAYS)
147148
netbox_device_bay.run()

plugins/modules/netbox_device_interface.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,9 @@
203203
type: str
204204
"""
205205

206-
from ansible.module_utils.basic import AnsibleModule
206+
from ansible_collections.fragmentedpacket.netbox_modules.plugins.module_utils.netbox_utils import (
207+
NetboxAnsibleModule,
208+
)
207209
from ansible_collections.fragmentedpacket.netbox_modules.plugins.module_utils.netbox_dcim import (
208210
NetboxDcimModule,
209211
NB_INTERFACES,
@@ -221,11 +223,14 @@ def main():
221223
state=dict(required=False, default="present", choices=["present", "absent"]),
222224
validate_certs=dict(type="bool", default=True),
223225
)
226+
required_if = [
227+
("state", "present", ["device", "name"]),
228+
("state", "absent", ["device", "name"]),
229+
]
224230

225-
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
226-
227-
if not module.params["data"].get("name"):
228-
module.fail_json(msg="missing name")
231+
module = NetboxAnsibleModule(
232+
argument_spec=argument_spec, supports_check_mode=True, required_if=required_if
233+
)
229234

230235
netbox_device_interface = NetboxDcimModule(module, NB_INTERFACES)
231236
netbox_device_interface.run()

plugins/modules/netbox_device_role.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,9 @@
101101
returned: always
102102
type: str
103103
"""
104-
105-
from ansible.module_utils.basic import AnsibleModule
104+
from ansible_collections.fragmentedpacket.netbox_modules.plugins.module_utils.netbox_utils import (
105+
NetboxAnsibleModule,
106+
)
106107
from ansible_collections.fragmentedpacket.netbox_modules.plugins.module_utils.netbox_dcim import (
107108
NetboxDcimModule,
108109
NB_DEVICE_ROLES,
@@ -120,12 +121,11 @@ def main():
120121
state=dict(required=False, default="present", choices=["present", "absent"]),
121122
validate_certs=dict(type="bool", default=True),
122123
)
124+
required_if = [("state", "present", ["name"]), ("state", "absent", ["name"])]
123125

124-
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
125-
126-
# Fail if name is not given
127-
if not module.params["data"].get("name"):
128-
module.fail_json(msg="missing name")
126+
module = NetboxAnsibleModule(
127+
argument_spec=argument_spec, supports_check_mode=True, required_if=required_if
128+
)
129129

130130
netbox_device_role = NetboxDcimModule(module, NB_DEVICE_ROLES)
131131
netbox_device_role.run()

plugins/modules/netbox_device_type.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,9 @@
142142
type: str
143143
"""
144144

145-
from ansible.module_utils.basic import AnsibleModule
145+
from ansible_collections.fragmentedpacket.netbox_modules.plugins.module_utils.netbox_utils import (
146+
NetboxAnsibleModule,
147+
)
146148
from ansible_collections.fragmentedpacket.netbox_modules.plugins.module_utils.netbox_dcim import (
147149
NetboxDcimModule,
148150
NB_DEVICE_TYPES,
@@ -160,12 +162,11 @@ def main():
160162
state=dict(required=False, default="present", choices=["present", "absent"]),
161163
validate_certs=dict(type="bool", default=True),
162164
)
165+
required_if = [("state", "present", ["slug"]), ("state", "absent", ["slug"])]
163166

164-
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
165-
166-
# Fail if name is not given
167-
if not module.params["data"].get("slug"):
168-
module.fail_json(msg="Missing slug")
167+
module = NetboxAnsibleModule(
168+
argument_spec=argument_spec, supports_check_mode=True, required_if=required_if
169+
)
169170

170171
netbox_device_type = NetboxDcimModule(module, NB_DEVICE_TYPES)
171172
netbox_device_type.run()

plugins/modules/netbox_inventory_item.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,9 @@
136136
type: str
137137
"""
138138

139-
from ansible.module_utils.basic import AnsibleModule
139+
from ansible_collections.fragmentedpacket.netbox_modules.plugins.module_utils.netbox_utils import (
140+
NetboxAnsibleModule,
141+
)
140142
from ansible_collections.fragmentedpacket.netbox_modules.plugins.module_utils.netbox_dcim import (
141143
NetboxDcimModule,
142144
NB_INVENTORY_ITEMS,
@@ -154,12 +156,14 @@ def main():
154156
state=dict(required=False, default="present", choices=["present", "absent"]),
155157
validate_certs=dict(type="bool", default=True),
156158
)
159+
required_if = [
160+
("state", "present", ["device", "name"]),
161+
("state", "absent", ["device", "name"]),
162+
]
157163

158-
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
159-
160-
# Fail if name is not given
161-
if not module.params["data"].get("name"):
162-
module.fail_json(msg="missing name")
164+
module = NetboxAnsibleModule(
165+
argument_spec=argument_spec, supports_check_mode=True, required_if=required_if
166+
)
163167

164168
netbox_inventory_item = NetboxDcimModule(module, NB_INVENTORY_ITEMS)
165169
netbox_inventory_item.run()

0 commit comments

Comments
 (0)