mirror of
https://github.com/thegeeklab/prometheus-pve-sd.git
synced 2024-11-16 14:40:41 +00:00
207 lines
6.8 KiB
Python
207 lines
6.8 KiB
Python
|
#!/usr/bin/env python3
|
||
|
"""Prometheus Discovery."""
|
||
|
|
||
|
import json
|
||
|
import re
|
||
|
import socket
|
||
|
from collections import defaultdict
|
||
|
|
||
|
from prometheuspvesd.config import SingleConfig
|
||
|
from prometheuspvesd.model import Host
|
||
|
from prometheuspvesd.model import HostList
|
||
|
from prometheuspvesd.utils import SingleLog
|
||
|
from prometheuspvesd.utils import to_bool
|
||
|
|
||
|
try:
|
||
|
from proxmoxer import ProxmoxAPI
|
||
|
HAS_PROXMOXER = True
|
||
|
except ImportError:
|
||
|
HAS_PROXMOXER = False
|
||
|
|
||
|
|
||
|
class Discovery():
|
||
|
"""Prometheus PVE Service Discovery."""
|
||
|
|
||
|
def __init__(self):
|
||
|
if not HAS_PROXMOXER:
|
||
|
self.logger.error(
|
||
|
"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.host_list = HostList()
|
||
|
|
||
|
def _auth(self):
|
||
|
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"]
|
||
|
)
|
||
|
|
||
|
def _get_names(self, pve_list, pve_type):
|
||
|
names = []
|
||
|
|
||
|
if pve_type == "node":
|
||
|
names = [node["node"] for node in pve_list]
|
||
|
elif pve_type == "pool":
|
||
|
names = [pool["poolid"] for pool in pve_list]
|
||
|
|
||
|
return names
|
||
|
|
||
|
def _get_variables(self, pve_list, pve_type):
|
||
|
variables = {}
|
||
|
|
||
|
if pve_type in ["qemu", "container"]:
|
||
|
for vm in pve_list:
|
||
|
nested = {}
|
||
|
for key, value in vm.items():
|
||
|
nested["proxmox_" + key] = value
|
||
|
variables[vm["name"]] = nested
|
||
|
|
||
|
return variables
|
||
|
|
||
|
def _get_ip_address(self, pve_type, pve_node, vmid):
|
||
|
|
||
|
def validate(address):
|
||
|
try:
|
||
|
# IP address validation
|
||
|
if socket.inet_aton(address):
|
||
|
# Ignore localhost
|
||
|
if address != "127.0.0.1":
|
||
|
return address
|
||
|
except socket.error:
|
||
|
return False
|
||
|
|
||
|
address = False
|
||
|
networks = False
|
||
|
if pve_type == "qemu":
|
||
|
# If qemu agent is enabled, try to gather the IP address
|
||
|
try:
|
||
|
if self.client.nodes(pve_node).get(pve_type, vmid, "agent", "info") is not None:
|
||
|
networks = self.client.nodes(pve_node).get(
|
||
|
"qemu", vmid, "agent", "network-get-interfaces"
|
||
|
)["result"]
|
||
|
except Exception: # noqa
|
||
|
pass
|
||
|
|
||
|
if networks:
|
||
|
if type(networks) is list:
|
||
|
for network in networks:
|
||
|
for ip_address in network["ip-addresses"]:
|
||
|
address = validate(ip_address["ip-address"])
|
||
|
|
||
|
if not address:
|
||
|
try:
|
||
|
config = self.client.nodes(pve_node).get(pve_type, vmid, "config")
|
||
|
sources = [config["net0"], config["ipconfig0"]]
|
||
|
|
||
|
for s in sources:
|
||
|
find = re.search(r"ip=(\d*\.\d*\.\d*\.\d*)", str(sources))
|
||
|
if find and find.group(1):
|
||
|
address = find.group(1)
|
||
|
break
|
||
|
except Exception: # noqa
|
||
|
pass
|
||
|
|
||
|
return address
|
||
|
|
||
|
def _exclude(self, pve_list):
|
||
|
filtered = []
|
||
|
for item in pve_list:
|
||
|
obj = defaultdict(dict, item)
|
||
|
if obj["template"] == 1:
|
||
|
continue
|
||
|
|
||
|
if obj["status"] in self.config.config["exclude_state"]:
|
||
|
continue
|
||
|
|
||
|
if obj["vmid"] in self.config.config["exclude_vmid"]:
|
||
|
continue
|
||
|
|
||
|
filtered.append(item.copy())
|
||
|
return filtered
|
||
|
|
||
|
def propagate(self):
|
||
|
self.host_list.clear()
|
||
|
|
||
|
for node in self._get_names(self.client.nodes.get(), "node"):
|
||
|
try:
|
||
|
qemu_list = self._exclude(self.client.nodes(node).qemu.get())
|
||
|
container_list = self._exclude(self.client.nodes(node).lxc.get())
|
||
|
except Exception as e: # noqa
|
||
|
self.logger.error("Proxmoxer API error: {0}".format(str(e)))
|
||
|
|
||
|
# Merge QEMU and Containers lists from this node
|
||
|
instances = self._get_variables(qemu_list, "qemu").copy()
|
||
|
instances.update(self._get_variables(container_list, "container"))
|
||
|
|
||
|
self.logger.info("Found {} targets".format(len(instances)))
|
||
|
for host in instances:
|
||
|
host_meta = instances[host]
|
||
|
vmid = host_meta["proxmox_vmid"]
|
||
|
|
||
|
try:
|
||
|
pve_type = host_meta["proxmox_type"]
|
||
|
except KeyError:
|
||
|
pve_type = "qemu"
|
||
|
|
||
|
config = self.client.nodes(node).get(pve_type, vmid, "config")
|
||
|
|
||
|
try:
|
||
|
description = (config["description"])
|
||
|
except KeyError:
|
||
|
description = None
|
||
|
except Exception as e: # noqa
|
||
|
self.logger.error("Proxmoxer API error: {0}".format(str(e)))
|
||
|
|
||
|
try:
|
||
|
metadata = json.loads(description)
|
||
|
except TypeError:
|
||
|
metadata = {}
|
||
|
except ValueError:
|
||
|
metadata = {"notes": description}
|
||
|
|
||
|
address = self._get_ip_address(pve_type, node, vmid) or host
|
||
|
|
||
|
prom_host = Host(vmid, host, address, pve_type)
|
||
|
|
||
|
config_flags = [("cpu", "sockets"), ("cores", "cores"), ("memory", "memory")]
|
||
|
meta_flags = [("status", "proxmox_status")]
|
||
|
|
||
|
for key, flag in config_flags:
|
||
|
if flag in config:
|
||
|
prom_host.add_label(key, config[flag])
|
||
|
|
||
|
for key, flag in meta_flags:
|
||
|
if flag in host_meta:
|
||
|
prom_host.add_label(key, host_meta[flag])
|
||
|
|
||
|
if "groups" in metadata:
|
||
|
prom_host.add_label("groups", ",".join(metadata["groups"]))
|
||
|
|
||
|
self.host_list.add_host(prom_host)
|
||
|
self.logger.debug("Discovered {}".format(prom_host))
|
||
|
|
||
|
for pool in self._get_names(self.client.pools.get(), "pool"):
|
||
|
try:
|
||
|
pool_list = self._exclude(self.client.pool(pool).get()["members"])
|
||
|
except Exception as e: # noqa
|
||
|
self.logger.error("Proxmoxer API error: {0}".format(str(e)))
|
||
|
|
||
|
members = [
|
||
|
member["name"]
|
||
|
for member in pool_list
|
||
|
if (member["type"] == "qemu" or member["type"] == "lxc")
|
||
|
]
|
||
|
|
||
|
for member in members:
|
||
|
self.inventory.add_host(group=pool, host=member)
|
||
|
|
||
|
return self.host_list
|