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

Added static group association model #453

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
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
1 change: 1 addition & 0 deletions plugins/lookup/lookup.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ def get_endpoint(nautobot, term):
"device-types": {"endpoint": nautobot.dcim.device_types},
"device-redundancy-groups": {"endpoint": nautobot.dcim.device_redundancy_groups},
"devices": {"endpoint": nautobot.dcim.devices},
"dynamic-groups": {"endpoint": nautobot.extras.dynamic_groups},
"export-templates": {"endpoint": nautobot.dcim.export_templates},
"front-port-templates": {"endpoint": nautobot.dcim.front_port_templates},
"front-ports": {"endpoint": nautobot.dcim.front_ports},
Expand Down
3 changes: 3 additions & 0 deletions plugins/module_utils/extras.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
NB_TAGS = "tags"
NB_STATUS = "statuses"
NB_RELATIONSHIP_ASSOCIATIONS = "relationship_associations"
NB_STATIC_GROUP_ASSOCIATIONS = "static_group_associations"
NB_CUSTOM_FIELDS = "custom_fields"
NB_CUSTOM_FIELD_CHOICES = "custom_field_choices"
NB_CONTACT = "contacts"
Expand Down Expand Up @@ -46,6 +47,8 @@ def run(self):
name = data["name"]
elif endpoint_name == "relationship_associations":
name = f"{data['source_type']} -> {data['destination_type']}"
elif endpoint_name == "static_group_association":
name = f"{data['dynamic_group']} -> {data['associated_object_id']}"
elif endpoint_name == "custom_field":
name = data["label"]
elif endpoint_name == "custom_field_choice":
Expand Down
3 changes: 3 additions & 0 deletions plugins/module_utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
"custom_field_choices",
"relationship_associations",
"roles",
"static_group_associations",
"statuses",
"tags",
"teams",
Expand Down Expand Up @@ -322,6 +323,7 @@
"roles": "role",
"route_targets": "route_target",
"services": "services",
"static_group_associations": "static_group_association",
"statuses": "statuses",
"tags": "tags",
"teams": "team",
Expand Down Expand Up @@ -421,6 +423,7 @@
"role": set(["name"]),
"route_target": set(["name"]),
"services": set(["device", "virtual_machine", "name", "port", "protocol"]),
"static_group_association": set(["dynamic_group", "associated_object_type", "associated_object_id"]),
"statuses": set(["name"]),
"tags": set(["name"]),
"tagged_vlans": set(["group", "name", "location", "vid", "vlan_group", "tenant"]),
Expand Down
104 changes: 104 additions & 0 deletions plugins/modules/static_group_association.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2022, 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: static_group_association
short_description: Creates or removes a static group association from Nautobot
description:
- Creates or removes a static group association from Nautobot
author:
- Network to Code (@networktocode)
- Travis Smith (@tsm1th)
version_added: "5.4.0"
extends_documentation_fragment:
- networktocode.nautobot.fragments.base
options:
dynamic_group:
description:
- The dynamic group to add the association to
required: true
type: raw
associated_object_type:
description:
- The app_label.model for the object in the relationship
required: true
type: str
associated_object_id:
description:
- The UUID of the object in the relationship
required: true
type: str
"""

EXAMPLES = r"""
- name: "Test static group association creation/deletion"
connection: local
hosts: localhost
gather_facts: False
tasks:
- name: Create static group association
networktocode.nautobot.static_group_association:
url: http://nautobot.local
token: thisIsMyToken
dynamic_group: 01234567-abcd-0123-abcd-012345678901
associated_object_type: dcim.device
associated_object_id: abcdefgh-0123-abcd-0123-abcdefghijkl

- name: Delete static group association
networktocode.nautobot.static_group_association:
url: http://nautobot.local
token: thisIsMyToken
dynamic_group: 01234567-abcd-0123-abcd-012345678901
associated_object_type: dcim.device
associated_object_id: abcdefgh-0123-abcd-0123-abcdefghijkl
state: absent
"""

RETURN = r"""
static_group_association:
description: Serialized object as created/existent/updated/deleted within Nautobot
returned: always
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_STATIC_GROUP_ASSOCIATIONS,
)
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(
dict(
dynamic_group=dict(required=True, type="raw"),
associated_object_type=dict(required=True, type="str"),
associated_object_id=dict(required=True, type="str"),
)
)

module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)

static_group_association = NautobotExtrasModule(module, NB_STATIC_GROUP_ASSOCIATIONS)
static_group_association.run()


if __name__ == "__main__": # pragma: no cover
main()
4 changes: 4 additions & 0 deletions tests/integration/nautobot-populate.py
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,10 @@ def make_nautobot_calls(endpoint, payload):
]
created_device_interface_roles = make_nautobot_calls(nb.extras.roles, device_interface_roles)

