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

Add support for all new cloud models in Nautobot 2.3+ #434

Merged
merged 28 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
50e345f
Added cloud_resource_type and tests
tsm1th Oct 31, 2024
519fb8c
Fixed linting issue
tsm1th Oct 31, 2024
9e803b7
Updated cloud test to only run on nautobot2.3
tsm1th Oct 31, 2024
419d0e9
Fixed indentation issue in test
tsm1th Oct 31, 2024
575e4f8
Added alias to handle name clashing and added lookup for new model
tsm1th Nov 1, 2024
9b7b418
Undoing change to dev.env
tsm1th Nov 1, 2024
3c08ec2
Removing erroneous shebang
tsm1th Nov 1, 2024
14966ed
Added cloud account module and tests
tsm1th Nov 2, 2024
16bb47a
Fixed examples
tsm1th Nov 2, 2024
12040fb
Added no_log to secret_group
tsm1th Nov 2, 2024
9e2e774
Added cloud service module and tests
tsm1th Nov 2, 2024
02d2f01
Added cloud network and tests, fixed invalid fields in cloud service
tsm1th Nov 2, 2024
c5734d7
Fixed linting error
tsm1th Nov 2, 2024
60fea30
Added cloud service network assignment module and tests
tsm1th Nov 3, 2024
fcba511
Added cloud network to prefix assignment module and tests
tsm1th Nov 3, 2024
44cd2ec
Added cloud network prefix assignment module and tests
tsm1th Nov 3, 2024
1c3dcdc
Added query params to cloud objects to prevent q query and fixed var …
tsm1th Nov 3, 2024
187176b
Merged develop into branch for pynautobot bump
tsm1th Nov 5, 2024
d5123e7
Fixed cosmetic issues in docs, tests, vars, etc.
tsm1th Nov 6, 2024
ed06a2d
Refactored circuit module to fix issue with alias 'provider' and upda…
tsm1th Nov 6, 2024
3b88d16
Merge branch 'develop' into u/tsm1th-add-cloud-modules
tsm1th Nov 6, 2024
d224589
Added extra_config to cloud_service module and updated test case
tsm1th Nov 6, 2024
f0a3cdc
Added back module creation after bad merge
tsm1th Nov 6, 2024
ff12575
Removed whitespace for linting
tsm1th Nov 6, 2024
f5108bf
Added extra_config to cloud service and updated idempotency tests
tsm1th Nov 7, 2024
9473044
Merge branch 'develop' into u/tsm1th-add-cloud-modules
tsm1th Nov 7, 2024
d949ef5
Swapped to alias names in all cloud tests
tsm1th Nov 8, 2024
269643f
Updated example in cloud account module
tsm1th Nov 11, 2024
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
6 changes: 6 additions & 0 deletions plugins/lookup/lookup.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,12 @@ def get_endpoint(nautobot, term):
"circuits": {"endpoint": nautobot.circuits.circuits},
"circuit-providers": {"endpoint": nautobot.circuits.providers},
"cables": {"endpoint": nautobot.dcim.cables},
"cloud-accounts": {"endpoint": nautobot.cloud.cloud_accounts},
"cloud-networks": {"endpoint": nautobot.cloud.cloud_networks},
"cloud-network-prefix-assignments": {"endpoint": nautobot.cloud.cloud_network_prefix_assignments},
"cloud-resource-types": {"endpoint": nautobot.cloud.cloud_resource_types},
"cloud-services": {"endpoint": nautobot.cloud.cloud_services},
"cloud-service-network-assignments": {"endpoint": nautobot.cloud.cloud_service_network_assignments},
"cluster-groups": {"endpoint": nautobot.virtualization.cluster_groups},
"cluster-types": {"endpoint": nautobot.virtualization.cluster_types},
"clusters": {"endpoint": nautobot.virtualization.clusters},
Expand Down
79 changes: 79 additions & 0 deletions plugins/module_utils/cloud.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2024, 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

from ansible_collections.networktocode.nautobot.plugins.module_utils.utils import (
NautobotModule,
ENDPOINT_NAME_MAPPING,
)

NB_CLOUD_SERVICES = "cloud_services"
NB_CLOUD_NETWORKS = "cloud_networks"
NB_CLOUD_NETWORK_PREFIX_ASSIGNMENTS = "cloud_network_prefix_assignments"
NB_CLOUD_SERVICE_NETWORK_ASSIGNMENTS = "cloud_service_network_assignments"
NB_CLOUD_ACCOUNTS = "cloud_accounts"
NB_CLOUD_RESOURCE_TYPES = "cloud_resource_types"


