netbox-cisco-ise 0.1.3__py3-none-any.whl → 0.1.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- netbox_cisco_ise/__init__.py +5 -2
- netbox_cisco_ise/ise_client.py +16 -4
- netbox_cisco_ise/templates/netbox_cisco_ise/device_tab.html +24 -0
- netbox_cisco_ise/templates/netbox_cisco_ise/endpoint_tab_content.html +216 -0
- netbox_cisco_ise/templates/netbox_cisco_ise/nad_tab_content.html +193 -0
- netbox_cisco_ise/urls.py +2 -1
- netbox_cisco_ise/views.py +83 -34
- {netbox_cisco_ise-0.1.3.dist-info → netbox_cisco_ise-0.1.5.dist-info}/METADATA +7 -1
- netbox_cisco_ise-0.1.5.dist-info/RECORD +16 -0
- {netbox_cisco_ise-0.1.3.dist-info → netbox_cisco_ise-0.1.5.dist-info}/WHEEL +1 -1
- netbox_cisco_ise-0.1.3.dist-info/RECORD +0 -13
- {netbox_cisco_ise-0.1.3.dist-info → netbox_cisco_ise-0.1.5.dist-info}/licenses/LICENSE +0 -0
- {netbox_cisco_ise-0.1.3.dist-info → netbox_cisco_ise-0.1.5.dist-info}/top_level.txt +0 -0
netbox_cisco_ise/__init__.py
CHANGED
|
@@ -7,7 +7,7 @@ Shows endpoint identity, profiling data, active session status, and network acce
|
|
|
7
7
|
|
|
8
8
|
from netbox.plugins import PluginConfig
|
|
9
9
|
|
|
10
|
-
__version__ = "0.1.
|
|
10
|
+
__version__ = "0.1.5"
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class CiscoISEConfig(PluginConfig):
|
|
@@ -49,7 +49,10 @@ class CiscoISEConfig(PluginConfig):
|
|
|
49
49
|
# {"manufacturer": "cisco", "device_type": ".*phone.*", "lookup": "endpoint"}, # Cisco phones by MAC
|
|
50
50
|
# ]
|
|
51
51
|
"device_mappings": [
|
|
52
|
-
{
|
|
52
|
+
{
|
|
53
|
+
"manufacturer": r"cisco",
|
|
54
|
+
"lookup": "nad",
|
|
55
|
+
}, # Default: Cisco devices as NADs
|
|
53
56
|
],
|
|
54
57
|
}
|
|
55
58
|
|
netbox_cisco_ise/ise_client.py
CHANGED
|
@@ -275,7 +275,9 @@ class ISEClient:
|
|
|
275
275
|
cached["cached"] = True
|
|
276
276
|
return cached
|
|
277
277
|
|
|
278
|
-
result = self._make_ers_request(
|
|
278
|
+
result = self._make_ers_request(
|
|
279
|
+
"/networkdevice", params={"filter": f"ipaddress.EQ.{ip_address}"}
|
|
280
|
+
)
|
|
279
281
|
|
|
280
282
|
return self._process_nad_result(result, cache_key)
|
|
281
283
|
|
|
@@ -295,7 +297,9 @@ class ISEClient:
|
|
|
295
297
|
cached["cached"] = True
|
|
296
298
|
return cached
|
|
297
299
|
|
|
298
|
-
result = self._make_ers_request(
|
|
300
|
+
result = self._make_ers_request(
|
|
301
|
+
"/networkdevice", params={"filter": f"name.CONTAINS.{name}"}
|
|
302
|
+
)
|
|
299
303
|
|
|
300
304
|
return self._process_nad_result(result, cache_key)
|
|
301
305
|
|
|
@@ -308,7 +312,11 @@ class ISEClient:
|
|
|
308
312
|
resources = search_result.get("resources", [])
|
|
309
313
|
|
|
310
314
|
if not resources:
|
|
311
|
-
return {
|
|
315
|
+
return {
|
|
316
|
+
"error": "Network device not found in ISE",
|
|
317
|
+
"not_found": True,
|
|
318
|
+
"is_nad": False,
|
|
319
|
+
}
|
|
312
320
|
|
|
313
321
|
# Get full NAD details
|
|
314
322
|
nad_id = resources[0].get("id")
|
|
@@ -357,7 +365,11 @@ class ISEClient:
|
|
|
357
365
|
"ro_community": bool(snmp_settings.get("roCommunity")),
|
|
358
366
|
"polling_interval": snmp_settings.get("pollingInterval"),
|
|
359
367
|
},
|
|
360
|
-
"trustsec_enabled": bool(
|
|
368
|
+
"trustsec_enabled": bool(
|
|
369
|
+
trustsec_settings.get("deviceAuthenticationSettings", {}).get(
|
|
370
|
+
"sgaDeviceId"
|
|
371
|
+
)
|
|
372
|
+
),
|
|
361
373
|
"coA_port": nad.get("coaPort"),
|
|
362
374
|
"cached": False,
|
|
363
375
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{% extends 'dcim/device/base.html' %}
|
|
2
|
+
{% load helpers %}
|
|
3
|
+
|
|
4
|
+
{% block content %}
|
|
5
|
+
{% if loading %}
|
|
6
|
+
<div id="ise-content"
|
|
7
|
+
hx-get="{% url 'plugins:netbox_cisco_ise:device_content' pk=object.pk %}"
|
|
8
|
+
hx-trigger="load"
|
|
9
|
+
hx-swap="innerHTML">
|
|
10
|
+
<div class="d-flex justify-content-center align-items-center py-5">
|
|
11
|
+
<div class="text-center">
|
|
12
|
+
<div class="spinner-border text-primary mb-3" role="status" style="width: 3rem; height: 3rem;">
|
|
13
|
+
<span class="visually-hidden">Loading...</span>
|
|
14
|
+
</div>
|
|
15
|
+
<p class="text-muted mb-0">Loading Cisco ISE data...</p>
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
{% elif error %}
|
|
20
|
+
<div class="alert alert-warning">
|
|
21
|
+
<i class="mdi mdi-alert"></i> {{ error }}
|
|
22
|
+
</div>
|
|
23
|
+
{% endif %}
|
|
24
|
+
{% endblock %}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
{% load helpers %}
|
|
2
|
+
|
|
3
|
+
<div class="row">
|
|
4
|
+
<div class="col-12">
|
|
5
|
+
{% if error %}
|
|
6
|
+
<div class="alert alert-warning">
|
|
7
|
+
<i class="mdi mdi-alert"></i> {{ error }}
|
|
8
|
+
</div>
|
|
9
|
+
{% elif ise_data %}
|
|
10
|
+
<div class="row">
|
|
11
|
+
<!-- Session Status Card -->
|
|
12
|
+
<div class="col-md-4 mb-3">
|
|
13
|
+
<div class="card h-100">
|
|
14
|
+
<div class="card-header">
|
|
15
|
+
<h5 class="card-title mb-0">
|
|
16
|
+
<i class="mdi mdi-lan-connect"></i> Session Status
|
|
17
|
+
</h5>
|
|
18
|
+
</div>
|
|
19
|
+
<div class="card-body text-center">
|
|
20
|
+
{% if session_data.connected %}
|
|
21
|
+
<div class="p-3 mb-2 bg-success rounded">
|
|
22
|
+
<span class="text-white fs-3 fw-bold">
|
|
23
|
+
<i class="mdi mdi-check-circle"></i> Connected
|
|
24
|
+
</span>
|
|
25
|
+
</div>
|
|
26
|
+
{% if session_data.framed_ip_address %}
|
|
27
|
+
<div class="mt-2">
|
|
28
|
+
<span class="text-muted">IP Address:</span>
|
|
29
|
+
<code class="ms-2">{{ session_data.framed_ip_address }}</code>
|
|
30
|
+
</div>
|
|
31
|
+
{% endif %}
|
|
32
|
+
{% if session_data.authorization_profile %}
|
|
33
|
+
<div class="mt-1">
|
|
34
|
+
<span class="text-muted">Authorization:</span>
|
|
35
|
+
<span class="badge bg-info text-dark ms-2">{{ session_data.authorization_profile }}</span>
|
|
36
|
+
</div>
|
|
37
|
+
{% endif %}
|
|
38
|
+
{% if session_data.acct_session_time %}
|
|
39
|
+
<div class="mt-1 text-muted small">
|
|
40
|
+
Session time: {{ session_data.acct_session_time }} seconds
|
|
41
|
+
</div>
|
|
42
|
+
{% endif %}
|
|
43
|
+
{% else %}
|
|
44
|
+
<div class="p-3 mb-2 bg-secondary rounded">
|
|
45
|
+
<span class="text-white fs-3 fw-bold">
|
|
46
|
+
<i class="mdi mdi-lan-disconnect"></i> Disconnected
|
|
47
|
+
</span>
|
|
48
|
+
</div>
|
|
49
|
+
<div class="mt-2 text-muted">
|
|
50
|
+
No active session found
|
|
51
|
+
</div>
|
|
52
|
+
{% endif %}
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<!-- Endpoint Identity Card -->
|
|
58
|
+
<div class="col-md-4 mb-3">
|
|
59
|
+
<div class="card h-100">
|
|
60
|
+
<div class="card-header">
|
|
61
|
+
<h5 class="card-title mb-0">
|
|
62
|
+
<i class="mdi mdi-account-badge"></i> Endpoint Identity
|
|
63
|
+
</h5>
|
|
64
|
+
</div>
|
|
65
|
+
<div class="card-body">
|
|
66
|
+
<table class="table table-sm table-borderless mb-0">
|
|
67
|
+
<tr>
|
|
68
|
+
<th class="text-muted" style="width: 40%">MAC Address:</th>
|
|
69
|
+
<td><code>{{ ise_data.mac_address }}</code></td>
|
|
70
|
+
</tr>
|
|
71
|
+
<tr>
|
|
72
|
+
<th class="text-muted">Profile:</th>
|
|
73
|
+
<td>{{ ise_data.profile_name|default:"N/A" }}</td>
|
|
74
|
+
</tr>
|
|
75
|
+
<tr>
|
|
76
|
+
<th class="text-muted">Identity Group:</th>
|
|
77
|
+
<td>{{ ise_data.group_name|default:"N/A" }}</td>
|
|
78
|
+
</tr>
|
|
79
|
+
<tr>
|
|
80
|
+
<th class="text-muted">Static Assignment:</th>
|
|
81
|
+
<td>
|
|
82
|
+
{% if ise_data.static_group_assignment %}
|
|
83
|
+
<span class="badge bg-success text-white">Yes</span>
|
|
84
|
+
{% else %}
|
|
85
|
+
<span class="badge bg-secondary text-white">No</span>
|
|
86
|
+
{% endif %}
|
|
87
|
+
</td>
|
|
88
|
+
</tr>
|
|
89
|
+
{% if ise_data.portal_user %}
|
|
90
|
+
<tr>
|
|
91
|
+
<th class="text-muted">Portal User:</th>
|
|
92
|
+
<td>{{ ise_data.portal_user }}</td>
|
|
93
|
+
</tr>
|
|
94
|
+
{% endif %}
|
|
95
|
+
</table>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<!-- Connection Details Card -->
|
|
101
|
+
<div class="col-md-4 mb-3">
|
|
102
|
+
<div class="card h-100">
|
|
103
|
+
<div class="card-header">
|
|
104
|
+
<h5 class="card-title mb-0">
|
|
105
|
+
<i class="mdi mdi-router-wireless"></i> Connection Details
|
|
106
|
+
</h5>
|
|
107
|
+
</div>
|
|
108
|
+
<div class="card-body">
|
|
109
|
+
{% if session_data.connected %}
|
|
110
|
+
<table class="table table-sm table-borderless mb-0">
|
|
111
|
+
{% if session_data.nas_ip_address %}
|
|
112
|
+
<tr>
|
|
113
|
+
<th class="text-muted" style="width: 40%">Connected To:</th>
|
|
114
|
+
<td><code>{{ session_data.nas_ip_address }}</code></td>
|
|
115
|
+
</tr>
|
|
116
|
+
{% endif %}
|
|
117
|
+
{% if session_data.nas_port_id %}
|
|
118
|
+
<tr>
|
|
119
|
+
<th class="text-muted">Port:</th>
|
|
120
|
+
<td>{{ session_data.nas_port_id }}</td>
|
|
121
|
+
</tr>
|
|
122
|
+
{% endif %}
|
|
123
|
+
{% if session_data.vlan %}
|
|
124
|
+
<tr>
|
|
125
|
+
<th class="text-muted">VLAN:</th>
|
|
126
|
+
<td>{{ session_data.vlan }}</td>
|
|
127
|
+
</tr>
|
|
128
|
+
{% endif %}
|
|
129
|
+
{% if session_data.ssid %}
|
|
130
|
+
<tr>
|
|
131
|
+
<th class="text-muted">SSID:</th>
|
|
132
|
+
<td>{{ session_data.ssid }}</td>
|
|
133
|
+
</tr>
|
|
134
|
+
{% endif %}
|
|
135
|
+
{% if session_data.auth_method %}
|
|
136
|
+
<tr>
|
|
137
|
+
<th class="text-muted">Auth Method:</th>
|
|
138
|
+
<td>{{ session_data.auth_method }}</td>
|
|
139
|
+
</tr>
|
|
140
|
+
{% endif %}
|
|
141
|
+
{% if session_data.security_group %}
|
|
142
|
+
<tr>
|
|
143
|
+
<th class="text-muted">Security Group:</th>
|
|
144
|
+
<td><span class="badge bg-primary text-white">{{ session_data.security_group }}</span></td>
|
|
145
|
+
</tr>
|
|
146
|
+
{% endif %}
|
|
147
|
+
</table>
|
|
148
|
+
{% else %}
|
|
149
|
+
<div class="text-center text-muted py-4">
|
|
150
|
+
<i class="mdi mdi-lan-disconnect mdi-48px"></i>
|
|
151
|
+
<p class="mt-2">No active connection</p>
|
|
152
|
+
</div>
|
|
153
|
+
{% endif %}
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
<!-- Custom Attributes -->
|
|
160
|
+
{% if ise_data.custom_attributes %}
|
|
161
|
+
<div class="row">
|
|
162
|
+
<div class="col-12 mb-3">
|
|
163
|
+
<div class="card">
|
|
164
|
+
<div class="card-header">
|
|
165
|
+
<h5 class="card-title mb-0">
|
|
166
|
+
<i class="mdi mdi-tag-multiple"></i> Custom Attributes
|
|
167
|
+
</h5>
|
|
168
|
+
</div>
|
|
169
|
+
<div class="card-body">
|
|
170
|
+
<table class="table table-sm table-hover">
|
|
171
|
+
<thead>
|
|
172
|
+
<tr>
|
|
173
|
+
<th>Attribute</th>
|
|
174
|
+
<th>Value</th>
|
|
175
|
+
</tr>
|
|
176
|
+
</thead>
|
|
177
|
+
<tbody>
|
|
178
|
+
{% for key, value in ise_data.custom_attributes.items %}
|
|
179
|
+
<tr>
|
|
180
|
+
<td>{{ key }}</td>
|
|
181
|
+
<td>{{ value }}</td>
|
|
182
|
+
</tr>
|
|
183
|
+
{% endfor %}
|
|
184
|
+
</tbody>
|
|
185
|
+
</table>
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
{% endif %}
|
|
191
|
+
|
|
192
|
+
<!-- Cache indicator and external link -->
|
|
193
|
+
<div class="mt-3 d-flex justify-content-between align-items-center">
|
|
194
|
+
<div>
|
|
195
|
+
{% if ise_data.cached %}
|
|
196
|
+
<span class="text-muted small">
|
|
197
|
+
<i class="mdi mdi-cached"></i> Data from cache
|
|
198
|
+
</span>
|
|
199
|
+
{% endif %}
|
|
200
|
+
</div>
|
|
201
|
+
<div>
|
|
202
|
+
{% if ise_url %}
|
|
203
|
+
<a href="{{ ise_url }}" target="_blank" class="btn btn-outline-primary btn-sm">
|
|
204
|
+
<i class="mdi mdi-open-in-new"></i> Open Cisco ISE
|
|
205
|
+
</a>
|
|
206
|
+
{% endif %}
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
209
|
+
|
|
210
|
+
{% else %}
|
|
211
|
+
<div class="alert alert-info">
|
|
212
|
+
<i class="mdi mdi-information"></i> No endpoint data available from ISE.
|
|
213
|
+
</div>
|
|
214
|
+
{% endif %}
|
|
215
|
+
</div>
|
|
216
|
+
</div>
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
{% load helpers %}
|
|
2
|
+
|
|
3
|
+
<div class="row">
|
|
4
|
+
<div class="col-12">
|
|
5
|
+
{% if error %}
|
|
6
|
+
<div class="alert alert-warning">
|
|
7
|
+
<i class="mdi mdi-alert"></i> {{ error }}
|
|
8
|
+
</div>
|
|
9
|
+
{% elif ise_data %}
|
|
10
|
+
<div class="row">
|
|
11
|
+
<!-- NAD Registration Status Card -->
|
|
12
|
+
<div class="col-md-4 mb-3">
|
|
13
|
+
<div class="card h-100">
|
|
14
|
+
<div class="card-header">
|
|
15
|
+
<h5 class="card-title mb-0">
|
|
16
|
+
<i class="mdi mdi-check-decagram"></i> ISE Registration
|
|
17
|
+
</h5>
|
|
18
|
+
</div>
|
|
19
|
+
<div class="card-body text-center">
|
|
20
|
+
<div class="p-3 mb-2 bg-success rounded">
|
|
21
|
+
<span class="text-white fs-3 fw-bold">
|
|
22
|
+
<i class="mdi mdi-check-circle"></i> Registered
|
|
23
|
+
</span>
|
|
24
|
+
</div>
|
|
25
|
+
{% if ise_data.name %}
|
|
26
|
+
<div class="mt-2">
|
|
27
|
+
<span class="text-muted">NAD Name:</span>
|
|
28
|
+
<strong class="ms-2">{{ ise_data.name }}</strong>
|
|
29
|
+
</div>
|
|
30
|
+
{% endif %}
|
|
31
|
+
{% if ise_data.profile_name %}
|
|
32
|
+
<div class="mt-1">
|
|
33
|
+
<span class="text-muted">Profile:</span>
|
|
34
|
+
<span class="badge bg-info text-dark ms-2">{{ ise_data.profile_name }}</span>
|
|
35
|
+
</div>
|
|
36
|
+
{% endif %}
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<!-- Device Details Card -->
|
|
42
|
+
<div class="col-md-4 mb-3">
|
|
43
|
+
<div class="card h-100">
|
|
44
|
+
<div class="card-header">
|
|
45
|
+
<h5 class="card-title mb-0">
|
|
46
|
+
<i class="mdi mdi-router"></i> Device Details
|
|
47
|
+
</h5>
|
|
48
|
+
</div>
|
|
49
|
+
<div class="card-body">
|
|
50
|
+
<table class="table table-sm table-borderless mb-0">
|
|
51
|
+
{% if ise_data.ip_addresses %}
|
|
52
|
+
<tr>
|
|
53
|
+
<th class="text-muted" style="width: 40%">IP Address(es):</th>
|
|
54
|
+
<td>
|
|
55
|
+
{% for ip in ise_data.ip_addresses %}
|
|
56
|
+
<code>{{ ip }}</code>{% if not forloop.last %}, {% endif %}
|
|
57
|
+
{% endfor %}
|
|
58
|
+
</td>
|
|
59
|
+
</tr>
|
|
60
|
+
{% endif %}
|
|
61
|
+
{% if ise_data.model_name %}
|
|
62
|
+
<tr>
|
|
63
|
+
<th class="text-muted">Model:</th>
|
|
64
|
+
<td>{{ ise_data.model_name }}</td>
|
|
65
|
+
</tr>
|
|
66
|
+
{% endif %}
|
|
67
|
+
{% if ise_data.software_version %}
|
|
68
|
+
<tr>
|
|
69
|
+
<th class="text-muted">Software:</th>
|
|
70
|
+
<td>{{ ise_data.software_version }}</td>
|
|
71
|
+
</tr>
|
|
72
|
+
{% endif %}
|
|
73
|
+
{% if ise_data.description %}
|
|
74
|
+
<tr>
|
|
75
|
+
<th class="text-muted">Description:</th>
|
|
76
|
+
<td>{{ ise_data.description }}</td>
|
|
77
|
+
</tr>
|
|
78
|
+
{% endif %}
|
|
79
|
+
{% if ise_data.coA_port %}
|
|
80
|
+
<tr>
|
|
81
|
+
<th class="text-muted">CoA Port:</th>
|
|
82
|
+
<td>{{ ise_data.coA_port }}</td>
|
|
83
|
+
</tr>
|
|
84
|
+
{% endif %}
|
|
85
|
+
</table>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<!-- Authentication Settings Card -->
|
|
91
|
+
<div class="col-md-4 mb-3">
|
|
92
|
+
<div class="card h-100">
|
|
93
|
+
<div class="card-header">
|
|
94
|
+
<h5 class="card-title mb-0">
|
|
95
|
+
<i class="mdi mdi-shield-key"></i> Authentication Settings
|
|
96
|
+
</h5>
|
|
97
|
+
</div>
|
|
98
|
+
<div class="card-body">
|
|
99
|
+
<table class="table table-sm table-borderless mb-0">
|
|
100
|
+
<tr>
|
|
101
|
+
<th class="text-muted" style="width: 50%">RADIUS:</th>
|
|
102
|
+
<td>
|
|
103
|
+
{% if ise_data.authentication_settings.radius_enabled %}
|
|
104
|
+
<span class="badge bg-success text-white">Enabled</span>
|
|
105
|
+
{% else %}
|
|
106
|
+
<span class="badge bg-warning text-dark">Disabled</span>
|
|
107
|
+
{% endif %}
|
|
108
|
+
</td>
|
|
109
|
+
</tr>
|
|
110
|
+
<tr>
|
|
111
|
+
<th class="text-muted">TACACS+:</th>
|
|
112
|
+
<td>
|
|
113
|
+
{% if ise_data.tacacs_settings.enabled %}
|
|
114
|
+
<span class="badge bg-success text-white">Enabled</span>
|
|
115
|
+
{% else %}
|
|
116
|
+
<span class="badge bg-warning text-dark">Disabled</span>
|
|
117
|
+
{% endif %}
|
|
118
|
+
</td>
|
|
119
|
+
</tr>
|
|
120
|
+
<tr>
|
|
121
|
+
<th class="text-muted">TrustSec:</th>
|
|
122
|
+
<td>
|
|
123
|
+
{% if ise_data.trustsec_enabled %}
|
|
124
|
+
<span class="badge bg-success text-white">Enabled</span>
|
|
125
|
+
{% else %}
|
|
126
|
+
<span class="badge bg-warning text-dark">Disabled</span>
|
|
127
|
+
{% endif %}
|
|
128
|
+
</td>
|
|
129
|
+
</tr>
|
|
130
|
+
{% if ise_data.snmp_settings.version %}
|
|
131
|
+
<tr>
|
|
132
|
+
<th class="text-muted">SNMP:</th>
|
|
133
|
+
<td>{{ ise_data.snmp_settings.version }}</td>
|
|
134
|
+
</tr>
|
|
135
|
+
{% endif %}
|
|
136
|
+
</table>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
<!-- Network Device Groups -->
|
|
143
|
+
{% if ise_data.groups %}
|
|
144
|
+
<div class="row">
|
|
145
|
+
<div class="col-12 mb-3">
|
|
146
|
+
<div class="card">
|
|
147
|
+
<div class="card-header">
|
|
148
|
+
<h5 class="card-title mb-0">
|
|
149
|
+
<i class="mdi mdi-folder-network"></i> Network Device Groups
|
|
150
|
+
</h5>
|
|
151
|
+
</div>
|
|
152
|
+
<div class="card-body">
|
|
153
|
+
<div class="row">
|
|
154
|
+
{% for category, value in ise_data.groups.items %}
|
|
155
|
+
<div class="col-md-3 col-sm-6 mb-2">
|
|
156
|
+
<div class="border rounded p-2">
|
|
157
|
+
<div class="text-muted small">{{ category }}</div>
|
|
158
|
+
<div class="fw-bold">{{ value }}</div>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
{% endfor %}
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
{% endif %}
|
|
168
|
+
|
|
169
|
+
<!-- Cache indicator and external link -->
|
|
170
|
+
<div class="mt-3 d-flex justify-content-between align-items-center">
|
|
171
|
+
<div>
|
|
172
|
+
{% if ise_data.cached %}
|
|
173
|
+
<span class="text-muted small">
|
|
174
|
+
<i class="mdi mdi-cached"></i> Data from cache
|
|
175
|
+
</span>
|
|
176
|
+
{% endif %}
|
|
177
|
+
</div>
|
|
178
|
+
<div>
|
|
179
|
+
{% if ise_url %}
|
|
180
|
+
<a href="{{ ise_url }}" target="_blank" class="btn btn-outline-primary btn-sm">
|
|
181
|
+
<i class="mdi mdi-open-in-new"></i> Open Cisco ISE
|
|
182
|
+
</a>
|
|
183
|
+
{% endif %}
|
|
184
|
+
</div>
|
|
185
|
+
</div>
|
|
186
|
+
|
|
187
|
+
{% else %}
|
|
188
|
+
<div class="alert alert-info">
|
|
189
|
+
<i class="mdi mdi-information"></i> This device is not registered as a Network Access Device in ISE.
|
|
190
|
+
</div>
|
|
191
|
+
{% endif %}
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
netbox_cisco_ise/urls.py
CHANGED
|
@@ -4,9 +4,10 @@ URL routing for NetBox Cisco ISE Plugin
|
|
|
4
4
|
|
|
5
5
|
from django.urls import path
|
|
6
6
|
|
|
7
|
-
from .views import ISESettingsView, TestConnectionView
|
|
7
|
+
from .views import DeviceISEContentView, ISESettingsView, TestConnectionView
|
|
8
8
|
|
|
9
9
|
urlpatterns = [
|
|
10
10
|
path("settings/", ISESettingsView.as_view(), name="settings"),
|
|
11
11
|
path("test-connection/", TestConnectionView.as_view(), name="test_connection"),
|
|
12
|
+
path("device/<int:pk>/content/", DeviceISEContentView.as_view(), name="device_content"),
|
|
12
13
|
]
|
netbox_cisco_ise/views.py
CHANGED
|
@@ -9,8 +9,10 @@ import re
|
|
|
9
9
|
|
|
10
10
|
from dcim.models import Device
|
|
11
11
|
from django.conf import settings
|
|
12
|
-
from django.
|
|
12
|
+
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
|
|
13
|
+
from django.http import HttpResponse, JsonResponse
|
|
13
14
|
from django.shortcuts import render
|
|
15
|
+
from django.template.loader import render_to_string
|
|
14
16
|
from django.views import View
|
|
15
17
|
from netbox.views import generic
|
|
16
18
|
from utilities.views import ViewTab, register_model_view
|
|
@@ -51,10 +53,18 @@ def get_device_lookup_method(device):
|
|
|
51
53
|
|
|
52
54
|
# Get device info for matching
|
|
53
55
|
manufacturer = device.device_type.manufacturer
|
|
54
|
-
manufacturer_slug =
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
manufacturer_slug = (
|
|
57
|
+
manufacturer.slug.lower() if manufacturer and manufacturer.slug else ""
|
|
58
|
+
)
|
|
59
|
+
manufacturer_name = (
|
|
60
|
+
manufacturer.name.lower() if manufacturer and manufacturer.name else ""
|
|
61
|
+
)
|
|
62
|
+
device_type_slug = (
|
|
63
|
+
device.device_type.slug.lower() if device.device_type.slug else ""
|
|
64
|
+
)
|
|
65
|
+
device_type_model = (
|
|
66
|
+
device.device_type.model.lower() if device.device_type.model else ""
|
|
67
|
+
)
|
|
58
68
|
|
|
59
69
|
# Check each mapping
|
|
60
70
|
for mapping in mappings:
|
|
@@ -66,12 +76,15 @@ def get_device_lookup_method(device):
|
|
|
66
76
|
manufacturer_match = False
|
|
67
77
|
if manufacturer_pattern:
|
|
68
78
|
try:
|
|
69
|
-
if re.search(
|
|
70
|
-
manufacturer_pattern,
|
|
71
|
-
):
|
|
79
|
+
if re.search(
|
|
80
|
+
manufacturer_pattern, manufacturer_slug, re.IGNORECASE
|
|
81
|
+
) or re.search(manufacturer_pattern, manufacturer_name, re.IGNORECASE):
|
|
72
82
|
manufacturer_match = True
|
|
73
83
|
except re.error:
|
|
74
|
-
if
|
|
84
|
+
if (
|
|
85
|
+
manufacturer_pattern in manufacturer_slug
|
|
86
|
+
or manufacturer_pattern in manufacturer_name
|
|
87
|
+
):
|
|
75
88
|
manufacturer_match = True
|
|
76
89
|
|
|
77
90
|
if not manufacturer_match:
|
|
@@ -81,12 +94,15 @@ def get_device_lookup_method(device):
|
|
|
81
94
|
if device_type_pattern:
|
|
82
95
|
device_type_match = False
|
|
83
96
|
try:
|
|
84
|
-
if re.search(
|
|
85
|
-
device_type_pattern,
|
|
86
|
-
):
|
|
97
|
+
if re.search(
|
|
98
|
+
device_type_pattern, device_type_slug, re.IGNORECASE
|
|
99
|
+
) or re.search(device_type_pattern, device_type_model, re.IGNORECASE):
|
|
87
100
|
device_type_match = True
|
|
88
101
|
except re.error:
|
|
89
|
-
if
|
|
102
|
+
if (
|
|
103
|
+
device_type_pattern in device_type_slug
|
|
104
|
+
or device_type_pattern in device_type_model
|
|
105
|
+
):
|
|
90
106
|
device_type_match = True
|
|
91
107
|
|
|
92
108
|
if not device_type_match:
|
|
@@ -119,10 +135,10 @@ def should_show_ise_tab(device):
|
|
|
119
135
|
|
|
120
136
|
@register_model_view(Device, name="cisco_ise", path="cisco-ise")
|
|
121
137
|
class DeviceISEView(generic.ObjectView):
|
|
122
|
-
"""Display Cisco ISE endpoint/NAD details for a Device."""
|
|
138
|
+
"""Display Cisco ISE endpoint/NAD details for a Device with async loading."""
|
|
123
139
|
|
|
124
140
|
queryset = Device.objects.all()
|
|
125
|
-
template_name = "netbox_cisco_ise/
|
|
141
|
+
template_name = "netbox_cisco_ise/device_tab.html"
|
|
126
142
|
|
|
127
143
|
tab = ViewTab(
|
|
128
144
|
label="Cisco ISE",
|
|
@@ -133,8 +149,31 @@ class DeviceISEView(generic.ObjectView):
|
|
|
133
149
|
)
|
|
134
150
|
|
|
135
151
|
def get(self, request, pk):
|
|
136
|
-
"""
|
|
137
|
-
device = Device.objects.
|
|
152
|
+
"""Render initial tab with loading spinner - content loads via htmx."""
|
|
153
|
+
device = Device.objects.get(pk=pk)
|
|
154
|
+
return render(
|
|
155
|
+
request,
|
|
156
|
+
self.template_name,
|
|
157
|
+
{
|
|
158
|
+
"object": device,
|
|
159
|
+
"tab": self.tab,
|
|
160
|
+
"loading": True,
|
|
161
|
+
},
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
class DeviceISEContentView(LoginRequiredMixin, PermissionRequiredMixin, View):
|
|
166
|
+
"""HTMX endpoint that returns ISE content for async loading."""
|
|
167
|
+
|
|
168
|
+
permission_required = "dcim.view_device"
|
|
169
|
+
|
|
170
|
+
def get(self, request, pk):
|
|
171
|
+
"""Fetch ISE data and return HTML content."""
|
|
172
|
+
device = (
|
|
173
|
+
Device.objects.select_related("device_type__manufacturer")
|
|
174
|
+
.prefetch_related("interfaces")
|
|
175
|
+
.get(pk=pk)
|
|
176
|
+
)
|
|
138
177
|
|
|
139
178
|
client = get_client()
|
|
140
179
|
config = settings.PLUGINS_CONFIG.get("netbox_cisco_ise", {})
|
|
@@ -156,7 +195,10 @@ class DeviceISEView(generic.ObjectView):
|
|
|
156
195
|
if "error" not in ise_data:
|
|
157
196
|
# Also get session data for connected endpoints
|
|
158
197
|
session_data = client.get_active_session_by_mac(mac_address)
|
|
159
|
-
if
|
|
198
|
+
if (
|
|
199
|
+
"error" in session_data
|
|
200
|
+
and session_data.get("connected") is False
|
|
201
|
+
):
|
|
160
202
|
# Not connected is fine, just no active session
|
|
161
203
|
pass
|
|
162
204
|
else:
|
|
@@ -177,8 +219,14 @@ class DeviceISEView(generic.ObjectView):
|
|
|
177
219
|
ise_data = client.get_network_device_by_ip(management_ip)
|
|
178
220
|
|
|
179
221
|
# If IP lookup failed or no IP, try hostname
|
|
180
|
-
|
|
181
|
-
|
|
222
|
+
# Use VC name for virtual chassis members (original hostname)
|
|
223
|
+
lookup_hostname = (
|
|
224
|
+
device.virtual_chassis.name
|
|
225
|
+
if device.virtual_chassis
|
|
226
|
+
else device.name
|
|
227
|
+
)
|
|
228
|
+
if ("error" in ise_data or not ise_data) and lookup_hostname:
|
|
229
|
+
ise_data = client.get_network_device_by_name(lookup_hostname)
|
|
182
230
|
|
|
183
231
|
if "error" in ise_data:
|
|
184
232
|
error = ise_data.get("error")
|
|
@@ -191,21 +239,22 @@ class DeviceISEView(generic.ObjectView):
|
|
|
191
239
|
|
|
192
240
|
# Choose template based on lookup type
|
|
193
241
|
if ise_data.get("is_nad"):
|
|
194
|
-
template = "netbox_cisco_ise/
|
|
242
|
+
template = "netbox_cisco_ise/nad_tab_content.html"
|
|
195
243
|
else:
|
|
196
|
-
template =
|
|
197
|
-
|
|
198
|
-
return
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
244
|
+
template = "netbox_cisco_ise/endpoint_tab_content.html"
|
|
245
|
+
|
|
246
|
+
return HttpResponse(
|
|
247
|
+
render_to_string(
|
|
248
|
+
template,
|
|
249
|
+
{
|
|
250
|
+
"object": device,
|
|
251
|
+
"ise_data": ise_data,
|
|
252
|
+
"session_data": session_data,
|
|
253
|
+
"error": error,
|
|
254
|
+
"ise_url": ise_url,
|
|
255
|
+
},
|
|
256
|
+
request=request,
|
|
257
|
+
)
|
|
209
258
|
)
|
|
210
259
|
|
|
211
260
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: netbox-cisco-ise
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: NetBox plugin for Cisco ISE integration - endpoint tracking, NAD management, and session visibility
|
|
5
5
|
Author-email: sieteunoseis <jeremy.worden@gmail.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -273,6 +273,12 @@ Contributions are welcome! Please:
|
|
|
273
273
|
2. Create a feature branch
|
|
274
274
|
3. Submit a pull request
|
|
275
275
|
|
|
276
|
+
## Support
|
|
277
|
+
|
|
278
|
+
If you find this plugin helpful, consider supporting development:
|
|
279
|
+
|
|
280
|
+
[](https://buymeacoffee.com/automatebldrs)
|
|
281
|
+
|
|
276
282
|
## Related Projects
|
|
277
283
|
|
|
278
284
|
- [netbox-catalyst-center](https://github.com/sieteunoseis/netbox-catalyst-center) - Catalyst Center integration for NetBox
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
netbox_cisco_ise/__init__.py,sha256=mDr1fM2VKoi_YUH2qOAo-CjZIdODiteLZtD_G9O-hKc,2203
|
|
2
|
+
netbox_cisco_ise/ise_client.py,sha256=sfeC-cLv0JWo_NdxZG9M4VEGNyLYCVymB6iItGCUvH8,16403
|
|
3
|
+
netbox_cisco_ise/navigation.py,sha256=mMN4EyzZl6ehoqflLeYy7logt39wpCN2TEsPfqn1VtI,507
|
|
4
|
+
netbox_cisco_ise/urls.py,sha256=MOGyzDEUw4RTbRwceIfTdSw86THx18zJ0ZAUwt4LDKQ,419
|
|
5
|
+
netbox_cisco_ise/views.py,sha256=s63WhYdAlhzoiOgSEw8cjjzkUuoHTgIjdPgA_uh7TDQ,10253
|
|
6
|
+
netbox_cisco_ise/templates/netbox_cisco_ise/device_tab.html,sha256=H8xS-cwU-oOCS7Sgo3RMD01UDnqrIKR2KU3G-GyR9O0,775
|
|
7
|
+
netbox_cisco_ise/templates/netbox_cisco_ise/endpoint_tab.html,sha256=aS7R0oTjkUmqlP7i2zfrFhYs9F-LknJgcomGGK60u7w,9868
|
|
8
|
+
netbox_cisco_ise/templates/netbox_cisco_ise/endpoint_tab_content.html,sha256=Cn3JDGVLcUv7u9pgK867nDdLBPPdY1ViGsr5KewDuRE,9795
|
|
9
|
+
netbox_cisco_ise/templates/netbox_cisco_ise/nad_tab.html,sha256=Y5rCGyoSqzRmfridJp2BmC3HkPGRhvWdZ3Mp-C81qYk,8783
|
|
10
|
+
netbox_cisco_ise/templates/netbox_cisco_ise/nad_tab_content.html,sha256=0KyCQ1E1hTC3gD_T4FuC_Ll8cuPVVji_ofqCCrTJxHk,8710
|
|
11
|
+
netbox_cisco_ise/templates/netbox_cisco_ise/settings.html,sha256=ZsnJ_AclJUDa-h7vJTd5Clh7pc3OOBx6Tf9Y_bx3oIE,8953
|
|
12
|
+
netbox_cisco_ise-0.1.5.dist-info/licenses/LICENSE,sha256=KmjHs19UP3vo7K2IWXkq3JDKG9PatSbqeLPwu3o2k7g,10761
|
|
13
|
+
netbox_cisco_ise-0.1.5.dist-info/METADATA,sha256=Yz0WsrapLikpia7TOXU0IjfMWwHNtuue1ERqqQwrPS0,9099
|
|
14
|
+
netbox_cisco_ise-0.1.5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
15
|
+
netbox_cisco_ise-0.1.5.dist-info/top_level.txt,sha256=LMP1ppZRzqtdaMGzz53KgacW_PEwyLSM9wwIMuBvJ00,17
|
|
16
|
+
netbox_cisco_ise-0.1.5.dist-info/RECORD,,
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
netbox_cisco_ise/__init__.py,sha256=WMZdp2U38yCv64YTcahNqLfj86Q_-95mDx4OHnivgmU,2156
|
|
2
|
-
netbox_cisco_ise/ise_client.py,sha256=zUMakBU6lmSi-digrMxr6JKAbqyvBAPWz4l1eD8iDa8,16228
|
|
3
|
-
netbox_cisco_ise/navigation.py,sha256=mMN4EyzZl6ehoqflLeYy7logt39wpCN2TEsPfqn1VtI,507
|
|
4
|
-
netbox_cisco_ise/urls.py,sha256=3tJHJyEQXYZ2WXw4zq1kds7xpgyHl1-HwVHlgtJA84E,304
|
|
5
|
-
netbox_cisco_ise/views.py,sha256=XK9_nERmb38tA8c8yZG5ys7tuR44Qk_Ec-PPhAwbJ7I,8833
|
|
6
|
-
netbox_cisco_ise/templates/netbox_cisco_ise/endpoint_tab.html,sha256=aS7R0oTjkUmqlP7i2zfrFhYs9F-LknJgcomGGK60u7w,9868
|
|
7
|
-
netbox_cisco_ise/templates/netbox_cisco_ise/nad_tab.html,sha256=Y5rCGyoSqzRmfridJp2BmC3HkPGRhvWdZ3Mp-C81qYk,8783
|
|
8
|
-
netbox_cisco_ise/templates/netbox_cisco_ise/settings.html,sha256=ZsnJ_AclJUDa-h7vJTd5Clh7pc3OOBx6Tf9Y_bx3oIE,8953
|
|
9
|
-
netbox_cisco_ise-0.1.3.dist-info/licenses/LICENSE,sha256=KmjHs19UP3vo7K2IWXkq3JDKG9PatSbqeLPwu3o2k7g,10761
|
|
10
|
-
netbox_cisco_ise-0.1.3.dist-info/METADATA,sha256=Bi_HJz8hdDLwTztuNkiMKiBAokOxnz5jZ8on8QPmX7s,8859
|
|
11
|
-
netbox_cisco_ise-0.1.3.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
|
|
12
|
-
netbox_cisco_ise-0.1.3.dist-info/top_level.txt,sha256=LMP1ppZRzqtdaMGzz53KgacW_PEwyLSM9wwIMuBvJ00,17
|
|
13
|
-
netbox_cisco_ise-0.1.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|