-
Notifications
You must be signed in to change notification settings - Fork 53
Expand file tree
/
Copy pathhelper.py
More file actions
147 lines (121 loc) · 5.2 KB
/
helper.py
File metadata and controls
147 lines (121 loc) · 5.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
"""General helper functions for the app."""
import os
import re
import socket
import netaddr
from django.contrib.contenttypes.models import ContentType
from nautobot.dcim.filters import DeviceFilterSet
from nautobot.dcim.models import Device
from netaddr.core import AddrFormatError
from nornir_nautobot.exceptions import NornirNautobotException
from nautobot_device_onboarding.exceptions import OnboardException
FIELDS_PK = {
"location",
"role",
}
FIELDS_NAME = {"tags"}
MARKDOWN_ESCAPE_RE = re.compile(r"(?<!\\)([\*_\[\]\(\)`])")
def format_log_message(message):
"""Format a message for inclusion in Nautobot job logs."""
return (
MARKDOWN_ESCAPE_RE.sub(r"\\\1", message)
.replace("<", "<")
.replace(">", ">")
.replace("\r\n", "<br>")
.replace("\n", "<br>")
)
def get_job_filter(data=None):
"""Helper function to return a the filterable list of OS's based on platform.name and a specific custom value."""
if not data:
data = {}
query = {}
# Translate instances from FIELDS set to list of primary keys
for field in FIELDS_PK:
if data.get(field):
query[field] = [str(data[field].id)]
# Translate instances from FIELDS set to list of names
for field in FIELDS_NAME:
if data.get(field):
query[field] = [str(data[field].id)]
# Handle case where object is from single device run all.
if data.get("devices") and isinstance(data["devices"], Device):
query.update({"id": [str(data["devices"].pk)]})
elif data.get("devices"):
query.update({"id": data["devices"].values_list("pk", flat=True)})
base_qs = Device.objects.all()
# {'debug': False, 'namespace': <Namespace: Global>, 'devices': <ConfigContextModelQuerySet [<Device: demo-cisco-xe>]>, 'location': None, 'device_role': None, 'tag': None, 'port': 22, 'timeout': 30}
devices_filtered = DeviceFilterSet(data=query, queryset=base_qs)
if not devices_filtered.qs.exists():
raise NornirNautobotException(
"`E3016:` The provided job parameters didn't match any devices detected. Please select the correct job parameters to correctly match devices."
)
devices_no_platform = devices_filtered.qs.filter(platform__isnull=True)
if devices_no_platform.exists():
raise NornirNautobotException(
f"`E3017:` The following device(s) {', '.join([device.name for device in devices_no_platform])} have no platform defined. Platform is required."
)
return devices_filtered.qs
def onboarding_task_fqdn_to_ip(address):
"""Method to assure OT has FQDN resolved to IP address and rewritten into OT.
If it is a DNS name, attempt to resolve the DNS address and assign the IP address to the
name.
Returns:
None
Raises:
OnboardException("fail-general"):
When a prefix was entered for an IP address
OnboardException("fail-dns"):
When a Name lookup via DNS fails to resolve an IP address
"""
try:
# If successful, this is an IP address and can pass
netaddr.IPAddress(address)
return address
# Raise an Exception for Prefix values
except ValueError as err:
raise OnboardException(f"fail-general - ERROR appears a prefix was entered: {address}") from err
# An AddrFormatError exception means that there is not an IP address in the field, and should continue on
except AddrFormatError:
try:
# Perform DNS Lookup
return socket.gethostbyname(address)
except socket.gaierror as err:
# DNS Lookup has failed, Raise an exception for unable to complete DNS lookup
raise OnboardException(f"fail-dns - ERROR failed to complete DNS lookup: {address}") from err
def check_for_required_file(directory, filename):
"""
Checks if a file named 'filename' exists within the specified directory.
Args:
directory: The path to the directory to check.
filename: The name of the file to check for.
Returns:
True if the 'index' file exists in the directory, False otherwise.
"""
try:
for f_name in os.listdir(directory):
if f_name == filename:
return True
return False
except FileNotFoundError:
return False
def add_content_type(job, model_to_add, target_object):
"""Add a content type to the valid content types of a target object.
Args:
job: The job object used for logging.
model_to_add: The model class to get the content type for.
target_object: The object to which the content type will be added.
Raises:
OnboardException: If adding the content type fails.
"""
try:
job.logger.info(
"Adding %s content type to valid content types for location type %s",
model_to_add.__name__,
target_object,
)
content_type = ContentType.objects.get_for_model(model_to_add)
target_object.content_types.add(content_type)
except Exception as e:
err_msg = f"Failed to add {model_to_add.__name__} to valid content types for {target_object}: {e}"
job.logger.error(err_msg)
raise OnboardException("fail-general - " + err_msg) from e