Add proxmox client wrapper

This commit is contained in:
Mathias Petermann 2022-03-30 22:36:46 +02:00
parent dfb18c1dd8
commit 8ff74fa420
2 changed files with 101 additions and 54 deletions

90
prometheuspvesd/client.py Normal file
View File

@ -0,0 +1,90 @@
"""Proxmox Client."""
import requests
from prometheus_client import Counter
from prometheuspvesd.config import SingleConfig
from prometheuspvesd.exception import APIError
from prometheuspvesd.logger import SingleLog
from prometheuspvesd.model import HostList
from prometheuspvesd.utils import to_bool
try:
from proxmoxer import ProxmoxAPI
HAS_PROXMOXER = True
except ImportError:
HAS_PROXMOXER = False
PVE_REQUEST_COUNT_TOTAL = Counter("pve_sd_requests_total", "Total count of requests to PVE API")
PVE_REQUEST_COUNT_ERROR_TOTAL = Counter(
"pve_sd_requests_error_total", "Total count of failed requests to PVE API"
)
class ProxmoxClient:
"""Proxmox API Client."""
def __init__(self):
if not HAS_PROXMOXER:
self.log.sysexit_with_message(
"The Proxmox VE Prometheus SD requires proxmoxer: "
"https://pypi.org/project/proxmoxer/"
)
self.config = SingleConfig()
self.log = SingleLog()
self.logger = SingleLog().logger
self.client = self._auth()
self.logger.debug("Successfully authenticated")
self.host_list = HostList()
def _auth(self):
try:
self.logger.debug(
"Trying to authenticate against {} as user {}".format(
self.config.config["pve"]["server"], self.config.config["pve"]["user"]
)
)
return ProxmoxAPI(
self.config.config["pve"]["server"],
user=self.config.config["pve"]["user"],
password=self.config.config["pve"]["password"],
verify_ssl=to_bool(self.config.config["pve"]["verify_ssl"]),
timeout=self.config.config["pve"]["auth_timeout"]
)
except requests.RequestException as e:
PVE_REQUEST_COUNT_ERROR_TOTAL.inc()
raise APIError(str(e))
def _do_request(self, *args):
PVE_REQUEST_COUNT_TOTAL.inc()
try:
# create a new tuple containing nodes and unpack it again for client.get
return self.client.get(*("nodes", *args))
except requests.RequestException as e:
PVE_REQUEST_COUNT_ERROR_TOTAL.inc()
raise APIError(str(e))
def get_nodes(self):
self.logger.debug("fetching all nodes")
return self._do_request()
def get_all_vms(self, pve_node):
self.logger.debug("fetching all vms on node {}".format(pve_node))
return self._do_request(pve_node, "qemu")
def get_all_containers(self, pve_node):
self.logger.debug("fetching all containers on node {}".format(pve_node))
return self._do_request(pve_node, "lxc")
def get_instance_config(self, pve_node, pve_type, vmid):
self.logger.debug("fetching instance config for {} on {}".format(vmid, pve_node))
return self._do_request(pve_node, pve_type, vmid, "config")
def get_agent_info(self, pve_node, pve_type, vmid):
self.logger.debug("fetching agent info for {} on {}".format(vmid, pve_node))
return self._do_request(pve_node, pve_type, vmid, "agent", "info")["result"]
def get_network_interfaces(self, pve_node, vmid):
self.logger.debug("fetching network interfaces for {} on {}".format(vmid, pve_node))
return self._do_request(pve_node, "qemu", vmid, "agent",
"network-get-interfaces")["result"]

View File

