From f08d7c459a9555d3791d4ab12d3610ac0304e70d Mon Sep 17 00:00:00 2001 From: Emerson Felipe Date: Tue, 26 Nov 2024 20:50:39 +0000 Subject: [PATCH 01/15] Add header to existent cards and start column testing. --- .../templates/netbox_proxbox/home.html | 93 ++++++++++++++++++- 1 file changed, 91 insertions(+), 2 deletions(-) diff --git a/netbox_proxbox/templates/netbox_proxbox/home.html b/netbox_proxbox/templates/netbox_proxbox/home.html index 0a8adae..9557430 100755 --- a/netbox_proxbox/templates/netbox_proxbox/home.html +++ b/netbox_proxbox/templates/netbox_proxbox/home.html @@ -303,7 +303,91 @@

- + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + +
+ Name + + Status + + Tenant + + Site + + Location + + Rack + + Role + + Manufacturer + + Type + + IP Address + + +
— No devices found —
+
+
+
+ +
+
+
+ + + + + + + + + + + + + + + + + +
+ Name + + Status +
TestOther test
TestOther test 2
+
+
+
+
+ +

Proxbox Configuration @@ -313,6 +397,7 @@

{% for px in configuration.netbox_proxbox.proxmox %}
+

Proxmox Cluster

@@ -435,13 +520,14 @@

{% endfor %}
+

Netbox

-
+
@@ -537,6 +623,8 @@

+

FastAPI (Backend Service)

+
@@ -599,6 +687,7 @@

