Skip to content

Commit ad5fe25

Browse files
new circuit modules (#37)
1 parent 84b2488 commit ad5fe25

File tree

10 files changed

+1252
-8
lines changed

10 files changed

+1252
-8
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
## Existing Modules
1313

1414
- netbox_aggregate
15+
- netbox_circuit
16+
- netbox_circuit_termination
17+
- netbox_circuit_type
1518
- netbox_cluster
1619
- netbox_cluster_group
1720
- netbox_cluster_type
@@ -26,6 +29,7 @@
2629
- netbox_manufacturer
2730
- netbox_platform
2831
- netbox_prefix
32+
- netbox_provider
2933
- netbox_rack_group
3034
- netbox_rack_role
3135
- netbox_rack

plugins/module_utils/netbox_circuits.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,19 @@
1010
from ansible_collections.fragmentedpacket.netbox_modules.plugins.module_utils.netbox_utils import (
1111
NetboxModule,
1212
ENDPOINT_NAME_MAPPING,
13+
SLUG_REQUIRED,
1314
)
1415
except ImportError:
1516
import sys
1617

1718
sys.path.append(".")
18-
from netbox_utils import NetboxModule, ENDPOINT_NAME_MAPPING
19+
from netbox_utils import NetboxModule, ENDPOINT_NAME_MAPPING, SLUG_REQUIRED
20+
21+
22+
NB_PROVIDERS = "providers"
23+
NB_CIRCUIT_TYPES = "circuit_types"
24+
NB_CIRCUIT_TERMINATIONS = "circuit_terminations"
25+
NB_CIRCUITS = "circuits"
1926

2027

2128
class NetboxCircuitsModule(NetboxModule):
@@ -27,6 +34,10 @@ def run(self):
2734
This function should have all necessary code for endpoints within the application
2835
to create/update/delete the endpoint objects
2936
Supported endpoints:
37+
- circuit_types
38+
- circuit_terminations
39+
- circuits
40+
- providers
3041
"""
3142
# Used to dynamically set key when returning results
3243
endpoint_name = ENDPOINT_NAME_MAPPING[self.endpoint]
@@ -40,9 +51,21 @@ def run(self):
4051
data = self.data
4152

4253
# Used for msg output
43-
name = data.get("name")
44-
45-
data["slug"] = self._to_slug(name)
54+
if data.get("name"):
55+
name = data["name"]
56+
elif data.get("slug"):
57+
name = data["slug"]
58+
elif data.get("cid"):
59+
name = data["cid"]
60+
elif data.get("circuit") and data.get("term_side"):
61+
circuit = self.nb.circuits.circuits.get(data["circuit"]).serialize()
62+
name = "{0}_{1}".format(
63+
circuit["cid"].replace(" ", "_"), data["term_side"]
64+
).lower()
65+
66+
if self.endpoint in SLUG_REQUIRED:
67+
if not data.get("slug"):
68+
data["slug"] = self._to_slug(name)
4669

4770
object_query_params = self._build_query_params(endpoint_name, data)
4871
try:

plugins/module_utils/netbox_utils.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
# Used to map endpoints to applications dynamically
2828
API_APPS_ENDPOINTS = dict(
29-
circuits=[],
29+
circuits=["circuits", "circuit_types", "circuit_terminations", "providers"],
3030
dcim=[
3131
"device_bays",
3232
"devices",
@@ -61,6 +61,9 @@
6161

6262
# Used to normalize data for the respective query types used to find endpoints
6363
QUERY_TYPES = dict(
64+
circuit="cid",
65+
circuit_termination="circuit",
66+
circuit_type="slug",
6467
cluster="name",
6568
cluster_group="slug",
6669
cluster_type="slug",
@@ -78,6 +81,7 @@
7881
primary_ip="address",
7982
primary_ip4="address",
8083
primary_ip6="address",
84+
provider="slug",
8185
rack="name",
8286
rack_group="slug",
8387
rack_role="slug",
@@ -97,6 +101,9 @@
97101

98102
# Specifies keys within data that need to be converted to ID and the endpoint to be used when queried
99103
CONVERT_TO_ID = dict(
104+
circuit="circuits",
105+
circuit_type="circuit_types",
106+
circuit_termination="circuit_terminations",
100107
cluster="clusters",
101108
cluster_group="cluster_groups",
102109
cluster_type="cluster_types",
@@ -117,6 +124,7 @@
117124
primary_ip="ip_addresses",
118125
primary_ip4="ip_addresses",
119126
primary_ip6="ip_addresses",
127+
provider="providers",
120128
rack="racks",
121129
rack_group="rack_groups",
122130
rack_role="rack_roles",
@@ -138,6 +146,9 @@
138146

139147
ENDPOINT_NAME_MAPPING = {
140148
"aggregates": "aggregate",
149+
"circuit_terminations": "circuit_termination",
150+
"circuit_types": "circuit_type",
151+
"circuits": "circuit",
141152
"clusters": "cluster",
142153
"cluster_groups": "cluster_group",
143154
"cluster_types": "cluster_type",
@@ -151,6 +162,7 @@
151162
"manufacturers": "manufacturer",
152163
"platforms": "platform",
153164
"prefixes": "prefix",
165+
"providers": "provider",
154166
"racks": "rack",
155167
"rack_groups": "rack_group",
156168
"rack_roles": "rack_role",
@@ -267,9 +279,16 @@
267279

268280
VIRTUAL_MACHINE_STATUS = dict(offline=0, active=1, staged=3)
269281

282+
CIRCUIT_STATUS = dict(
283+
deprovisioning=0, active=1, planned=2, provisioning=3, offline=4, decommissioned=5,
284+
)
285+
270286
# This is used when attempting to search for existing endpoints
271287
ALLOWED_QUERY_PARAMS = {
272288
"aggregate": set(["prefix", "rir"]),
289+
"circuit": set(["cid"]),
290+
"circuit_type": set(["slug"]),
291+
"circuit_termination": set(["circuit", "term_side"]),
273292
"cluster": set(["name", "type"]),
274293
"cluster_group": set(["slug"]),
275294
"cluster_type": set(["slug"]),
@@ -290,6 +309,7 @@
290309
"prefix": set(["prefix", "vrf"]),
291310
"primary_ip4": set(["address", "vrf"]),
292311
"primary_ip6": set(["address", "vrf"]),
312+
"provider": set(["slug"]),
293313
"rack": set(["name", "site"]),
294314
"rack_group": set(["slug"]),
295315
"rack_role": set(["slug"]),
@@ -309,11 +329,23 @@
309329
}
310330

311331
QUERY_PARAMS_IDS = set(
312-
["cluster", "device", "group", "rir", "vrf", "site", "vlan_group", "tenant", "type"]
332+
[
333+
"circuit",
334+
"cluster",
335+
"device",
336+
"group",
337+
"rir",
338+
"vrf",
339+
"site",
340+
"vlan_group",
341+
"tenant",
342+
"type",
343+
]
313344
)
314345

315346
# This is used when converting static choices to an ID value acceptable to Netbox API
316347
REQUIRED_ID_FIND = {
348+
"circuits": [{"status": CIRCUIT_STATUS}],
317349
"devices": [{"status": DEVICE_STATUS, "face": FACE_ID}],
318350
"device_types": [{"subdevice_role": SUBDEVICE_ROLES}],
319351
"interfaces": [{"form_factor": INTF_FORM_FACTOR, "mode": INTF_MODE}],
@@ -328,6 +360,7 @@
328360

329361
# This is used to map non-clashing keys to Netbox API compliant keys to prevent bad logic in code for similar keys but different modules
330362
CONVERT_KEYS = {
363+
"circuit_type": "type",
331364
"cluster_type": "type",
332365
"cluster_group": "group",
333366
"parent_region": "parent",
@@ -342,6 +375,7 @@
342375

343376
# This is used to dynamically conver name to slug on endpoints requiring a slug
344377
SLUG_REQUIRED = {
378+
"circuit_types",
345379
"cluster_groups",
346380
"cluster_types",
347381
"device_roles",
@@ -353,6 +387,7 @@
353387
"roles",
354388
"manufacturers",
355389
"platforms",
390+
"providers",
356391
"vlan_groups",
357392
}
358393

plugins/modules/netbox_circuit.py

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
# Copyright: (c) 2019, Mikhail Yohman (@FragmentedPacket) <mikhail.yohman@gmail.com>
4+
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
5+
6+
from __future__ import absolute_import, division, print_function
7+
8+
__metaclass__ = type
9+
10+
ANSIBLE_METADATA = {
11+
"metadata_version": "1.1",
12+
"status": ["preview"],
13+
"supported_by": "community",
14+
}
15+
16+
DOCUMENTATION = r"""
17+
---
18+
module: netbox_circuit
19+
short_description: Create, update or delete circuits within Netbox
20+
description:
21+
- Creates, updates or removes circuits from Netbox
22+
notes:
23+
- Tags should be defined as a YAML list
24+
- This should be ran with connection C(local) and hosts C(localhost)
25+
author:
26+
- Mikhail Yohman (@FragmentedPacket)
27+
requirements:
28+
- pynetbox
29+
version_added: '0.1.0'
30+
options:
31+
netbox_url:
32+
description:
33+
- URL of the Netbox instance resolvable by Ansible control host
34+
required: true
35+
netbox_token:
36+
description:
37+
- The token created within Netbox to authorize API access
38+
required: true
39+
data:
40+
description:
41+
- Defines the circuit configuration
42+
suboptions:
43+
cid:
44+
description:
45+
- The circuit id of the circuit
46+
required: true
47+
provider:
48+
description:
49+
- The provider of the circuit
50+
required: true
51+
circuit_type:
52+
description:
53+
- The circuit type of the circuit
54+
status:
55+
description:
56+
- The status of the circuit
57+
choices:
58+
- Active
59+
- Offline
60+
- Planned
61+
- Provisioning
62+
- Deprovisioning
63+
- Decommissioned
64+
tenant:
65+
description:
66+
- The tenant assigned to the circuit
67+
install_date:
68+
description:
69+
- The date the circuit was installed. e.g. YYYY-MM-DD
70+
commit_rate:
71+
description:
72+
- Commit rate of the circuit (Kbps)
73+
description:
74+
description:
75+
- Description of the circuit
76+
comments:
77+
description:
78+
- Comments related to circuit
79+
tags:
80+
description:
81+
- Any tags that the device may need to be associated with
82+
custom_fields:
83+
description:
84+
- must exist in Netbox
85+
state:
86+
description:
87+
- Use C(present) or C(absent) for adding or removing.
88+
choices: [ absent, present ]
89+
default: present
90+
validate_certs:
91+
description:
92+
- If C(no), SSL certificates will not be validated. This should only be used on personally controlled sites using self-signed certificates.
93+
default: 'yes'
94+
type: bool
95+
"""
96+
97+
EXAMPLES = r"""
98+
- name: "Test Netbox modules"
99+
connection: local
100+
hosts: localhost
101+
gather_facts: False
102+
103+
tasks:
104+
- name: Create circuit within Netbox with only required information
105+
netbox_circuit:
106+
netbox_url: http://netbox.local
107+
netbox_token: thisIsMyToken
108+
data:
109+
cid: Test Circuit
110+
provider: Test Provider
111+
circuit_type: Test Circuit Type
112+
state: present
113+
114+
- name: Update circuit with other fields
115+
netbox_circuit:
116+
netbox_url: http://netbox.local
117+
netbox_token: thisIsMyToken
118+
data:
119+
cid: Test-Circuit-1000
120+
provider: Test Provider
121+
circuit_type: Test Circuit Type
122+
status: Active
123+
tenant: Test Tenant
124+
install_date: "2018-12-25"
125+
commit_rate: 10000
126+
description: Test circuit
127+
comments: "FAST CIRCUIT"
128+
state: present
129+
130+
- name: Delete circuit within netbox
131+
netbox_circuit:
132+
netbox_url: http://netbox.local
133+
netbox_token: thisIsMyToken
134+
data:
135+
cid: Test-Circuit-1000
136+
state: absent
137+
"""
138+
139+
RETURN = r"""
140+
circuit:
141+
description: Serialized object as created or already existent within Netbox
142+
returned: success (when I(state=present))
143+
type: dict
144+
msg:
145+
description: Message indicating failure or info about what has been achieved
146+
returned: always
147+
type: str
148+
"""
149+
150+
from ansible_collections.fragmentedpacket.netbox_modules.plugins.module_utils.netbox_utils import (
151+
NetboxAnsibleModule,
152+
)
153+
from ansible_collections.fragmentedpacket.netbox_modules.plugins.module_utils.netbox_circuits import (
154+
NetboxCircuitsModule,
155+
NB_CIRCUITS,
156+
)
157+
158+
159+
def main():
160+
"""
161+
Main entry point for module execution
162+
"""
163+
argument_spec = dict(
164+
netbox_url=dict(type="str", required=True),
165+
netbox_token=dict(type="str", required=True, no_log=True),
166+
data=dict(type="dict", required=True),
167+
state=dict(required=False, default="present", choices=["present", "absent"]),
168+
validate_certs=dict(type="bool", default=True),
169+
)
170+
171+
required_if = [("state", "present", ["cid"]), ("state", "absent", ["cid"])]
172+
173+
module = NetboxAnsibleModule(
174+
argument_spec=argument_spec, supports_check_mode=True, required_if=required_if
175+
)
176+
177+
netbox_circuit = NetboxCircuitsModule(module, NB_CIRCUITS)
178+
netbox_circuit.run()
179+
180+
181+
if __name__ == "__main__":
182+
main()

0 commit comments

Comments
 (0)