Skip to content
Merged
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
1 change: 1 addition & 0 deletions plugins/module_utils/netbox_extras.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

NB_CONFIG_CONTEXTS = "config_contexts"
NB_TAGS = "tags"
NB_CUSTOM_FIELDS = "custom_fields"


class NetboxExtrasModule(NetboxModule):
Expand Down
5 changes: 4 additions & 1 deletion plugins/module_utils/netbox_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
"site_groups",
"virtual_chassis",
],
extras=["config_contexts", "tags"],
extras=["config_contexts", "tags", "custom_fields"],
ipam=[
"aggregates",
"ip_addresses",
Expand Down Expand Up @@ -109,6 +109,7 @@
config_context="name",
contact_group="name",
contact_role="name",
custom_field="name",
device="name",
device_role="slug",
device_type="slug",
Expand Down Expand Up @@ -279,6 +280,7 @@
"contacts": "contact",
"contact_groups": "contact_group",
"contact_roles": "contact_role",
"custom_fields": "custom_field",
"device_bays": "device_bay",
"device_bay_templates": "device_bay_template",
"devices": "device",
Expand Down Expand Up @@ -360,6 +362,7 @@
"contact": set(["name", "group"]),
"contact_group": set(["name"]),
"contact_role": set(["name"]),
"custom_field": set(["name"]),
"dcim.consoleport": set(["name", "device"]),
"dcim.consoleserverport": set(["name", "device"]),
"dcim.frontport": set(["name", "device", "rear_port"]),
Expand Down
199 changes: 199 additions & 0 deletions plugins/modules/netbox_custom_field.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2022, Martin Rødvand (@rodvand) <[email protected]>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import absolute_import, division, print_function

__metaclass__ = type

DOCUMENTATION = r"""
---
module: netbox_custom_field
short_description: Creates, updates or deletes custom fields within NetBox
description:
- Creates, updates or removes custom fields from NetBox
notes:
- This should be ran with connection C(local) and hosts C(localhost)
author:
- Martin Rødvand (@rodvand)
requirements:
- pynetbox
version_added: "3.6.0"
extends_documentation_fragment:
- netbox.netbox.common
options:
data:
type: dict
description:
- Defines the custom field
suboptions:
content_types:
description:
- The content type(s) to apply this custom field to
required: false
type: list
elements: raw
type:
description:
- The type of custom field
required: false
type: raw
name:
description:
- Name of the custom field
required: true
type: str
label:
description:
- Label of the custom field
required: false
type: str
description:
description:
- Description of the custom field
required: false
type: str
required:
description:
- Whether the custom field is required
required: false
type: bool
filter_logic:
description:
- Filter logic of the custom field
required: false
type: raw
default:
description:
- Default value of the custom field
required: false
type: raw
weight:
description:
- Fields with higher weights appear lower in a form
required: false
type: int
validation_minimum:
description:
- The minimum allowed value (for numeric fields)
required: false
type: int
validation_maximum:
description:
- The maximum allowed value (for numeric fields)
required: false
type: int
validation_regex:
description:
- The regular expression to enforce on text fields
required: false
type: str
choices:
description:
- List of available choices (for selection fields)
required: false
type: list
elements: str
required: true
"""

EXAMPLES = r"""
- name: "Test NetBox custom_fields module"
connection: local
hosts: localhost
tasks:
- name: Create a custom field on device and virtual machine
netbox_custom_field:
netbox_url: http://netbox.local
netbox_token: thisIsMyToken
data:
content_types:
- dcim.device
- virtualization.virtualmachine
name: A Custom Field
type: text

- name: Update the custom field to make it required
netbox_custom_field:
netbox_url: http://netbox.local
netbox_token: thisIsMyToken
data:
name: A Custom Field
required: yes

- name: Delete the custom field
netbox_custom_field:
netbox_url: http://netbox.local
netbox_token: thisIsMyToken
data:
name: A Custom Field
state: absent
"""

RETURN = r"""
custom_field:
description: Serialized object as created/existent/updated/deleted within NetBox
returned: always
type: dict
msg:
description: Message indicating failure or info about what has been achieved
returned: always
type: str
"""

from ansible_collections.netbox.netbox.plugins.module_utils.netbox_utils import (
NetboxAnsibleModule,
NETBOX_ARG_SPEC,
)
from ansible_collections.netbox.netbox.plugins.module_utils.netbox_extras import (
NetboxExtrasModule,
NB_CUSTOM_FIELDS,
)
from copy import deepcopy


def main():
"""
Main entry point for module execution
"""
argument_spec = deepcopy(NETBOX_ARG_SPEC)
argument_spec.update(
dict(
data=dict(
type="dict",
required=True,
options=dict(
content_types=dict(required=False, type="list", elements="raw"),
type=dict(required=False, type="raw"),
name=dict(required=True, type="str"),
label=dict(required=False, type="str"),
description=dict(required=False, type="str"),
required=dict(required=False, type="bool"),
filter_logic=dict(required=False, type="raw"),
default=dict(required=False, type="raw"),
weight=dict(required=False, type="int"),
validation_minimum=dict(required=False, type="int"),
validation_maximum=dict(required=False, type="int"),
validation_regex=dict(required=False, type="str"),
choices=dict(required=False, type="list", elements="str"),
),
)
)
)

required_if = [
("state", "present", ["content_types", "name"]),
("state", "absent", ["name"]),
]

module = NetboxAnsibleModule(
argument_spec=argument_spec, supports_check_mode=True, required_if=required_if
)

netbox_custom_field = NetboxExtrasModule(module, NB_CUSTOM_FIELDS)
netbox_custom_field.run()


if __name__ == "__main__": # pragma: no cover
main()
11 changes: 10 additions & 1 deletion tests/integration/targets/v3.1/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -198,4 +198,13 @@
tags:
- netbox_wireless_link
tags:
- netbox_wireless_link
- netbox_wireless_link

- name: "NETBOX_CUSTOM_FIELD TESTS"
include_tasks:
file: "netbox_custom_field.yml"
apply:
tags:
- netbox_custom_field
tags:
- netbox_custom_field
108 changes: 108 additions & 0 deletions tests/integration/targets/v3.1/tasks/netbox_custom_field.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
---
##
##
### NETBOX_CUSTOM_FIELD
##
##
- name: "CUSTOM_FIELD 1: Necessary info creation"
netbox.netbox.netbox_custom_field:
netbox_url: http://localhost:32768
netbox_token: 0123456789abcdef0123456789abcdef01234567
data:
content_types:
- "dcim.device"
name: A_CustomField
type: text
state: present
register: test_one

- name: "CUSTOM_FIELD 1: ASSERT - Necessary info creation"
assert:
that:
- test_one is changed
- test_one['diff']['before']['state'] == "absent"
- test_one['diff']['after']['state'] == "present"
- test_one['custom_field']['name'] == "A_CustomField"
- test_one['custom_field']['required'] == false
- test_one['custom_field']['content_types'] == ["dcim.device"]
- test_one['custom_field']['type'] == "text"
- test_one['custom_field']['weight'] == 100
- test_one['msg'] == "custom_field A_CustomField created"

- name: "CUSTOM_FIELD 2: Create duplicate"
netbox.netbox.netbox_custom_field:
netbox_url: http://localhost:32768
netbox_token: 0123456789abcdef0123456789abcdef01234567
data:
content_types:
- "dcim.device"
name: A_CustomField
state: present
register: test_two

- name: "CUSTOM_FIELD 2: ASSERT - Create duplicate"
assert:
that:
- not test_two['changed']
- test_two['custom_field']['name'] == "A_CustomField"
- test_two['msg'] == "custom_field A_CustomField already exists"

- name: "CUSTOM_FIELD 3: Update data and make it required"
netbox.netbox.netbox_custom_field:
netbox_url: http://localhost:32768
netbox_token: 0123456789abcdef0123456789abcdef01234567
data:
content_types:
- "dcim.device"
name: "A_CustomField"
description: "Added a description"
required: yes
state: present
register: test_three

- name: "CUSTOM_FIELD 3: ASSERT - Updated"
assert:
that:
- test_three is changed
- test_three['diff']['after']['description'] == "Added a description"
- test_three['diff']['after']['required'] == true
- test_three['custom_field']['name'] == "A_CustomField"
- test_three['msg'] == "custom_field A_CustomField updated"

- name: "CUSTOM_FIELD 4: Change content type"
netbox.netbox.netbox_custom_field:
netbox_url: http://localhost:32768
netbox_token: 0123456789abcdef0123456789abcdef01234567
data:
content_types:
- "virtualization.virtualmachine"
name: "A_CustomField"
description: "Added a description"
required: yes
state: present
register: test_four

- name: "CUSTOM_FIELD 4: ASSERT - Change content type"
assert:
that:
- test_four is changed
- test_four['diff']['after']['content_types'] == ["virtualization.virtualmachine"]
- test_four['custom_field']['name'] == "A_CustomField"
- test_four['msg'] == "custom_field A_CustomField updated"

- name: "CUSTOM_FIELD 5: Delete"
netbox.netbox.netbox_custom_field:
netbox_url: http://localhost:32768
netbox_token: 0123456789abcdef0123456789abcdef01234567
data:
name: "A_CustomField"
state: absent
register: test_five

- name: "CUSTOM_FIELD 5: ASSERT - Deleted"
assert:
that:
- test_five is changed
- test_five['diff']['after']['state'] == "absent"
- test_five['custom_field']['name'] == "A_CustomField"
- test_five['msg'] == "custom_field A_CustomField deleted"