Skip to content

Commit 52930f8

Browse files
Add legacy netbox_interface but mark it as deprecated (#127)
1 parent 928621f commit 52930f8

File tree

1 file changed

+362
-0
lines changed

1 file changed

+362
-0
lines changed

plugins/modules/netbox_interface.py

Lines changed: 362 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,362 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
4+
# Copyright: (c) 2018, Mikhail Yohman (@FragmentedPacket) <mikhail.yohman@gmail.com>
5+
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6+
7+
from __future__ import absolute_import, division, print_function
8+
9+
__metaclass__ = type
10+
11+
ANSIBLE_METADATA = {
12+
"metadata_version": "1.1",
13+
"status": ["preview"],
14+
"supported_by": "community",
15+
}
16+
17+
DOCUMENTATION = r"""
18+
---
19+
module: netbox_interface
20+
short_description: Creates or removes interfaces from Netbox
21+
description:
22+
- Creates or removes interfaces from Netbox
23+
deprecated:
24+
removed_in: 2.10
25+
alternative: netbox.netbox.netbox_device_interface
26+
why: >
27+
There was a large refactoring undertaken when migrating these modules to Ansible Collections to improve reliability and testing
28+
abilities.
29+
notes:
30+
- Tags should be defined as a YAML list
31+
- This should be ran with connection C(local) and hosts C(localhost)
32+
author:
33+
- Mikhail Yohman (@FragmentedPacket)
34+
requirements:
35+
- pynetbox
36+
version_added: "2.8"
37+
options:
38+
netbox_url:
39+
description:
40+
- URL of the Netbox instance resolvable by Ansible control host
41+
required: true
42+
type: str
43+
netbox_token:
44+
description:
45+
- The token created within Netbox to authorize API access
46+
required: true
47+
type: str
48+
data:
49+
description:
50+
- Defines the prefix configuration
51+
suboptions:
52+
device:
53+
description:
54+
- Name of the device the interface will be associated with (case-sensitive)
55+
required: true
56+
type: str
57+
name:
58+
description:
59+
- Name of the interface to be created
60+
required: true
61+
type: str
62+
form_factor:
63+
description:
64+
- |
65+
Form factor of the interface:
66+
ex. 1000Base-T (1GE), Virtual, 10GBASE-T (10GE)
67+
This has to be specified exactly as what is found within UI
68+
type: str
69+
enabled:
70+
description:
71+
- Sets whether interface shows enabled or disabled
72+
type: bool
73+
lag:
74+
description:
75+
- Parent LAG interface will be a member of
76+
type: dict
77+
mtu:
78+
description:
79+
- The MTU of the interface
80+
type: str
81+
mac_address:
82+
description:
83+
- The MAC address of the interface
84+
type: str
85+
mgmt_only:
86+
description:
87+
- This interface is used only for out-of-band management
88+
type: bool
89+
description:
90+
description:
91+
- The description of the prefix
92+
type: str
93+
mode:
94+
description:
95+
- The mode of the interface
96+
choices:
97+
- Access
98+
- Tagged
99+
- Tagged All
100+
type: str
101+
untagged_vlan:
102+
description:
103+
- The untagged VLAN to be assigned to interface
104+
type: dict
105+
tagged_vlans:
106+
description:
107+
- A list of tagged VLANS to be assigned to interface. Mode must be set to either C(Tagged) or C(Tagged All)
108+
type: list
109+
tags:
110+
description:
111+
- Any tags that the prefix may need to be associated with
112+
type: list
113+
required: true
114+
state:
115+
description:
116+
- Use C(present) or C(absent) for adding or removing.
117+
choices: [ absent, present ]
118+
default: present
119+
type: str
120+
validate_certs:
121+
description:
122+
- |
123+
If C(no), SSL certificates will not be validated.
124+
This should only be used on personally controlled sites using self-signed certificates.
125+
default: "yes"
126+
type: bool
127+
"""
128+
129+
EXAMPLES = r"""
130+
- name: "Test Netbox interface module"
131+
connection: local
132+
hosts: localhost
133+
gather_facts: False
134+
tasks:
135+
- name: Create interface within Netbox with only required information
136+
netbox_interface:
137+
netbox_url: http://netbox.local
138+
netbox_token: thisIsMyToken
139+
data:
140+
device: test100
141+
name: GigabitEthernet1
142+
state: present
143+
- name: Delete interface within netbox
144+
netbox_interface:
145+
netbox_url: http://netbox.local
146+
netbox_token: thisIsMyToken
147+
data:
148+
device: test100
149+
name: GigabitEthernet1
150+
state: absent
151+
- name: Create LAG with several specified options
152+
netbox_interface:
153+
netbox_url: http://netbox.local
154+
netbox_token: thisIsMyToken
155+
data:
156+
device: test100
157+
name: port-channel1
158+
form_factor: Link Aggregation Group (LAG)
159+
mtu: 1600
160+
mgmt_only: false
161+
mode: Access
162+
state: present
163+
- name: Create interface and assign it to parent LAG
164+
netbox_interface:
165+
netbox_url: http://netbox.local
166+
netbox_token: thisIsMyToken
167+
data:
168+
device: test100
169+
name: GigabitEthernet1
170+
enabled: false
171+
form_factor: 1000Base-t (1GE)
172+
lag:
173+
name: port-channel1
174+
mtu: 1600
175+
mgmt_only: false
176+
mode: Access
177+
state: present
178+
- name: Create interface as a trunk port
179+
netbox_interface:
180+
netbox_url: http://netbox.local
181+
netbox_token: thisIsMyToken
182+
data:
183+
device: test100
184+
name: GigabitEthernet25
185+
enabled: false
186+
form_factor: 1000Base-t (1GE)
187+
untagged_vlan:
188+
name: Wireless
189+
site: Test Site
190+
tagged_vlans:
191+
- name: Data
192+
site: Test Site
193+
- name: VoIP
194+
site: Test Site
195+
mtu: 1600
196+
mgmt_only: true
197+
mode: Tagged
198+
state: present
199+
"""
200+
201+
RETURN = r"""
202+
interface:
203+
description: Serialized object as created or already existent within Netbox
204+
returned: on creation
205+
type: dict
206+
msg:
207+
description: Message indicating failure or info about what has been achieved
208+
returned: always
209+
type: str
210+
"""
211+
212+
import json
213+
import traceback
214+
215+
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
216+
from ansible.module_utils.net_tools.netbox.netbox_utils import (
217+
find_ids,
218+
normalize_data,
219+
create_netbox_object,
220+
delete_netbox_object,
221+
update_netbox_object,
222+
INTF_FORM_FACTOR,
223+
INTF_MODE,
224+
)
225+
from ansible.module_utils.compat import ipaddress
226+
from ansible.module_utils._text import to_text
227+
228+
229+
PYNETBOX_IMP_ERR = None
230+
try:
231+
import pynetbox
232+
233+
HAS_PYNETBOX = True
234+
except ImportError:
235+
PYNETBOX_IMP_ERR = traceback.format_exc()
236+
HAS_PYNETBOX = False
237+
238+
239+
def main():
240+
"""
241+
Main entry point for module execution
242+
"""
243+
argument_spec = dict(
244+
netbox_url=dict(type="str", required=True),
245+
netbox_token=dict(type="str", required=True, no_log=True),
246+
data=dict(type="dict", required=True),
247+
state=dict(required=False, default="present", choices=["present", "absent"]),
248+
validate_certs=dict(type="bool", default=True),
249+
)
250+
251+
global module
252+
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
253+
254+
# Fail module if pynetbox is not installed
255+
if not HAS_PYNETBOX:
256+
module.fail_json(
257+
msg=missing_required_lib("pynetbox"), exception=PYNETBOX_IMP_ERR
258+
)
259+
# Assign variables to be used with module
260+
app = "dcim"
261+
endpoint = "interfaces"
262+
url = module.params["netbox_url"]
263+
token = module.params["netbox_token"]
264+
data = module.params["data"]
265+
state = module.params["state"]
266+
validate_certs = module.params["validate_certs"]
267+
# Attempt to create Netbox API object
268+
try:
269+
nb = pynetbox.api(url, token=token, ssl_verify=validate_certs)
270+
except Exception:
271+
module.fail_json(msg="Failed to establish connection to Netbox API")
272+
try:
273+
nb_app = getattr(nb, app)
274+
except AttributeError:
275+
module.fail_json(msg="Incorrect application specified: %s" % (app))
276+
nb_endpoint = getattr(nb_app, endpoint)
277+
norm_data = normalize_data(data)
278+
try:
279+
norm_data = _check_and_adapt_data(nb, norm_data)
280+
281+
if "present" in state:
282+
return module.exit_json(
283+
**ensure_interface_present(nb, nb_endpoint, norm_data)
284+
)
285+
else:
286+
return module.exit_json(
287+
**ensure_interface_absent(nb, nb_endpoint, norm_data)
288+
)
289+
except pynetbox.RequestError as e:
290+
return module.fail_json(msg=json.loads(e.error))
291+
except ValueError as e:
292+
return module.fail_json(msg=str(e))
293+
except AttributeError as e:
294+
return module.fail_json(msg=str(e))
295+
296+
297+
def _check_and_adapt_data(nb, data):
298+
data = find_ids(nb, data)
299+
300+
if data.get("form_factor"):
301+
data["form_factor"] = INTF_FORM_FACTOR.get(data["form_factor"].lower())
302+
if data.get("mode"):
303+
data["mode"] = INTF_MODE.get(data["mode"].lower())
304+
305+
return data
306+
307+
308+
def ensure_interface_present(nb, nb_endpoint, data):
309+
"""
310+
:returns dict(interface, msg, changed): dictionary resulting of the request,
311+
where 'interface' is the serialized interface fetched or newly created in Netbox
312+
"""
313+
314+
if not isinstance(data, dict):
315+
changed = False
316+
return {"msg": data, "changed": changed}
317+
318+
nb_intf = nb_endpoint.get(name=data["name"], device_id=data["device"])
319+
result = dict()
320+
321+
if not nb_intf:
322+
intf, diff = create_netbox_object(nb_endpoint, data, module.check_mode)
323+
changed = True
324+
msg = "Interface %s created" % (data["name"])
325+
else:
326+
intf, diff = update_netbox_object(nb_intf, data, module.check_mode)
327+
if intf is False:
328+
module.fail_json(
329+
msg="Request failed, couldn't update device: %s" % (data["name"])
330+
)
331+
if diff:
332+
msg = "Interface %s updated" % (data["name"])
333+
changed = True
334+
result["diff"] = diff
335+
else:
336+
msg = "Interface %s already exists" % (data["name"])
337+
changed = False
338+
result.update({"interface": intf, "msg": msg, "changed": changed})
339+
return result
340+
341+
342+
def ensure_interface_absent(nb, nb_endpoint, data):
343+
"""
344+
:returns dict(msg, changed, diff)
345+
"""
346+
nb_intf = nb_endpoint.get(name=data["name"], device_id=data["device"])
347+
result = dict()
348+
if nb_intf:
349+
dummy, diff = delete_netbox_object(nb_intf, module.check_mode)
350+
changed = True
351+
msg = "Interface %s deleted" % (data["name"])
352+
result["diff"] = diff
353+
else:
354+
msg = "Interface %s already absent" % (data["name"])
355+
changed = False
356+
357+
result.update({"msg": msg, "changed": changed})
358+
return result
359+
360+
361+
if __name__ == "__main__":
362+
main()

0 commit comments

Comments
 (0)