Skip to content
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

Adds modules for custom fields #285

Merged
merged 3 commits into from
Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 2 additions & 0 deletions plugins/lookup/lookup.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ def get_endpoint(nautobot, term):
"console-ports": {"endpoint": nautobot.dcim.console_ports},
"console-server-port-templates": {"endpoint": nautobot.dcim.console_server_port_templates},
"console-server-ports": {"endpoint": nautobot.dcim.console_server_ports},
"custom-fields": {"endpoint": nautobot.extras.custom_fields},
"custom-field-choices": {"endpoint": nautobot.extras.custom_field_choices},
"device-bay-templates": {"endpoint": nautobot.dcim.device_bay_templates},
"device-bays": {"endpoint": nautobot.dcim.device_bays},
"device-types": {"endpoint": nautobot.dcim.device_types},
Expand Down
6 changes: 6 additions & 0 deletions plugins/module_utils/extras.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
NB_TAGS = "tags"
NB_STATUS = "statuses"
NB_RELATIONSHIP_ASSOCIATIONS = "relationship_associations"
NB_CUSTOM_FIELDS = "custom_fields"
NB_CUSTOM_FIELD_CHOICES = "custom_field_choices"


class NautobotExtrasModule(NautobotModule):
Expand Down Expand Up @@ -42,6 +44,10 @@ def run(self):
name = data["name"]
elif endpoint_name == "relationship_associations":
name = f"{data['source_type']} -> {data['destination_type']}"
elif endpoint_name == "custom_field":
name = data["label"]
elif endpoint_name == "custom_field_choice":
name = data["value"]
else:
name = data.get("id")

Expand Down
15 changes: 13 additions & 2 deletions plugins/module_utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,18 +65,25 @@
"rear_port_templates",
"virtual_chassis",
],
extras=["tags", "statuses", "relationship_associations", "roles"],
extras=[
"custom_fields",
"custom_field_choices",
"relationship_associations",
"roles",
"statuses",
"tags",
],
ipam=[
"ip_addresses",
"ip_address_to_interface",
"namespaces",
"prefixes",
"rirs",
"route_targets",
"services",
"vlans",
"vlan_groups",
"vrfs",
"services",
],
plugins=[],
secrets=[],
Expand Down Expand Up @@ -212,6 +219,8 @@
"console_port_templates": "console_port_template",
"console_server_ports": "console_server_port",
"console_server_port_templates": "console_server_port_template",
"custom_fields": "custom_field",
"custom_field_choices": "custom_field_choice",
"device_bays": "device_bay",
"device_bay_templates": "device_bay_template",
"devices": "device",
Expand Down Expand Up @@ -270,6 +279,8 @@
"console_port_template": set(["name", "device_type"]),
"console_server_port": set(["name", "device"]),
"console_server_port_template": set(["name", "device_type"]),
"custom_field": set(["label"]),
"custom_field_choice": set(["value", "custom_field"]),
"dcim.consoleport": set(["name", "device"]),
"dcim.consoleserverport": set(["name", "device"]),
"dcim.frontport": set(["name", "device", "rear_port"]),
Expand Down
218 changes: 218 additions & 0 deletions plugins/modules/custom_field.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2023, Network to Code (@networktocode) <info@networktocode.com>
# 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: custom_field
short_description: Creates or removes custom fields from Nautobot
description:
- Creates or removes custom fields from Nautobot
notes:
- This should be ran with connection C(local) and hosts C(localhost)
author:
- Joe Wesch (@joewesch)
requirements:
- pynautobot
version_added: "5.1.0"
extends_documentation_fragment:
- networktocode.nautobot.fragments.base
options:
label:
description:
- Name of the field as displayed to users
required: true
type: str
version_added: "5.1.0"
grouping:
description:
- Human-readable grouping that this custom field belongs to
required: false
type: str
version_added: "5.1.0"
key:
description:
- Internal name of this field
- Required if I(state=present) and the custom field does not exist yet
required: false
type: str
version_added: "5.1.0"
type:
description:
- Data type of this field
- Required if I(state=present) and the custom field does not exist yet
- I(type=select) and I(type=multi-select) require choices to be defined separately with the I(custom_field_choice) module
required: false
choices:
- text
- integer
- boolean
- date
- url
- select
- multi-select
- json
- markdown
type: str
version_added: "5.1.0"
weight:
description:
- Position this field should be displayed in
required: false
type: int
version_added: "5.1.0"
description:
description:
- Description of this field
- Also used as the help text when editing models using this custom field
- Markdown is supported
required: false
type: str
version_added: "5.1.0"
required:
description:
- Whether or not a value is required for this field when editing models
required: false
type: bool
version_added: "5.1.0"
default:
description:
- Default value for this field when editing models
- Must be in JSON format
required: false
type: raw
version_added: "5.1.0"
filter_logic:
description:
- Filter logic to apply when filtering models based on this field
- Only compatible with I(type=text), I(type=url) and I(type=json)
required: false
type: str
choices:
- disabled
- loose
- exact
version_added: "5.1.0"
advanced_ui:
description:
- Whether or not to display this field in the advanced tab
required: false
type: bool
version_added: "5.1.0"
content_types:
description:
- Content types that this field should be available for
- Required if I(state=present) and the custom field does not exist yet
required: false
type: list
elements: str
version_added: "5.1.0"
validation_minimum:
description:
- Minimum value allowed for this field
- Only compatible with I(type=integer)
required: false
type: int
version_added: "5.1.0"
validation_maximum:
description:
- Maximum value allowed for this field
- Only compatible with I(type=integer)
required: false
type: int
version_added: "5.1.0"
validation_regex:
description:
- Regular expression that this field must match
- Only compatible with I(type=text)
required: false
type: str
version_added: "5.1.0"
"""

EXAMPLES = r"""
- name: Create custom field within Nautobot with only required information
networktocode.nautobot.custom_field:
url: http://nautobot.local
token: thisIsMyToken
label: My Custom Field
key: my_custom_field
type: text
state: present

