diff --git a/docs/content/configuration/defaults.md b/docs/content/configuration/defaults.md index a8754e1..27e27ef 100644 --- a/docs/content/configuration/defaults.md +++ b/docs/content/configuration/defaults.md @@ -25,6 +25,7 @@ loop_delay: 300 service: true exclude_state: [] +# needs to be a list of strings exclude_vmid: [] pve: diff --git a/docs/content/usage/_index.md b/docs/content/usage/_index.md index e4c5251..1c21d59 100644 --- a/docs/content/usage/_index.md +++ b/docs/content/usage/_index.md @@ -16,7 +16,9 @@ run prometheus-pve-sd -vv --loop-delay 900 -o /etc/prometheus/pve.json The following list of meta labels can be used to relabel your scrape results: -`__meta_pve_ip` +`__meta_pve_ipv4` + +`__meta_pve_ipv6` `__meta_pve_name` diff --git a/prometheuspvesd/discovery.py b/prometheuspvesd/discovery.py index 1e950d4..04e3905 100644 --- a/prometheuspvesd/discovery.py +++ b/prometheuspvesd/discovery.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 """Prometheus Discovery.""" +import ipaddress import json import re -import socket from collections import defaultdict import requests @@ -85,19 +85,18 @@ class Discovery(): return variables - def _get_ip_address(self, pve_type, pve_node, vmid): + def _get_ip_addresses(self, pve_type, pve_node, vmid): - def validate(address): + def validate_ip(address: object) -> object: try: - # IP address validation - if socket.inet_aton(address): - # Ignore localhost - if address != "127.0.0.1": - return address - except socket.error: + if not ipaddress.ip_address(address).is_loopback \ + and not ipaddress.ip_address(address).is_link_local: + return address + except ValueError: return False - address = False + ipv4_address = False + ipv6_address = False networks = False if pve_type == "qemu": # If qemu agent is enabled, try to gather the IP address @@ -114,23 +113,46 @@ class Discovery(): if type(networks) is list: for network in networks: for ip_address in network["ip-addresses"]: - address = validate(ip_address["ip-address"]) + if ip_address["ip-address-type"] == "ipv4": + ipv4_address = validate_ip(ip_address["ip-address"]) + elif ip_address["ip-address-type"] == "ipv6": + ipv6_address = validate_ip(ip_address["ip-address"]) - if not address: + if not ipv4_address: try: PVE_REQUEST_COUNT_TOTAL.inc() config = self.client.nodes(pve_node).get(pve_type, vmid, "config") - sources = [config["net0"], config["ipconfig0"]] + if "ipconfig0" in config.keys(): + sources = [config["net0"], config["ipconfig0"]] + else: + sources = [config["net0"]] for s in sources: - find = re.search(r"ip=(\d*\.\d*\.\d*\.\d*)", str(sources)) + find = re.search(r"ip=(\d*\.\d*\.\d*\.\d*)", str(s)) if find and find.group(1): - address = find.group(1) + ipv4_address = find.group(1) break except Exception: # noqa # nosec pass - return address + if not ipv6_address: + try: + PVE_REQUEST_COUNT_TOTAL.inc() + config = self.client.nodes(pve_node).get(pve_type, vmid, "config") + if "ipconfig0" in config.keys(): + sources = [config["net0"], config["ipconfig0"]] + else: + sources = [config["net0"]] + + for s in sources: + find = re.search(r"ip=(\d*:\d*:\d*:\d*:\d*:\d*)", str(s)) + if find and find.group(1): + ipv6_address = find.group(1) + break + except Exception: # noqa # nosec + pass + + return ipv4_address, ipv6_address def _exclude(self, pve_list): filtered = [] @@ -196,9 +218,9 @@ class Discovery(): except ValueError: metadata = {"notes": description} - address = self._get_ip_address(pve_type, node, vmid) or host + ipv4_address, ipv6_address = self._get_ip_addresses(pve_type, node, vmid) or host - prom_host = Host(vmid, host, address, pve_type) + prom_host = Host(vmid, host, ipv4_address, ipv6_address, pve_type) config_flags = [("cpu", "sockets"), ("cores", "cores"), ("memory", "memory")] meta_flags = [("status", "proxmox_status")] diff --git a/prometheuspvesd/model.py b/prometheuspvesd/model.py index 732e4ef..07176dc 100644 --- a/prometheuspvesd/model.py +++ b/prometheuspvesd/model.py @@ -5,19 +5,22 @@ class Host: """Represents a virtual machine or container in PVE.""" - def __init__(self, vmid, hostname, ip_address, pve_type): + def __init__(self, vmid, hostname, ipv4_address, ipv6_address, pve_type): self.hostname = hostname - self.ip_address = ip_address + self.ipv4_address = ipv4_address + self.ipv6_address = ipv6_address self.vmid = vmid self.pve_type = pve_type self.labels = {} - self.labels["__meta_pve_ip"] = ip_address + self.labels["__meta_pve_ipv4"] = ipv4_address + self.labels["__meta_pve_ipv6"] = ipv6_address self.labels["__meta_pve_name"] = hostname self.labels["__meta_pve_type"] = pve_type self.labels["__meta_pve_vmid"] = str(vmid) def __str__(self): - return f"{self.hostname}({self.vmid}): {self.pve_type} {self.ip_address}" + return f"{self.hostname}({self.vmid}): {self.pve_type} \ + {self.ipv4_address} {self.ipv6_address}" def add_label(self, key, value): key = key.replace("-", "_").replace(" ", "_")