diff --git a/plugins/lookup/nb_lookup.py b/plugins/lookup/nb_lookup.py index c3a5173b1..01e4d56aa 100644 --- a/plugins/lookup/nb_lookup.py +++ b/plugins/lookup/nb_lookup.py @@ -206,8 +206,6 @@ def get_endpoint(netbox, term): "job-results": {"endpoint": netbox.extras.job_results}, "journal-entries": {"endpoint": netbox.extras.journal_entries}, "locations": {"endpoint": netbox.dcim.locations}, - "l2vpn-terminations": {"endpoint": netbox.ipam.l2vpn_terminations}, - "l2vpns": {"endpoint": netbox.ipam.l2vpns}, "manufacturers": {"endpoint": netbox.dcim.manufacturers}, "module-bays": {"endpoint": netbox.dcim.module_bays}, "module-bay-templates": {"endpoint": netbox.dcim.module_bay_templates}, @@ -257,9 +255,10 @@ def get_endpoint(netbox, term): "webhooks": {"endpoint": netbox.extras.webhooks}, } - major, minor, patch = map(int, pynetbox.__version__.split(".")) + major, minor, patch = tuple(map(int, pynetbox.__version__.split("."))) + netbox_versiontuple = tuple(map(int, netbox.version.split("."))) - if major >= 6 and minor >= 4 and patch >= 0: + if (major, minor, patch) >= (6, 4): netbox_endpoint_map["wireless-lan-groups"] = { "endpoint": netbox.wireless.wireless_lan_groups } @@ -273,10 +272,6 @@ def get_endpoint(netbox, term): "endpoint": netbox.wireless.wireless_links } - if major < 7 and minor >= 0 and patch >= 1: - netbox_endpoint_map["secret-roles"] = {"endpoint": netbox.secrets.secret_roles} - netbox_endpoint_map["secrets"] = {"endpoint": netbox.secrets.secrets} - else: if "wireless" in term: Display().v( @@ -284,6 +279,34 @@ def get_endpoint(netbox, term): % (major, minor, patch) ) + if (major, minor, patch) < (7, 0, 1): + netbox_endpoint_map["secret-roles"] = {"endpoint": netbox.secrets.secret_roles} + netbox_endpoint_map["secrets"] = {"endpoint": netbox.secrets.secrets} + + if netbox_versiontuple >= (3, 7): + if (major, minor, patch) >= (7, 3): + netbox_endpoint_map["l2vpn-terminations"] = { + "endpoint": netbox.vpn.l2vpn_terminations + } + netbox_endpoint_map["l2vpns"] = {"endpoint": netbox.vpn.l2vpns} + netbox_endpoint_map["tunnel-terminations"] = { + "endpoint": netbox.vpn.tunnel_terminations + } + netbox_endpoint_map["tunnels"] = {"endpoint": netbox.vpn.tunnels} + + else: + if "l2vpn" in term: + Display().v( + "pynetbox version %d.%d.%d does not support vpn app; please update to v7.3.0 or newer." + % (major, minor, patch) + ) + + else: + netbox_endpoint_map["l2vpn-terminations"] = { + "endpoint": netbox.ipam.l2vpn_terminations + } + netbox_endpoint_map["l2vpns"] = {"endpoint": netbox.ipam.l2vpns} + return netbox_endpoint_map[term]["endpoint"] diff --git a/plugins/module_utils/netbox_utils.py b/plugins/module_utils/netbox_utils.py index 2695df7b4..04c327ff8 100644 --- a/plugins/module_utils/netbox_utils.py +++ b/plugins/module_utils/netbox_utils.py @@ -32,89 +32,103 @@ # Used to map endpoints to applications dynamically API_APPS_ENDPOINTS = dict( - circuits=[ - "circuits", - "circuit_types", - "circuit_terminations", - "providers", - "provider_networks", - ], - dcim=[ - "cables", - "console_ports", - "console_port_templates", - "console_server_ports", - "console_server_port_templates", - "device_bays", - "device_bay_templates", - "devices", - "device_roles", - "device_types", - "front_ports", - "front_port_templates", - "interfaces", - "interface_templates", - "inventory_items", - "inventory_item_roles", - "locations", - "manufacturers", - "module_types", - "platforms", - "power_feeds", - "power_outlets", - "power_outlet_templates", - "power_panels", - "power_ports", - "power_port_templates", - "racks", - "rack_groups", - "rack_roles", - "rear_ports", - "rear-ports", - "rear_port_templates", - "regions", - "sites", - "site_groups", - "virtual_chassis", - ], - extras=[ - "config_contexts", - "config_templates", - "tags", - "custom_fields", - "custom_links", - "export_templates", - "journal_entries", - "webhooks", - ], - ipam=[ - "aggregates", - "asns", - "fhrp_groups", - "fhrp_group_assignments", - "ip_addresses", - "l2vpns", - "l2vpn_terminations", - "prefixes", - "rirs", - "roles", - "route_targets", - "service_templates", - "vlans", - "vlan_groups", - "vrfs", - "services", - ], - secrets=[], - tenancy=["tenants", "tenant_groups", "contacts", "contact_groups", "contact_roles"], - virtualization=[ - "cluster_groups", - "cluster_types", - "clusters", - "virtual_machines", - "virtual_disks", - ], - wireless=["wireless_lans", "wireless_lan_groups", "wireless_links"], + circuits={ + "circuits": {}, + "circuit_types": {}, + "circuit_terminations": {}, + "providers": {}, + "provider_networks": {}, + }, + dcim={ + "cables": {}, + "console_ports": {}, + "console_port_templates": {}, + "console_server_ports": {}, + "console_server_port_templates": {}, + "device_bays": {}, + "device_bay_templates": {}, + "devices": {}, + "device_roles": {}, + "device_types": {}, + "front_ports": {}, + "front_port_templates": {}, + "interfaces": {}, + "interface_templates": {}, + "inventory_items": {}, + "inventory_item_roles": {}, + "locations": {}, + "manufacturers": {}, + "module_types": {}, + "platforms": {}, + "power_feeds": {}, + "power_outlets": {}, + "power_outlet_templates": {}, + "power_panels": {}, + "power_ports": {}, + "power_port_templates": {}, + "racks": {}, + "rack_groups": {}, + "rack_roles": {}, + "rear_ports": {}, + "rear-ports": {}, + "rear_port_templates": {}, + "regions": {}, + "sites": {}, + "site_groups": {}, + "virtual_chassis": {}, + }, + extras={ + "config_contexts": {}, + "config_templates": {}, + "tags": {}, + "custom_fields": {}, + "custom_links": {}, + "export_templates": {}, + "journal_entries": {}, + "webhooks": {}, + }, + ipam={ + "aggregates": {}, + "asns": {}, + "fhrp_groups": {}, + "fhrp_group_assignments": {}, + "ip_addresses": {}, + "l2vpns": {"deprecated": "3.7"}, + "l2vpn_terminations": {"deprecated": "3.7"}, + "prefixes": {}, + "rirs": {}, + "roles": {}, + "route_targets": {}, + "service_templates": {}, + "vlans": {}, + "vlan_groups": {}, + "vrfs": {}, + "services": {}, + }, + secrets={}, + tenancy={ + "tenants": {}, + "tenant_groups": {}, + "contacts": {}, + "contact_groups": {}, + "contact_roles": {}, + }, + virtualization={ + "cluster_groups": {}, + "cluster_types": {}, + "clusters": {}, + "virtual_machines": {}, + "virtual_disks": {}, + }, + wireless={ + "wireless_lans": {}, + "wireless_lan_groups": {}, + "wireless_links": {}, + }, + vpn={ + "l2vpns": {"introduced": "3.7"}, + "l2vpn_terminations": {"introduced": "3.7"}, + }, ) # Used to normalize data for the respective query types used to find endpoints @@ -1122,7 +1136,19 @@ def _find_app(self, endpoint): """ nb_app = None for k, v in API_APPS_ENDPOINTS.items(): - if endpoint in v: + if endpoint in v.keys(): + if "introduced" in v[endpoint]: + pre_introduction = self._version_check_greater( + v[endpoint]["introduced"], self.version + ) + if pre_introduction: + continue + if "deprecated" in v[endpoint]: + after_deprecation = self._version_check_greater( + self.version, v[endpoint]["deprecated"], greater_or_equal=True + ) + if after_deprecation: + continue nb_app = k if nb_app: diff --git a/plugins/module_utils/netbox_vpn.py b/plugins/module_utils/netbox_vpn.py new file mode 100644 index 000000000..833389433 --- /dev/null +++ b/plugins/module_utils/netbox_vpn.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +# Copyright: (c) 2018, Mikhail Yohman (@fragmentedpacket) +# Copyright: (c) 2024, Fred De Backer (@freddebacker) +# 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 + +# Import necessary packages + +from ansible_collections.netbox.netbox.plugins.module_utils.netbox_utils import ( + NetboxModule, + ENDPOINT_NAME_MAPPING, + SLUG_REQUIRED, +) + + +NB_L2VPNS = "l2vpns" +NB_L2VPN_TERMINATIONS = "l2vpn_terminations" + + +class NetboxVpnModule(NetboxModule): + def __init__(self, module, endpoint): + super().__init__(module, endpoint) + + def run(self): + """ + This function should have all necessary code for endpoints within the application + to create/update/delete the endpoint objects + Supported endpoints: + - l2vpns + - l2vpn_terminations + """ + # 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 + + if self.endpoint == "l2vpn_terminations": + name = "l2vpn %s <> %s %s" % ( + data.get("l2vpn"), + data.get("assigned_object_type"), + data.get("assigned_object_id"), + ) + else: + name = data.get("name") + + if self.endpoint in SLUG_REQUIRED: + if not data.get("slug"): + data["slug"] = self._to_slug(name) + + 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) diff --git a/plugins/modules/netbox_l2vpn.py b/plugins/modules/netbox_l2vpn.py index 907f8f70f..8002660f9 100644 --- a/plugins/modules/netbox_l2vpn.py +++ b/plugins/modules/netbox_l2vpn.py @@ -143,11 +143,16 @@ from ansible_collections.netbox.netbox.plugins.module_utils.netbox_utils import ( NetboxAnsibleModule, + NetboxModule, NETBOX_ARG_SPEC, ) from ansible_collections.netbox.netbox.plugins.module_utils.netbox_ipam import ( NetboxIpamModule, - NB_L2VPNS, + NB_L2VPNS as NB_IPAM_L2VPNS, +) +from ansible_collections.netbox.netbox.plugins.module_utils.netbox_vpn import ( + NetboxVpnModule, + NB_L2VPNS as NB_VPN_L2VPNS, ) from copy import deepcopy @@ -186,7 +191,12 @@ def main(): argument_spec=argument_spec, supports_check_mode=True, required_if=required_if ) - netbox_l2vpn = NetboxIpamModule(module, NB_L2VPNS) + netbox_l2vpn = NetboxModule(module, "") + if netbox_l2vpn._find_app(NB_IPAM_L2VPNS) == "ipam": + netbox_l2vpn = NetboxIpamModule(module, NB_IPAM_L2VPNS) + if netbox_l2vpn._find_app(NB_VPN_L2VPNS) == "vpn": + netbox_l2vpn = NetboxVpnModule(module, NB_VPN_L2VPNS) + netbox_l2vpn.run() diff --git a/plugins/modules/netbox_l2vpn_termination.py b/plugins/modules/netbox_l2vpn_termination.py index d065abff5..965a38785 100644 --- a/plugins/modules/netbox_l2vpn_termination.py +++ b/plugins/modules/netbox_l2vpn_termination.py @@ -105,12 +105,17 @@ from ansible_collections.netbox.netbox.plugins.module_utils.netbox_utils import ( NetboxAnsibleModule, + NetboxModule, NETBOX_ARG_SPEC, ) from ansible_collections.netbox.netbox.plugins.module_utils.netbox_ipam import ( NetboxIpamModule, - NB_L2VPN_TERMINATIONS, + NB_L2VPN_TERMINATIONS as NB_IPAM_L2VPN_TERMINATIONS, +) +from ansible_collections.netbox.netbox.plugins.module_utils.netbox_vpn import ( + NetboxVpnModule, + NB_L2VPN_TERMINATIONS as NB_VPN_L2VPN_TERMINATIONS, ) @@ -146,7 +151,13 @@ def main(): ) module = NetboxAnsibleModule(argument_spec=argument_spec, supports_check_mode=True) - netbox_l2vpn_termination = NetboxIpamModule(module, NB_L2VPN_TERMINATIONS) + + netbox_l2vpn_termination = NetboxModule(module, "") + if netbox_l2vpn_termination._find_app(NB_IPAM_L2VPN_TERMINATIONS) == "ipam": + netbox_l2vpn_termination = NetboxIpamModule(module, NB_IPAM_L2VPN_TERMINATIONS) + if netbox_l2vpn_termination._find_app(NB_VPN_L2VPN_TERMINATIONS) == "vpn": + netbox_l2vpn_termination = NetboxVpnModule(module, NB_VPN_L2VPN_TERMINATIONS) + netbox_l2vpn_termination.run()