Skip to content
Open
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
2 changes: 2 additions & 0 deletions changelogs/fragments/1459-add-netbox-data-sources.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
minor_changes:
- netbox_data_source - New module `#1459 <https://github.com/netbox-community/ansible_modules/pull/1459>`
84 changes: 84 additions & 0 deletions plugins/module_utils/netbox_core.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2025, Daniel Chiquito (@dchiquito) <daniel.chiquito@gmail.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

from ansible_collections.netbox.netbox.plugins.module_utils.netbox_utils import (
NetboxModule,
ENDPOINT_NAME_MAPPING,
SLUG_REQUIRED,
)

NB_DATA_SOURCES = "data_sources"


class NetboxCoreModule(NetboxModule):
def __init__(self, module, endpoint):
super().__init__(module, endpoint)

def _handle_state_new(self, nb_app, nb_endpoint, endpoint_name, data):
if self.state == "new":
self.nb_object, diff = self._create_netbox_object(nb_endpoint, data)
self.result["msg"] = "%s created" % (endpoint_name)
self.result["changed"] = True
self.result["diff"] = diff

def run(self):
"""
This function should have all necessary code for endpoints within the application
to create/update/delete the endpoint objects
Supported endpoints:
- data_sources
"""
# Used to dynamically set key when returning results
endpoint_name = ENDPOINT_NAME_MAPPING[self.endpoint]

self.result = {"changed": False}

application = self._find_app(self.endpoint)
nb_app = getattr(self.nb, application)
nb_endpoint = getattr(nb_app, self.endpoint)
user_query_params = self.module.params.get("query_params")

data = self.data

# Used for msg output
if data.get("name"):
name = data["name"]
elif data.get("slug"):
name = data["slug"]

if self.endpoint in SLUG_REQUIRED:
if not data.get("slug"):
data["slug"] = self._to_slug(name)

# Make color params lowercase
if data.get("color"):
data["color"] = data["color"].lower()

# Handle journal entry
if self.state == "new" and endpoint_name == "journal_entry":
self._handle_state_new(nb_app, nb_endpoint, endpoint_name, data)
else:
object_query_params = self._build_query_params(
endpoint_name, data, user_query_params
)
self.nb_object = self._nb_endpoint_get(
nb_endpoint, object_query_params, name
)

if self.state == "present":
self._ensure_object_exists(nb_endpoint, endpoint_name, name, data)
elif self.state == "absent":
self._ensure_object_absent(endpoint_name, name)

try:
serialized_object = self.nb_object.serialize()
except AttributeError:
serialized_object = self.nb_object

self.result.update({endpoint_name: serialized_object})

