Skip to content

Commit

Permalink
Feature: L2vpn termination module (netbox-community#994)
Browse files Browse the repository at this point in the history
* Added netbox_l2vps_termination module
* Added tests for netbox_l2vps_termination module
* Added L2VPNs creation to netbox-deploy.py
  • Loading branch information
andrii-konts authored and rodvand committed May 1, 2023
1 parent 6e7edaf commit 9536dfe
Show file tree
Hide file tree
Showing 9 changed files with 402 additions and 1 deletion.
1 change: 1 addition & 0 deletions meta/runtime.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ action_groups:
- netbox_ipam_role
- netbox_journal_entry
- netbox_l2vpn
- netbox_l2vpn_termination
- netbox_location
- netbox_manufacturer
- netbox_module_type
Expand Down
6 changes: 6 additions & 0 deletions plugins/module_utils/netbox_ipam.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,12 @@ def run(self):
data.get("interface_type"),
data.get("interface_id"),
)
elif 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")

Expand Down
19 changes: 18 additions & 1 deletion plugins/module_utils/netbox_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@
inventory_item_role="name",
import_targets="name",
l2vpn="name",
l2vpn_termination="id",
location="slug",
manufacturer="slug",
module_type="model",
Expand Down Expand Up @@ -329,6 +330,7 @@
"inventory_item_roles": "inventory_item_role",
"ip_addresses": "ip_address",
"l2vpns": "l2vpn",
"l2vpn_terminations": "l2vpn_termination",
"locations": "location",
"manufacturers": "manufacturer",
"module_types": "module_type",
Expand Down Expand Up @@ -440,6 +442,9 @@
["address", "vrf", "device", "interface", "assigned_object", "virtual_machine"]
),
"l2vpn": set(["name"]),
"l2vpn_termination": set(
["l2vpn", "assigned_object_type", "interface_id", "vlan_id", "vminterface_id"]
),
"lag": set(["name"]),
"location": set(["name", "slug", "site"]),
"module_type": set(["model"]),
Expand Down Expand Up @@ -1004,7 +1009,19 @@ def _build_query_params(
"name": module_data.get("power_port_template"),
}
query_dict.update(power_port_template)

elif parent == "l2vpn_termination":
query_param_mapping = {
"dcim.interface": "interface_id",
"ipam.vlan": "vlan_id",
"virtualization.vminterface": "vminterface_id",
}
query_key = query_param_mapping[module_data.get("assigned_object_type")]
query_dict.update(
{
"l2vpn_id": query_dict.pop("l2vpn"),
query_key: module_data.get("assigned_object_id"),
}
)
elif "_template" in parent:
if query_dict.get("device_type"):
query_dict["devicetype_id"] = query_dict.pop("device_type")
Expand Down
154 changes: 154 additions & 0 deletions plugins/modules/netbox_l2vpn_termination.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2023, Andrii Konts (@andrii-konts) <[email protected]>
# 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_l2vpn_termination
short_description: Create, update or delete L2VPNs terminations within NetBox
description:
- Creates, updates or removes L2VPNs terminations from NetBox
notes:
- Tags should be defined as a YAML list
- This should be ran with connection C(local) and hosts C(localhost)
author:
- Andrii Konts (@andrii-konts)
requirements:
- pynetbox
seealso:
- name: FHRP Group Model reference
description: NetBox Documentation for FHRP Group Model.
link: https://docs.netbox.dev/en/stable/models/ipam/l2vpntermination/
version_added: '3.13.0'
extends_documentation_fragment:
- netbox.netbox.common
options:
data:
type: dict
description:
- Defines the L2VPN termination configuration
suboptions:
l2vpn:
description:
- L2vpn object id
required: true
type: int
assigned_object_type:
description:
- Assigned object type
required: true
choices:
- dcim.interface
- ipam.vlan
- virtualization.vminterface
type: str
assigned_object_id:
description:
- Assigned object id
required: true
type: int
tags:
description:
- Any tags that the L2VPN termination 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
"""

EXAMPLES = r"""
- hosts: localhost
connection: local
module_defaults:
group/netbox.netbox.netbox:
netbox_url: "http://netbox.local"
netbox_token: "thisIsMyToken"
tasks:
- name: Create L2VPN termination within NetBox with only required information
netbox.netbox.netbox_l2vpn_termination:
data:
l2vpn: 1
assigned_object_type: dcim.interface
assigned_object_id: 32
state: present
- name: Delete L2VPN termination within netbox
netbox.netbox.netbox_l2vpn_termination:
data:
l2vpn: 1
assigned_object_type: dcim.interface
assigned_object_id: 32
state: absent
"""

RETURN = r"""
l2vpn_termination:
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_ipam import (
NetboxIpamModule,
NB_L2VPN_TERMINATIONS,
)


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(
l2vpn=dict(required=True, type="int"),
assigned_object_type=dict(
required=True,
choices=[
"dcim.interface",
"ipam.vlan",
"virtualization.vminterface",
],
),
assigned_object_id=dict(required=True, type="int"),
tags=dict(required=False, type="list", elements="raw"),
custom_fields=dict(required=False, type="dict"),
),
),
)
)

module = NetboxAnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
netbox_l2vpn_termination = NetboxIpamModule(module, NB_L2VPN_TERMINATIONS)
netbox_l2vpn_termination.run()