# Create dynamic group of type static assignment
dynamic_groups = [{"name": "TestStaticAssociations", "content_type": "dcim.device", "group_type": "static"}]
created_dynamic_groups = make_nautobot_calls(nb.extras.dynamic_groups, dynamic_groups)


if ERRORS:
sys.exit("Errors have occurred when creating objects, and should have been printed out. Check previous output.")
9 changes: 9 additions & 0 deletions tests/integration/targets/latest/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -680,3 +680,12 @@
- cloud_network_prefix_assignment
tags:
- cloud_network_prefix_assignment

- name: "PYNAUTOBOT_STATIC_GROUP_ASSOCIATION TESTS"
include_tasks:
file: "static_group_association.yml"
apply:
tags:
- static_group_association
tags:
- static_group_association
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
---
##
##
### PYNAUTOBOT_STATIC_GROUP_ASSOCIATION
##
##

- name: "NAUTOBOT 2.3+ STATIC GROUP ASSOCIATION TESTS"
when:
- "nautobot_version is version('2.3', '>=')"
block:
- set_fact:
static_dynamic_group: "{{ lookup('networktocode.nautobot.lookup', 'dynamic-groups', api_endpoint=nautobot_url, token=nautobot_token, api_filter='name=\"TestStaticAssociations\"') }}"
static_device: "{{ lookup('networktocode.nautobot.lookup', 'devices', api_endpoint=nautobot_url, token=nautobot_token, api_filter='name=TestDeviceR1') }}"

- name: "STATIC GROUP ASSOCIATION 1: Necessary info creation"
networktocode.nautobot.static_group_association:
url: "{{ nautobot_url }}"
token: "{{ nautobot_token }}"
dynamic_group: "TestStaticAssociations"
associated_object_type: "dcim.device"
associated_object_id: "{{ static_device['key'] }}"
state: present
register: test_one

- name: "STATIC GROUP ASSOCIATION 1: ASSERT - Necessary info creation"
assert:
that:
- test_one is changed
- test_one['diff']['before']['state'] == "absent"
- test_one['diff']['after']['state'] == "present"
- test_one['static_group_association']['dynamic_group'] == static_dynamic_group['key']
- test_one['static_group_association']['associated_object_type'] == "dcim.device"
- test_one['static_group_association']['associated_object_id'] == static_device['key']
- "'created' in test_one['msg']"

- name: "STATIC GROUP ASSOCIATION 2: Test duplication association (Idempotency)"
networktocode.nautobot.static_group_association:
url: "{{ nautobot_url }}"
token: "{{ nautobot_token }}"
dynamic_group: "{{ static_dynamic_group['key'] }}"
associated_object_type: "dcim.device"
associated_object_id: "{{ static_device['key'] }}"
state: present
register: test_two

- name: "STATIC GROUP ASSOCIATION 2: ASSERT - Item already exists (Idempotency)"
assert:
that:
- test_two is not changed
- test_two['static_group_association']['dynamic_group'] == static_dynamic_group['key']
- "'already exists' in test_two['msg']"

- name: "STATIC GROUP ASSOCIATION 3: Test absent state"
networktocode.nautobot.static_group_association:
url: "{{ nautobot_url }}"
token: "{{ nautobot_token }}"
dynamic_group: "{{ static_dynamic_group['key'] }}"
associated_object_type: "dcim.device"
associated_object_id: "{{ static_device['key'] }}"
state: absent
register: test_three

- name: "STATIC GROUP ASSOCIATION 3: ASSERT - Item removed"
assert:
that:
- test_three is changed
- test_three['diff']['before']['state'] == "present"
- test_three['diff']['after']['state'] == "absent"
- test_three['static_group_association']['dynamic_group'] == static_dynamic_group['key']
- test_three['static_group_association']['associated_object_type'] == "dcim.device"
- test_three['static_group_association']['associated_object_id'] == static_device['key']
- "'deleted' in test_three['msg']"

- name: "STATIC GROUP ASSOCIATION 4: Test absent state (Idempotent)"
networktocode.nautobot.static_group_association:
url: "{{ nautobot_url }}"
token: "{{ nautobot_token }}"
dynamic_group: "{{ static_dynamic_group['key'] }}"
associated_object_type: "dcim.device"
associated_object_id: "{{ static_device['key'] }}"
state: absent
register: test_four

- name: "STATIC GROUP ASSOCIATION 4: ASSERT - Item not removed (Idempotent)"
assert:
that:
- test_four is not changed
- "'already absent' in test_four['msg']"