class NautobotCloudModule(NautobotModule):
def run(self):
"""
This function should have all necessary code for endpoints within the application
to create/update/delete the endpoint objects
Supported endpoints:
- cloud_services
- cloud_networks
- cloud_network_prefix_assignments
- cloud_service_network_assignments
- cloud_accounts
- cloud_resource_types
"""
# 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 endpoint_name == "cloud_service_network_assignment":
cloud_service = self.module.params["cloud_service"]
cloud_network = self.module.params["cloud_network"]

name = "%s <> %s" % (
cloud_service,
cloud_network,
)
elif endpoint_name == "cloud_network_prefix_assignment":
cloud_network = self.module.params["cloud_network"]
cloud_prefix = self.module.params["cloud_prefix"]

name = "%s <> %s" % (cloud_network, cloud_prefix)

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)
42 changes: 40 additions & 2 deletions plugins/module_utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@
# Used to map endpoints to applications dynamically
API_APPS_ENDPOINTS = dict(
circuits=["circuits", "circuit_types", "circuit_terminations", "providers"],
cloud=[
"cloud_accounts",
"cloud_networks",
"cloud_network_prefix_assignments",
"cloud_resource_types",
"cloud_services",
"cloud_service_network_assignments",
],
dcim=[
"cables",
"console_ports",
Expand Down Expand Up @@ -100,8 +108,13 @@
# Used to normalize data for the respective query types used to find endpoints
QUERY_TYPES = dict(
circuit="cid",
circuit_provider="name",
circuit_termination="circuit",
circuit_type="name",
cloud_network="name",
cloud_prefix="prefix",
cloud_provider="name",
cloud_service="name",
cluster="name",
cluster_group="name",
cluster_type="name",
Expand All @@ -120,6 +133,7 @@
master="name",
nat_inside="address",
nat_outside="address",
parent_cloud_network="name",
parent_location="name",
parent_location_type="name",
parent_rack_group="name",
Expand All @@ -131,7 +145,6 @@
primary_ip="address",
primary_ip4="address",
primary_ip6="address",
provider="name",
rack="name",
rack_group="name",
rear_port="name",
Expand All @@ -153,9 +166,18 @@
# Specifies keys within data that need to be converted to ID and the endpoint to be used when queried
CONVERT_TO_ID = {
"circuit": "circuits",
"circuit_provider": "providers",
"circuit_type": "circuit_types",
"circuit_termination": "circuit_terminations",
"circuits.circuittermination": "circuit_terminations",
"cloud_account": "cloud_accounts",
"cloud_prefix": "prefixes",
"cloud_provider": "manufacturers",
"cloud_resource_type": "cloud_resource_types",
"cloud_network": "cloud_networks",
"cloud_network_prefix_assignment": "cloud_network_prefix_assignments",
"cloud_service": "cloud_services",
"cloud_service_network_assignment": "cloud_service_network_assignments",
"cluster": "clusters",
"cluster_group": "cluster_groups",
"cluster_type": "cluster_types",
Expand Down Expand Up @@ -190,6 +212,7 @@
"namespace": "namespaces",
"platform": "platforms",
"parent_rack_group": "rack_groups",
"parent_cloud_network": "cloud_networks",
"parent_location": "locations",
"parent_location_type": "location_types",
"parent_tenant_group": "tenant_groups",
Expand All @@ -199,7 +222,6 @@
"primary_ip": "ip_addresses",
"primary_ip4": "ip_addresses",
"primary_ip6": "ip_addresses",
"provider": "providers",
"rack": "racks",
"rack_group": "rack_groups",
"rear_port": "rear_ports",
Expand Down Expand Up @@ -231,6 +253,12 @@
"circuit_terminations": "circuit_termination",
"circuit_types": "circuit_type",
"circuits": "circuit",
"cloud_accounts": "cloud_account",
"cloud_networks": "cloud_network",
"cloud_network_prefix_assignments": "cloud_network_prefix_assignment",
"cloud_resource_types": "cloud_resource_type",
"cloud_services": "cloud_service",
"cloud_service_network_assignments": "cloud_service_network_assignment",
"clusters": "cluster",
"cluster_groups": "cluster_group",
"cluster_types": "cluster_type",
Expand Down Expand Up @@ -299,6 +327,12 @@
"circuit_type": set(["name"]),
"circuit_termination": set(["circuit", "term_side"]),
"circuits.circuittermination": set(["circuit", "term_side"]),
"cloud_account": set(["name"]),
"cloud_network": set(["name"]),
"cloud_network_prefix_assignment": set(["cloud_network", "prefix"]),
"cloud_resource_type": set(["name"]),
"cloud_service": set(["name"]),
"cloud_service_network_assignment": set(["cloud_service", "cloud_network"]),
"cluster": set(["name", "type"]),
"cluster_group": set(["name"]),
"cluster_type": set(["name"]),
Expand Down Expand Up @@ -434,6 +468,10 @@

# This is used to map non-clashing keys to Nautobot API compliant keys to prevent bad logic in code for similar keys but different modules
CONVERT_KEYS = {
"circuit_provider": "provider",
"cloud_prefix": "prefix",
"cloud_provider": "provider",
"parent_cloud_network": "parent",
"parent_rack_group": "parent",
"parent_location": "parent",
"parent_location_type": "parent",
Expand Down
6 changes: 4 additions & 2 deletions plugins/modules/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@
required: true
type: str
version_added: "3.0.0"
provider:
circuit_provider:
aliases:
- provider
description:
- The provider of the circuit
required: false
Expand Down Expand Up @@ -157,7 +159,7 @@ def main():
argument_spec.update(
dict(
cid=dict(required=True, type="str"),
provider=dict(required=False, type="raw"),
circuit_provider=dict(required=False, type="raw", aliases=["provider"]),
circuit_type=dict(required=False, type="raw"),
status=dict(required=False, type="raw"),
tenant=dict(required=False, type="raw"),
Expand Down
128 changes: 128 additions & 0 deletions plugins/modules/cloud_account.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2024, 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: cloud_account
short_description: Creates or removes cloud account from Nautobot
description:
- Creates or removes cloud account from Nautobot
notes:
- Tags should be defined as a YAML list
- This should be ran with connection C(local) and hosts C(localhost)
author:
- Travis Smith (@tsm1th)
requirements:
- pynautobot
version_added: "5.4.0"
extends_documentation_fragment:
- networktocode.nautobot.fragments.base
- networktocode.nautobot.fragments.tags
- networktocode.nautobot.fragments.custom_fields
options:
name:
description:
- The name of the cloud account
required: true
type: str
account_number:
description:
- Required if I(state=present) and the cloud account does not exist yet
required: false
type: str
description:
description:
- The description of the cloud account
required: false
type: str
cloud_provider:
aliases:
- provider
description:
- Required if I(state=present) and the cloud account does not exist yet
required: false
type: raw
secrets_group:
description:
- The secrets group of the cloud account
required: false
type: raw
"""

EXAMPLES = r"""
---
- name: Create a cloud account
networktocode.nautobot.cloud_account:
url: http://nautobot.local
token: thisIsMyToken
name: Cisco Quantum Account
provider: Cisco
description: A quantum account for Cisco
account_number: "654321"
secrets_group: "{{ my_secrets_group['key'] }}"
state: present
vars:
my_secrets_group: "{{ lookup('networktocode.nautobot.lookup', 'secrets-groups', api_endpoint=nautobot_url, token=nautobot_token, api_filter='name=\"My Secrets Group\"') }}"

- name: Delete a cloud account
networktocode.nautobot.cloud_account:
url: http://nautobot.local
token: thisIsMyToken
name: Cisco Quantum Account
state: absent
"""

RETURN = r"""
cloud_account:
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,
TAGS_ARG_SPEC,
CUSTOM_FIELDS_ARG_SPEC,
)
from ansible_collections.networktocode.nautobot.plugins.module_utils.cloud import (
NautobotCloudModule,
NB_CLOUD_ACCOUNTS,
)
from ansible.module_utils.basic import AnsibleModule
from copy import deepcopy


def main():
"""
Main entry point for module execution
"""
argument_spec = deepcopy(NAUTOBOT_ARG_SPEC)
argument_spec.update(deepcopy(TAGS_ARG_SPEC))
argument_spec.update(deepcopy(CUSTOM_FIELDS_ARG_SPEC))
argument_spec.update(
dict(
name=dict(required=True, type="str"),
description=dict(required=False, type="str"),
account_number=dict(required=False, type="str"),
cloud_provider=dict(required=False, type="raw", aliases=["provider"]),
secrets_group=dict(required=False, type="raw", no_log=False),
)
)

module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
cloud_account = NautobotCloudModule(module, NB_CLOUD_ACCOUNTS)
cloud_account.run()


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