mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-04-28 13:21:25 -07:00
Allow Netbox device modification (#53631)
* netbox_device: Allow device modification * Add ability to update and existing device * Allow check_mode * Fail when device name is missing * Fail when cannot resolve ID instead of taking ID 1 by default * netbox_device: Add diff output * netbox: Some refactoring * Add diff output and check_mode to netbox_ip_address * Deduplicate redundant code into netbox_utils * netbox_utils: A few unit tests
This commit is contained in:
parent
a4c42ba687
commit
c637104078
4 changed files with 248 additions and 55 deletions
|
@ -14,9 +14,9 @@ ANSIBLE_METADATA = {'metadata_version': '1.1',
|
|||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: netbox_device
|
||||
short_description: Create or delete devices within Netbox
|
||||
short_description: Create, update or delete devices within Netbox
|
||||
description:
|
||||
- Creates or removes devices from Netbox
|
||||
- Creates, updates or removes devices from Netbox
|
||||
notes:
|
||||
- Tags should be defined as a YAML list
|
||||
- This should be ran with connection C(local) and hosts C(localhost)
|
||||
|
@ -42,12 +42,13 @@ options:
|
|||
name:
|
||||
description:
|
||||
- The name of the device
|
||||
required: true
|
||||
device_type:
|
||||
description:
|
||||
- Required if I(state=present)
|
||||
- Required if I(state=present) and the device does not exist yet
|
||||
device_role:
|
||||
description:
|
||||
- Required if I(state=present)
|
||||
- Required if I(state=present) and the device does not exist yet
|
||||
tenant:
|
||||
description:
|
||||
- The tenant that the device will be assigned to
|
||||
|
@ -62,7 +63,7 @@ options:
|
|||
- Asset tag that is associated to the device
|
||||
site:
|
||||
description:
|
||||
- Required if I(state=present)
|
||||
- Required if I(state=present) and the device does not exist yet
|
||||
rack:
|
||||
description:
|
||||
- The name of the rack to assign the device to
|
||||
|
@ -119,7 +120,7 @@ EXAMPLES = r'''
|
|||
netbox_url: http://netbox.local
|
||||
netbox_token: thisIsMyToken
|
||||
data:
|
||||
name: Test (not really required, but helpful)
|
||||
name: Test Device
|
||||
device_type: C9410R
|
||||
device_role: Core Switch
|
||||
site: Main
|
||||
|
@ -130,7 +131,7 @@ EXAMPLES = r'''
|
|||
netbox_url: http://netbox.local
|
||||
netbox_token: thisIsMyToken
|
||||
data:
|
||||
name: Test
|
||||
name: Test Device
|
||||
state: absent
|
||||
|
||||
- name: Create device with tags
|
||||
|
@ -138,7 +139,7 @@ EXAMPLES = r'''
|
|||
netbox_url: http://netbox.local
|
||||
netbox_token: thisIsMyToken
|
||||
data:
|
||||
name: Test
|
||||
name: Another Test Device
|
||||
device_type: C9410R
|
||||
device_role: Core Switch
|
||||
site: Main
|
||||
|
@ -146,24 +147,22 @@ EXAMPLES = r'''
|
|||
- Schnozzberry
|
||||
state: present
|
||||
|
||||
- name: Create device and assign to rack and position
|
||||
- name: Update the rack and position of an existing device
|
||||
netbox_device:
|
||||
netbox_url: http://netbox.local
|
||||
netbox_token: thisIsMyToken
|
||||
data:
|
||||
name: Test
|
||||
device_type: C9410R
|
||||
device_role: Core Switch
|
||||
site: Main
|
||||
name: Test Device
|
||||
rack: Test Rack
|
||||
position: 10
|
||||
face: Front
|
||||
state: present
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
device:
|
||||
description: Serialized object as created or already existent within Netbox
|
||||
returned: on creation
|
||||
returned: success (when I(state=present))
|
||||
type: dict
|
||||
msg:
|
||||
description: Message indicating failure or info about what has been achieved
|
||||
|
@ -175,7 +174,15 @@ import json
|
|||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
from ansible.module_utils.net_tools.netbox.netbox_utils import find_ids, normalize_data, DEVICE_STATUS, FACE_ID
|
||||
from ansible.module_utils.net_tools.netbox.netbox_utils import (
|
||||
find_ids,
|
||||
normalize_data,
|
||||
create_netbox_object,
|
||||
delete_netbox_object,
|
||||
update_netbox_object,
|
||||
DEVICE_STATUS,
|
||||
FACE_ID
|
||||
)
|
||||
|
||||
PYNETBOX_IMP_ERR = None
|
||||
try:
|
||||
|
@ -198,13 +205,18 @@ def main():
|
|||
validate_certs=dict(type="bool", default=True)
|
||||
)
|
||||
|
||||
global module
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=False)
|
||||
supports_check_mode=True)
|
||||
|
||||
# Fail module if pynetbox is not installed
|
||||
if not HAS_PYNETBOX:
|
||||
module.fail_json(msg=missing_required_lib('pynetbox'), exception=PYNETBOX_IMP_ERR)
|
||||
|
||||
# Fail if device name is not given
|
||||
if not module.params["data"].get("name"):
|
||||
module.fail_json(msg="missing device name")
|
||||
|
||||
# Assign variables to be used with module
|
||||
app = 'dcim'
|
||||
endpoint = 'devices'
|
||||
|
@ -228,59 +240,72 @@ def main():
|
|||
norm_data = normalize_data(data)
|
||||
try:
|
||||
if 'present' in state:
|
||||
return module.exit_json(
|
||||
**ensure_device_present(nb, nb_endpoint, norm_data)
|
||||
)
|
||||
result = ensure_device_present(nb, nb_endpoint, norm_data)
|
||||
else:
|
||||
return module.exit_json(
|
||||
**ensure_device_absent(nb_endpoint, norm_data)
|
||||
)
|
||||
result = ensure_device_absent(nb_endpoint, norm_data)
|
||||
return module.exit_json(**result)
|
||||
except pynetbox.RequestError as e:
|
||||
return module.fail_json(msg=json.loads(e.error))
|
||||
except ValueError as e:
|
||||
return module.fail_json(msg=str(e))
|
||||
|
||||
|
||||
def ensure_device_present(nb, nb_endpoint, data):
|
||||
def _find_ids(nb, data):
|
||||
if data.get("status"):
|
||||
data["status"] = DEVICE_STATUS.get(data["status"].lower())
|
||||
if data.get("face"):
|
||||
data["face"] = FACE_ID.get(data["face"].lower())
|
||||
return find_ids(nb, data)
|
||||
|
||||
|
||||
def ensure_device_present(nb, nb_endpoint, normalized_data):
|
||||
'''
|
||||
:returns dict(device, msg, changed): dictionary resulting of the request,
|
||||
:returns dict(device, msg, changed, diff): dictionary resulting of the request,
|
||||
where `device` is the serialized device fetched or newly created in
|
||||
Netbox
|
||||
'''
|
||||
data = _find_ids(nb, normalized_data)
|
||||
nb_device = nb_endpoint.get(name=data["name"])
|
||||
result = {}
|
||||
if not nb_device:
|
||||
device = _netbox_create_device(nb, nb_endpoint, data).serialize()
|
||||
changed = True
|
||||
device, diff = create_netbox_object(nb_endpoint, data, module.check_mode)
|
||||
msg = "Device %s created" % (data["name"])
|
||||
changed = True
|
||||
result["diff"] = diff
|
||||
else:
|
||||
msg = "Device %s already exists" % (data["name"])
|
||||
device = nb_device.serialize()
|
||||
changed = False
|
||||
|
||||
return {"device": device, "msg": msg, "changed": changed}
|
||||
|
||||
|
||||
def _netbox_create_device(nb, nb_endpoint, data):
|
||||
if data.get("status"):
|
||||
data["status"] = DEVICE_STATUS.get(data["status"].lower(), 0)
|
||||
if data.get("face"):
|
||||
data["face"] = FACE_ID.get(data["face"].lower(), 0)
|
||||
data = find_ids(nb, data)
|
||||
return nb_endpoint.create(data)
|
||||
device, diff = update_netbox_object(nb_device, data, module.check_mode)
|
||||
if device is False:
|
||||
module.fail_json(
|
||||
msg="Request failed, couldn't update device: %s" % data["name"]
|
||||
)
|
||||
if diff:
|
||||
msg = "Device %s updated" % (data["name"])
|
||||
changed = True
|
||||
result["diff"] = diff
|
||||
else:
|
||||
msg = "Device %s already exists" % (data["name"])
|
||||
changed = False
|
||||
result.update({"device": device, "changed": changed, "msg": msg})
|
||||
return result
|
||||
|
||||
|
||||
def ensure_device_absent(nb_endpoint, data):
|
||||
'''
|
||||
:returns dict(msg, changed)
|
||||
:returns dict(msg, changed, diff)
|
||||
'''
|
||||
device = nb_endpoint.get(name=data["name"])
|
||||
if device:
|
||||
device.delete()
|
||||
nb_device = nb_endpoint.get(name=data["name"])
|
||||
result = {}
|
||||
if nb_device:
|
||||
dummy, diff = delete_netbox_object(nb_device, module.check_mode)
|
||||
msg = 'Device %s deleted' % (data["name"])
|
||||
changed = True
|
||||
result["diff"] = diff
|
||||
else:
|
||||
msg = 'Device %s already absent' % (data["name"])
|
||||
changed = False
|
||||
|
||||
return {"msg": msg, "changed": changed}
|
||||
result.update({"changed": changed, "msg": msg})
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue