netbox-vcenter-server 0.3.0__tar.gz → 0.4.0__tar.gz

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.
Files changed (23) hide show
  1. {netbox_vcenter_server-0.3.0/netbox_vcenter_server.egg-info → netbox_vcenter_server-0.4.0}/PKG-INFO +1 -1
  2. {netbox_vcenter_server-0.3.0 → netbox_vcenter_server-0.4.0}/netbox_vcenter/__init__.py +1 -1
  3. {netbox_vcenter_server-0.3.0 → netbox_vcenter_server-0.4.0}/netbox_vcenter/forms.py +12 -2
  4. {netbox_vcenter_server-0.3.0 → netbox_vcenter_server-0.4.0}/netbox_vcenter/templates/netbox_vcenter/dashboard.html +24 -3
  5. {netbox_vcenter_server-0.3.0 → netbox_vcenter_server-0.4.0}/netbox_vcenter/urls.py +1 -0
  6. {netbox_vcenter_server-0.3.0 → netbox_vcenter_server-0.4.0}/netbox_vcenter/views.py +49 -0
  7. {netbox_vcenter_server-0.3.0 → netbox_vcenter_server-0.4.0/netbox_vcenter_server.egg-info}/PKG-INFO +1 -1
  8. {netbox_vcenter_server-0.3.0 → netbox_vcenter_server-0.4.0}/pyproject.toml +1 -1
  9. {netbox_vcenter_server-0.3.0 → netbox_vcenter_server-0.4.0}/LICENSE +0 -0
  10. {netbox_vcenter_server-0.3.0 → netbox_vcenter_server-0.4.0}/README.md +0 -0
  11. {netbox_vcenter_server-0.3.0 → netbox_vcenter_server-0.4.0}/netbox_vcenter/api/__init__.py +0 -0
  12. {netbox_vcenter_server-0.3.0 → netbox_vcenter_server-0.4.0}/netbox_vcenter/api/urls.py +0 -0
  13. {netbox_vcenter_server-0.3.0 → netbox_vcenter_server-0.4.0}/netbox_vcenter/client.py +0 -0
  14. {netbox_vcenter_server-0.3.0 → netbox_vcenter_server-0.4.0}/netbox_vcenter/navigation.py +0 -0
  15. {netbox_vcenter_server-0.3.0 → netbox_vcenter_server-0.4.0}/netbox_vcenter/templates/netbox_vcenter/compare.html +0 -0
  16. {netbox_vcenter_server-0.3.0 → netbox_vcenter_server-0.4.0}/netbox_vcenter/templates/netbox_vcenter/import.html +0 -0
  17. {netbox_vcenter_server-0.3.0 → netbox_vcenter_server-0.4.0}/netbox_vcenter/templatetags/__init__.py +0 -0
  18. {netbox_vcenter_server-0.3.0 → netbox_vcenter_server-0.4.0}/netbox_vcenter/templatetags/vcenter_tags.py +0 -0
  19. {netbox_vcenter_server-0.3.0 → netbox_vcenter_server-0.4.0}/netbox_vcenter_server.egg-info/SOURCES.txt +0 -0
  20. {netbox_vcenter_server-0.3.0 → netbox_vcenter_server-0.4.0}/netbox_vcenter_server.egg-info/dependency_links.txt +0 -0
  21. {netbox_vcenter_server-0.3.0 → netbox_vcenter_server-0.4.0}/netbox_vcenter_server.egg-info/requires.txt +0 -0
  22. {netbox_vcenter_server-0.3.0 → netbox_vcenter_server-0.4.0}/netbox_vcenter_server.egg-info/top_level.txt +0 -0
  23. {netbox_vcenter_server-0.3.0 → netbox_vcenter_server-0.4.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: netbox-vcenter-server
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: NetBox plugin for viewing and importing VMs from VMware vCenter servers
5
5
  Author-email: sieteunoseis <jeremy.worden@gmail.com>
6
6
  License: Apache-2.0
@@ -7,7 +7,7 @@ Supports multiple vCenter servers with per-server caching and VM import to NetBo
7
7
 
8
8
  from netbox.plugins import PluginConfig
9
9
 
10
- __version__ = "0.3.0"
10
+ __version__ = "0.4.0"
11
11
 
12
12
 
13
13
  class VcenterConfig(PluginConfig):
@@ -5,6 +5,16 @@ from django.conf import settings
5
5
  from virtualization.models import Cluster
6
6
 
7
7
 
8
+ class ClusterChoiceField(forms.ModelChoiceField):
9
+ """Custom ModelChoiceField that displays Cluster Group > Cluster Name."""
10
+
11
+ def label_from_instance(self, obj):
12
+ """Return label in 'Group > Name' format."""
13
+ if obj.group:
14
+ return f"{obj.group.name} > {obj.name}"
15
+ return obj.name
16
+
17
+
8
18
  class VCenterConnectForm(forms.Form):
9
19
  """Form for connecting to a vCenter server."""
10
20
 
@@ -41,8 +51,8 @@ class VCenterConnectForm(forms.Form):
41
51
  class VMImportForm(forms.Form):
42
52
  """Form for importing VMs from vCenter to NetBox."""
43
53
 
44
- cluster = forms.ModelChoiceField(
45
- queryset=Cluster.objects.all(),
54
+ cluster = ClusterChoiceField(
55
+ queryset=Cluster.objects.select_related("group").order_by("group__name", "name"),
46
56
  label="Target Cluster",
47
57
  help_text="NetBox cluster to assign the imported VMs to",
48
58
  widget=forms.Select(attrs={"class": "form-select"}),
@@ -52,7 +52,13 @@
52
52
  </div>
53
53
  {% endif %}
54
54
 
55
- <form method="post">
55
+ <!-- Error/status messages from htmx -->
56
+ <div id="sync-status"></div>
57
+
58
+ <form hx-post="{% url 'plugins:netbox_vcenter:sync' %}"
59
+ hx-target="#sync-status"
60
+ hx-indicator="#sync-loading"
61
+ hx-disabled-elt="#sync-btn">
56
62
  {% csrf_token %}
57
63
 
58
64
  <div class="mb-3">
@@ -78,8 +84,14 @@
78
84
  <br><small class="text-muted">{{ form.verify_ssl.help_text }}</small>
79
85
  </div>
80
86
 
81
- <button type="submit" class="btn btn-primary">
82
- <i class="mdi mdi-sync"></i> Connect & Sync
87
+ <button type="submit" class="btn btn-primary" id="sync-btn">
88
+ <span id="sync-loading" class="htmx-indicator">
89
+ <span class="spinner-border spinner-border-sm me-1" role="status"></span>
90
+ Connecting...
91
+ </span>
92
+ <span class="sync-ready">
93
+ <i class="mdi mdi-sync"></i> Connect & Sync
94
+ </span>
83
95
  </button>
84
96
  </form>
85
97
  </div>
@@ -253,6 +265,15 @@
253
265
  </div>
254
266
  {% endif %}
255
267
 
268
+ <style>
269
+ /* Hide loading indicator by default, show when htmx is processing */
270
+ .htmx-indicator { display: none; }
271
+ .htmx-request .htmx-indicator { display: inline; }
272
+ .htmx-request .sync-ready { display: none; }
273
+ /* Disable form inputs while syncing */
274
+ .htmx-request input, .htmx-request select { pointer-events: none; opacity: 0.6; }
275
+ </style>
276
+
256
277
  <script>
257
278
  document.addEventListener('DOMContentLoaded', function() {
258
279
  const selectAll = document.getElementById('select-all');
@@ -6,6 +6,7 @@ from . import views
6
6
 
7
7
  urlpatterns = [
8
8
  path("", views.VCenterDashboardView.as_view(), name="dashboard"),
9
+ path("sync/", views.VCenterSyncView.as_view(), name="sync"),
9
10
  path("refresh/<str:server>/", views.VCenterRefreshView.as_view(), name="refresh"),
10
11
  path("import/", views.VMImportView.as_view(), name="import"),
11
12
  path("compare/", views.VMComparisonView.as_view(), name="compare"),
@@ -310,6 +310,55 @@ class VCenterRefreshView(View):
310
310
  return redirect(f"/plugins/vcenter/?server={server}")
311
311
 
312
312
 
313
+ class VCenterSyncView(View):
314
+ """Async sync endpoint for htmx - connects to vCenter and caches VMs."""
315
+
316
+ def post(self, request):
317
+ """Connect to vCenter and fetch VMs, return htmx response."""
318
+ from django.http import HttpResponse
319
+
320
+ form = VCenterConnectForm(request.POST)
321
+
322
+ if not form.is_valid():
323
+ return HttpResponse(
324
+ '<div class="alert alert-danger"><i class="mdi mdi-alert-circle"></i> '
325
+ "Invalid form data. Please check your inputs.</div>",
326
+ status=400,
327
+ )
328
+
329
+ server = form.cleaned_data["server"]
330
+ username = form.cleaned_data["username"]
331
+ password = form.cleaned_data["password"]
332
+ verify_ssl = form.cleaned_data.get("verify_ssl", False)
333
+
334
+ # Connect and fetch VMs (this is the slow part with Duo MFA)
335
+ vms, error = connect_and_fetch(server, username, password, verify_ssl)
336
+
337
+ if error:
338
+ return HttpResponse(
339
+ f'<div class="alert alert-danger"><i class="mdi mdi-alert-circle"></i> '
340
+ f"<strong>Connection Failed:</strong> {error}</div>",
341
+ status=400,
342
+ )
343
+
344
+ # Cache the data (no timeout - persists until manual refresh)
345
+ cache_data = {
346
+ "vms": vms,
347
+ "timestamp": timezone.now().isoformat(),
348
+ "server": server,
349
+ "count": len(vms),
350
+ }
351
+ cache.set(get_cache_key(server), cache_data, None)
352
+
353
+ # Add success message for the redirected page
354
+ messages.success(request, f"Successfully synced {len(vms)} VMs from {server}")
355
+
356
+ # Return HX-Redirect header to trigger page reload with new data
357
+ response = HttpResponse(status=200)
358
+ response["HX-Redirect"] = f"/plugins/vcenter/?server={server}"
359
+ return response
360
+
361
+
313
362
  class VMImportView(View):
314
363
  """Import selected VMs from vCenter to NetBox."""
315
364
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: netbox-vcenter-server
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: NetBox plugin for viewing and importing VMs from VMware vCenter servers
5
5
  Author-email: sieteunoseis <jeremy.worden@gmail.com>
6
6
  License: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "netbox-vcenter-server"
7
- version = "0.3.0"
7
+ version = "0.4.0"
8
8
  description = "NetBox plugin for viewing and importing VMs from VMware vCenter servers"
9
9
  readme = "README.md"
10
10
  license = {text = "Apache-2.0"}