From c2276c277407eb2157458b25ffc3b59619b904b2 Mon Sep 17 00:00:00 2001 From: Louis Date: Fri, 10 Jul 2026 11:14:29 +0200 Subject: [PATCH] Add configurable Proxmox API timeout --- COMPATIBILITY.md | 1 + README.md | 2 ++ netbox_vmware_importer/__init__.py | 2 +- netbox_vmware_importer/forms.py | 14 +++++++- .../0004_add_endpoint_api_timeout.py | 23 +++++++++++++ netbox_vmware_importer/models.py | 6 ++++ netbox_vmware_importer/sync.py | 32 ++++++++++++++----- .../vcenterendpoint.html | 4 +++ pyproject.toml | 2 +- 9 files changed, 75 insertions(+), 11 deletions(-) create mode 100644 netbox_vmware_importer/migrations/0004_add_endpoint_api_timeout.py diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index 7bc78ff..fce14eb 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -12,3 +12,4 @@ | 0.1.7 | 4.4.0 | 4.6.x | | 0.1.8 | 4.4.0 | 4.6.x | | 0.2.0 | 4.4.0 | 4.6.x | +| 0.2.1 | 4.4.0 | 4.6.x | diff --git a/README.md b/README.md index b944b72..f0160e8 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,8 @@ sudo systemctl restart netbox netbox-rq 6. Optional Regex-Filter und Sync-Intervall setzen. 7. Auf der Detailseite `Sync jetzt starten` ausfuehren. +Bei selbstsignierten Proxmox-Zertifikaten `Validate SSL` deaktiviert lassen. Wenn Proxmox langsam antwortet, kann `API timeout` am Endpoint erhoeht werden. + Wenn `Sync interval minutes` gesetzt ist, prueft ein Systemjob alle fuenf Minuten, welche Endpoints faellig sind, und stellt die eigentlichen Sync-Jobs in die Queue. Ab Version `0.1.8` wird beim Speichern eines Endpoints zusaetzlich ein wiederkehrender NetBox-Job fuer genau diesen Endpoint geplant. Nach einem Update vorhandene Endpoints einmal speichern oder `netbox-rq` neu starten und bis zum naechsten Systemjob-Lauf warten. diff --git a/netbox_vmware_importer/__init__.py b/netbox_vmware_importer/__init__.py index df49ae2..d2a7594 100644 --- a/netbox_vmware_importer/__init__.py +++ b/netbox_vmware_importer/__init__.py @@ -5,7 +5,7 @@ class VMwareImporterConfig(PluginConfig): name = "netbox_vmware_importer" verbose_name = "Virtualization Importer" description = "Synchronize VMware vSphere and Proxmox VE virtual machines into NetBox." - version = "0.2.0" + version = "0.2.1" author = "Internal NetBox Team" base_url = "vmware-importer" min_version = "4.4.0" diff --git a/netbox_vmware_importer/forms.py b/netbox_vmware_importer/forms.py index 33a71c5..40d4e51 100644 --- a/netbox_vmware_importer/forms.py +++ b/netbox_vmware_importer/forms.py @@ -46,7 +46,18 @@ class VCenterEndpointForm(NetBoxModelForm): fieldsets = ( FieldSet("name", "slug", "enabled", "comments", name=_("Endpoint")), - FieldSet("provider", "host", "port", "username", "auth_method", "token_name", "password", "validate_ssl", name=_("Connection")), + FieldSet( + "provider", + "host", + "port", + "username", + "auth_method", + "token_name", + "password", + "validate_ssl", + "request_timeout_seconds", + name=_("Connection"), + ), FieldSet("tenant", "site", "cluster", name=_("NetBox target")), FieldSet( "include_name_regex", @@ -77,6 +88,7 @@ class VCenterEndpointForm(NetBoxModelForm): "token_name", "password", "validate_ssl", + "request_timeout_seconds", "tenant", "site", "cluster", diff --git a/netbox_vmware_importer/migrations/0004_add_endpoint_api_timeout.py b/netbox_vmware_importer/migrations/0004_add_endpoint_api_timeout.py new file mode 100644 index 0000000..5e473aa --- /dev/null +++ b/netbox_vmware_importer/migrations/0004_add_endpoint_api_timeout.py @@ -0,0 +1,23 @@ +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("netbox_vmware_importer", "0003_add_proxmox_endpoint_fields"), + ] + + operations = [ + migrations.AddField( + model_name="vcenterendpoint", + name="request_timeout_seconds", + field=models.PositiveIntegerField( + default=120, + help_text="HTTP/API read timeout in seconds.", + validators=[ + django.core.validators.MinValueValidator(5), + django.core.validators.MaxValueValidator(600), + ], + ), + ), + ] diff --git a/netbox_vmware_importer/models.py b/netbox_vmware_importer/models.py index d01cc20..ec964ba 100644 --- a/netbox_vmware_importer/models.py +++ b/netbox_vmware_importer/models.py @@ -60,6 +60,11 @@ class VCenterEndpoint(JobsMixin, NetBoxModel): default=False, help_text=_("Validate the endpoint TLS certificate."), ) + request_timeout_seconds = models.PositiveIntegerField( + default=120, + validators=[MinValueValidator(5), MaxValueValidator(600)], + help_text=_("HTTP/API read timeout in seconds."), + ) tenant = models.ForeignKey( to="tenancy.Tenant", on_delete=models.PROTECT, @@ -180,6 +185,7 @@ class VCenterEndpoint(JobsMixin, NetBoxModel): "auth_method", "token_name", "validate_ssl", + "request_timeout_seconds", "tenant", "site", "cluster", diff --git a/netbox_vmware_importer/sync.py b/netbox_vmware_importer/sync.py index 5a7a022..c40e406 100644 --- a/netbox_vmware_importer/sync.py +++ b/netbox_vmware_importer/sync.py @@ -14,8 +14,10 @@ from .choices import EndpointAuthMethodChoices, EndpointProviderChoices try: import requests + from urllib3.exceptions import InsecureRequestWarning except ImportError: # pragma: no cover - handled at runtime inside NetBox requests = None + InsecureRequestWarning = None try: from pyVim.connect import Disconnect, SmartConnect @@ -230,6 +232,7 @@ class ProxmoxClient: self.endpoint = endpoint self.session = None self.base_url = f"https://{endpoint.host}:{endpoint.port}/api2/json" + self.timeout = (10, endpoint.request_timeout_seconds or 120) def __enter__(self): if requests is None: @@ -237,6 +240,8 @@ class ProxmoxClient: self.session = requests.Session() self.session.verify = self.endpoint.validate_ssl + if not self.endpoint.validate_ssl and InsecureRequestWarning is not None: + requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning) if self.endpoint.auth_method == EndpointAuthMethodChoices.METHOD_API_TOKEN: token_id = self.endpoint.token_name if "!" in self.endpoint.token_name else f"{self.endpoint.username}!{self.endpoint.token_name}" @@ -251,15 +256,14 @@ class ProxmoxClient: self.session.close() def _authenticate_with_password(self): - response = self.session.post( - self._url("access/ticket"), + response = self._request( + "POST", + "access/ticket", data={ "username": self.endpoint.username, "password": self.endpoint.password, }, - timeout=30, ) - response.raise_for_status() data = response.json().get("data") or {} ticket = data.get("ticket") csrf_token = data.get("CSRFPreventionToken") @@ -274,9 +278,21 @@ class ProxmoxClient: def _url(self, path): return f"{self.base_url}/{path.strip('/')}" + def _request(self, method, path, **kwargs): + url = self._url(path) + try: + response = self.session.request(method, url, timeout=self.timeout, **kwargs) + response.raise_for_status() + return response + except requests.Timeout as exc: + raise ProxmoxConnectionError( + f"Proxmox API {method} {path} timed out after {self.timeout[1]} seconds." + ) from exc + except requests.RequestException as exc: + raise ProxmoxConnectionError(f"Proxmox API {method} {path} failed: {exc}") from exc + def _get(self, path, params=None): - response = self.session.get(self._url(path), params=params, timeout=30) - response.raise_for_status() + response = self._request("GET", path, params=params) return response.json().get("data") def iter_virtual_machines(self): @@ -448,7 +464,7 @@ class ProxmoxClient: def _merge_qemu_agent_interfaces(self, node, vmid, interfaces): try: response = self._get(f"nodes/{node}/qemu/{vmid}/agent/network-get-interfaces") or {} - except requests.RequestException: + except (requests.RequestException, ProxmoxConnectionError): return agent_interfaces = response.get("result") if isinstance(response, dict) else response @@ -488,7 +504,7 @@ class ProxmoxClient: def _get_qemu_guest_os(self, node, vmid): try: response = self._get(f"nodes/{node}/qemu/{vmid}/agent/get-osinfo") or {} - except requests.RequestException: + except (requests.RequestException, ProxmoxConnectionError): return "" result = response.get("result") if isinstance(response, dict) else None diff --git a/netbox_vmware_importer/templates/netbox_vmware_importer/vcenterendpoint.html b/netbox_vmware_importer/templates/netbox_vmware_importer/vcenterendpoint.html index dfb84f6..98eb6b5 100644 --- a/netbox_vmware_importer/templates/netbox_vmware_importer/vcenterendpoint.html +++ b/netbox_vmware_importer/templates/netbox_vmware_importer/vcenterendpoint.html @@ -40,6 +40,10 @@ Validate SSL {{ object.validate_ssl|yesno:"Yes,No" }} + + API timeout + {{ object.request_timeout_seconds }} seconds + diff --git a/pyproject.toml b/pyproject.toml index eedf076..1841262 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "netbox-vmware-importer" -version = "0.2.0" +version = "0.2.1" description = "NetBox plugin to synchronize VMware vSphere and Proxmox VE virtual machines into NetBox." readme = "README.md" requires-python = ">=3.12"