@ -6,63 +6,32 @@ import json
import re
from collections import defaultdict
import requests
from prometheus_client import Counter
from prometheus_client import Gauge
from prometheus_client import Summary
from prometheuspvesd.client import ProxmoxClient
from prometheuspvesd.config import SingleConfig
from prometheuspvesd.exception import APIError
from prometheuspvesd.logger import SingleLog
from prometheuspvesd.model import Host
from prometheuspvesd.model import HostList
from prometheuspvesd.utils import to_bool
try:
from proxmoxer import ProxmoxAPI
HAS_PROXMOXER = True
except ImportError:
HAS_PROXMOXER = False
PROPAGATION_TIME = Summary(
"pve_sd_propagate_seconds", "Time spent propagating the inventory from PVE"
)
HOST_GAUGE = Gauge("pve_sd_hosts", "Number of hosts discovered by PVE SD")
PVE_REQUEST_COUNT_TOTAL = Counter("pve_sd_requests_total", "Total count of requests to PVE API")
PVE_REQUEST_COUNT_ERROR_TOTAL = Counter(
"pve_sd_requests_error_total", "Total count of failed requests to PVE API"
)
class Discovery():
"""Prometheus PVE Service Discovery."""
def __init__(self):
if not HAS_PROXMOXER:
self.log.sysexit_with_message(
"The Proxmox VE Prometheus SD requires proxmoxer: "
"https://pypi.org/project/proxmoxer/"
)
self.config = SingleConfig()
self.log = SingleLog()
self.logger = SingleLog().logger
self.client = self._auth()
self.client = ProxmoxClient()
self.host_list = HostList()
def _auth(self):
try:
return ProxmoxAPI(
self.config.config["pve"]["server"],
user=self.config.config["pve"]["user"],
password=self.config.config["pve"]["password"],
verify_ssl=to_bool(self.config.config["pve"]["verify_ssl"]),
timeout=self.config.config["pve"]["auth_timeout"]
)
except requests.RequestException as e:
PVE_REQUEST_COUNT_ERROR_TOTAL.inc()
raise APIError(str(e))
def _get_names(self, pve_list, pve_type):
names = []
@ -92,11 +61,8 @@ class Discovery():
if pve_type == "qemu":
# If qemu agent is enabled, try to gather the IP address
try:
PVE_REQUEST_COUNT_TOTAL.inc()
if self.client.get("nodes", pve_node, pve_type, vmid, "agent", "info") is not None:
networks = self.client.get(
"nodes", pve_node, "qemu", vmid, "agent", "network-get-interfaces"
)["result"]
if self.client.get_agent_info(pve_node, pve_type, vmid) is not None:
networks = self.client.get_network_interfaces(pve_node, vmid)
except Exception: # noqa # nosec
pass
@ -108,10 +74,9 @@ class Discovery():
elif ip_address["ip-address-type"] == "ipv6" and not ipv6_address:
ipv6_address = self._validate_ip(ip_address["ip-address"])
if not ipv4_address:
config = self.client.get_instance_config(pve_node, pve_type, vmid)
if config and not ipv4_address:
try:
PVE_REQUEST_COUNT_TOTAL.inc()
config = self.client.get("nodes", pve_node, pve_type, vmid, "config")
if "ipconfig0" in config.keys():
sources = [config["net0"], config["ipconfig0"]]
else:
@ -125,10 +90,8 @@ class Discovery():
except Exception: # noqa # nosec
pass
if not ipv6_address:
if config and not ipv6_address:
try:
PVE_REQUEST_COUNT_TOTAL.inc()
config = self.client.get("nodes", pve_node, pve_type, vmid, "config")
if "ipconfig0" in config.keys():
sources = [config["net0"], config["ipconfig0"]]
else:
@ -194,15 +157,11 @@ class Discovery():
def propagate(self):
self.host_list.clear()
PVE_REQUEST_COUNT_TOTAL.inc()
for node in self._get_names(self.client.get("nodes"), "node"):
for node in self._get_names(self.client.get_nodes(), "node"):
try:
PVE_REQUEST_COUNT_TOTAL.inc()
qemu_list = self._filter(self.client.get("nodes", node, "qemu"))
PVE_REQUEST_COUNT_TOTAL.inc()
container_list = self._filter(self.client.get("nodes", node, "lxc"))
qemu_list = self._filter(self.client.get_all_vms(node))
container_list = self._filter(self.client.get_all_containers(node))
except Exception as e: # noqa
PVE_REQUEST_COUNT_ERROR_TOTAL.inc()
raise APIError(str(e))
# Merge QEMU and Containers lists from this node
@ -220,15 +179,13 @@ class Discovery():
except KeyError:
pve_type = "qemu"
PVE_REQUEST_COUNT_TOTAL.inc()
config = self.client.get("nodes", node, pve_type, vmid, "config")
config = self.client.get_instance_config(node, pve_type, vmid)
try:
description = (config["description"])
except KeyError:
description = None
except Exception as e: # noqa
PVE_REQUEST_COUNT_ERROR_TOTAL.inc()
raise APIError(str(e))
try: