Skip to content

Commit 9d096c8

Browse files
authored
Add Custom Fields module (#719)
1 parent ec5af3b commit 9d096c8

File tree

5 files changed

+322
-2
lines changed

5 files changed

+322
-2
lines changed

plugins/module_utils/netbox_extras.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
NB_CONFIG_CONTEXTS = "config_contexts"
1515
NB_TAGS = "tags"
16+
NB_CUSTOM_FIELDS = "custom_fields"
1617

1718

1819
class NetboxExtrasModule(NetboxModule):

plugins/module_utils/netbox_utils.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474
"site_groups",
7575
"virtual_chassis",
7676
],
77-
extras=["config_contexts", "tags"],
77+
extras=["config_contexts", "tags", "custom_fields"],
7878
ipam=[
7979
"aggregates",
8080
"ip_addresses",
@@ -109,6 +109,7 @@
109109
config_context="name",
110110
contact_group="name",
111111
contact_role="name",
112+
custom_field="name",
112113
device="name",
113114
device_role="slug",
114115
device_type="slug",
@@ -279,6 +280,7 @@
279280
"contacts": "contact",
280281
"contact_groups": "contact_group",
281282
"contact_roles": "contact_role",
283+
"custom_fields": "custom_field",
282284
"device_bays": "device_bay",
283285
"device_bay_templates": "device_bay_template",
284286
"devices": "device",
@@ -360,6 +362,7 @@
360362
"contact": set(["name", "group"]),
361363
"contact_group": set(["name"]),
362364
"contact_role": set(["name"]),
365+
"custom_field": set(["name"]),
363366
"dcim.consoleport": set(["name", "device"]),
364367
"dcim.consoleserverport": set(["name", "device"]),
365368
"dcim.frontport": set(["name", "device", "rear_port"]),
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
# Copyright: (c) 2022, Martin Rødvand (@rodvand) <p@tristero.se>
4+
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
5+
6+
from __future__ import absolute_import, division, print_function
7+
8+
__metaclass__ = type
9+
10+
DOCUMENTATION = r"""
11+
---
12+
module: netbox_custom_field
13+
short_description: Creates, updates or deletes custom fields within NetBox
14+
description:
15+
- Creates, updates or removes custom fields from NetBox
16+
notes:
17+
- This should be ran with connection C(local) and hosts C(localhost)
18+
author:
19+
- Martin Rødvand (@rodvand)
20+
requirements:
21+
- pynetbox
22+
version_added: "3.6.0"
23+
extends_documentation_fragment:
24+
- netbox.netbox.common
25+
options:
26+
data:
27+
type: dict
28+
description:
29+
- Defines the custom field
30+
suboptions:
31+
content_types:
32+
description:
33+
- The content type(s) to apply this custom field to
34+
required: false
35+
type: list
36+
elements: raw
37+
type:
38+
description:
39+
- The type of custom field
40+
required: false
41+
type: raw
42+
name:
43+
description:
44+
- Name of the custom field
45+
required: true
46+
type: str
47+
label:
48+
description:
49+
- Label of the custom field
50+
required: false
51+
type: str
52+
description:
53+
description:
54+
- Description of the custom field
55+
required: false
56+
type: str
57+
required:
58+
description:
59+
- Whether the custom field is required
60+
required: false
61+
type: bool
62+
filter_logic:
63+
description:
64+
- Filter logic of the custom field
65+
required: false
66+
type: raw
67+
default:
68+
description:
69+
- Default value of the custom field
70+
required: false
71+
type: raw
72+
weight:
73+
description:
74+
- Fields with higher weights appear lower in a form
75+
required: false
76+
type: int
77+
validation_minimum:
78+
description:
79+
- The minimum allowed value (for numeric fields)
80+
required: false
81+
type: int
82+
validation_maximum:
83+
description:
84+
- The maximum allowed value (for numeric fields)
85+
required: false
86+
type: int
87+
validation_regex:
88+
description:
89+
- The regular expression to enforce on text fields
90+
required: false
91+
type: str
92+
choices:
93+
description:
94+
- List of available choices (for selection fields)
95+
required: false
96+
type: list
97+
elements: str
98+
required: true
99+
"""
100+
101+
EXAMPLES = r"""
102+
- name: "Test NetBox custom_fields module"
103+
connection: local
104+
hosts: localhost
105+
tasks:
106+
- name: Create a custom field on device and virtual machine
107+
netbox_custom_field:
108+
netbox_url: http://netbox.local
109+
netbox_token: thisIsMyToken
110+
data:
111+
content_types:
112+
- dcim.device
113+
- virtualization.virtualmachine
114+
name: A Custom Field
115+
type: text
116+
117+
- name: Update the custom field to make it required
118+
netbox_custom_field:
119+
netbox_url: http://netbox.local
120+
netbox_token: thisIsMyToken
121+
data:
122+
name: A Custom Field
123+
required: yes
124+
125+
- name: Delete the custom field
126+
netbox_custom_field:
127+
netbox_url: http://netbox.local
128+
netbox_token: thisIsMyToken
129+
data:
130+
name: A Custom Field
131+
state: absent
132+
"""
133+
134+
RETURN = r"""
135+
custom_field:
136+
description: Serialized object as created/existent/updated/deleted within NetBox
137+
returned: always
138+
type: dict
139+
msg:
140+
description: Message indicating failure or info about what has been achieved
141+
returned: always
142+
type: str
143+
"""
144+
145+
from ansible_collections.netbox.netbox.plugins.module_utils.netbox_utils import (
146+
NetboxAnsibleModule,
147+
NETBOX_ARG_SPEC,
148+
)
149+
from ansible_collections.netbox.netbox.plugins.module_utils.netbox_extras import (
150+
NetboxExtrasModule,
151+
NB_CUSTOM_FIELDS,
152+
)
153+
from copy import deepcopy
154+
155+
156+
def main():
157+
"""
158+
Main entry point for module execution
159+
"""
160+
argument_spec = deepcopy(NETBOX_ARG_SPEC)
161+
argument_spec.update(
162+
dict(
163+
data=dict(
164+
type="dict",
165+
required=True,
166+
options=dict(
167+
content_types=dict(required=False, type="list", elements="raw"),
168+
type=dict(required=False, type="raw"),
169+
name=dict(required=True, type="str"),
170+
label=dict(required=False, type="str"),
171+
description=dict(required=False, type="str"),
172+
required=dict(required=False, type="bool"),
173+
filter_logic=dict(required=False, type="raw"),
174+
default=dict(required=False, type="raw"),
175+
weight=dict(required=False, type="int"),
176+
validation_minimum=dict(required=False, type="int"),
177+
validation_maximum=dict(required=False, type="int"),
178+
validation_regex=dict(required=False, type="str"),
179+
choices=dict(required=False, type="list", elements="str"),
180+
),
181+
)
182+
)
183+
)
184+
185+
required_if = [
186+
("state", "present", ["content_types", "name"]),
187+
("state", "absent", ["name"]),
188+
]
189+
190+
module = NetboxAnsibleModule(
191+
argument_spec=argument_spec, supports_check_mode=True, required_if=required_if
192+
)
193+
194+
netbox_custom_field = NetboxExtrasModule(module, NB_CUSTOM_FIELDS)
195+
netbox_custom_field.run()
196+
197+
198+
if __name__ == "__main__": # pragma: no cover
199+
main()

tests/integration/targets/v3.1/tasks/main.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,4 +198,13 @@
198198
tags:
199199
- netbox_wireless_link
200200
tags:
201-
- netbox_wireless_link
201+
- netbox_wireless_link
202+
203+
- name: "NETBOX_CUSTOM_FIELD TESTS"
204+
include_tasks:
205+
file: "netbox_custom_field.yml"
206+
apply:
207+
tags:
208+
- netbox_custom_field
209+
tags:
210+
- netbox_custom_field
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
---
2+
##
3+
##
4+
### NETBOX_CUSTOM_FIELD
5+
##
6+
##
7+
- name: "CUSTOM_FIELD 1: Necessary info creation"
8+
netbox.netbox.netbox_custom_field:
9+
netbox_url: http://localhost:32768
10+
netbox_token: 0123456789abcdef0123456789abcdef01234567
11+
data:
12+
content_types:
13+
- "dcim.device"
14+
name: A_CustomField
15+
type: text
16+
state: present
17+
register: test_one
18+
19+
- name: "CUSTOM_FIELD 1: ASSERT - Necessary info creation"
20+
assert:
21+
that:
22+
- test_one is changed
23+
- test_one['diff']['before']['state'] == "absent"
24+
- test_one['diff']['after']['state'] == "present"
25+
- test_one['custom_field']['name'] == "A_CustomField"
26+
- test_one['custom_field']['required'] == false
27+
- test_one['custom_field']['content_types'] == ["dcim.device"]
28+
- test_one['custom_field']['type'] == "text"
29+
- test_one['custom_field']['weight'] == 100
30+
- test_one['msg'] == "custom_field A_CustomField created"
31+
32+
- name: "CUSTOM_FIELD 2: Create duplicate"
33+
netbox.netbox.netbox_custom_field:
34+
netbox_url: http://localhost:32768
35+
netbox_token: 0123456789abcdef0123456789abcdef01234567
36+
data:
37+
content_types:
38+
- "dcim.device"
39+
name: A_CustomField
40+
state: present
41+
register: test_two
42+
43+
- name: "CUSTOM_FIELD 2: ASSERT - Create duplicate"
44+
assert:
45+
that:
46+
- not test_two['changed']
47+
- test_two['custom_field']['name'] == "A_CustomField"
48+
- test_two['msg'] == "custom_field A_CustomField already exists"
49+
50+
- name: "CUSTOM_FIELD 3: Update data and make it required"
51+
netbox.netbox.netbox_custom_field:
52+
netbox_url: http://localhost:32768
53+
netbox_token: 0123456789abcdef0123456789abcdef01234567
54+
data:
55+
content_types:
56+
- "dcim.device"
57+
name: "A_CustomField"
58+
description: "Added a description"
59+
required: yes
60+
state: present
61+
register: test_three
62+
63+
- name: "CUSTOM_FIELD 3: ASSERT - Updated"
64+
assert:
65+
that:
66+
- test_three is changed
67+
- test_three['diff']['after']['description'] == "Added a description"
68+
- test_three['diff']['after']['required'] == true
69+
- test_three['custom_field']['name'] == "A_CustomField"
70+
- test_three['msg'] == "custom_field A_CustomField updated"
71+
72+
- name: "CUSTOM_FIELD 4: Change content type"
73+
netbox.netbox.netbox_custom_field:
74+
netbox_url: http://localhost:32768
75+
netbox_token: 0123456789abcdef0123456789abcdef01234567
76+
data:
77+
content_types:
78+
- "virtualization.virtualmachine"
79+
name: "A_CustomField"
80+
description: "Added a description"
81+
required: yes
82+
state: present
83+
register: test_four
84+
85+
- name: "CUSTOM_FIELD 4: ASSERT - Change content type"
86+
assert:
87+
that:
88+
- test_four is changed
89+
- test_four['diff']['after']['content_types'] == ["virtualization.virtualmachine"]
90+
- test_four['custom_field']['name'] == "A_CustomField"
91+
- test_four['msg'] == "custom_field A_CustomField updated"
92+
93+
- name: "CUSTOM_FIELD 5: Delete"
94+
netbox.netbox.netbox_custom_field:
95+
netbox_url: http://localhost:32768
96+
netbox_token: 0123456789abcdef0123456789abcdef01234567
97+
data:
98+
name: "A_CustomField"
99+
state: absent
100+
register: test_five
101+
102+
- name: "CUSTOM_FIELD 5: ASSERT - Deleted"
103+
assert:
104+
that:
105+
- test_five is changed
106+
- test_five['diff']['after']['state'] == "absent"
107+
- test_five['custom_field']['name'] == "A_CustomField"
108+
- test_five['msg'] == "custom_field A_CustomField deleted"

0 commit comments

Comments
 (0)