+ From 565eb452278019f045925726b3759a2a6d5c77bf Mon Sep 17 00:00:00 2001 From: Emerson Felipe Date: Wed, 27 Nov 2024 01:33:44 +0000 Subject: [PATCH 02/15] Add virtual machine websocket message to console. TODO: represent it on GUI dynamically. Also new buttons of syncing added. --- .../routes/proxbox/clusters/__init__.py | 6 +- netbox_proxbox/main.py | 5 + .../templates/netbox_proxbox/home.html | 102 ++++++++++-------- 3 files changed, 66 insertions(+), 47 deletions(-) diff --git a/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py b/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py index ef4515c..59348f2 100755 --- a/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py +++ b/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py @@ -502,12 +502,12 @@ class VirtualMachineStatus(Enum): created_virtual_machines: list = [] - devices: dict = {} clusters: dict = {} + for vm in virtual_machines: - - print(f"\n[VM] {vm}\n") + print(f"\n\n\n[VM] {vm}\n\n\n") + await websocket.send_json(vm) vm_node: str = vm.get("node") print(f"vm_node: {vm_node} | {type(vm_node)}") diff --git a/netbox_proxbox/main.py b/netbox_proxbox/main.py index bfa6337..8fb5a33 100755 --- a/netbox_proxbox/main.py +++ b/netbox_proxbox/main.py @@ -173,6 +173,11 @@ async def websocket_endpoint( await get_nodes(nb=nb, pxs=pxs, websocket=websocket) await get_virtual_machines(nb=nb, pxs=pxs, websocket=websocket) + if data == "Sync Nodes": + await get_nodes(nb=nb, pxs=pxs, websocket=websocket) + + if data == "Sync Virtual Machines": + await get_virtual_machines(nb=nb, pxs=pxs, websocket=websocket) #await websocket.send_text(f"Message text was: {data}") diff --git a/netbox_proxbox/templates/netbox_proxbox/home.html b/netbox_proxbox/templates/netbox_proxbox/home.html index 9557430..6a42879 100755 --- a/netbox_proxbox/templates/netbox_proxbox/home.html +++ b/netbox_proxbox/templates/netbox_proxbox/home.html @@ -14,12 +14,20 @@ ws.onmessage = function(event) { // Add WebSockets Messages came from FasstAPI backend on GUI + console.log(event.data) + + try { + var jsonMessage = JSON.parse(event.data) + console.log(jsonMessage) + } catch (error) { + // do nothing + } var messages = document.getElementById('messages') var message = document.createElement('li') message.style.lineHeight = '170%' - + message.innerHTML = event.data messages.appendChild(message) @@ -73,12 +81,22 @@ } - function sendMessage(event) { + function fullUpdate(event) { // Send Websocket Message ws.send("Start") event.preventDefault() } + function syncNodes(event) { + ws.send("Sync Nodes") + event.preventDefault() + } + + function syncVirtualMachines(event) { + ws.send("Sync Virtual Machines") + event.preventDefault() + } + async function FastAPIConnectionTest(fastapiEndpoint) { let fastapi_docs_endpoint = `${fastapiEndpoint}/docs` @@ -292,26 +310,36 @@
{# Full Update Button#}
-
+ {% if perms.netbox_proxbox.add_proxmoxvm %} - - - + + {% endif %} + +
+ {% if perms.netbox_proxbox.add_proxmoxvm %} + + {% endif %} + +
+ {% if perms.netbox_proxbox.add_proxmoxvm %} + {% endif %}
-

+

Virtual Machines

- + @@ -319,25 +347,25 @@ Status - - - - -
+ VM ID + Name - Tenant + Role - Site + Cluster - Location + Cluster Type - Rack + Device - Role + Virtual CPUs - Manufacturer + Memory - Type + Disk Space IP Address @@ -348,38 +376,24 @@
— No devices found —
-
-
-
- -
-
-
- - - - + + + + + + + + + + + - - - - - - - - - +
- Name - - Status - 100100100100100100100100100100100
TestOther test
TestOther test 2
From 4cbde272eb8c53f28b40a68b22b3d9ef46bbdb06 Mon Sep 17 00:00:00 2001 From: Emerson Felipe Date: Wed, 27 Nov 2024 14:42:46 +0000 Subject: [PATCH 03/15] New Virtual Machine VM table being populated via websocket with raw Proxmox API response --- .../routes/proxbox/clusters/__init__.py | 2 +- .../templates/netbox_proxbox/home.html | 106 ++++++++++++++---- 2 files changed, 88 insertions(+), 20 deletions(-) diff --git a/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py b/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py index 59348f2..03db4ce 100755 --- a/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py +++ b/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py @@ -507,7 +507,7 @@ class VirtualMachineStatus(Enum): for vm in virtual_machines: print(f"\n\n\n[VM] {vm}\n\n\n") - await websocket.send_json(vm) + await websocket.send_json({'type': 'virtual_machine', 'data': vm}) vm_node: str = vm.get("node") print(f"vm_node: {vm_node} | {type(vm_node)}") diff --git a/netbox_proxbox/templates/netbox_proxbox/home.html b/netbox_proxbox/templates/netbox_proxbox/home.html index 6a42879..dde360e 100755 --- a/netbox_proxbox/templates/netbox_proxbox/home.html +++ b/netbox_proxbox/templates/netbox_proxbox/home.html @@ -10,28 +10,109 @@ const websocketEndpoint = `wss://${uvicornHost}:${uvicornPort}/ws` // Instantiate WebSocket connection to FastAPI backend. - var ws = new WebSocket(websocketEndpoint); + let ws = new WebSocket(websocketEndpoint); ws.onmessage = function(event) { // Add WebSockets Messages came from FasstAPI backend on GUI console.log(event.data) + // Get Virtual Machine Table + let virtualMachineTable = document.getElementById('virtual-machine-table-data') + + if (!virtualMachineTable) { + console.log("Virtual Machine Table not found.") + return + } + try { - var jsonMessage = JSON.parse(event.data) + let jsonMessage = JSON.parse(event.data) + if (jsonMessage.type == "virtual_machine") { + console.log(jsonMessage.data) + console.log('test') + + console.log(virtualMachineTable) + + // Create Table Row + let vmTableRow = document.createElement('tr') + + // Create Table Data + let vmIdData = document.createElement('td') + vmIdData.id = `vm-${jsonMessage.data.name}-id-data` + vmIdData.innerHTML = jsonMessage.data.vmid + vmTableRow.appendChild(vmIdData) + + let vmNameData = document.createElement('td') + vmNameData.id = `vm-${jsonMessage.data.name}-name-data` + vmNameData.innerHTML = jsonMessage.data.name + vmTableRow.appendChild(vmNameData) + + let vmStatusData = document.createElement('td') + vmStatusData.id = `vm-${jsonMessage.data.name}-status-data` + vmStatusData.innerHTML = jsonMessage.data.status + vmTableRow.appendChild(vmStatusData) + + let vmRoleData = document.createElement('td') + vmRoleData.id = `vm-${jsonMessage.data.name}-role-data` + vmRoleData.innerHTML = "Undefined" + vmTableRow.appendChild(vmRoleData) + + let vmClusterData = document.createElement('td') + vmClusterData.id = `vm-${jsonMessage.data.name}-cluster-data` + vmClusterData.innerHTML = "Undefined" + vmTableRow.appendChild(vmClusterData) + + let vmClusterTypeData = document.createElement('td') + vmClusterTypeData.id = `vm-${jsonMessage.data.name}-cluster-type-data` + vmClusterTypeData.innerHTML = "Undefined" + vmTableRow.appendChild(vmClusterTypeData) + + let vmDeviceData = document.createElement('td') + vmDeviceData.id = `vm-${jsonMessage.data.name}-device-data` + vmDeviceData.innerHTML = jsonMessage.data.node + vmTableRow.appendChild(vmDeviceData) + + let vmVirtualCPUsData = document.createElement('td') + vmVirtualCPUsData.id = `vm-${jsonMessage.data.name}-virtual-cpus-data` + vmVirtualCPUsData.innerHTML = jsonMessage.data.maxcpu + vmTableRow.appendChild(vmVirtualCPUsData) + + let vmMemoryData = document.createElement('td') + vmMemoryData.id = `vm-${jsonMessage.data.name}-memory-data` + vmMemoryData.innerHTML = Math.ceil(jsonMessage.data.maxmem / 1000000) + vmTableRow.appendChild(vmMemoryData) + + let vmDiskSpaceData = document.createElement('td') + vmDiskSpaceData.id = `vm-${jsonMessage.data.name}-disk-space-data` + vmDiskSpaceData.innerHTML = Math.ceil(jsonMessage.data.maxdisk / 1000000) + vmTableRow.appendChild(vmDiskSpaceData) + + let vmIPAddressData = document.createElement('td') + vmIPAddressData.id = `vm-${jsonMessage.data.name}-ip-address-data` + vmIPAddressData.innerHTML = "Undefined" + vmTableRow.appendChild(vmIPAddressData) + + + virtualMachineTable.appendChild(vmTableRow) + console.log(virtualMachineTable) + + console.log(vmTableRow) + } + console.log(jsonMessage) } catch (error) { // do nothing + console.log(error) } - var messages = document.getElementById('messages') - var message = document.createElement('li') + let messages = document.getElementById('messages') + let message = document.createElement('li') message.style.lineHeight = '170%' message.innerHTML = event.data messages.appendChild(message) - var test = document.getElementById('scrollable-div') + let test = document.getElementById('scrollable-div') test.scrollTop = test.scrollHeight }; @@ -375,20 +456,7 @@

Virtual Machines

- - - 100 - 100 - 100 - 100 - 100 - 100 - 100 - 100 - 100 - 100 - 100 - + + +
+ +
+ + + + + +
+ + +
+ + Netbox Logo + + +
+ + +
+ Unknown +
+ +
+ +
+
+

This service MUST be running to Proxbox work.
It's the backend which communicates with Proxmox Clusters.

+
+ +
+ + + + + + {% if configuration.netbox_proxbox.fastapi.uvicorn_host %} + + {% else %} + + {% endif %} + + + + {% if configuration.netbox_proxbox.fastapi.uvicorn_port %} + + {% else %} + + {% endif %} + +
Uvicorn Host{{ configuration.netbox_proxbox.fastapi.uvicorn_host }}{{ default_config.fastapi.uvicorn_host }} (default)
Uvicorn Port{{ configuration.netbox_proxbox.fastapi.uvicorn_port }}{{ default_config.fastapi.uvicorn_port }} (default)
+
+ +
+ +
+
+
+
+
\ No newline at end of file diff --git a/netbox_proxbox/templates/netbox_proxbox/home.html b/netbox_proxbox/templates/netbox_proxbox/home.html index 13ef258..26e81e9 100755 --- a/netbox_proxbox/templates/netbox_proxbox/home.html +++ b/netbox_proxbox/templates/netbox_proxbox/home.html @@ -44,6 +44,9 @@ virtualMachinesDiv.style.display = "block" + let vmTableDefaultTd = document.getElementById('virtual-machines-table-default-td') + vmTableDefaultTd.style.display = "none" + console.log(jsonMessage.data) console.log('test') @@ -445,67 +448,7 @@

-
-

Virtual Machines

-
-
-
- - - - - - - - - - - - - - - - - - - - - -
- Netbox ID - - VM/CT ID - - Name - - Status - - Device - - Cluster - - Cluster Type - - Role - - Virtual CPUs - - Memory (MB) - - Disk Space (GB) - - IP Address - - -
-
-
-
-
+ {% include "netbox_proxbox/virtual_machines_table.html" %}
@@ -514,334 +457,14 @@

+ {% for px in configuration.netbox_proxbox.proxmox %} -
-
-

Proxmox Cluster

- -
- -
-
- - -
- - Proxmox Logo - -
- - -
- Unknown -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - {% if px.domain %} - - {% else %} - - {% endif %} - - - - {% if px.http_port %} - - {% else %} - - {% endif %} - - - - {% if px.user %} - - {% else %} - - {% endif %} - - - - {% if px.password %} - - {% else %} - - {% endif %} - - - {% if px.token.name %} - - {% else %} - - {% endif %} - - - - - - - {% if px.ssl %} - - {% else %} - - {% endif %} - -
Proxmox Cluster Name - - Empty - -
Proxmox Cluster Mode - - Empty - -
Proxmox Version - - Empty - -
Proxmox RepoID - - Empty - -
Domain / IP{{ px.domain }}{{ default_config.proxmox.domain }} (default)
HTTP Port{{ px.http_port }}{{ default_config.proxmox.http_port }} (default)
Proxmox User{{ px.user }}{{ default_config.proxmox.user }} (default)
Proxmox Passwordpassword defined in configuration.py(secret) (default)
Token Name{{ px.token.name }}{{ default_config.proxmox.token.name }} (default)
Token Value(secret)
SSL{{ px.ssl }}{{ default_config.proxmox.ssl }} (default)
-
- -
-
-
-
+ {% include "netbox_proxbox/proxmox_card.html" %} {% endfor %} -
-
-

Netbox

- - - -
- -
-
- - -
- - Netbox Logo - - -
- - -
- Unknown -
- -
- -
- - - - - {% if configuration.netbox_proxbox.netbox.domain %} - - {% else %} - - {% endif %} - - - - {% if configuration.netbox_proxbox.netbox.http_port %} - - {% else %} - - {% endif %} - - - - {% if configuration.netbox_proxbox.netbox.token %} - - {% else %} - - {% endif %} - - - - - - - - - - - - - - - - - -
Domain / IP{{ configuration.netbox_proxbox.netbox.domain }} {{ default_config.netbox.domain }} (default)
HTTP Port{{ configuration.netbox_proxbox.netbox.http_port }}{{ default_config.netbox.http_port }} (default)
Token - - Token will not show here for security. - - {{ default_config.netbox.token }} (default)
Netbox Version - - Empty - -
Python Version - - Empty - -
Django Version - - Empty - -
Netbox Proxbox Version - - Empty - -
-
- -
-
-
-
-
-
-

FastAPI (Backend Service)

- - - -
- -
- - - - - -
- - -
- - Netbox Logo - - -
- - -
- Unknown -
- -
- -
-
-

This service MUST be running to Proxbox work.
It's the backend which communicates with Proxmox Clusters.

-
- -
- - - - - - {% if configuration.netbox_proxbox.fastapi.uvicorn_host %} - - {% else %} - - {% endif %} - - - - {% if configuration.netbox_proxbox.fastapi.uvicorn_port %} - - {% else %} - - {% endif %} - -
Uvicorn Host{{ configuration.netbox_proxbox.fastapi.uvicorn_host }}{{ default_config.fastapi.uvicorn_host }} (default)
Uvicorn Port{{ configuration.netbox_proxbox.fastapi.uvicorn_port }}{{ default_config.fastapi.uvicorn_port }} (default)
-
- -
- -
-
-
-
-
+ {% include "netbox_proxbox/netbox_card.html" %} + + {% include "netbox_proxbox/fastapi_card.html" %}
diff --git a/netbox_proxbox/templates/netbox_proxbox/netbox_card.html b/netbox_proxbox/templates/netbox_proxbox/netbox_card.html new file mode 100644 index 0000000..9fa2a58 --- /dev/null +++ b/netbox_proxbox/templates/netbox_proxbox/netbox_card.html @@ -0,0 +1,105 @@ +{% load static %} + +
+
+

Netbox

+ + + +
+ +
+
+ + +
+ + Netbox Logo + + +
+ + +
+ Unknown +
+ +
+ + +
+ + + + + {% if configuration.netbox_proxbox.netbox.domain %} + + {% else %} + + {% endif %} + + + + {% if configuration.netbox_proxbox.netbox.http_port %} + + {% else %} + + {% endif %} + + + + {% if configuration.netbox_proxbox.netbox.token %} + + {% else %} + + {% endif %} + + + + + + + + + + + + + + + + + +
Domain / IP{{ configuration.netbox_proxbox.netbox.domain }} {{ default_config.netbox.domain }} (default)
HTTP Port{{ configuration.netbox_proxbox.netbox.http_port }}{{ default_config.netbox.http_port }} (default)
Token + + Token will not show here for security. + + {{ default_config.netbox.token }} (default)
Netbox Version + + Empty + +
Python Version + + Empty + +
Django Version + + Empty + +
Netbox Proxbox Version + + Empty + +
+
+ +
+
+
+
\ No newline at end of file diff --git a/netbox_proxbox/templates/netbox_proxbox/proxmox_card.html b/netbox_proxbox/templates/netbox_proxbox/proxmox_card.html new file mode 100644 index 0000000..b15cbd3 --- /dev/null +++ b/netbox_proxbox/templates/netbox_proxbox/proxmox_card.html @@ -0,0 +1,124 @@ +{% load static %} + +
+
+

Proxmox Cluster

+ +
+ +
+
+ + +
+ + Proxmox Logo + +
+ + +
+ Unknown +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + {% if px.domain %} + + {% else %} + + {% endif %} + + + + {% if px.http_port %} + + {% else %} + + {% endif %} + + + + {% if px.user %} + + {% else %} + + {% endif %} + + + + {% if px.password %} + + {% else %} + + {% endif %} + + + {% if px.token.name %} + + {% else %} + + {% endif %} + + + + + + + {% if px.ssl %} + + {% else %} + + {% endif %} + +
Proxmox Cluster Name + + Empty + +
Proxmox Cluster Mode + + Empty + +
Proxmox Version + + Empty + +
Proxmox RepoID + + Empty + +
Domain / IP{{ px.domain }}{{ default_config.proxmox.domain }} (default)
HTTP Port{{ px.http_port }}{{ default_config.proxmox.http_port }} (default)
Proxmox User{{ px.user }}{{ default_config.proxmox.user }} (default)
Proxmox Passwordpassword defined in configuration.py(secret) (default)
Token Name{{ px.token.name }}{{ default_config.proxmox.token.name }} (default)
Token Value(secret)
SSL{{ px.ssl }}{{ default_config.proxmox.ssl }} (default)
+
+ +
+
+
+
\ No newline at end of file diff --git a/netbox_proxbox/templates/netbox_proxbox/virtual_machines.html b/netbox_proxbox/templates/netbox_proxbox/virtual_machines.html new file mode 100644 index 0000000..2dbc2ff --- /dev/null +++ b/netbox_proxbox/templates/netbox_proxbox/virtual_machines.html @@ -0,0 +1,11 @@ +{% extends 'base/layout.html' %} +{% load static %} + +{% block content %} +
+

+ Proxmox Virtual Machines and Containers +

+
+ {% include "netbox_proxbox/virtual_machines_table.html" %} +{% endblock %} \ No newline at end of file diff --git a/netbox_proxbox/templates/netbox_proxbox/virtual_machines_table.html b/netbox_proxbox/templates/netbox_proxbox/virtual_machines_table.html new file mode 100644 index 0000000..3087cdb --- /dev/null +++ b/netbox_proxbox/templates/netbox_proxbox/virtual_machines_table.html @@ -0,0 +1,60 @@ +
+

Virtual Machines

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ Netbox ID + + VM/CT ID + + Name + + Status + + Device + + Cluster + + Cluster Type + + Role + + Virtual CPUs + + Memory (MB) + + Disk Space (GB) + + IP Address + + +
— No virtual machines could be synced with Proxmox —
+
+
+
+
\ No newline at end of file diff --git a/netbox_proxbox/urls.py b/netbox_proxbox/urls.py index e65640c..fcfec15 100755 --- a/netbox_proxbox/urls.py +++ b/netbox_proxbox/urls.py @@ -5,6 +5,7 @@ urlpatterns = [ # Home View path('', views.HomeView.as_view(), name='home'), + path('virtual_machines', views.VirtualMachinesView.as_view(), name='virtual_machines'), path('contributing/', views.ContributingView.as_view(), name='contributing'), path('community/', views.CommunityView.as_view(), name='community'), diff --git a/netbox_proxbox/views.py b/netbox_proxbox/views.py index f7ba4c1..f823a88 100755 --- a/netbox_proxbox/views.py +++ b/netbox_proxbox/views.py @@ -60,6 +60,15 @@ def get(self, request): ) +class VirtualMachinesView(View): + template = 'netbox_proxbox/virtual_machines.html' + + def get(self, request): + return render( + request, + self.template + ) + class ContributingView(View): """ **ContributingView** handles the rendering of the contributing page for the Proxbox project. From 15b4d78f554230c61e02bd93695ab0be08a92124 Mon Sep 17 00:00:00 2001 From: Emerson Felipe Date: Thu, 28 Nov 2024 01:25:18 +0000 Subject: [PATCH 06/15] Add 'templates/netbox_proxbox/home' folder to organize Plugin Homepage --- .../templates/netbox_proxbox/home.html | 59 ++----------------- .../{ => home}/fastapi_card.html | 0 .../netbox_proxbox/home/json_config.html | 43 ++++++++++++++ .../netbox_proxbox/home/log_messages.html | 8 +++ .../{ => home}/netbox_card.html | 0 .../{ => home}/proxmox_card.html | 0 6 files changed, 56 insertions(+), 54 deletions(-) rename netbox_proxbox/templates/netbox_proxbox/{ => home}/fastapi_card.html (100%) create mode 100644 netbox_proxbox/templates/netbox_proxbox/home/json_config.html create mode 100644 netbox_proxbox/templates/netbox_proxbox/home/log_messages.html rename netbox_proxbox/templates/netbox_proxbox/{ => home}/netbox_card.html (100%) rename netbox_proxbox/templates/netbox_proxbox/{ => home}/proxmox_card.html (100%) diff --git a/netbox_proxbox/templates/netbox_proxbox/home.html b/netbox_proxbox/templates/netbox_proxbox/home.html index 26e81e9..1173aab 100755 --- a/netbox_proxbox/templates/netbox_proxbox/home.html +++ b/netbox_proxbox/templates/netbox_proxbox/home.html @@ -459,66 +459,17 @@

{% for px in configuration.netbox_proxbox.proxmox %} - {% include "netbox_proxbox/proxmox_card.html" %} + {% include "netbox_proxbox/home/proxmox_card.html" %} {% endfor %} - {% include "netbox_proxbox/netbox_card.html" %} + {% include "netbox_proxbox/home/netbox_card.html" %} - {% include "netbox_proxbox/fastapi_card.html" %} + {% include "netbox_proxbox/home/fastapi_card.html" %}

- -

Log Messages

-
-
-
    -
-
-
+ {% include "netbox_proxbox/home/log_messages.html" %} - -

- -

-
-
-
-
-
- -

Configuration (PLUGINS_CONFIG)

-
- -
-
{{ configuration_json }}
-
-
-
- -
-
-
- -

Default Config

-
- -
-
{{ default_config_json }}
-
-
-
-
-
+ {% include "netbox_proxbox/home/json_config.html" %}
{% endblock %} diff --git a/netbox_proxbox/templates/netbox_proxbox/fastapi_card.html b/netbox_proxbox/templates/netbox_proxbox/home/fastapi_card.html similarity index 100% rename from netbox_proxbox/templates/netbox_proxbox/fastapi_card.html rename to netbox_proxbox/templates/netbox_proxbox/home/fastapi_card.html diff --git a/netbox_proxbox/templates/netbox_proxbox/home/json_config.html b/netbox_proxbox/templates/netbox_proxbox/home/json_config.html new file mode 100644 index 0000000..a3b5023 --- /dev/null +++ b/netbox_proxbox/templates/netbox_proxbox/home/json_config.html @@ -0,0 +1,43 @@ + +

+ +

+
+
+
+
+
+ +

Configuration (PLUGINS_CONFIG)

+
+ +
+
{{ configuration_json }}
+
+
+
+ +
+
+
+ +

Default Config

+
+ +
+
{{ default_config_json }}
+
+
+
+
+
\ No newline at end of file diff --git a/netbox_proxbox/templates/netbox_proxbox/home/log_messages.html b/netbox_proxbox/templates/netbox_proxbox/home/log_messages.html new file mode 100644 index 0000000..2e2db9f --- /dev/null +++ b/netbox_proxbox/templates/netbox_proxbox/home/log_messages.html @@ -0,0 +1,8 @@ + +

Log Messages

+
+
+
    +
+
+
\ No newline at end of file diff --git a/netbox_proxbox/templates/netbox_proxbox/netbox_card.html b/netbox_proxbox/templates/netbox_proxbox/home/netbox_card.html similarity index 100% rename from netbox_proxbox/templates/netbox_proxbox/netbox_card.html rename to netbox_proxbox/templates/netbox_proxbox/home/netbox_card.html diff --git a/netbox_proxbox/templates/netbox_proxbox/proxmox_card.html b/netbox_proxbox/templates/netbox_proxbox/home/proxmox_card.html similarity index 100% rename from netbox_proxbox/templates/netbox_proxbox/proxmox_card.html rename to netbox_proxbox/templates/netbox_proxbox/home/proxmox_card.html From 7cf0090c515a8b86ef54ffc279c30b45e839ef2a Mon Sep 17 00:00:00 2001 From: Emerson Felipe Date: Thu, 28 Nov 2024 01:28:35 +0000 Subject: [PATCH 07/15] Move homepage vanilla javascript to other template file --- .../templates/netbox_proxbox/home.html | 425 +----------------- .../templates/netbox_proxbox/home/script.html | 421 +++++++++++++++++ 2 files changed, 424 insertions(+), 422 deletions(-) create mode 100644 netbox_proxbox/templates/netbox_proxbox/home/script.html diff --git a/netbox_proxbox/templates/netbox_proxbox/home.html b/netbox_proxbox/templates/netbox_proxbox/home.html index 1173aab..dc1aaee 100755 --- a/netbox_proxbox/templates/netbox_proxbox/home.html +++ b/netbox_proxbox/templates/netbox_proxbox/home.html @@ -2,427 +2,8 @@ {% load static %} {% block content %} - + {% include "netbox_proxbox/home/script.html" %} +
@@ -477,4 +58,4 @@

{{ block.super }} {% include "netbox_proxbox/footer.html" %} -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/netbox_proxbox/templates/netbox_proxbox/home/script.html b/netbox_proxbox/templates/netbox_proxbox/home/script.html new file mode 100644 index 0000000..547ca1f --- /dev/null +++ b/netbox_proxbox/templates/netbox_proxbox/home/script.html @@ -0,0 +1,421 @@ + \ No newline at end of file From fd2c000641bfc61c7f848f2f7251560e6fc7197c Mon Sep 17 00:00:00 2001 From: Emerson Felipe Date: Thu, 28 Nov 2024 02:24:03 +0000 Subject: [PATCH 08/15] Added new plugin page (view) for 'Virtual Machine' Updates. --- netbox_proxbox/main.py | 41 +++++- .../templates/netbox_proxbox/home.html | 4 +- .../templates/netbox_proxbox/home/script.html | 115 +---------------- .../netbox_proxbox/virtual_machines.html | 70 +++++++++- .../virtual_machines_table/script.html | 121 ++++++++++++++++++ .../table.html} | 0 netbox_proxbox/views.py | 7 +- 7 files changed, 238 insertions(+), 120 deletions(-) create mode 100644 netbox_proxbox/templates/netbox_proxbox/virtual_machines_table/script.html rename netbox_proxbox/templates/netbox_proxbox/{virtual_machines_table.html => virtual_machines_table/table.html} (100%) diff --git a/netbox_proxbox/main.py b/netbox_proxbox/main.py index 8fb5a33..f91b413 100755 --- a/netbox_proxbox/main.py +++ b/netbox_proxbox/main.py @@ -155,6 +155,7 @@ async def proxmoxer_exception_handler(request: Request, exc: ProxboxException): } ) +from netbox_proxbox.backend.routes.proxbox.clusters import get_nodes, get_virtual_machines @app.websocket("/ws") async def websocket_endpoint( @@ -164,23 +165,51 @@ async def websocket_endpoint( ): await websocket.accept() - from netbox_proxbox.backend.routes.proxbox.clusters import get_nodes, get_virtual_machines - while True: data = await websocket.receive_text() if data == "Start": await get_nodes(nb=nb, pxs=pxs, websocket=websocket) await get_virtual_machines(nb=nb, pxs=pxs, websocket=websocket) - + await websocket.close() + if data == "Sync Nodes": await get_nodes(nb=nb, pxs=pxs, websocket=websocket) + await websocket.close() if data == "Sync Virtual Machines": await get_virtual_machines(nb=nb, pxs=pxs, websocket=websocket) - - #await websocket.send_text(f"Message text was: {data}") - + await websocket.close() + + else: + print("Invalid command.") + await websocket.send_text("Invalid command.") + await websocket.close() + + await websocket.close() + + + +@app.websocket("/ws/virtual-machine") +async def websocket_vm_endpoint( + nb: NetboxSessionDep, + pxs: ProxmoxSessionsDep, + websocket: WebSocket +): + await websocket.accept() + + while True: + data = await websocket.receive_text() + + if data == "Sync Virtual Machines": + await get_virtual_machines(nb=nb, pxs=pxs, websocket=websocket) + await websocket.close() + + else: + print("Invalid command.") + await websocket.send_text("Invalid command.") + await websocket.close() + # diff --git a/netbox_proxbox/templates/netbox_proxbox/home.html b/netbox_proxbox/templates/netbox_proxbox/home.html index dc1aaee..c989357 100755 --- a/netbox_proxbox/templates/netbox_proxbox/home.html +++ b/netbox_proxbox/templates/netbox_proxbox/home.html @@ -3,7 +3,7 @@ {% block content %} {% include "netbox_proxbox/home/script.html" %} - +
@@ -29,7 +29,7 @@

- {% include "netbox_proxbox/virtual_machines_table.html" %} + {% include "netbox_proxbox/virtual_machines_table/table.html" %}
diff --git a/netbox_proxbox/templates/netbox_proxbox/home/script.html b/netbox_proxbox/templates/netbox_proxbox/home/script.html index 547ca1f..c8da66c 100644 --- a/netbox_proxbox/templates/netbox_proxbox/home/script.html +++ b/netbox_proxbox/templates/netbox_proxbox/home/script.html @@ -1,3 +1,6 @@ + +{% include "netbox_proxbox/virtual_machines_table/script.html" %} + + +

Proxmox Virtual Machines and Containers

- {% include "netbox_proxbox/virtual_machines_table.html" %} + +
+
+ {% if perms.netbox_proxbox.add_proxmoxvm %} + + {% endif %} +
+
+ {% include "netbox_proxbox/virtual_machines_table/table.html" %} {% endblock %} \ No newline at end of file diff --git a/netbox_proxbox/templates/netbox_proxbox/virtual_machines_table/script.html b/netbox_proxbox/templates/netbox_proxbox/virtual_machines_table/script.html new file mode 100644 index 0000000..4b79ff2 --- /dev/null +++ b/netbox_proxbox/templates/netbox_proxbox/virtual_machines_table/script.html @@ -0,0 +1,121 @@ + diff --git a/netbox_proxbox/templates/netbox_proxbox/virtual_machines_table.html b/netbox_proxbox/templates/netbox_proxbox/virtual_machines_table/table.html similarity index 100% rename from netbox_proxbox/templates/netbox_proxbox/virtual_machines_table.html rename to netbox_proxbox/templates/netbox_proxbox/virtual_machines_table/table.html diff --git a/netbox_proxbox/views.py b/netbox_proxbox/views.py index f823a88..fde09f7 100755 --- a/netbox_proxbox/views.py +++ b/netbox_proxbox/views.py @@ -64,9 +64,14 @@ class VirtualMachinesView(View): template = 'netbox_proxbox/virtual_machines.html' def get(self, request): + plugin_configuration = configuration.PLUGINS_CONFIG + return render( request, - self.template + self.template, + { + "configuration": plugin_configuration + } ) class ContributingView(View): From b6051a549150e96253eda2d56d6e08ca36f77fe4 Mon Sep 17 00:00:00 2001 From: Emerson Felipe Date: Thu, 28 Nov 2024 02:29:05 +0000 Subject: [PATCH 09/15] Added 'prelaunch' status for possible VM status on 'VirtualMachineStatus(Enum)' --- netbox_proxbox/backend/routes/proxbox/clusters/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py b/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py index 03db4ce..8b7f5de 100755 --- a/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py +++ b/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py @@ -490,6 +490,7 @@ class VirtualMachineStatus(Enum): """ active = "running" offline = "stopped" + prelaunch = "prelaunch" result = [] From 49cc9b594e5c3cc378098f94371df80ad322f800 Mon Sep 17 00:00:00 2001 From: Emerson Felipe Date: Thu, 28 Nov 2024 12:55:39 +0000 Subject: [PATCH 10/15] Minimize code repetition on Virtual Machines Table --- .../routes/proxbox/clusters/__init__.py | 21 +++-- .../netbox_proxbox/devices_table/script.html | 0 .../netbox_proxbox/devices_table/table.html | 0 .../templates/netbox_proxbox/home/script.html | 2 +- .../netbox_proxbox/virtual_machines.html | 6 +- .../virtual_machines_table/script.html | 92 ++++++------------- .../virtual_machines_table/table.html | 3 + 7 files changed, 52 insertions(+), 72 deletions(-) create mode 100644 netbox_proxbox/templates/netbox_proxbox/devices_table/script.html create mode 100644 netbox_proxbox/templates/netbox_proxbox/devices_table/table.html diff --git a/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py b/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py index 8b7f5de..1cf7977 100755 --- a/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py +++ b/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py @@ -508,7 +508,9 @@ class VirtualMachineStatus(Enum): for vm in virtual_machines: print(f"\n\n\n[VM] {vm}\n\n\n") - await websocket.send_json({'type': 'virtual_machine', 'data': vm}) + + # Creates a new row in table for each virtual machine + await websocket.send_json({'object': 'virtual_machine', 'type': 'create', 'data': vm}) vm_node: str = vm.get("node") print(f"vm_node: {vm_node} | {type(vm_node)}") @@ -517,13 +519,20 @@ class VirtualMachineStatus(Enum): Get Device from Netbox based on Proxmox Node Name only if it's not already in the devices dict This way we are able to minimize the number of requests to Netbox API """ - if devices.get(vm_node) is None: - devices[vm_node] = await Device(nb = nb, websocket = websocket).get(name = vm.get("node")) - - device = devices[vm_node] - print(f"devices[vm_node]: {devices[vm_node]} | {device}") + device = None + if devices.get(vm_node) is None: + # Try to get the device from Netbox, if not found, create it + device = await Device(nb = nb, websocket = websocket).get(name = vm.get("node")) + + if device: + devices[vm_node] = device + + print(f'device: {device}') + + await websocket.send_json({'object': 'device', 'type': 'create', 'data': device}) + """ Get Cluster from Netbox based on Cluster Name only if it's not already in the devices dict This way we are able to minimize the number of requests to Netbox API diff --git a/netbox_proxbox/templates/netbox_proxbox/devices_table/script.html b/netbox_proxbox/templates/netbox_proxbox/devices_table/script.html new file mode 100644 index 0000000..e69de29 diff --git a/netbox_proxbox/templates/netbox_proxbox/devices_table/table.html b/netbox_proxbox/templates/netbox_proxbox/devices_table/table.html new file mode 100644 index 0000000..e69de29 diff --git a/netbox_proxbox/templates/netbox_proxbox/home/script.html b/netbox_proxbox/templates/netbox_proxbox/home/script.html index c8da66c..1c6d85a 100644 --- a/netbox_proxbox/templates/netbox_proxbox/home/script.html +++ b/netbox_proxbox/templates/netbox_proxbox/home/script.html @@ -25,7 +25,7 @@ } if (jsonMessage) { - if (jsonMessage.type == "virtual_machine") { + if (jsonMessage.object == "virtual_machine") { populateVirtualMachinesTable(jsonMessage) } } diff --git a/netbox_proxbox/templates/netbox_proxbox/virtual_machines.html b/netbox_proxbox/templates/netbox_proxbox/virtual_machines.html index c339d43..a34bacf 100644 --- a/netbox_proxbox/templates/netbox_proxbox/virtual_machines.html +++ b/netbox_proxbox/templates/netbox_proxbox/virtual_machines.html @@ -1,8 +1,6 @@ {% extends 'base/layout.html' %} {% load static %} - - {% block content %} {% include "netbox_proxbox/virtual_machines_table/script.html" %} @@ -40,8 +38,10 @@ } if (jsonMessage) { - if (jsonMessage.type == "virtual_machine") { + if (jsonMessage.object == "virtual_machine") { populateVirtualMachinesTable(jsonMessage) + } else if (jsonMessage.object == "device") { + console.log(jsonMessage) } } } diff --git a/netbox_proxbox/templates/netbox_proxbox/virtual_machines_table/script.html b/netbox_proxbox/templates/netbox_proxbox/virtual_machines_table/script.html index 4b79ff2..8f60a12 100644 --- a/netbox_proxbox/templates/netbox_proxbox/virtual_machines_table/script.html +++ b/netbox_proxbox/templates/netbox_proxbox/virtual_machines_table/script.html @@ -38,75 +38,43 @@ // Create Table Row let vmTableRow = document.createElement('tr') - let vmNetboxIdData = document.createElement('td') - vmNetboxIdData.id = `vm-${jsonMessage.data.name}-netbox-id-data` - vmNetboxIdData.innerHTML = undefinedHtml - vmTableRow.appendChild(vmNetboxIdData) - - // Create Table Data - let vmIdData = document.createElement('td') - vmIdData.id = `vm-${jsonMessage.data.name}-id-data` - vmIdData.innerHTML = `${jsonMessage.data.vmid}` - vmTableRow.appendChild(vmIdData) - - let vmNameData = document.createElement('td') - vmNameData.id = `vm-${jsonMessage.data.name}-name-data` - vmNameData.innerHTML = `${jsonMessage.data.name}` - vmTableRow.appendChild(vmNameData) + /* + * Create Table Data Element + * @param {string} type - Type of Table Data Element + * @param {string} innerHTML - Inner HTML of Table Data Element + * @returns {HTMLTableDataCellElement} - Table Data Element + */ + function createTdElement(type, innerHTML) { + let tdElement = document.createElement('td') + tdElement.id = `vm-${jsonMessage.data.name}-${type}-data` + tdElement.innerHTML = innerHTML + return tdElement + } - let vmStatusData = document.createElement('td') - vmStatusData.id = `vm-${jsonMessage.data.name}-status-data` + let vmStatusDataHtml = undefinedHtml if (jsonMessage.data.status === "running") { - vmStatusData.innerHTML = `${jsonMessage.data.status}` + vmStatusDataHtml = `${jsonMessage.data.status}` } else if (jsonMessage.data.status === "stopped") { - vmStatusData.innerHTML = `${jsonMessage.data.status}` + vmStatusDataHtml = `${jsonMessage.data.status}` } else { - vmStatusData.innerHTML = `${jsonMessage.data.status}` + vmStatusDataHtml = `${jsonMessage.data.status}` } - vmTableRow.appendChild(vmStatusData) - - let vmDeviceData = document.createElement('td') - vmDeviceData.id = `vm-${jsonMessage.data.name}-device-data` - vmDeviceData.innerHTML = `${jsonMessage.data.node}` - vmTableRow.appendChild(vmDeviceData) - - let vmClusterData = document.createElement('td') - vmClusterData.id = `vm-${jsonMessage.data.name}-cluster-data` - vmClusterData.innerHTML = undefinedHtml - vmTableRow.appendChild(vmClusterData) - - let vmClusterTypeData = document.createElement('td') - vmClusterTypeData.id = `vm-${jsonMessage.data.name}-cluster-type-data` - vmClusterTypeData.innerHTML = undefinedHtml - vmTableRow.appendChild(vmClusterTypeData) - - let vmRoleData = document.createElement('td') - vmRoleData.id = `vm-${jsonMessage.data.name}-role-data` - vmRoleData.innerHTML = undefinedHtml - vmTableRow.appendChild(vmRoleData) - - let vmVirtualCPUsData = document.createElement('td') - vmVirtualCPUsData.id = `vm-${jsonMessage.data.name}-virtual-cpus-data` - vmVirtualCPUsData.innerHTML = jsonMessage.data.maxcpu - vmTableRow.appendChild(vmVirtualCPUsData) - - let vmMemoryData = document.createElement('td') - vmMemoryData.id = `vm-${jsonMessage.data.name}-memory-data` - vmMemoryData.innerHTML = `${Math.ceil(jsonMessage.data.maxmem / 1000000)}` - vmTableRow.appendChild(vmMemoryData) - - let vmDiskSpaceData = document.createElement('td') - vmDiskSpaceData.id = `vm-${jsonMessage.data.name}-disk-space-data` - vmDiskSpaceData.innerHTML = `${Math.ceil(jsonMessage.data.maxdisk / 1000000)}` - vmTableRow.appendChild(vmDiskSpaceData) - - let vmIPAddressData = document.createElement('td') - vmIPAddressData.id = `vm-${jsonMessage.data.name}-ip-address-data` - vmIPAddressData.innerHTML = undefinedHtml - vmTableRow.appendChild(vmIPAddressData) - + // Populate Table Row with Table Data parsed from Websocket JSON message + vmTableRow.appendChild(createTdElement(`status`, ``)) + vmTableRow.appendChild(createTdElement(`netbox-id`, undefinedHtml)) + vmTableRow.appendChild(createTdElement(`id`, `${jsonMessage.data.vmid}`)) + vmTableRow.appendChild(createTdElement(`name`, `${jsonMessage.data.name}`)) + vmTableRow.appendChild(createTdElement(`status`, vmStatusDataHtml)) + vmTableRow.appendChild(createTdElement(`device`, `${jsonMessage.data.node}`)) + vmTableRow.appendChild(createTdElement(`cluster`, undefinedHtml)) + vmTableRow.appendChild(createTdElement(`cluster-type`, undefinedHtml)) + vmTableRow.appendChild(createTdElement(`role`, undefinedHtml)) + vmTableRow.appendChild(createTdElement(`virtual-cpus`, jsonMessage.data.maxcpu)) + vmTableRow.appendChild(createTdElement(`memory`, `${Math.ceil(jsonMessage.data.maxmem / 1000000)}`)) + vmTableRow.appendChild(createTdElement(`disk-space`, `${Math.ceil(jsonMessage.data.maxdisk / 1000000)}`)) + vmTableRow.appendChild(createTdElement(`ip-address`, undefinedHtml)) virtualMachineTable.appendChild(vmTableRow) console.log(virtualMachineTable) diff --git a/netbox_proxbox/templates/netbox_proxbox/virtual_machines_table/table.html b/netbox_proxbox/templates/netbox_proxbox/virtual_machines_table/table.html index 3087cdb..41baf52 100644 --- a/netbox_proxbox/templates/netbox_proxbox/virtual_machines_table/table.html +++ b/netbox_proxbox/templates/netbox_proxbox/virtual_machines_table/table.html @@ -6,6 +6,9 @@

Virtual Machines

+ + Status + Netbox ID From e5f47226670e60fc7b4a91cc894eece42f86a647 Mon Sep 17 00:00:00 2001 From: Emerson Felipe Date: Thu, 28 Nov 2024 13:57:52 +0000 Subject: [PATCH 11/15] Add new template page 'Nodes (Devices)' and raw table for node sync --- netbox_proxbox/navigation.py | 7 +- .../templates/netbox_proxbox/nodes.html | 80 +++++++++++++++++++ .../netbox_proxbox/nodes_table/script.html | 0 .../netbox_proxbox/nodes_table/table.html | 63 +++++++++++++++ .../virtual_machines_table/table.html | 2 +- netbox_proxbox/urls.py | 1 + netbox_proxbox/views.py | 15 ++++ 7 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 netbox_proxbox/templates/netbox_proxbox/nodes.html create mode 100644 netbox_proxbox/templates/netbox_proxbox/nodes_table/script.html create mode 100644 netbox_proxbox/templates/netbox_proxbox/nodes_table/table.html diff --git a/netbox_proxbox/navigation.py b/netbox_proxbox/navigation.py index a7ea69c..d484f53 100755 --- a/netbox_proxbox/navigation.py +++ b/netbox_proxbox/navigation.py @@ -6,6 +6,11 @@ link_text='Full Update', ) +nodes_item = PluginMenuItem( + link='plugins:netbox_proxbox:nodes', + link_text='Nodes (Devices)', +) + virtual_machines_item = PluginMenuItem( link='plugins:netbox_proxbox:virtual_machines', link_text='Virtual Machines', @@ -45,7 +50,7 @@ menu = PluginMenu( label='Proxbox', groups=( - ('Proxmox Plugin', (fullupdate_item, virtual_machines_item,)), + ('Proxmox Plugin', (fullupdate_item, nodes_item, virtual_machines_item,)), ('Join our community', (contributing_item, community_item,)), ), icon_class='mdi mdi-dns' diff --git a/netbox_proxbox/templates/netbox_proxbox/nodes.html b/netbox_proxbox/templates/netbox_proxbox/nodes.html new file mode 100644 index 0000000..b02b0e4 --- /dev/null +++ b/netbox_proxbox/templates/netbox_proxbox/nodes.html @@ -0,0 +1,80 @@ +{% extends 'base/layout.html' %} +{% load static %} + +{% block content %} + {% include "netbox_proxbox/nodes_table/script.html" %} + + + + +
+

+ Proxmox Nodes (Devices) +

+
+ +
+
+ {% if perms.netbox_proxbox.add_proxmoxvm %} + + {% endif %} +
+
+ + {% include "netbox_proxbox/nodes_table/table.html" %} +{% endblock %} \ No newline at end of file diff --git a/netbox_proxbox/templates/netbox_proxbox/nodes_table/script.html b/netbox_proxbox/templates/netbox_proxbox/nodes_table/script.html new file mode 100644 index 0000000..e69de29 diff --git a/netbox_proxbox/templates/netbox_proxbox/nodes_table/table.html b/netbox_proxbox/templates/netbox_proxbox/nodes_table/table.html new file mode 100644 index 0000000..2d3ec3d --- /dev/null +++ b/netbox_proxbox/templates/netbox_proxbox/nodes_table/table.html @@ -0,0 +1,63 @@ +
+

Nodes

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ Status + + Netbox ID + + Node ID + + Name + + Node Status + + Role + + Manufacturer + + Type + + VM/CT Count + + IP Address + + Memory (MB) + + Disk Space (GB) + + IP Address + + +
— No nodes could be synced with Proxmox —
+
+
+
+
\ No newline at end of file diff --git a/netbox_proxbox/templates/netbox_proxbox/virtual_machines_table/table.html b/netbox_proxbox/templates/netbox_proxbox/virtual_machines_table/table.html index 41baf52..35cc925 100644 --- a/netbox_proxbox/templates/netbox_proxbox/virtual_machines_table/table.html +++ b/netbox_proxbox/templates/netbox_proxbox/virtual_machines_table/table.html @@ -19,7 +19,7 @@

Virtual Machines

Name - Status + VM/CT Status Device diff --git a/netbox_proxbox/urls.py b/netbox_proxbox/urls.py index fcfec15..eccd8d3 100755 --- a/netbox_proxbox/urls.py +++ b/netbox_proxbox/urls.py @@ -5,6 +5,7 @@ urlpatterns = [ # Home View path('', views.HomeView.as_view(), name='home'), + path('nodes', views.NodesView.as_view(), name='nodes'), path('virtual_machines', views.VirtualMachinesView.as_view(), name='virtual_machines'), path('contributing/', views.ContributingView.as_view(), name='contributing'), path('community/', views.CommunityView.as_view(), name='community'), diff --git a/netbox_proxbox/views.py b/netbox_proxbox/views.py index fde09f7..8e64204 100755 --- a/netbox_proxbox/views.py +++ b/netbox_proxbox/views.py @@ -60,6 +60,21 @@ def get(self, request): ) +class NodesView(View): + template = 'netbox_proxbox/nodes.html' + + def get(self, request): + plugin_configuration = configuration.PLUGINS_CONFIG + + return render( + request, + self.template, + { + "configuration": plugin_configuration + } + ) + + class VirtualMachinesView(View): template = 'netbox_proxbox/virtual_machines.html' From faf7ba840861514a6b099e73a61547d888fd9085 Mon Sep 17 00:00:00 2001 From: Emerson Felipe Date: Sun, 1 Dec 2024 16:05:25 +0000 Subject: [PATCH 12/15] Add 'Nodes' Table on 'Virtual Machines' Template View and starts the dynamic data population on this table. --- .../routes/proxbox/clusters/__init__.py | 3 + .../netbox_proxbox/nodes_table/script.html | 74 +++++++++++++++++++ .../netbox_proxbox/nodes_table/table.html | 11 +-- .../netbox_proxbox/virtual_machines.html | 4 + .../virtual_machines_table/script.html | 17 +---- 5 files changed, 87 insertions(+), 22 deletions(-) diff --git a/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py b/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py index 1cf7977..c30b53e 100755 --- a/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py +++ b/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py @@ -515,6 +515,9 @@ class VirtualMachineStatus(Enum): vm_node: str = vm.get("node") print(f"vm_node: {vm_node} | {type(vm_node)}") + await websocket.send_json({'object': 'device', 'type': 'create', 'data': { 'name': vm_node }}) + + """ Get Device from Netbox based on Proxmox Node Name only if it's not already in the devices dict This way we are able to minimize the number of requests to Netbox API diff --git a/netbox_proxbox/templates/netbox_proxbox/nodes_table/script.html b/netbox_proxbox/templates/netbox_proxbox/nodes_table/script.html index e69de29..1fdc769 100644 --- a/netbox_proxbox/templates/netbox_proxbox/nodes_table/script.html +++ b/netbox_proxbox/templates/netbox_proxbox/nodes_table/script.html @@ -0,0 +1,74 @@ + diff --git a/netbox_proxbox/templates/netbox_proxbox/nodes_table/table.html b/netbox_proxbox/templates/netbox_proxbox/nodes_table/table.html index 2d3ec3d..1eefc2e 100644 --- a/netbox_proxbox/templates/netbox_proxbox/nodes_table/table.html +++ b/netbox_proxbox/templates/netbox_proxbox/nodes_table/table.html @@ -1,5 +1,5 @@ -
-

Nodes

+
+

Nodes

@@ -42,18 +42,15 @@

Nodes

Disk Space (GB) - - IP Address - - + - — No nodes could be synced with Proxmox — + — No nodes could be synced with Proxmox — diff --git a/netbox_proxbox/templates/netbox_proxbox/virtual_machines.html b/netbox_proxbox/templates/netbox_proxbox/virtual_machines.html index a34bacf..1e5b947 100644 --- a/netbox_proxbox/templates/netbox_proxbox/virtual_machines.html +++ b/netbox_proxbox/templates/netbox_proxbox/virtual_machines.html @@ -3,6 +3,7 @@ {% block content %} {% include "netbox_proxbox/virtual_machines_table/script.html" %} + {% include "netbox_proxbox/nodes_table/script.html" %} From dc8e0d7afd5963085687883b0bb5be3214614c12 Mon Sep 17 00:00:00 2001 From: Emerson Felipe Date: Sun, 1 Dec 2024 21:23:10 +0000 Subject: [PATCH 13/15] Start working on 'bootstrap' new Netbox Generic module --- .../routes/netbox/generic/bootstrap.py | 142 +++++++++++++++++ .../routes/proxbox/clusters/__init__.py | 147 +++++------------- netbox_proxbox/main.py | 30 +++- .../templates/netbox_proxbox/common.html | 26 ++++ .../netbox_proxbox/nodes_table/script.html | 130 +++++++++------- .../netbox_proxbox/virtual_machines.html | 19 ++- .../virtual_machines_table/script.html | 136 ++++++++-------- 7 files changed, 386 insertions(+), 244 deletions(-) create mode 100644 netbox_proxbox/backend/routes/netbox/generic/bootstrap.py create mode 100644 netbox_proxbox/templates/netbox_proxbox/common.html diff --git a/netbox_proxbox/backend/routes/netbox/generic/bootstrap.py b/netbox_proxbox/backend/routes/netbox/generic/bootstrap.py new file mode 100644 index 0000000..6b68207 --- /dev/null +++ b/netbox_proxbox/backend/routes/netbox/generic/bootstrap.py @@ -0,0 +1,142 @@ +# TODO: Create Default Custom Fields +from netbox_proxbox.backend.session.netbox import NetboxSessionDep +from netbox_proxbox.backend.logging import log + +from fastapi import WebSocket + +async def create_default_custom_fields( + nb: NetboxSessionDep, + websocket: WebSocket, + custom_field: str, +): + if custom_field == "proxmox_vm_id": + custom_field_id = nb.session.extras.custom_fields.create( + { + "object_types": [ + "virtualization.virtualmachine" + ], + "type": "integer", + "name": "proxmox_vm_id", + "label": "VM ID", + "description": "Proxmox Virtual Machine or Container ID", + "ui_visible": "always", + "ui_editable": "hidden", + "weight": 100, + "filter_logic": "loose", + "search_weight": 1000, + "group_name": "Proxmox" + } + ) + + if custom_field_id: + print(f"Custom Field Created: {custom_field_id}") + return custom_field_id + + if custom_field == "proxmox_start_at_boot": + start_at_boot_field = nb.session.extras.custom_fields.create( + { + "object_types": [ + "virtualization.virtualmachine" + ], + "type": "boolean", + "name": "proxmox_start_at_boot", + "label": "Start at Boot", + "description": "Proxmox Start at Boot Option", + "ui_visible": "always", + "ui_editable": "hidden", + "weight": 100, + "filter_logic": "loose", + "search_weight": 1000, + "group_name": "Proxmox" + } + ) + + if start_at_boot_field: + print(f"Custom Field Created: {start_at_boot_field}") + return start_at_boot_field + + if custom_field == "proxmox_unprivileged_container": + start_unprivileged_field = nb.session.extras.custom_fields.create( + { + "object_types": [ + "virtualization.virtualmachine" + ], + "type": "boolean", + "name": "proxmox_unprivileged_container", + "label": "Unprivileged Container", + "description": "Proxmox Unprivileged Container", + "ui_visible": "if-set", + "ui_editable": "hidden", + "weight": 100, + "filter_logic": "loose", + "search_weight": 1000, + "group_name": "Proxmox" + } + ) + + if start_unprivileged_field: + print(f"Custom Field Created: {start_unprivileged_field}") + return start_unprivileged_field + + if custom_field == "proxmox_qemu_agent": + start_qemu_agent_field = nb.session.extras.custom_fields.create( + { + "object_types": [ + "virtualization.virtualmachine" + ], + "type": "boolean", + "name": "proxmox_qemu_agent", + "label": "QEMU Guest Agent", + "description": "Proxmox QEMU Guest Agent", + "ui_visible": "if-set", + "ui_editable": "hidden", + "weight": 100, + "filter_logic": "loose", + "search_weight": 1000, + "group_name": "Proxmox" + } + ) + + if start_qemu_agent_field: + print(f"Custom Field Created: {start_qemu_agent_field}") + return start_qemu_agent_field + + if custom_field == "proxmox_search_domain": + search_domain_field = nb.session.extras.custom_fields.create( + { + "object_types": [ + "virtualization.virtualmachine" + ], + "type": "text", + "name": "proxmox_search_domain", + "label": "Search Domain", + "description": "Proxmox Search Domain", + "ui_visible": "if-set", + "ui_editable": "hidden", + "weight": 100, + "filter_logic": "loose", + "search_weight": 1000, + "group_name": "Proxmox" + } + ) + + if search_domain_field: + print(f"Custom Field Created: {search_domain_field}") + return search_domain_field + + return None + +# TODO: Create Default Objects +async def create_default_objects(): + # Create Default Sites + + # Create Default Device Types + + # Create Default Device Roles + + # Create Default Cluster Types + + # Create Default Clusters + + # Create Default Devices + pass \ No newline at end of file diff --git a/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py b/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py index c30b53e..d8e0023 100755 --- a/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py +++ b/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py @@ -5,8 +5,12 @@ from netbox_proxbox.backend.session.netbox import NetboxSessionDep from netbox_proxbox.backend.logging import log - from netbox_proxbox.backend.exception import ProxboxException, exception_log +from netbox_proxbox.backend.routes.netbox.generic.bootstrap import ( + create_default_custom_fields, + create_default_objects, +) + from netbox_proxbox.backend import ( ClusterType, @@ -529,43 +533,52 @@ class VirtualMachineStatus(Enum): # Try to get the device from Netbox, if not found, create it device = await Device(nb = nb, websocket = websocket).get(name = vm.get("node")) + await websocket.send_json({'object': 'device', 'type': 'update', 'data': device}) + if device: devices[vm_node] = device print(f'device: {device}') - await websocket.send_json({'object': 'device', 'type': 'create', 'data': device}) + await websocket.send_json({'object': 'device', 'type': 'update', 'data': device}) """ Get Cluster from Netbox based on Cluster Name only if it's not already in the devices dict This way we are able to minimize the number of requests to Netbox API """ + cluster = None if clusters.get(px.name) is None: - clusters[px.name] = await Cluster(nb = nb, websocket = websocket).get(name = px.name) + cluster = await Cluster(nb = nb, websocket = websocket).get(name = px.name) + + if cluster: + await websocket.send_json({'object': 'cluster', 'type': 'update', 'data': cluster}) - cluster = clusters[px.name] + clusters[px.name] = cluster - - - role = await DeviceRole(nb = nb, websocket = websocket).get(slug = vm.get("type")) - if role is not None: - - vm_type = vm.get("type") - - vm_name = None - - color = "000000" + # Get Virtual Machine Role from Netbox based on Proxmox VM Type + vm_type = vm.get("type") + role = await DeviceRole(nb = nb, websocket = websocket).get(slug = vm_type) + + # If Role not found, create it + if not role: + # Set the Virtual Machine Role based on Proxmox VM Type if vm_type == "qemu": vm_name = "Virtual Machine (QEMU)" color = "00ffff" description = "Proxmox Virtual Machine" - - if vm_type == "lxc": + + elif vm_type == "lxc": vm_name = "Container (LXC)" color = "7fffd4" description = "Proxmox Container" + else: + vm_name: str = "Unknown" + color: str = "000000" + description: str = "VM Type not found. Neither QEMU nor LXC." + + # Create Virtual Machine Role based on Proxmox VM Type role = await DeviceRole(nb = nb, websocket = websocket).post(data = { "name": vm_name, "slug": vm_type, @@ -578,127 +591,39 @@ class VirtualMachineStatus(Enum): Proxmox Virtual Machine ID Custom Field """ + # Get Custom Field from Netbox based on Custom Field Name custom_field_id = nb.session.extras.custom_fields.get(name="proxmox_vm_id") if not custom_field_id: - custom_field_id = nb.session.extras.custom_fields.create( - { - "object_types": [ - "virtualization.virtualmachine" - ], - "type": "integer", - "name": "proxmox_vm_id", - "label": "VM ID", - "description": "Proxmox Virtual Machine or Container ID", - "ui_visible": "always", - "ui_editable": "hidden", - "weight": 100, - "filter_logic": "loose", - "search_weight": 1000, - "group_name": "Proxmox" - } - ) + await create_default_custom_fields(nb=nb, websocket=websocket, custom_field="proxmox_vm_id") """ Proxmox Start at Boot Custom Field """ start_at_boot_field = nb.session.extras.custom_fields.get(name="proxmox_start_at_boot") - if not start_at_boot_field: - start_at_boot_field = nb.session.extras.custom_fields.create( - { - "object_types": [ - "virtualization.virtualmachine" - ], - "type": "boolean", - "name": "proxmox_start_at_boot", - "label": "Start at Boot", - "description": "Proxmox Start at Boot Option", - "ui_visible": "always", - "ui_editable": "hidden", - "weight": 100, - "filter_logic": "loose", - "search_weight": 1000, - "group_name": "Proxmox" - } - ) + await create_default_custom_fields(nb=nb, websocket=websocket, custom_field="proxmox_start_at_boot") """ Proxmox Unprivileged Container Custom Field - """ + """ start_unprivileged_field = nb.session.extras.custom_fields.get(name="proxmox_unprivileged_container") - if not start_unprivileged_field: - start_unprivileged_field = nb.session.extras.custom_fields.create( - { - "object_types": [ - "virtualization.virtualmachine" - ], - "type": "boolean", - "name": "proxmox_unprivileged_container", - "label": "Unprivileged Container", - "description": "Proxmox Unprivileged Container", - "ui_visible": "if-set", - "ui_editable": "hidden", - "weight": 100, - "filter_logic": "loose", - "search_weight": 1000, - "group_name": "Proxmox" - } - ) - - unprivileged_container = None - + await create_default_custom_fields(nb=nb, websocket=websocket, custom_field="proxmox_unprivileged_container") """ Proxmox QEMU Guest Agent Custom Field """ start_qemu_agent_field = nb.session.extras.custom_fields.get(name="proxmox_qemu_agent") - if not start_qemu_agent_field: - start_qemu_agent_field = nb.session.extras.custom_fields.create( - { - "object_types": [ - "virtualization.virtualmachine" - ], - "type": "boolean", - "name": "proxmox_qemu_agent", - "label": "QEMU Guest Agent", - "description": "Proxmox QEMU Guest Agent", - "ui_visible": "if-set", - "ui_editable": "hidden", - "weight": 100, - "filter_logic": "loose", - "search_weight": 1000, - "group_name": "Proxmox" - } - ) + await create_default_custom_fields(nb=nb, websocket=websocket, custom_field="proxmox_qemu_agent") """ Proxmox Search Domain Field """ search_domain_field = nb.session.extras.custom_fields.get(name="proxmox_search_domain") - if not search_domain_field: - search_domain_field = nb.session.extras.custom_fields.create( - { - "object_types": [ - "virtualization.virtualmachine" - ], - "type": "text", - "name": "proxmox_search_domain", - "label": "Search Domain", - "description": "Proxmox Search Domain", - "ui_visible": "if-set", - "ui_editable": "hidden", - "weight": 100, - "filter_logic": "loose", - "search_weight": 1000, - "group_name": "Proxmox" - } - ) - - + await create_default_custom_fields(nb=nb, websocket=websocket, custom_field="proxmox_search_domain") platform = None search_domain = None diff --git a/netbox_proxbox/main.py b/netbox_proxbox/main.py index f91b413..2529602 100755 --- a/netbox_proxbox/main.py +++ b/netbox_proxbox/main.py @@ -163,10 +163,21 @@ async def websocket_endpoint( pxs: ProxmoxSessionsDep, websocket: WebSocket ): - await websocket.accept() + try: + await websocket.accept() + except Exception as error: + print(f"Error while accepting WebSocket connection: {error}") + await websocket.close() + data = None + while True: - data = await websocket.receive_text() + try: + data = await websocket.receive_text() + except Exception as error: + print(f"Error while receiving data from WebSocket: {error}") + await websocket.close() + break if data == "Start": await get_nodes(nb=nb, pxs=pxs, websocket=websocket) @@ -196,10 +207,21 @@ async def websocket_vm_endpoint( pxs: ProxmoxSessionsDep, websocket: WebSocket ): - await websocket.accept() + try: + await websocket.accept() + except Exception as error: + print(f"Error while accepting WebSocket connection: {error}") + await websocket.close() + + data = None while True: - data = await websocket.receive_text() + try: + data = await websocket.receive_text() + except Exception as error: + print(f"Error while receiving data from WebSocket: {error}") + await websocket.close() + break if data == "Sync Virtual Machines": await get_virtual_machines(nb=nb, pxs=pxs, websocket=websocket) diff --git a/netbox_proxbox/templates/netbox_proxbox/common.html b/netbox_proxbox/templates/netbox_proxbox/common.html new file mode 100644 index 0000000..c51281e --- /dev/null +++ b/netbox_proxbox/templates/netbox_proxbox/common.html @@ -0,0 +1,26 @@ + diff --git a/netbox_proxbox/templates/netbox_proxbox/nodes_table/script.html b/netbox_proxbox/templates/netbox_proxbox/nodes_table/script.html index 1fdc769..d733602 100644 --- a/netbox_proxbox/templates/netbox_proxbox/nodes_table/script.html +++ b/netbox_proxbox/templates/netbox_proxbox/nodes_table/script.html @@ -1,74 +1,90 @@ - + +{% endblock %} \ No newline at end of file diff --git a/netbox_proxbox/templates/netbox_proxbox/virtual_machines.html b/netbox_proxbox/templates/netbox_proxbox/virtual_machines.html index 1e5b947..21e7953 100644 --- a/netbox_proxbox/templates/netbox_proxbox/virtual_machines.html +++ b/netbox_proxbox/templates/netbox_proxbox/virtual_machines.html @@ -27,22 +27,32 @@ ws_vm.onmessage = function(event) { // Add WebSockets Messages came from FasstAPI backend on GUI - console.log(event.data) let jsonMessage = undefined + if (!event.data) { + return + } + try { jsonMessage = JSON.parse(event.data) } catch (error) { - // do nothing - console.log("Could not parse JSON message.") + + // Parse HTML Document + const parser = new DOMParser(); + const htmlDocument = parser.parseFromString(event.data, 'text/html'); + + // Check if HTML Document is undefined + if (typeof htmlDocument === undefined) { + // If HTML Document is undefined, console log it. + console.log(`ERROR: ${error}`) + } } if (jsonMessage) { if (jsonMessage.object == "virtual_machine") { populateVirtualMachinesTable(jsonMessage) } else if (jsonMessage.object == "device") { - console.log(jsonMessage) populateNodesTable(jsonMessage) } } @@ -80,4 +90,5 @@

{% include "netbox_proxbox/nodes_table/table.html" %} {% include "netbox_proxbox/virtual_machines_table/table.html" %} + {% include "netbox_proxbox/home/log_messages.html" %} {% endblock %} \ No newline at end of file diff --git a/netbox_proxbox/templates/netbox_proxbox/virtual_machines_table/script.html b/netbox_proxbox/templates/netbox_proxbox/virtual_machines_table/script.html index 402518e..77a51d4 100644 --- a/netbox_proxbox/templates/netbox_proxbox/virtual_machines_table/script.html +++ b/netbox_proxbox/templates/netbox_proxbox/virtual_machines_table/script.html @@ -1,76 +1,76 @@ - + +{% endblock %} From 7fd0122acd4ee01dbefcfb5a95ada3a1f66e6df4 Mon Sep 17 00:00:00 2001 From: Emerson Felipe Date: Fri, 27 Dec 2024 13:17:47 +0000 Subject: [PATCH 14/15] Simplify even more Custom Field checking/creation --- .../routes/proxbox/clusters/__init__.py | 61 +++++++------------ 1 file changed, 21 insertions(+), 40 deletions(-) diff --git a/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py b/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py index d8e0023..f6a2001 100755 --- a/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py +++ b/netbox_proxbox/backend/routes/proxbox/clusters/__init__.py @@ -587,58 +587,39 @@ class VirtualMachineStatus(Enum): "description": description }) - """ - Proxmox Virtual Machine ID Custom Field - """ - - # Get Custom Field from Netbox based on Custom Field Name - custom_field_id = nb.session.extras.custom_fields.get(name="proxmox_vm_id") - - if not custom_field_id: - await create_default_custom_fields(nb=nb, websocket=websocket, custom_field="proxmox_vm_id") - - """ - Proxmox Start at Boot Custom Field - """ - start_at_boot_field = nb.session.extras.custom_fields.get(name="proxmox_start_at_boot") - if not start_at_boot_field: - await create_default_custom_fields(nb=nb, websocket=websocket, custom_field="proxmox_start_at_boot") - - """ - Proxmox Unprivileged Container Custom Field - """ - start_unprivileged_field = nb.session.extras.custom_fields.get(name="proxmox_unprivileged_container") - if not start_unprivileged_field: - await create_default_custom_fields(nb=nb, websocket=websocket, custom_field="proxmox_unprivileged_container") - - """ - Proxmox QEMU Guest Agent Custom Field - """ - start_qemu_agent_field = nb.session.extras.custom_fields.get(name="proxmox_qemu_agent") - if not start_qemu_agent_field: - await create_default_custom_fields(nb=nb, websocket=websocket, custom_field="proxmox_qemu_agent") - - """ - Proxmox Search Domain Field - """ - search_domain_field = nb.session.extras.custom_fields.get(name="proxmox_search_domain") - if not search_domain_field: - await create_default_custom_fields(nb=nb, websocket=websocket, custom_field="proxmox_search_domain") + + # Proxmox Custom Fields on Netbox + proxmox_custom_field_list: list = [ + "proxmox_vm_id", + "proxmox_start_at_boot", + "proxmox_unprivileged_container", + "proxmox_qemu_agent", + "proxmox_search_domain" + ] + + # Look for existing custom fields and create it, if not found. + for cf in proxmox_custom_field_list: + get_current_cf = nb.session.extras.custom_fields.get(name=cf) + + if not get_current_cf: + await create_default_custom_fields(nb=nb, websocket=websocket, custom_field=cf) platform = None search_domain = None - + unprivileged_container = False + #if vm.get("type") == 'lxc': print(px.session.nodes.get(f'nodes/{vm.get("node")}/lxc/{vm.get("vmid")}/config') if vm.get("type") == 'lxc': vm_config = px.session.nodes(vm.get("node")).lxc(vm.get("vmid")).config.get() - platform_name = vm_config.get("ostype").capitalize() - platform_slug = vm_config.get("ostype") + platform_name = vm_config.get("ostype", "Name not found.").capitalize() + platform_slug = vm_config.get("ostype", "Slug not found.") search_domain = vm_config.get("searchdomain", None) unprivileged = int(vm_config.get("unprivileged", 0)) + if unprivileged == 1: unprivileged_container = True From 7dffd4ea55790d9e38416b3ed2778ccc9c062c38 Mon Sep 17 00:00:00 2001 From: Emerson Felipe Date: Fri, 27 Dec 2024 15:09:13 +0000 Subject: [PATCH 15/15] Update starlette and httpcore version specification --- requirements.txt | 4 ++-- setup.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index 2511aad..d1c8867 100755 --- a/requirements.txt +++ b/requirements.txt @@ -49,7 +49,7 @@ graphql-relay==3.2.0 gunicorn==23.0.0 h11==0.14.0 httpx -httpcore +httpcore<0.14.0,>=0.13.3 httptools==0.6.1 idna==3.10 inflection==0.5.1 @@ -128,7 +128,7 @@ sniffio==1.3.1 social-auth-app-django==5.4.2 social-auth-core==4.5.4 sqlparse==0.5.1 -starlette<0.39.0,>=0.37.2 +starlette<0.42.0,>=0.40.0 strawberry-graphql==0.243.1 strawberry-graphql-django==0.48.0 svgwrite==1.4.3 diff --git a/setup.py b/setup.py index 96905bb..1f2de99 100755 --- a/setup.py +++ b/setup.py @@ -26,13 +26,13 @@ 'pynetbox>=5', 'proxmoxer>=1', 'fastapi[all]', - 'starlette', + 'starlette<0.42.0,>=0.40.0', 'uvicorn[standard]', 'websockets', 'jinja2', 'ujson>=5.7.0', 'orjson>=3.8.9', - 'httpcore', + 'httpcore<0.14.0,>=0.13.3', 'pydantic', ]