From 6b1d1830990fc1307d223f24daab255b734e4111 Mon Sep 17 00:00:00 2001 From: Jeff Groom Date: Tue, 18 Apr 2023 13:53:38 -0600 Subject: [PATCH 1/3] WIP --- plugins/module_utils/netbox_dcim.py | 1 + plugins/module_utils/netbox_utils.py | 8 +- .../netbox_module_interface_template.py | 158 ++++++++++++++++++ 3 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 plugins/modules/netbox_module_interface_template.py diff --git a/plugins/module_utils/netbox_dcim.py b/plugins/module_utils/netbox_dcim.py index 719f76b4f..242ae5a2b 100644 --- a/plugins/module_utils/netbox_dcim.py +++ b/plugins/module_utils/netbox_dcim.py @@ -91,6 +91,7 @@ def run(self): - inventory_item_roles - locations - manufacturers + - module_types - platforms - power_feeds - power_outlets diff --git a/plugins/module_utils/netbox_utils.py b/plugins/module_utils/netbox_utils.py index ce1b6462c..188209e5c 100644 --- a/plugins/module_utils/netbox_utils.py +++ b/plugins/module_utils/netbox_utils.py @@ -230,6 +230,8 @@ "lag": "interfaces", "manufacturer": "manufacturers", "master": "devices", + "module_type": "module_types", + "module_types": "module_types", "nat_inside": "ip_addresses", "nat_outside": "ip_addresses", "platform": "platforms", @@ -389,6 +391,7 @@ "sites", "roles", "device_types", + "module_types", "platforms", "cluster_types", "cluster_groups", @@ -431,7 +434,7 @@ "interface": set(["name", "device", "virtual_machine"]), "interface_a": set(["name", "device"]), "interface_b": set(["name", "device"]), - "interface_template": set(["name", "device_type"]), + "interface_template": set(["name", "device_type", "module_type"]), "inventory_item": set(["name", "device"]), "inventory_item_role": set(["name"]), "ip_address": set(["address", "vrf", "device", "interface", "assigned_object"]), @@ -1008,6 +1011,8 @@ def _build_query_params( elif "_template" in parent: if query_dict.get("device_type"): query_dict["devicetype_id"] = query_dict.pop("device_type") + if query_dict.get("module_type"): + query_dict["moduletype_id"] = query_dict.pop("module_type") if not query_dict: provided_kwargs = child.keys() if child else module_data.keys() @@ -1123,6 +1128,7 @@ def _find_ids(self, data, user_query_params): "sites", "roles", "device_types", + "module_types", "platforms", "cluster_groups", "contact_groups", diff --git a/plugins/modules/netbox_module_interface_template.py b/plugins/modules/netbox_module_interface_template.py new file mode 100644 index 000000000..1bb9d01e1 --- /dev/null +++ b/plugins/modules/netbox_module_interface_template.py @@ -0,0 +1,158 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# © 2023 Ciena +# Licensed under the GNU General Public License v3.0 only +# SPDX-License-Identifier: GPL-3.0-only + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = r""" +--- +module: netbox_module_interface_template +short_description: Creates or removes interfaces on modules from NetBox +description: + - Creates or removes interfaces from NetBox +notes: + - Tags should be defined as a YAML list + - This should be ran with connection C(local) and hosts C(localhost) +author: + - Jeff Groom (@jgroom33) +requirements: + - pynetbox +version_added: "0.3.12" +extends_documentation_fragment: + - netbox.netbox.common +options: + data: + description: + - Defines the interface template configuration + suboptions: + module_type: + description: + - Name of the module the interface template will be associated with (case-sensitive) + required: true + type: raw + name: + description: + - Name of the interface template to be created + required: true + type: str + type: + description: + - | + Form factor of the interface: + ex. 1000Base-T (1GE), Virtual, 10GBASE-T (10GE) + This has to be specified exactly as what is found within UI + required: true + type: str + mgmt_only: + description: + - This interface template is used only for out-of-band management + required: false + type: bool + poe_mode: + description: + - This interface has PoE ability (NetBox release 3.3 and later) + required: false + type: raw + version_added: "3.8.0" + poe_type: + description: + - This interface's power type (NetBox release 3.3 and later) + required: false + type: raw + version_added: "3.8.0" + required: true + type: dict +""" + +EXAMPLES = r""" +- name: "Test NetBox interface template module" + connection: local + hosts: localhost + gather_facts: False + tasks: + - name: Create interface template within NetBox with only required information + netbox.netbox.netbox_module_interface_template: + netbox_url: http://netbox.local + netbox_token: thisIsMyToken + data: + module_type: Arista Test + name: 10GBASE-T (10GE) + type: 10gbase-t + state: present + - name: Delete interface template within netbox + netbox.netbox.netbox_module_interface_template: + netbox_url: http://netbox.local + netbox_token: thisIsMyToken + data: + module_type: Arista Test + name: 10GBASE-T (10GE) + type: 10gbase-t + state: absent +""" + +RETURN = r""" +interface_template: + 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_dcim import ( + NetboxDcimModule, + NB_INTERFACE_TEMPLATES, +) +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( + module_type=dict(required=True, type="raw"), + name=dict(required=True, type="str"), + type=dict( + required=True, + type="str", + ), + mgmt_only=dict(required=False, type="bool"), + poe_type=dict(required=False, type="raw"), + poe_mode=dict(required=False, type="raw"), + ), + ), + ) + ) + + required_if = [ + ("state", "present", ["module_type", "name", "type"]), + ("state", "absent", ["module_type", "name", "type"]), + ] + + module = NetboxAnsibleModule( + argument_spec=argument_spec, supports_check_mode=True, required_if=required_if + ) + + netbox_module_interface_template = NetboxDcimModule(module, NB_INTERFACE_TEMPLATES) + netbox_module_interface_template.run() + + +if __name__ == "__main__": # pragma: no cover + main() From 79e336fb9d56de7feeb0afb1e19c8ba1759c7402 Mon Sep 17 00:00:00 2001 From: Jeff Groom Date: Tue, 18 Apr 2023 17:56:32 -0600 Subject: [PATCH 2/3] add module bays --- meta/runtime.yml | 1 + plugins/module_utils/netbox_dcim.py | 2 + plugins/module_utils/netbox_utils.py | 7 ++ plugins/modules/netbox_module_bay.py | 145 +++++++++++++++++++++++++++ 4 files changed, 155 insertions(+) create mode 100644 plugins/modules/netbox_module_bay.py diff --git a/meta/runtime.yml b/meta/runtime.yml index a512a116d..e36f25c70 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -49,6 +49,7 @@ action_groups: - netbox_l2vpn - netbox_location - netbox_manufacturer + - netbox_module_bay - netbox_module_type - netbox_platform - netbox_power_feed diff --git a/plugins/module_utils/netbox_dcim.py b/plugins/module_utils/netbox_dcim.py index 242ae5a2b..52c3c32b1 100644 --- a/plugins/module_utils/netbox_dcim.py +++ b/plugins/module_utils/netbox_dcim.py @@ -32,6 +32,7 @@ NB_INVENTORY_ITEM_ROLES = "inventory_item_roles" NB_LOCATIONS = "locations" NB_MANUFACTURERS = "manufacturers" +NB_MODULE_BAYS = "module_bays" NB_MODULE_TYPES = "module_types" NB_PLATFORMS = "platforms" NB_POWER_FEEDS = "power_feeds" @@ -91,6 +92,7 @@ def run(self): - inventory_item_roles - locations - manufacturers + - module_bays - module_types - platforms - power_feeds diff --git a/plugins/module_utils/netbox_utils.py b/plugins/module_utils/netbox_utils.py index 188209e5c..4590b2cb6 100644 --- a/plugins/module_utils/netbox_utils.py +++ b/plugins/module_utils/netbox_utils.py @@ -58,6 +58,7 @@ "inventory_item_roles", "locations", "manufacturers", + "module_bays", "module_types", "platforms", "power_feeds", @@ -142,6 +143,7 @@ l2vpn="name", location="slug", manufacturer="slug", + module_bay="name", module_type="model", nat_inside="address", nat_outside="address", @@ -230,6 +232,8 @@ "lag": "interfaces", "manufacturer": "manufacturers", "master": "devices", + "module_bay": "module_bays", + "module_bays": "module_bays", "module_type": "module_types", "module_types": "module_types", "nat_inside": "ip_addresses", @@ -333,6 +337,7 @@ "l2vpns": "l2vpn", "locations": "location", "manufacturers": "manufacturer", + "module_bays": "module_bay", "module_types": "module_type", "platforms": "platform", "power_feeds": "power_feed", @@ -391,6 +396,7 @@ "sites", "roles", "device_types", + "module_bays", "module_types", "platforms", "cluster_types", @@ -445,6 +451,7 @@ "l2vpn": set(["name"]), "lag": set(["name"]), "location": set(["name", "slug", "site"]), + "module_bay": set(["name"]), "module_type": set(["model"]), "manufacturer": set(["slug"]), "master": set(["name"]), diff --git a/plugins/modules/netbox_module_bay.py b/plugins/modules/netbox_module_bay.py new file mode 100644 index 000000000..08b2cecb1 --- /dev/null +++ b/plugins/modules/netbox_module_bay.py @@ -0,0 +1,145 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright: (c) 2022, Martin Rødvand (@rodvand) +# 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_module_bay +short_description: Create, update or delete module bay within NetBox +description: + - Creates, updates or removes module bay from NetBox +notes: + - Tags should be defined as a YAML list + - This should be ran with connection C(local) and hosts C(localhost) +author: + - Jeff Groom (@jgroom33) +requirements: + - pynetbox +version_added: '3.13.0' +extends_documentation_fragment: + - netbox.netbox.common +options: + data: + description: + - Defines the module bay configuration + suboptions: + device: + description: + - The device of the module bay + required: true + type: raw + name: + description: + - The model of the module type + required: true + type: raw + position: + description: + - The position of the module bay + required: true + type: string + tags: + description: + - Any tags that the module bay may need to be associated with + required: false + type: list + elements: raw + custom_fields: + description: + - must exist in NetBox + required: false + type: dict + required: true + type: dict +""" + +EXAMPLES = r""" +- name: "Test NetBox modules" + connection: local + hosts: localhost + gather_facts: False + + tasks: + - name: Create module bay within NetBox with only required information + netbox.netbox.netbox_module_bay: + netbox_url: http://netbox.local + netbox_token: thisIsMyToken + data: + device: ws-test-3750 + name: ws-test-3750-slot-0 + position: 0 + manufacturer: Test Manufacturer + state: present + + - name: Delete module bay within netbox + netbox.netbox.netbox_module_bay: + netbox_url: http://netbox.local + netbox_token: thisIsMyToken + data: + name: ws-test-3750-slot-0 + state: absent +""" + +RETURN = r""" +module_type: + description: Serialized object as created or already existent within NetBox + 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.netbox.netbox.plugins.module_utils.netbox_utils import ( + NetboxAnsibleModule, + NETBOX_ARG_SPEC, +) +from ansible_collections.netbox.netbox.plugins.module_utils.netbox_dcim import ( + NetboxDcimModule, + NB_MODULE_BAYS, +) +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( + device=dict(required=True, type="raw"), + name=dict(required=True, type="raw"), + position=dict(required=True, type="str"), + tags=dict(required=False, type="list", elements="raw"), + custom_fields=dict(required=False, type="dict"), + ), + ), + ) + ) + + required_if = [ + ("state", "present", ["device", "name", "position"]), + ("state", "absent", ["name"]), + ] + + module = NetboxAnsibleModule( + argument_spec=argument_spec, supports_check_mode=True, required_if=required_if + ) + + netbox_module_bay = NetboxDcimModule(module, NB_MODULE_BAYS) + netbox_module_bay.run() + + +if __name__ == "__main__": # pragma: no cover + main() From 4b2bf98b990c8d966ec3beb6b81227e3477cbff1 Mon Sep 17 00:00:00 2001 From: Jeff Groom Date: Wed, 19 Apr 2023 10:58:38 -0600 Subject: [PATCH 3/3] module module --- meta/runtime.yml | 1 + plugins/module_utils/netbox_dcim.py | 4 + plugins/module_utils/netbox_utils.py | 6 + plugins/modules/netbox_module.py | 147 ++++++++++++++++++ plugins/modules/netbox_module_bay.py | 2 +- .../netbox_module_interface_template.py | 9 +- 6 files changed, 162 insertions(+), 7 deletions(-) create mode 100644 plugins/modules/netbox_module.py diff --git a/meta/runtime.yml b/meta/runtime.yml index e36f25c70..8a3cdf2ff 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -49,6 +49,7 @@ action_groups: - netbox_l2vpn - netbox_location - netbox_manufacturer + - netbox_module - netbox_module_bay - netbox_module_type - netbox_platform diff --git a/plugins/module_utils/netbox_dcim.py b/plugins/module_utils/netbox_dcim.py index 52c3c32b1..0a78966ac 100644 --- a/plugins/module_utils/netbox_dcim.py +++ b/plugins/module_utils/netbox_dcim.py @@ -32,6 +32,7 @@ NB_INVENTORY_ITEM_ROLES = "inventory_item_roles" NB_LOCATIONS = "locations" NB_MANUFACTURERS = "manufacturers" +NB_MODULES = "modules" NB_MODULE_BAYS = "module_bays" NB_MODULE_TYPES = "module_types" NB_PLATFORMS = "platforms" @@ -92,6 +93,7 @@ def run(self): - inventory_item_roles - locations - manufacturers + - modules - module_bays - module_types - platforms @@ -153,6 +155,8 @@ def run(self): data.get("termination_b_type"), termination_b_name, ) + elif data.get("asset_tag"): + name = data["asset_tag"] if self.endpoint in SLUG_REQUIRED: if not data.get("slug"): diff --git a/plugins/module_utils/netbox_utils.py b/plugins/module_utils/netbox_utils.py index 4590b2cb6..6fdcae58f 100644 --- a/plugins/module_utils/netbox_utils.py +++ b/plugins/module_utils/netbox_utils.py @@ -58,6 +58,7 @@ "inventory_item_roles", "locations", "manufacturers", + "modules", "module_bays", "module_types", "platforms", @@ -143,6 +144,7 @@ l2vpn="name", location="slug", manufacturer="slug", + module="asset_tag", module_bay="name", module_type="model", nat_inside="address", @@ -232,6 +234,8 @@ "lag": "interfaces", "manufacturer": "manufacturers", "master": "devices", + "module": "modules", + "modules": "modules", "module_bay": "module_bays", "module_bays": "module_bays", "module_type": "module_types", @@ -337,6 +341,7 @@ "l2vpns": "l2vpn", "locations": "location", "manufacturers": "manufacturer", + "modules": "module", "module_bays": "module_bay", "module_types": "module_type", "platforms": "platform", @@ -451,6 +456,7 @@ "l2vpn": set(["name"]), "lag": set(["name"]), "location": set(["name", "slug", "site"]), + "module": set(["asset_tag"]), "module_bay": set(["name"]), "module_type": set(["model"]), "manufacturer": set(["slug"]), diff --git a/plugins/modules/netbox_module.py b/plugins/modules/netbox_module.py new file mode 100644 index 000000000..9da0df654 --- /dev/null +++ b/plugins/modules/netbox_module.py @@ -0,0 +1,147 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright: (c) 2023, Jeff Groom (@jgroom33) +# 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_module +short_description: Create, update or delete module within NetBox +description: + - Creates, updates or removes module from NetBox +notes: + - Tags should be defined as a YAML list + - This should be ran with connection C(local) and hosts C(localhost) +author: + - Jeff Groom (@jgroom33) +requirements: + - pynetbox +version_added: '3.13.0' +extends_documentation_fragment: + - netbox.netbox.common +options: + data: + description: + - Defines the module configuration + suboptions: + device: + description: + - The device name of the module + required: true + type: raw + module_bay: + description: + - The module bay name of the module + required: true + type: raw + module_type: + description: + - The module type model of the module + required: true + type: string + tags: + description: + - Any tags that the module may need to be associated with + required: false + type: list + elements: raw + custom_fields: + description: + - must exist in NetBox + required: false + type: dict + required: true + type: dict +""" + +EXAMPLES = r""" +- name: "Test NetBox modules" + connection: local + hosts: localhost + gather_facts: False + + tasks: + - name: Create module within NetBox with only required information + netbox.netbox.netbox_module: + netbox_url: http://netbox.local + netbox_token: thisIsMyToken + data: + module_bay: ws-test-3750-slot-0 + state: present + + - name: Delete module within netbox + netbox.netbox.netbox_module: + netbox_url: http://netbox.local + netbox_token: thisIsMyToken + data: + module_bay: ws-test-3750-slot-0 + state: absent +""" + +RETURN = r""" +module_type: + description: Serialized object as created or already existent within NetBox + 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.netbox.netbox.plugins.module_utils.netbox_utils import ( + NetboxAnsibleModule, + NETBOX_ARG_SPEC, +) +from ansible_collections.netbox.netbox.plugins.module_utils.netbox_dcim import ( + NetboxDcimModule, + NB_MODULES, +) +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( + device=dict(required=False, type="raw"), + module_bay=dict(required=False, type="raw"), + module_type=dict(required=False, type="raw"), + status=dict(required=False, type="raw"), + serial=dict(required=False, type="str"), + asset_tag=dict(required=True, type="str"), + description=dict(required=False, type="str"), + comments=dict(required=False, type="str"), + tags=dict(required=False, type="list", elements="raw"), + custom_fields=dict(required=False, type="dict"), + ), + ), + ) + ) + + required_if = [ + ("state", "present", ["asset_tag", "device", "module_bay", "module_type"]), + ("state", "absent", ["asset_tag"]), + ] + + module = NetboxAnsibleModule( + argument_spec=argument_spec, supports_check_mode=True, required_if=required_if + ) + + netbox_module = NetboxDcimModule(module, NB_MODULES) + netbox_module.run() + + +if __name__ == "__main__": # pragma: no cover + main() diff --git a/plugins/modules/netbox_module_bay.py b/plugins/modules/netbox_module_bay.py index 08b2cecb1..565953063 100644 --- a/plugins/modules/netbox_module_bay.py +++ b/plugins/modules/netbox_module_bay.py @@ -1,6 +1,6 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# Copyright: (c) 2022, Martin Rødvand (@rodvand) +# Copyright: (c) 2023, Jeff Groom (@jgroom33) # 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 diff --git a/plugins/modules/netbox_module_interface_template.py b/plugins/modules/netbox_module_interface_template.py index 1bb9d01e1..b8f4780e7 100644 --- a/plugins/modules/netbox_module_interface_template.py +++ b/plugins/modules/netbox_module_interface_template.py @@ -1,8 +1,7 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# © 2023 Ciena -# Licensed under the GNU General Public License v3.0 only -# SPDX-License-Identifier: GPL-3.0-only +# Copyright: (c) 2023, Jeff Groom (@jgroom33) +# 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 @@ -21,7 +20,7 @@ - Jeff Groom (@jgroom33) requirements: - pynetbox -version_added: "0.3.12" +version_added: "3.13.0" extends_documentation_fragment: - netbox.netbox.common options: @@ -57,13 +56,11 @@ - This interface has PoE ability (NetBox release 3.3 and later) required: false type: raw - version_added: "3.8.0" poe_type: description: - This interface's power type (NetBox release 3.3 and later) required: false type: raw - version_added: "3.8.0" required: true type: dict """