- name: Create custom field within Nautobot with all information
networktocode.nautobot.custom_field:
url: http://nautobot.local
token: thisIsMyToken
label: My Custom Field
grouping: My Grouping
key: my_custom_field
type: text
weight: 100
description: My Description
required: true
default: My Default
filter_logic: loose
advanced_ui: true
content_types:
- dcim.device
validation_minimum: 0
validation_maximum: 100
validation_regex: ^[a-z]+$
state: present
"""

RETURN = r"""
custom_field:
description: Serialized object as created or already existent within Nautobot
returned: success (when I(state=present))
type: dict
msg:
description: Message indicating failure or info about what has been achieved
returned: always
type: str
"""

from ansible_collections.networktocode.nautobot.plugins.module_utils.utils import NAUTOBOT_ARG_SPEC
from ansible_collections.networktocode.nautobot.plugins.module_utils.extras import (
NautobotExtrasModule,
NB_CUSTOM_FIELDS,
)
from ansible.module_utils.basic import AnsibleModule
from copy import deepcopy


def main():
"""Execute custom field module."""
argument_spec = deepcopy(NAUTOBOT_ARG_SPEC)
argument_spec.update(
dict(
label=dict(required=True, type="str"),
grouping=dict(required=False, type="str"),
key=dict(required=False, type="str"),
type=dict(required=False, choices=["text", "integer", "boolean", "date", "url", "select", "multi-select", "json", "markdown"], type="str"),
weight=dict(required=False, type="int"),
description=dict(required=False, type="str"),
required=dict(required=False, type="bool"),
default=dict(required=False, type="raw"),
filter_logic=dict(required=False, choices=["disabled", "loose", "exact"], type="str"),
advanced_ui=dict(required=False, type="bool"),
content_types=dict(required=False, type="list", elements="str"),
validation_minimum=dict(required=False, type="int"),
validation_maximum=dict(required=False, type="int"),
validation_regex=dict(required=False, type="str"),
)
)

module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
custom_field = NautobotExtrasModule(module, NB_CUSTOM_FIELDS)
custom_field.run()


if __name__ == "__main__": # pragma: no cover
main()
95 changes: 95 additions & 0 deletions plugins/modules/custom_field_choice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2023, Network to Code (@networktocode) <info@networktocode.com>
# 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: custom_field_choice
short_description: Creates or removes custom field choices from Nautobot
description:
- Creates or removes custom field choices from Nautobot
notes:
- This should be ran with connection C(local) and hosts C(localhost)
author:
- Joe Wesch (@joewesch)
requirements:
- pynautobot
version_added: "5.1.0"
extends_documentation_fragment:
- networktocode.nautobot.fragments.base
options:
value:
description:
- Value of this choice
required: true
type: str
version_added: "5.1.0"
weight:
description:
- Weight of this choice
required: false
type: int
version_added: "5.1.0"
custom_field:
description:
- Custom field this choice belongs to
required: true
type: raw
version_added: "5.1.0"
"""

EXAMPLES = r"""
---
- name: Create a custom field choice
networktocode.nautobot.custom_field_choice:
url: http://nautobot.local
token: thisIsMyToken
value: "Choice 1"
weight: 100
custom_field: "Custom Field 1"
state: present
"""

RETURN = r"""
custom_field_choice:
description: Serialized object as created or already existent within Nautobot
returned: success (when I(state=present))
type: dict
msg:
description: Message indicating failure or info about what has been achieved
returned: always
type: str
"""

from ansible_collections.networktocode.nautobot.plugins.module_utils.utils import NAUTOBOT_ARG_SPEC
from ansible_collections.networktocode.nautobot.plugins.module_utils.extras import (
NautobotExtrasModule,
NB_CUSTOM_FIELD_CHOICES,
)
from ansible.module_utils.basic import AnsibleModule
from copy import deepcopy


def main():
"""Execute custom field choice module."""
argument_spec = deepcopy(NAUTOBOT_ARG_SPEC)
argument_spec.update(
dict(
value=dict(required=True, type="str"),
weight=dict(required=False, type="int"),
custom_field=dict(required=True, type="raw"),
)
)

module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
custom_field_choice = NautobotExtrasModule(module, NB_CUSTOM_FIELD_CHOICES)
custom_field_choice.run()


if __name__ == "__main__": # pragma: no cover
main()
Loading
Loading