Add configurable Proxmox API timeout
This commit is contained in:
@@ -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 |
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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),
|
||||
],
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -40,6 +40,10 @@
|
||||
<th scope="row">Validate SSL</th>
|
||||
<td>{{ object.validate_ssl|yesno:"Yes,No" }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">API timeout</th>
|
||||
<td>{{ object.request_timeout_seconds }} seconds</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user