self.module.exit_json(**self.result)
5 changes: 5 additions & 0 deletions plugins/module_utils/netbox_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
"providers": {},
"provider_networks": {},
},
core={
"data_sources": {},
},
dcim={
"cables": {},
"console_ports": {},
Expand Down Expand Up @@ -371,6 +374,7 @@
"custom_fields": "custom_field",
"custom_field_choice_sets": "choice_set",
"custom_links": "custom_link",
"data_sources": "data_source",
"device_bays": "device_bay",
"device_bay_templates": "device_bay_template",
"devices": "device",
Expand Down Expand Up @@ -482,6 +486,7 @@
"custom_field_choice_set": set(["name"]),
"choice_set": set(["name"]),
"custom_link": set(["name"]),
"data_source": set(["name"]),
"dcim.consoleport": set(["name", "device"]),
"dcim.consoleserverport": set(["name", "device"]),
"dcim.frontport": set(["name", "device", "rear_port"]),
Expand Down
190 changes: 190 additions & 0 deletions plugins/modules/netbox_data_source.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2025, Daniel Chiquito (@dchiquito) <daniel.chiquito@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

__metaclass__ = type

DOCUMENTATION = r"""
---
module: netbox_data_source
short_description: Creates or removes data sources from NetBox
description:
- Creates or removes data sources from NetBox
author:
- Daniel Chiquito (@dchiquito)
requirements:
- pynetbox
version_added: "3.22.0"
extends_documentation_fragment:
- netbox.netbox.common
options:
data:
type: dict
description:
- Defines the data source configuration
suboptions:
name:
description:
- Name of the data source
required: true
type: str
type:
description:
- The origin of the data source
choices:
- local
- git
- amazon-s3
required: false
type: str
source_url:
description:
- URL of the data source to be created
required: false
type: str
enabled:
description:
- Whether or not this data source can be synced
required: false
type: bool
description:
description:
- Description of the data source
required: false
type: str
ignore_rules:
description:
- Patterns (one per line) matching files to ignore when syncing
required: false
type: str
sync_interval:
description:
- The interval in seconds between syncs
required: false
choices:
- 1
- 60
- 720
- 1440
- 10080
- 43200
type: int
comments:
description:
- Comments about the data source
required: false
type: str
required: true
"""

EXAMPLES = r"""
- name: "Test NetBox modules"
connection: local
hosts: localhost
gather_facts: false

tasks:
- name: "Create a new data source with only required information"
netbox.netbox.netbox_data_source:
netbox_url: http://netbox.local
netbox_token: thisIsMyToken
data:
name: "Data Source 1"
type: "local"
source_url: "/tmp/data-source.txt"
enabled: true
state: present
- name: "Update that data source with other fields"
netbox.netbox.netbox_data_source:
netbox_url: http://netbox.local
netbox_token: thisIsMyToken
data:
name: "Data Source 1"
type: "amazon-s3"
source_url: "path/to/bucket"
enabled: false
description: "My first data source"
ignore_rules: ".*\nfoo.txt\n*.yml"
sync_interval: 1440
comments: "Some commentary on this data source"
state: present
- name: "Delete the data source"
netbox.netbox.netbox_data_source:
netbox_url: http://netbox.local
netbox_token: thisIsMyToken
data:
name: "Data Source 1"
state: absent
"""

RETURN = r"""
data_source:
description: Serialized object as created or already existent within NetBox
returned: on creation
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_core import (
NetboxCoreModule,
NB_DATA_SOURCES,
)
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(
name=dict(required=True, type="str"),
type=dict(
required=False,
choices=["local", "git", "amazon-s3"],
type="str",
),
source_url=dict(required=False, type="str"),
enabled=dict(required=False, type="bool"),
description=dict(required=False, type="str"),
ignore_rules=dict(required=False, type="str"),
sync_interval=dict(
required=False,
choices=[1, 60, 60 * 12, 60 * 24, 60 * 24 * 7, 60 * 24 * 30],
type="int",
),
comments=dict(required=False, type="str"),
),
),
)
)

required_if = [
("state", "present", ["name", "type", "source_url", "enabled"]),
("state", "absent", ["name"]),
]

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

netbox_data_source = NetboxCoreModule(module, NB_DATA_SOURCES)
netbox_data_source.run()


if __name__ == "__main__": # pragma: no cover
main()
75 changes: 75 additions & 0 deletions tests/integration/targets/v4.0/tasks/netbox_data_source.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
---
##
##
### NETBOX_DATA_SOURCE
##
##
- name: "DATA SOURCE 1: Create"
netbox.netbox.netbox_data_source:
netbox_url: http://localhost:32768
netbox_token: "0123456789abcdef0123456789abcdef01234567"
data:
name: "Data Source 1"
type: "local"
source_url: "/tmp/data-source.txt"
enabled: true
state: present
register: test_one

- name: "DATA SOURCE 1: Assert - Create"
ansible.builtin.assert:
that:
- test_one is changed
- test_one['diff']['before']['state'] == "absent"
- test_one['diff']['after']['state'] == "present"
- test_one['data_source']['name'] == "Data Source 1"
- test_one['data_source']['type'] == "local"
- test_one['data_source']['source_url'] == "/tmp/data-source.txt"
- test_one['data_source']['enabled'] == true
- test_one['msg'] == "data_source Data Source 1 created"

- name: "DATA SOURCE 2: Update"
netbox.netbox.netbox_data_source:
netbox_url: http://localhost:32768
netbox_token: "0123456789abcdef0123456789abcdef01234567"
data:
name: "Data Source 1"
type: "amazon-s3"
source_url: "path/to/bucket"
enabled: false
description: "My first data source"
ignore_rules: ".*\nfoo.txt\n*.yml"
sync_interval: 1440
comments: "Some commentary on this data source"
state: present
register: test_two

- name: "DATA SOURCE 2: Assert - Update"
ansible.builtin.assert:
that:
- test_two is changed
- test_two['data_source']['name'] == "Data Source 1"
- test_two['data_source']['type'] == "amazon-s3"
- test_two['data_source']['source_url'] == "path/to/bucket"
- test_two['data_source']['enabled'] == false
- test_two['data_source']['description'] == "My first data source"
- test_two['data_source']['ignore_rules'] == ".*\nfoo.txt\n*.yml"
- test_two['data_source']['sync_interval'] == 1440
- test_two['data_source']['comments'] == "Some commentary on this data source"
- test_two['msg'] == "data_source Data Source 1 updated"

- name: "DATA SOURCE 3: Delete"
netbox.netbox.netbox_data_source:
netbox_url: http://localhost:32768
netbox_token: "0123456789abcdef0123456789abcdef01234567"
data:
name: "Data Source 1"
state: absent
register: test_three

- name: "DATA SOURCE 3: Assert - Delete"
ansible.builtin.assert:
that:
- test_three is changed
- test_three['diff']['before']['state'] == "present"
- test_three['diff']['after']['state'] == "absent"
Loading
Loading