4
4
from django .views .generic import View
5
5
from django .http import HttpResponse , StreamingHttpResponse
6
6
from django .shortcuts import render
7
+ from django .contrib .contenttypes .models import ContentType
7
8
from tempfile import NamedTemporaryFile
8
9
9
10
from nautobot .dcim .models import Interface , Device
10
- from nautobot .extras .models import Status , Tag
11
+ from nautobot .extras .models import Status , Tag , Role
11
12
from nautobot .ipam .models import VLAN , VLANGroup , IPAddress
12
13
from nautobot .dcim .models .device_components import (
13
14
InterfaceModeChoices ,
@@ -55,8 +56,10 @@ def __init__(self, pk) -> None:
55
56
self .interface_types_inverse = {v : k for k , v in self .interface_types .items ()}
56
57
self .statuses = InterfaceStatusChoices .as_dict ().values ()
57
58
self .vlan_modes = InterfaceModeChoices .values ()
59
+ _roles_ct = ContentType .objects .get (app_label = "dcim" , model = "interface" )
60
+ self .valid_roles = list (Role .objects .filter (content_types = _roles_ct ))
58
61
59
- #
62
+ #
60
63
if self .device_obj .vlan_group :
61
64
self .vlan_group = self .device_obj .vlan_group
62
65
else :
@@ -82,6 +85,7 @@ def _vlan_to_string(vlan):
82
85
{
83
86
"Name" : "name" ,
84
87
"Status" : "status.name" ,
88
+ "Role" : Coalesce ("role.name" , default = "" ),
85
89
"Label" : "label" ,
86
90
"Type" : ("type" , lambda t : ctx .interface_types [t ]),
87
91
"Enabled" : "enabled" ,
@@ -99,7 +103,6 @@ def _vlan_to_string(vlan):
99
103
"ip_addresses" ,
100
104
([("address" , str )], lambda lst : "\n " .join (lst )),
101
105
),
102
- # intf["IP Addresses"] = "\n".join([str(ip) for ip in intf["ip_addresses"]])
103
106
"Custom Fields" : (
104
107
"cf" ,
105
108
(
@@ -156,6 +159,9 @@ def _vlan_to_string(vlan):
156
159
dvws ["D1" ] = "valid vlans"
157
160
for i , v in enumerate (ctx .valid_vlans ):
158
161
dvws [f"D{ i + 2 } " ] = _vlan_to_string (v )
162
+ dvws ["E1" ] = "roles"
163
+ for i , r in enumerate (ctx .valid_roles ):
164
+ dvws [f"E{ i + 2 } " ] = r .name
159
165
160
166
# boolean columns
161
167
bool_dv = DataValidation (type = "list" , formula1 = '"TRUE,FALSE"' )
@@ -166,18 +172,14 @@ def _vlan_to_string(vlan):
166
172
bool_dv .add (f"{ col_letter } 2:{ col_letter } { length } " )
167
173
168
174
# LAG column
169
- lag_dv = DataValidation (
170
- type = "list" , formula1 = f"'Interfaces'!$A$2:$A${ length } " , allow_blank = True
171
- )
175
+ lag_dv = DataValidation (type = "list" , formula1 = f"'Interfaces'!$A$2:$A${ length } " , allow_blank = True )
172
176
lag_dv .hide_drop_down = False
173
177
ws .add_data_validation (lag_dv )
174
178
col_letter = get_column_letter (columns_by_name ["LAG" ])
175
179
lag_dv .add (f"{ col_letter } 2:{ col_letter } { length } " )
176
180
177
181
# status
178
- status_dv = DataValidation (
179
- type = "list" , formula1 = f"'_data_validation_lists'!$A$2:$A${ len (ctx .statuses )+ 1 } "
180
- )
182
+ status_dv = DataValidation (type = "list" , formula1 = f"'_data_validation_lists'!$A$2:$A${ len (ctx .statuses )+ 1 } " )
181
183
status_dv .hide_drop_down = False
182
184
ws .add_data_validation (status_dv )
183
185
col_letter = get_column_letter (columns_by_name ["Status" ])
@@ -215,6 +217,17 @@ def _vlan_to_string(vlan):
215
217
col_letter = get_column_letter (columns_by_name ["Untagged VLAN" ])
216
218
vlans_dv .add (f"{ col_letter } 2:{ col_letter } { length } " )
217
219
220
+ # roles
221
+ roles_dv = DataValidation (
222
+ type = "list" ,
223
+ formula1 = f"'_data_validation_lists'!$E$2:$E${ len (ctx .valid_roles )+ 1 } " ,
224
+ allow_blank = True ,
225
+ )
226
+ roles_dv .hide_drop_down = False
227
+ ws .add_data_validation (roles_dv )
228
+ col_letter = get_column_letter (columns_by_name ["Role" ])
229
+ roles_dv .add (f"{ col_letter } 2:{ col_letter } { length } " )
230
+
218
231
with NamedTemporaryFile () as tmp :
219
232
wb .save (tmp .name )
220
233
tmp .seek (0 )
@@ -256,9 +269,7 @@ def _parse_ipaddress_list(ips: str):
256
269
if not ips :
257
270
return []
258
271
return [
259
- IPAddress .objects .get_or_create (
260
- address = ip , defaults = dict (status = Status .objects .get (name = "Active" ))
261
- )[0 ]
272
+ IPAddress .objects .get_or_create (address = ip , defaults = dict (status = Status .objects .get (name = "Active" )))[0 ]
262
273
for ip in ips .split ("\n " )
263
274
]
264
275
@@ -283,12 +294,11 @@ def _parse_tags(tags):
283
294
),
284
295
"label" : ("Label" , lambda l : l if l else "" ),
285
296
"type" : ("Type" , lambda t : ctx .interface_types_inverse [t ]),
297
+ "role" : ("Role" , lambda r : Role .objects .get (name = r ) if r else None ),
286
298
"enabled" : "Enabled" ,
287
299
"lag" : (
288
300
"LAG" ,
289
- lambda l : Interface .objects .get (name = l , device_id = pk )
290
- if l
291
- else None ,
301
+ lambda l : Interface .objects .get (name = l , device_id = pk ) if l else None ,
292
302
),
293
303
"mgmt_only" : "Management Only" ,
294
304
"description" : ("Description" , lambda d : d if d else "" ),
@@ -366,9 +376,7 @@ def excel_processing():
366
376
# We've been sent here by the widget on the device page
367
377
# but the data we've been sent here with failed server-side validation.
368
378
# We need to render the form again, but with the errors from the form included
369
- form_html = render (
370
- request , "device_interfaces_excel.html" , {"form" : form , "pk" : pk }
371
- )
379
+ form_html = render (request , "device_interfaces_excel.html" , {"form" : form , "pk" : pk })
372
380
return form_html
373
381
# return HttpResponse(f"<div class='alert alert-danger'>Invalid file format. Please try again and make sure you're uploading a .xlsx file</div>{form_html.content}")
374
382
0 commit comments