if __name__ == "__main__":
main()
17 changes: 17 additions & 0 deletions tests/integration/netbox-deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,23 @@ def make_netbox_calls(endpoint, payload):
]
created_route_targets = make_netbox_calls(nb.ipam.route_targets, route_targets)

## Create L2VPNs
l2vpns = [
{
"identifier": 111111,
"name": "Test L2VPN 1",
"slug": "Test_L2VPN_1",
"type": "vxlan",
},
{
"identifier": 222222,
"name": "Test L2VPN 2",
"slug": "Test_L2VPN_2",
"type": "vxlan",
},
]
created_l2vpns = make_netbox_calls(nb.ipam.l2vpns, l2vpns)

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

- name: "NETBOX_L2VPN_TERMINATION TESTS"
include_tasks:
file: "netbox_l2vpn_termination.yml"
apply:
tags:
- netbox_l2vpn_termination
tags:
- netbox_l2vpn_termination

- name: "NETBOX_INVENTORY_ITEM_ROLE TESTS"
include_tasks:
file: "netbox_inventory_item_role.yml"
Expand Down
94 changes: 94 additions & 0 deletions tests/integration/targets/v3.3/tasks/netbox_l2vpn_termination.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
---
##
##
### NETBOX_L2VPN_TERMINATION
##
##
- name: "L2VPN_TERMINATION 1: Necessary info creation"
netbox.netbox.netbox_l2vpn_termination:
netbox_url: http://localhost:32768
netbox_token: 0123456789abcdef0123456789abcdef01234567
data:
l2vpn: 1
assigned_object_type: dcim.interface
assigned_object_id: 1
state: present
register: test_one

- name: "L2VPN_TERMINATION 1: ASSERT - Necessary info creation"
ansible.builtin.assert:
that:
- test_one is changed
- test_one['diff']['before']['state'] == "absent"
- test_one['diff']['after']['state'] == "present"
- test_one['l2vpn_termination']['l2vpn'] == 1
- test_one['l2vpn_termination']['assigned_object_type'] == "dcim.interface"
- test_one['l2vpn_termination']['assigned_object_id'] == 1
- test_one['msg'] == "l2vpn_termination l2vpn 1 <> dcim.interface 1 created"

- name: "L2VPN_TERMINATION 2: Create duplicate"
netbox.netbox.netbox_l2vpn_termination:
netbox_url: http://localhost:32768
netbox_token: 0123456789abcdef0123456789abcdef01234567
data:
l2vpn: 1
assigned_object_type: dcim.interface
assigned_object_id: 1
state: present
register: test_two

- name: "L2VPN_TERMINATION 2: ASSERT - Create duplicate"
ansible.builtin.assert:
that:
- not test_two['changed']
- test_two['l2vpn_termination']['l2vpn'] == 1
- test_two['l2vpn_termination']['assigned_object_type'] == "dcim.interface"
- test_two['l2vpn_termination']['assigned_object_id'] == 1
- test_two['msg'] == "l2vpn_termination l2vpn 1 <> dcim.interface 1 already exists"

- name: "L2VPN_TERMINATION 3: Update"
netbox.netbox.netbox_l2vpn_termination:
netbox_url: http://localhost:32768
netbox_token: 0123456789abcdef0123456789abcdef01234567
data:
l2vpn: 1
assigned_object_type: dcim.interface
assigned_object_id: 1
tags:
- "Schnozzberry"
state: present
register: test_three

- name: "L2VPN_TERMINATION 3: ASSERT - Updated"
ansible.builtin.assert:
that:
- test_three is changed
- test_three['diff']['after']['tags'][0] == 4
- test_three['l2vpn_termination']['l2vpn'] == 1
- test_three['l2vpn_termination']['assigned_object_type'] == "dcim.interface"
- test_three['l2vpn_termination']['assigned_object_id'] == 1
- test_three['l2vpn_termination']['tags'][0] == 4
- test_three['msg'] == "l2vpn_termination l2vpn 1 <> dcim.interface 1 updated"

- name: "L2VPN_TERMINATION 4: Delete"
netbox.netbox.netbox_l2vpn_termination:
netbox_url: http://localhost:32768
netbox_token: 0123456789abcdef0123456789abcdef01234567
data:
l2vpn: 1
assigned_object_type: dcim.interface
assigned_object_id: 1
state: absent
register: test_four

- name: "L2VPN_TERMINATION 4: ASSERT - Delete"
ansible.builtin.assert:
that:
- test_four is changed
- test_four['diff']['before']['state'] == "present"
- test_four['diff']['after']['state'] == "absent"
- test_four['l2vpn_termination']['l2vpn'] == 1
- test_four['l2vpn_termination']['assigned_object_type'] == "dcim.interface"
- test_four['l2vpn_termination']['assigned_object_id'] == 1
- test_four['l2vpn_termination']['tags'][0] == 4
- test_four['msg'] == "l2vpn_termination l2vpn 1 <> dcim.interface 1 deleted"
9 changes: 9 additions & 0 deletions tests/integration/targets/v3.4/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,15 @@
tags:
- netbox_l2vpn

- name: "NETBOX_L2VPN_TERMINATION TESTS"
include_tasks:
file: "netbox_l2vpn_termination.yml"
apply:
tags:
- netbox_l2vpn_termination
tags:
- netbox_l2vpn_termination

- name: "NETBOX_INVENTORY_ITEM_ROLE TESTS"
include_tasks:
file: "netbox_inventory_item_role.yml"
Expand Down
Loading

0 comments on commit 9536dfe

Please sign in to comment.