From 3f124d35960c3a2ef8f728f80b3f6bd9eafb0c49 Mon Sep 17 00:00:00 2001 From: nicolargo Date: Tue, 27 May 2025 10:46:05 +0200 Subject: [PATCH] Add support for InfluxDB 3 Core #3182 --- Makefile | 9 +- conf/glances.conf | 25 +- docs/api.rst | 1062 ++++++++--------- docs/gw/influxdb.rst | 54 +- docs/man/glances.1 | 2 +- glances/exports/export.py | 101 +- glances/exports/glances_influxdb/__init__.py | 82 +- glances/exports/glances_influxdb2/__init__.py | 94 +- glances/exports/glances_influxdb3/__init__.py | 98 ++ optional-requirements.txt | 3 +- pyproject.toml | 1 + ...influxdb.sh => test_export_influxdb_v1.sh} | 28 +- tests/test_export_influxdb_v3.sh | 75 ++ 13 files changed, 901 insertions(+), 733 deletions(-) create mode 100644 glances/exports/glances_influxdb3/__init__.py rename tests/{test_export_influxdb.sh => test_export_influxdb_v1.sh} (62%) create mode 100755 tests/test_export_influxdb_v3.sh diff --git a/Makefile b/Makefile index 2cf40338..d71604e0 100644 --- a/Makefile +++ b/Makefile @@ -117,10 +117,13 @@ test-min-with-upgrade: venv-min-upgrade ## Upgrade deps and run unit tests in mi test-export-csv: ## Run interface tests with CSV /bin/bash ./tests/test_export_csv.sh -test-export-influxdb: ## Run interface tests with InfluxDB - /bin/bash ./tests/test_export_influxdb.sh +test-export-influxdb-v1: ## Run interface tests with InfluxDB version 1 (Legacy) + /bin/bash ./tests/test_export_influxdb_v1.sh -test-export: test-export-csv test-export-influxdb ## Tests all exports +test-export-influxdb-v3: ## Run interface tests with InfluxDB version 3 (Core) + /bin/bash ./tests/test_export_influxdb_v3.sh + +test-export: test-export-csv test-export-influxdb-v1 test-export-influxdb-v3 ## Tests all exports # =================================================================== # Linters, profilers and cyber security diff --git a/conf/glances.conf b/conf/glances.conf index 16d97e03..616a413d 100644 --- a/conf/glances.conf +++ b/conf/glances.conf @@ -623,7 +623,7 @@ style=DarkStyle [influxdb] # !!! # Will be DEPRECATED in future release. -# Please have a look on the new influxdb2 export module (compatible with InfluxDB 1.8.x and 2.x) +# Please have a look on the new influxdb3 export module # !!! # Configuration for the --export influxdb option # https://influxdb.com/ @@ -652,7 +652,28 @@ port=8086 protocol=http org=nicolargo bucket=glances -token=EjFUTWe8U-MIseEAkaVIgVnej_TrnbdvEcRkaB1imstW7gapSqy6_6-8XD-yd51V0zUUpDy-kAdVD1purDLuxA== +token=PUT_YOUR_INFLUXDB2_TOKEN_HERE +# Set the interval between two exports (in seconds) +# If the interval is set to 0, the Glances refresh time is used (default behavor) +#interval=0 +# Prefix will be added for all measurement name +# Ex: prefix=foo +# => foo.cpu +# => foo.mem +# You can also use dynamic values +#prefix=foo +# Following tags will be added for all measurements +# You can also use dynamic values. +# Note: hostname and name (for process) are always added as a tag +#tags=foo:bar,spam:eggs,domain:`domainname` + +[influxdb3] +# Configuration for the --export influxdb3 option +# https://influxdb.com/ +host=http://localhost:8181 +org=nicolargo +database=glances +token=PUT_YOUR_INFLUXDB3_TOKEN_HERE # Set the interval between two exports (in seconds) # If the interval is set to 0, the Glances refresh time is used (default behavor) #interval=0 diff --git a/docs/api.rst b/docs/api.rst index 9259afcf..7231544f 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -160,7 +160,7 @@ Get plugin stats:: "refresh": 3.0, "regex": True, "result": None, - "timer": 0.5764081478118896}, + "timer": 0.47667741775512695}, {"count": 0, "countmax": 20.0, "countmin": None, @@ -169,7 +169,7 @@ Get plugin stats:: "refresh": 3.0, "regex": True, "result": None, - "timer": 0.575993537902832}] + "timer": 0.4765753746032715}] Fields descriptions: @@ -197,7 +197,7 @@ Get a specific item when field matches the given value:: "refresh": 3.0, "regex": True, "result": None, - "timer": 0.5764081478118896}]} + "timer": 0.47667741775512695}]} GET cloud --------- @@ -245,21 +245,21 @@ Get plugin stats:: "engine": "docker", "id": "37b79baf008ea602934be8d16d76064f79a032c11e4f6fe6e811b3c46ac20fd0", "image": ["ghcr.io/open-webui/open-webui:ollama"], - "io": {"cumulative_ior": 4654899200, "cumulative_iow": 0}, + "io": {"cumulative_ior": 618508288, "cumulative_iow": 0}, "io_rx": None, "io_wx": None, "key": "name", - "memory": {"inactive_file": 2240512, - "limit": 16421879808, - "usage": 108617728}, + "memory": {"inactive_file": 20922368, + "limit": 16421883904, + "usage": 113471488}, "memory_percent": None, - "memory_usage": 108617728, + "memory_usage": 113471488, "name": "open-webui", - "network": {"cumulative_rx": 352836, "cumulative_tx": 2027}, + "network": {"cumulative_rx": 189915, "cumulative_tx": 2159}, "network_rx": None, "network_tx": None, "status": "running", - "uptime": "2 days"}] + "uptime": "yesterday"}] Fields descriptions: @@ -295,21 +295,21 @@ Get a specific item when field matches the given value:: "engine": "docker", "id": "37b79baf008ea602934be8d16d76064f79a032c11e4f6fe6e811b3c46ac20fd0", "image": ["ghcr.io/open-webui/open-webui:ollama"], - "io": {"cumulative_ior": 4654899200, "cumulative_iow": 0}, + "io": {"cumulative_ior": 618508288, "cumulative_iow": 0}, "io_rx": None, "io_wx": None, "key": "name", - "memory": {"inactive_file": 2240512, - "limit": 16421879808, - "usage": 108617728}, + "memory": {"inactive_file": 20922368, + "limit": 16421883904, + "usage": 113471488}, "memory_percent": None, - "memory_usage": 108617728, + "memory_usage": 113471488, "name": "open-webui", - "network": {"cumulative_rx": 352836, "cumulative_tx": 2027}, + "network": {"cumulative_rx": 189915, "cumulative_tx": 2159}, "network_rx": None, "network_tx": None, "status": "running", - "uptime": "2 days"}]} + "uptime": "yesterday"}]} GET core -------- @@ -336,19 +336,19 @@ Get plugin stats:: # curl http://localhost:61208/api/4/cpu {"cpucore": 16, - "ctx_switches": 357256952, + "ctx_switches": 110124988, "guest": 0.0, - "idle": 79.3, - "interrupts": 273197252, - "iowait": 0.5, + "idle": 93.2, + "interrupts": 91446752, + "iowait": 0.1, "irq": 0.0, "nice": 0.0, - "soft_interrupts": 96460008, + "soft_interrupts": 37996687, "steal": 0.0, "syscalls": 0, - "system": 4.1, - "total": 20.0, - "user": 16.2} + "system": 2.7, + "total": 5.9, + "user": 3.9} Fields descriptions: @@ -381,7 +381,7 @@ Fields descriptions: Get a specific field:: # curl http://localhost:61208/api/4/cpu/total - {"total": 20.0} + {"total": 5.9} GET diskio ---------- @@ -391,14 +391,14 @@ Get plugin stats:: # curl http://localhost:61208/api/4/diskio [{"disk_name": "nvme0n1", "key": "disk_name", - "read_bytes": 185722438656, - "read_count": 5016797, - "write_bytes": 109080605696, - "write_count": 4167808}, + "read_bytes": 7939079680, + "read_count": 316989, + "write_bytes": 13072667648, + "write_count": 1097171}, {"disk_name": "nvme0n1p1", "key": "disk_name", - "read_bytes": 8523264, - "read_count": 1036, + "read_bytes": 8415232, + "read_count": 953, "write_bytes": 5120, "write_count": 3}] @@ -434,10 +434,10 @@ Get a specific item when field matches the given value:: # curl http://localhost:61208/api/4/diskio/disk_name/value/nvme0n1 {"nvme0n1": [{"disk_name": "nvme0n1", "key": "disk_name", - "read_bytes": 185722438656, - "read_count": 5016797, - "write_bytes": 109080605696, - "write_count": 4167808}]} + "read_bytes": 7939079680, + "read_count": 316989, + "write_bytes": 13072667648, + "write_count": 1097171}]} GET folders ----------- @@ -464,14 +464,14 @@ Get plugin stats:: # curl http://localhost:61208/api/4/fs [{"device_name": "/dev/mapper/ubuntu--vg-ubuntu--lv", - "free": 796481773568, + "free": 795803656192, "fs_type": "ext4", "key": "mnt_point", "mnt_point": "/", "options": "rw,relatime", - "percent": 16.4, + "percent": 16.5, "size": 1003736440832, - "used": 156192161792}, + "used": 156870279168}, {"device_name": "zsfpool", "free": 41680896, "fs_type": "zfs", @@ -502,14 +502,14 @@ Get a specific item when field matches the given value:: # curl http://localhost:61208/api/4/fs/mnt_point/value// {"/": [{"device_name": "/dev/mapper/ubuntu--vg-ubuntu--lv", - "free": 796481773568, + "free": 795803656192, "fs_type": "ext4", "key": "mnt_point", "mnt_point": "/", "options": "rw,relatime", - "percent": 16.4, + "percent": 16.5, "size": 1003736440832, - "used": 156192161792}]} + "used": 156870279168}]} GET gpu ------- @@ -582,9 +582,9 @@ Get plugin stats:: # curl http://localhost:61208/api/4/load {"cpucore": 16, - "min1": 1.13623046875, - "min15": 1.49267578125, - "min5": 1.626953125} + "min1": 2.14111328125, + "min15": 1.15234375, + "min5": 1.4111328125} Fields descriptions: @@ -596,7 +596,7 @@ Fields descriptions: Get a specific field:: # curl http://localhost:61208/api/4/load/min1 - {"min1": 1.13623046875} + {"min1": 2.14111328125} GET mem ------- @@ -604,16 +604,16 @@ GET mem Get plugin stats:: # curl http://localhost:61208/api/4/mem - {"active": 9813442560, - "available": 5063471104, - "buffers": 349396992, - "cached": 4470894592, - "free": 5063471104, - "inactive": 3420487680, - "percent": 69.2, - "shared": 842686464, - "total": 16421879808, - "used": 11358408704} + {"active": 8626487296, + "available": 5448634368, + "buffers": 372654080, + "cached": 5088337920, + "free": 5448634368, + "inactive": 4633042944, + "percent": 66.8, + "shared": 859938816, + "total": 16421883904, + "used": 10973249536} Fields descriptions: @@ -632,7 +632,7 @@ Fields descriptions: Get a specific field:: # curl http://localhost:61208/api/4/mem/total - {"total": 16421879808} + {"total": 16421883904} GET memswap ----------- @@ -640,13 +640,13 @@ GET memswap Get plugin stats:: # curl http://localhost:61208/api/4/memswap - {"free": 1352712192, - "percent": 68.5, - "sin": 5744689152, - "sout": 9121898496, + {"free": 1380184064, + "percent": 67.9, + "sin": 177889280, + "sout": 3054333952, "time_since_update": 1, "total": 4294963200, - "used": 2942251008} + "used": 2914779136} Fields descriptions: @@ -671,32 +671,32 @@ Get plugin stats:: # curl http://localhost:61208/api/4/network [{"alias": None, "bytes_all": None, - "bytes_all_gauge": 17117621800, + "bytes_all_gauge": 1113983505, "bytes_all_rate_per_sec": None, "bytes_recv": None, - "bytes_recv_gauge": 16887065638, + "bytes_recv_gauge": 881611898, "bytes_recv_rate_per_sec": None, "bytes_sent": None, - "bytes_sent_gauge": 230556162, + "bytes_sent_gauge": 232371607, "bytes_sent_rate_per_sec": None, "interface_name": "wlp0s20f3", "key": "interface_name", "speed": 0, - "time_since_update": 0.5878756046295166}, + "time_since_update": 0.4796278476715088}, {"alias": None, "bytes_all": None, - "bytes_all_gauge": 2987523469, + "bytes_all_gauge": 192074, "bytes_all_rate_per_sec": None, "bytes_recv": None, - "bytes_recv_gauge": 37914459, + "bytes_recv_gauge": 2159, "bytes_recv_rate_per_sec": None, "bytes_sent": None, - "bytes_sent_gauge": 2949609010, + "bytes_sent_gauge": 189915, "bytes_sent_rate_per_sec": None, - "interface_name": "virbr0", + "interface_name": "veth274f692", "key": "interface_name", "speed": 10485760000, - "time_since_update": 0.5878756046295166}] + "time_since_update": 0.4796278476715088}] Fields descriptions: @@ -718,25 +718,25 @@ Fields descriptions: Get a specific field:: # curl http://localhost:61208/api/4/network/interface_name - {"interface_name": ["wlp0s20f3", "virbr0", "veth7d9b0dc", "vnet4"]} + {"interface_name": ["wlp0s20f3", "veth274f692"]} Get a specific item when field matches the given value:: # curl http://localhost:61208/api/4/network/interface_name/value/wlp0s20f3 {"wlp0s20f3": [{"alias": None, "bytes_all": None, - "bytes_all_gauge": 17117621800, + "bytes_all_gauge": 1113983505, "bytes_all_rate_per_sec": None, "bytes_recv": None, - "bytes_recv_gauge": 16887065638, + "bytes_recv_gauge": 881611898, "bytes_recv_rate_per_sec": None, "bytes_sent": None, - "bytes_sent_gauge": 230556162, + "bytes_sent_gauge": 232371607, "bytes_sent_rate_per_sec": None, "interface_name": "wlp0s20f3", "key": "interface_name", "speed": 0, - "time_since_update": 0.5878756046295166}]} + "time_since_update": 0.4796278476715088}]} GET now ------- @@ -744,7 +744,7 @@ GET now Get plugin stats:: # curl http://localhost:61208/api/4/now - {"custom": "2025-04-21 10:37:32 CEST", "iso": "2025-04-21T10:37:32+02:00"} + {"custom": "2025-05-27 10:44:34 CEST", "iso": "2025-05-27T10:44:34+02:00"} Fields descriptions: @@ -754,7 +754,7 @@ Fields descriptions: Get a specific field:: # curl http://localhost:61208/api/4/now/iso - {"iso": "2025-04-21T10:37:32+02:00"} + {"iso": "2025-05-27T10:44:34+02:00"} GET percpu ---------- @@ -766,22 +766,7 @@ Get plugin stats:: "dpc": None, "guest": 0.0, "guest_nice": 0.0, - "idle": 41.0, - "interrupt": None, - "iowait": 1.0, - "irq": 0.0, - "key": "cpu_number", - "nice": 0.0, - "softirq": 0.0, - "steal": 0.0, - "system": 11.0, - "total": 59.0, - "user": 3.0}, - {"cpu_number": 1, - "dpc": None, - "guest": 0.0, - "guest_nice": 0.0, - "idle": 44.0, + "idle": 37.0, "interrupt": None, "iowait": 0.0, "irq": 0.0, @@ -789,9 +774,24 @@ Get plugin stats:: "nice": 0.0, "softirq": 0.0, "steal": 0.0, - "system": 5.0, - "total": 56.0, - "user": 9.0}] + "system": 8.0, + "total": 63.0, + "user": 0.0}, + {"cpu_number": 1, + "dpc": None, + "guest": 0.0, + "guest_nice": 0.0, + "idle": 46.0, + "interrupt": None, + "iowait": 0.0, + "irq": 0.0, + "key": "cpu_number", + "nice": 0.0, + "softirq": 0.0, + "steal": 0.0, + "system": 0.0, + "total": 54.0, + "user": 0.0}] Fields descriptions: @@ -827,7 +827,7 @@ Get plugin stats:: "port": 0, "refresh": 30, "rtt_warning": None, - "status": 0.010781, + "status": 0.016056, "timeout": 3}] Fields descriptions: @@ -855,7 +855,7 @@ Get a specific item when field matches the given value:: "port": 0, "refresh": 30, "rtt_warning": None, - "status": 0.010781, + "status": 0.016056, "timeout": 3}]} GET processcount @@ -864,7 +864,7 @@ GET processcount Get plugin stats:: # curl http://localhost:61208/api/4/processcount - {"pid_max": 0, "running": 2, "sleeping": 426, "thread": 2277, "total": 567} + {"pid_max": 0, "running": 1, "sleeping": 435, "thread": 2312, "total": 566} Fields descriptions: @@ -877,7 +877,7 @@ Fields descriptions: Get a specific field:: # curl http://localhost:61208/api/4/processcount/total - {"total": 567} + {"total": 566} GET processlist --------------- @@ -885,158 +885,7 @@ GET processlist Get plugin stats:: # curl http://localhost:61208/api/4/processlist - [{"cmdline": ["/usr/bin/qemu-system-x86_64", - "-name", - "guest=Kali_Linux_2024,debug-threads=on", - "-S", - "-object", - "{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/var/lib/libvirt/qemu/domain-5-Kali_Linux_2024/master-key.aes"}", - "-machine", - "pc-q35-8.2,usb=off,vmport=off,dump-guest-core=off,memory-backend=pc.ram,hpet=off,acpi=on", - "-accel", - "kvm", - "-cpu", - "host,migratable=on", - "-m", - "size=4194304k", - "-object", - "{"qom-type":"memory-backend-ram","id":"pc.ram","size":4294967296}", - "-overcommit", - "mem-lock=off", - "-smp", - "2,sockets=2,cores=1,threads=1", - "-uuid", - "4cddcd77-02af-48cb-92f7-7ac4055fc043", - "-no-user-config", - "-nodefaults", - "-chardev", - "socket,id=charmonitor,fd=33,server=on,wait=off", - "-mon", - "chardev=charmonitor,id=monitor,mode=control", - "-rtc", - "base=utc,driftfix=slew", - "-global", - "kvm-pit.lost_tick_policy=delay", - "-no-shutdown", - "-global", - "ICH9-LPC.disable_s3=1", - "-global", - "ICH9-LPC.disable_s4=1", - "-boot", - "strict=on", - "-device", - "{"driver":"pcie-root-port","port":16,"chassis":1,"id":"pci.1","bus":"pcie.0","multifunction":true,"addr":"0x2"}", - "-device", - "{"driver":"pcie-root-port","port":17,"chassis":2,"id":"pci.2","bus":"pcie.0","addr":"0x2.0x1"}", - "-device", - "{"driver":"pcie-root-port","port":18,"chassis":3,"id":"pci.3","bus":"pcie.0","addr":"0x2.0x2"}", - "-device", - "{"driver":"pcie-root-port","port":19,"chassis":4,"id":"pci.4","bus":"pcie.0","addr":"0x2.0x3"}", - "-device", - "{"driver":"pcie-root-port","port":20,"chassis":5,"id":"pci.5","bus":"pcie.0","addr":"0x2.0x4"}", - "-device", - "{"driver":"pcie-root-port","port":21,"chassis":6,"id":"pci.6","bus":"pcie.0","addr":"0x2.0x5"}", - "-device", - "{"driver":"pcie-root-port","port":22,"chassis":7,"id":"pci.7","bus":"pcie.0","addr":"0x2.0x6"}", - "-device", - "{"driver":"pcie-root-port","port":23,"chassis":8,"id":"pci.8","bus":"pcie.0","addr":"0x2.0x7"}", - "-device", - "{"driver":"pcie-root-port","port":24,"chassis":9,"id":"pci.9","bus":"pcie.0","multifunction":true,"addr":"0x3"}", - "-device", - "{"driver":"pcie-root-port","port":25,"chassis":10,"id":"pci.10","bus":"pcie.0","addr":"0x3.0x1"}", - "-device", - "{"driver":"pcie-root-port","port":26,"chassis":11,"id":"pci.11","bus":"pcie.0","addr":"0x3.0x2"}", - "-device", - "{"driver":"pcie-root-port","port":27,"chassis":12,"id":"pci.12","bus":"pcie.0","addr":"0x3.0x3"}", - "-device", - "{"driver":"pcie-root-port","port":28,"chassis":13,"id":"pci.13","bus":"pcie.0","addr":"0x3.0x4"}", - "-device", - "{"driver":"pcie-root-port","port":29,"chassis":14,"id":"pci.14","bus":"pcie.0","addr":"0x3.0x5"}", - "-device", - "{"driver":"qemu-xhci","p2":15,"p3":15,"id":"usb","bus":"pci.2","addr":"0x0"}", - "-device", - "{"driver":"virtio-serial-pci","id":"virtio-serial0","bus":"pci.3","addr":"0x0"}", - "-blockdev", - "{"driver":"file","filename":"/home/nicolargo/ISOs/kali-linux-2024.2-qemu-amd64.qcow2","node-name":"libvirt-1-storage","auto-read-only":true,"discard":"unmap"}", - "-blockdev", - "{"node-name":"libvirt-1-format","read-only":false,"driver":"qcow2","file":"libvirt-1-storage","backing":null}", - "-device", - "{"driver":"virtio-blk-pci","bus":"pci.4","addr":"0x0","drive":"libvirt-1-format","id":"virtio-disk0","bootindex":1}", - "-netdev", - "{"type":"tap","fd":"34","vhost":true,"vhostfd":"36","id":"hostnet0"}", - "-device", - "{"driver":"virtio-net-pci","netdev":"hostnet0","id":"net0","mac":"52:54:00:64:66:3f","bus":"pci.1","addr":"0x0"}", - "-chardev", - "pty,id=charserial0", - "-device", - "{"driver":"isa-serial","chardev":"charserial0","id":"serial0","index":0}", - "-chardev", - "socket,id=charchannel0,fd=32,server=on,wait=off", - "-device", - "{"driver":"virtserialport","bus":"virtio-serial0.0","nr":1,"chardev":"charchannel0","id":"channel0","name":"org.qemu.guest_agent.0"}", - "-chardev", - "spicevmc,id=charchannel1,name=vdagent", - "-device", - "{"driver":"virtserialport","bus":"virtio-serial0.0","nr":2,"chardev":"charchannel1","id":"channel1","name":"com.redhat.spice.0"}", - "-device", - "{"driver":"usb-tablet","id":"input0","bus":"usb.0","port":"1"}", - "-audiodev", - "{"id":"audio1","driver":"spice"}", - "-spice", - "port=5900,addr=127.0.0.1,disable-ticketing=on,image-compression=off,seamless-migration=on", - "-device", - "{"driver":"virtio-vga","id":"video0","max_outputs":1,"bus":"pcie.0","addr":"0x1"}", - "-device", - "{"driver":"ich9-intel-hda","id":"sound0","bus":"pcie.0","addr":"0x1b"}", - "-device", - "{"driver":"hda-duplex","id":"sound0-codec0","bus":"sound0.0","cad":0,"audiodev":"audio1"}", - "-global", - "ICH9-LPC.noreboot=off", - "-watchdog-action", - "reset", - "-chardev", - "spicevmc,id=charredir0,name=usbredir", - "-device", - "{"driver":"usb-redir","chardev":"charredir0","id":"redir0","bus":"usb.0","port":"2"}", - "-chardev", - "spicevmc,id=charredir1,name=usbredir", - "-device", - "{"driver":"usb-redir","chardev":"charredir1","id":"redir1","bus":"usb.0","port":"3"}", - "-device", - "{"driver":"virtio-balloon-pci","id":"balloon0","bus":"pci.5","addr":"0x0"}", - "-object", - "{"qom-type":"rng-random","id":"objrng0","filename":"/dev/urandom"}", - "-device", - "{"driver":"virtio-rng-pci","rng":"objrng0","id":"rng0","bus":"pci.6","addr":"0x0"}", - "-sandbox", - "on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny", - "-msg", - "timestamp=on"], - "cpu_percent": 0.0, - "cpu_times": {"children_system": 0.0, - "children_user": 0.0, - "iowait": 0.0, - "system": 88.83, - "user": 2007.29}, - "gids": {"effective": 993, "real": 993, "saved": 993}, - "io_counters": [0, 0, 0, 0, 0], - "key": "pid", - "memory_info": {"data": 4766662656, - "dirty": 0, - "lib": 0, - "rss": 3268034560, - "shared": 69865472, - "text": 7229440, - "vms": 9601773568}, - "memory_percent": 19.900490066965176, - "name": "qemu-system-x86_64", - "nice": 0, - "num_threads": 8, - "pid": 148818, - "status": "S", - "time_since_update": 1, - "username": "libvirt-qemu"}, - {"cmdline": ["/snap/code/181/usr/share/code/code", + [{"cmdline": ["/snap/code/193/usr/share/code/code", "--type=utility", "--utility-sub-type=node.mojom.NodeService", "--lang=en-US", @@ -1044,7 +893,7 @@ Get plugin stats:: "--no-sandbox", "--dns-result-order=ipv4first", "--inspect-port=0", - "--crashpad-handler-pid=13069", + "--crashpad-handler-pid=12776", "--enable-crash-reporter=864d4bb7-dd20-4851-830f-29e81dd93517,no_channel", "--user-data-dir=/home/nicolargo/.config/Code", "--standard-schemes=vscode-webview,vscode-file", @@ -1054,80 +903,99 @@ Get plugin stats:: "--service-worker-schemes=vscode-webview", "--code-cache-schemes=vscode-webview,vscode-file", "--shared-files=v8_context_snapshot_data:100", - "--field-trial-handle=3,i,12995238425644643594,17255034491750702452,262144", - "--disable-features=CalculateNativeWinOcclusion,PlzDedicatedWorker,SpareRendererForSitePerProcess", + "--field-trial-handle=3,i,8675254667491921424,564610109630475014,262144", + "--enable-features=DocumentPolicyIncludeJSCallStacksInCrashReports,EarlyEstablishGpuChannel,EstablishGpuChannelAsync", + "--disable-features=CalculateNativeWinOcclusion,SpareRendererForSitePerProcess", "--variations-seed-version"], "cpu_percent": 0.0, - "cpu_times": {"children_system": 2007.03, - "children_user": 1183.64, + "cpu_times": {"children_system": 471.74, + "children_user": 147.05, "iowait": 0.0, - "system": 868.19, - "user": 2207.17}, + "system": 526.93, + "user": 2401.08}, "gids": {"effective": 1000, "real": 1000, "saved": 1000}, - "io_counters": [0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + "io_counters": [437005312, + 607666176, 0, 0, 0, + 322137088, + 135368704, 0, 0, 0, + 76328960, + 491520, 0, 0, 0, + 10312704, + 159744, 0, 0, 0, + 163840, 0, 0, 0, 0, + 72953856, 0, 0, 0, 0, + 13445120, + 126976, 0, 0, 0, + 15028224, 0, 0, 0, 0, + 73690112, + 195317760, 0, 0, 0, + 844800, 0, 0, 0, 0, + 663552, 0, 0, 0, 0, + 455680, + 44584960, 0, 0, 0, + 40048640, + 808263680, 0, 0, 0, + 2324480, 0, 0, 0, 0, + 6102016, 0, 0, 0, 0, + 36453376, 0, 0, 0, 0, + 56055808, + 10477568, 0, 0, 0, @@ -1136,57 +1004,115 @@ Get plugin stats:: 0, 0, 0, + 265216, 0, 0, 0, 0, + 4233216, 0, 0, 0, 0, + 803840, 0, 0, 0, 0, + 3489792, 0, 0, 0, 0, + 495616, 0, 0, 0, 0, + 178176, 0, 0, 0, 0, + 5767168, 0, 0, 0, 0, + 43987968, + 23666688, 0, 0, 0, + 3426304, + 962560, 0, 0, 0, + 972800, 0, 0, 0, 0], "key": "pid", - "memory_info": {"data": 2930241536, + "memory_info": {"data": 3651760128, "dirty": 0, "lib": 0, - "rss": 1220956160, - "shared": 64512000, - "text": 138166272, - "vms": 1272582012928}, - "memory_percent": 7.434935429287487, + "rss": 1959587840, + "shared": 129040384, + "text": 139026432, + "vms": 1280588754944}, + "memory_percent": 11.932783421533559, "name": "code", "nice": 0, - "num_threads": 57, - "pid": 13228, + "num_threads": 61, + "pid": 13009, + "status": "S", + "time_since_update": 1, + "username": "nicolargo"}, + {"cmdline": ["/snap/code/193/usr/share/code/code", + "--type=utility", + "--utility-sub-type=node.mojom.NodeService", + "--lang=en-US", + "--service-sandbox-type=none", + "--no-sandbox", + "--dns-result-order=ipv4first", + "--inspect-port=0", + "--crashpad-handler-pid=12776", + "--enable-crash-reporter=864d4bb7-dd20-4851-830f-29e81dd93517,no_channel", + "--user-data-dir=/home/nicolargo/.config/Code", + "--standard-schemes=vscode-webview,vscode-file", + "--secure-schemes=vscode-webview,vscode-file", + "--cors-schemes=vscode-webview,vscode-file", + "--fetch-schemes=vscode-webview,vscode-file", + "--service-worker-schemes=vscode-webview", + "--code-cache-schemes=vscode-webview,vscode-file", + "--shared-files=v8_context_snapshot_data:100", + "--field-trial-handle=3,i,8675254667491921424,564610109630475014,262144", + "--enable-features=DocumentPolicyIncludeJSCallStacksInCrashReports,EarlyEstablishGpuChannel,EstablishGpuChannelAsync", + "--disable-features=CalculateNativeWinOcclusion,SpareRendererForSitePerProcess", + "--variations-seed-version"], + "cpu_percent": 0.0, + "cpu_times": {"children_system": 245.05, + "children_user": 91.45, + "iowait": 0.0, + "system": 175.06, + "user": 181.79}, + "gids": {"effective": 1000, "real": 1000, "saved": 1000}, + "io_counters": [322137088, 135368704, 0, 0, 0], + "key": "pid", + "memory_info": {"data": 2051534848, + "dirty": 0, + "lib": 0, + "rss": 779325440, + "shared": 93945856, + "text": 139026432, + "vms": 1250669309952}, + "memory_percent": 4.745651866471751, + "name": "code", + "nice": 0, + "num_threads": 18, + "pid": 12982, "status": "S", "time_since_update": 1, "username": "nicolargo"}] @@ -1213,123 +1139,122 @@ GET programlist Get plugin stats:: # curl http://localhost:61208/api/4/programlist - [{"childrens": [148818], - "cmdline": ["qemu-system-x86_64"], - "cpu_percent": 0, - "cpu_times": {"children_system": 0.0, - "children_user": 0.0, - "iowait": 0.0, - "system": 88.83, - "user": 2007.29}, - "io_counters": [0, 0, 0, 0, 0], - "memory_info": {"data": 4766662656, - "dirty": 0, - "lib": 0, - "rss": 3268034560, - "shared": 69865472, - "text": 7229440, - "vms": 9601773568}, - "memory_percent": 19.900490066965176, - "name": "qemu-system-x86_64", - "nice": 0, - "nprocs": 1, - "num_threads": 8, - "pid": "_", - "status": "S", - "time_since_update": 1, - "username": "libvirt-qemu"}, - {"childrens": [13228, - 13129, - 13789, - 16748, - 14286, - 13427, - 13047, - 676539, - 16747, - 484047, - 16772, - 13239, - 13238, - 13184, - 16764, - 13738, - 16773, - 13109, - 13641, - 13932, - 13050, - 13049], + [{"childrens": [13009, + 12982, + 14962, + 12880, + 14859, + 14754, + 12874, + 15346, + 12755, + 21785, + 21786, + 14763, + 12998, + 12930, + 21733, + 13000, + 21803, + 14993, + 37630, + 21732, + 13677, + 12999, + 13798, + 13711, + 14088, + 12847, + 12758, + 12757], "cmdline": ["code"], "cpu_percent": 0, - "cpu_times": {"children_system": 2009.3699999999997, - "children_user": 1195.04, - "system": 1753.8400000000001, - "user": 8253.88}, - "io_counters": [0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + "cpu_times": {"children_system": 720.6599999999999, + "children_user": 257.92999999999995, + "system": 1076.7100000000007, + "user": 5801.529999999999}, + "io_counters": [437005312, + 607666176, 0, 0, 0, + 322137088, + 135368704, 0, 0, 0, + 76328960, + 491520, 0, 0, 0, + 10312704, + 159744, 0, 0, 0, + 163840, 0, 0, 0, 0, + 72953856, 0, 0, 0, 0, + 13445120, + 126976, 0, 0, 0, + 15028224, 0, 0, 0, 0, + 73690112, + 195317760, 0, 0, 0, + 844800, 0, 0, 0, 0, + 663552, 0, 0, 0, 0, + 455680, + 44584960, 0, 0, 0, + 40048640, + 808263680, 0, 0, 0, + 2324480, 0, 0, 0, 0, + 6102016, 0, 0, 0, 0, + 36453376, 0, 0, 0, 0, + 56055808, + 10477568, 0, 0, 0, @@ -1338,54 +1263,91 @@ Get plugin stats:: 0, 0, 0, + 265216, 0, 0, 0, 0, + 4233216, 0, 0, 0, 0, + 803840, 0, 0, 0, 0, + 3489792, 0, 0, 0, 0, + 495616, 0, 0, 0, 0, + 178176, 0, 0, 0, 0, + 5767168, 0, 0, 0, 0, + 43987968, + 23666688, 0, 0, 0, + 3426304, + 962560, 0, 0, 0, + 972800, 0, 0, 0, 0], - "memory_info": {"data": 18168127488, - "rss": 3867561984, - "shared": 768823296, - "text": 3039657984, - "vms": 22532554018816}, - "memory_percent": 23.5512744534636, + "memory_info": {"data": 25453400064, + "rss": 7429939200, + "shared": 2272882688, + "text": 3892740096, + "vms": 30116498821120}, + "memory_percent": 45.244134250579094, "name": "code", "nice": 0, - "nprocs": 22, - "num_threads": 334, + "nprocs": 28, + "num_threads": 462, + "pid": "_", + "status": "S", + "time_since_update": 1, + "username": "nicolargo"}, + {"childrens": [6484], + "cmdline": ["WebExtensions"], + "cpu_percent": 0, + "cpu_times": {"children_system": 0.0, + "children_user": 0.0, + "iowait": 0.0, + "system": 60.16, + "user": 378.83}, + "io_counters": [17175552, 0, 0, 0, 0], + "memory_info": {"data": 1012625408, + "dirty": 0, + "lib": 0, + "rss": 566517760, + "shared": 114372608, + "text": 860160, + "vms": 25258516480}, + "memory_percent": 3.4497732617754595, + "name": "WebExtensions", + "nice": 0, + "nprocs": 1, + "num_threads": 27, "pid": "_", "status": "S", "time_since_update": 1, @@ -1421,90 +1383,30 @@ GET quicklook Get plugin stats:: # curl http://localhost:61208/api/4/quicklook - {"cpu": 20.0, + {"cpu": 5.9, "cpu_hz": 4475000000.0, - "cpu_hz_current": 793651937.5, + "cpu_hz_current": 813255500.0, "cpu_log_core": 16, "cpu_name": "13th Gen Intel(R) Core(TM) i7-13620H", "cpu_phys_core": 10, - "load": 9.3, - "mem": 69.2, + "load": 7.2, + "mem": 66.8, "percpu": [{"cpu_number": 0, "dpc": None, "guest": 0.0, "guest_nice": 0.0, - "idle": 41.0, + "idle": 37.0, "interrupt": None, - "iowait": 1.0, + "iowait": 0.0, "irq": 0.0, "key": "cpu_number", "nice": 0.0, "softirq": 0.0, "steal": 0.0, - "system": 11.0, - "total": 59.0, - "user": 3.0}, + "system": 8.0, + "total": 63.0, + "user": 0.0}, {"cpu_number": 1, - "dpc": None, - "guest": 0.0, - "guest_nice": 0.0, - "idle": 44.0, - "interrupt": None, - "iowait": 0.0, - "irq": 0.0, - "key": "cpu_number", - "nice": 0.0, - "softirq": 0.0, - "steal": 0.0, - "system": 5.0, - "total": 56.0, - "user": 9.0}, - {"cpu_number": 2, - "dpc": None, - "guest": 0.0, - "guest_nice": 0.0, - "idle": 47.0, - "interrupt": None, - "iowait": 0.0, - "irq": 0.0, - "key": "cpu_number", - "nice": 0.0, - "softirq": 0.0, - "steal": 0.0, - "system": 1.0, - "total": 53.0, - "user": 9.0}, - {"cpu_number": 3, - "dpc": None, - "guest": 0.0, - "guest_nice": 0.0, - "idle": 49.0, - "interrupt": None, - "iowait": 0.0, - "irq": 0.0, - "key": "cpu_number", - "nice": 0.0, - "softirq": 0.0, - "steal": 0.0, - "system": 2.0, - "total": 51.0, - "user": 8.0}, - {"cpu_number": 4, - "dpc": None, - "guest": 0.0, - "guest_nice": 0.0, - "idle": 44.0, - "interrupt": None, - "iowait": 0.0, - "irq": 0.0, - "key": "cpu_number", - "nice": 0.0, - "softirq": 0.0, - "steal": 0.0, - "system": 5.0, - "total": 56.0, - "user": 7.0}, - {"cpu_number": 5, "dpc": None, "guest": 0.0, "guest_nice": 0.0, @@ -1516,44 +1418,14 @@ Get plugin stats:: "nice": 0.0, "softirq": 0.0, "steal": 0.0, - "system": 2.0, + "system": 0.0, "total": 54.0, - "user": 9.0}, - {"cpu_number": 6, + "user": 0.0}, + {"cpu_number": 2, "dpc": None, "guest": 0.0, "guest_nice": 0.0, - "idle": 34.0, - "interrupt": None, - "iowait": 0.0, - "irq": 0.0, - "key": "cpu_number", - "nice": 0.0, - "softirq": 0.0, - "steal": 0.0, - "system": 8.0, - "total": 66.0, - "user": 15.0}, - {"cpu_number": 7, - "dpc": None, - "guest": 0.0, - "guest_nice": 0.0, - "idle": 43.0, - "interrupt": None, - "iowait": 0.0, - "irq": 0.0, - "key": "cpu_number", - "nice": 0.0, - "softirq": 0.0, - "steal": 0.0, - "system": 2.0, - "total": 57.0, - "user": 12.0}, - {"cpu_number": 8, - "dpc": None, - "guest": 0.0, - "guest_nice": 0.0, - "idle": 52.0, + "idle": 46.0, "interrupt": None, "iowait": 0.0, "irq": 0.0, @@ -1562,69 +1434,9 @@ Get plugin stats:: "softirq": 0.0, "steal": 0.0, "system": 0.0, - "total": 48.0, - "user": 6.0}, - {"cpu_number": 9, - "dpc": None, - "guest": 0.0, - "guest_nice": 0.0, - "idle": 48.0, - "interrupt": None, - "iowait": 0.0, - "irq": 0.0, - "key": "cpu_number", - "nice": 0.0, - "softirq": 0.0, - "steal": 0.0, - "system": 0.0, - "total": 52.0, - "user": 9.0}, - {"cpu_number": 10, - "dpc": None, - "guest": 0.0, - "guest_nice": 0.0, - "idle": 49.0, - "interrupt": None, - "iowait": 0.0, - "irq": 0.0, - "key": "cpu_number", - "nice": 0.0, - "softirq": 0.0, - "steal": 0.0, - "system": 2.0, - "total": 51.0, - "user": 7.0}, - {"cpu_number": 11, - "dpc": None, - "guest": 0.0, - "guest_nice": 0.0, - "idle": 51.0, - "interrupt": None, - "iowait": 0.0, - "irq": 0.0, - "key": "cpu_number", - "nice": 0.0, - "softirq": 0.0, - "steal": 0.0, - "system": 1.0, - "total": 49.0, - "user": 7.0}, - {"cpu_number": 12, - "dpc": None, - "guest": 0.0, - "guest_nice": 0.0, - "idle": 44.0, - "interrupt": None, - "iowait": 0.0, - "irq": 0.0, - "key": "cpu_number", - "nice": 0.0, - "softirq": 0.0, - "steal": 0.0, - "system": 2.0, - "total": 56.0, - "user": 11.0}, - {"cpu_number": 13, + "total": 54.0, + "user": 0.0}, + {"cpu_number": 3, "dpc": None, "guest": 0.0, "guest_nice": 0.0, @@ -1636,29 +1448,104 @@ Get plugin stats:: "nice": 0.0, "softirq": 0.0, "steal": 0.0, - "system": 2.0, + "system": 0.0, "total": 53.0, - "user": 10.0}, - {"cpu_number": 14, + "user": 0.0}, + {"cpu_number": 4, "dpc": None, "guest": 0.0, "guest_nice": 0.0, - "idle": 44.0, + "idle": 39.0, "interrupt": None, - "iowait": 2.0, + "iowait": 1.0, "irq": 0.0, "key": "cpu_number", "nice": 0.0, "softirq": 0.0, "steal": 0.0, - "system": 3.0, - "total": 56.0, - "user": 7.0}, - {"cpu_number": 15, + "system": 5.0, + "total": 61.0, + "user": 1.0}, + {"cpu_number": 5, "dpc": None, "guest": 0.0, "guest_nice": 0.0, - "idle": 44.0, + "idle": 47.0, + "interrupt": None, + "iowait": 0.0, + "irq": 0.0, + "key": "cpu_number", + "nice": 0.0, + "softirq": 0.0, + "steal": 0.0, + "system": 0.0, + "total": 53.0, + "user": 0.0}, + {"cpu_number": 6, + "dpc": None, + "guest": 0.0, + "guest_nice": 0.0, + "idle": 42.0, + "interrupt": None, + "iowait": 0.0, + "irq": 0.0, + "key": "cpu_number", + "nice": 0.0, + "softirq": 0.0, + "steal": 0.0, + "system": 4.0, + "total": 58.0, + "user": 0.0}, + {"cpu_number": 7, + "dpc": None, + "guest": 0.0, + "guest_nice": 0.0, + "idle": 24.0, + "interrupt": None, + "iowait": 0.0, + "irq": 0.0, + "key": "cpu_number", + "nice": 0.0, + "softirq": 0.0, + "steal": 0.0, + "system": 10.0, + "total": 76.0, + "user": 11.0}, + {"cpu_number": 8, + "dpc": None, + "guest": 0.0, + "guest_nice": 0.0, + "idle": 46.0, + "interrupt": None, + "iowait": 0.0, + "irq": 0.0, + "key": "cpu_number", + "nice": 0.0, + "softirq": 0.0, + "steal": 0.0, + "system": 0.0, + "total": 54.0, + "user": 1.0}, + {"cpu_number": 9, + "dpc": None, + "guest": 0.0, + "guest_nice": 0.0, + "idle": 46.0, + "interrupt": None, + "iowait": 0.0, + "irq": 0.0, + "key": "cpu_number", + "nice": 0.0, + "softirq": 0.0, + "steal": 0.0, + "system": 0.0, + "total": 54.0, + "user": 0.0}, + {"cpu_number": 10, + "dpc": None, + "guest": 0.0, + "guest_nice": 0.0, + "idle": 46.0, "interrupt": None, "iowait": 0.0, "irq": 0.0, @@ -1667,9 +1554,84 @@ Get plugin stats:: "softirq": 0.0, "steal": 0.0, "system": 1.0, - "total": 56.0, - "user": 13.0}], - "swap": 68.5} + "total": 54.0, + "user": 0.0}, + {"cpu_number": 11, + "dpc": None, + "guest": 0.0, + "guest_nice": 0.0, + "idle": 47.0, + "interrupt": None, + "iowait": 0.0, + "irq": 0.0, + "key": "cpu_number", + "nice": 0.0, + "softirq": 0.0, + "steal": 0.0, + "system": 0.0, + "total": 53.0, + "user": 0.0}, + {"cpu_number": 12, + "dpc": None, + "guest": 0.0, + "guest_nice": 0.0, + "idle": 46.0, + "interrupt": None, + "iowait": 0.0, + "irq": 0.0, + "key": "cpu_number", + "nice": 0.0, + "softirq": 0.0, + "steal": 0.0, + "system": 1.0, + "total": 54.0, + "user": 1.0}, + {"cpu_number": 13, + "dpc": None, + "guest": 0.0, + "guest_nice": 0.0, + "idle": 46.0, + "interrupt": None, + "iowait": 0.0, + "irq": 0.0, + "key": "cpu_number", + "nice": 0.0, + "softirq": 0.0, + "steal": 0.0, + "system": 1.0, + "total": 54.0, + "user": 0.0}, + {"cpu_number": 14, + "dpc": None, + "guest": 0.0, + "guest_nice": 0.0, + "idle": 46.0, + "interrupt": None, + "iowait": 0.0, + "irq": 0.0, + "key": "cpu_number", + "nice": 0.0, + "softirq": 0.0, + "steal": 0.0, + "system": 0.0, + "total": 54.0, + "user": 0.0}, + {"cpu_number": 15, + "dpc": None, + "guest": 0.0, + "guest_nice": 0.0, + "idle": 46.0, + "interrupt": None, + "iowait": 0.0, + "irq": 0.0, + "key": "cpu_number", + "nice": 0.0, + "softirq": 0.0, + "steal": 0.0, + "system": 0.0, + "total": 54.0, + "user": 0.0}], + "swap": 67.9} Fields descriptions: @@ -1707,14 +1669,14 @@ Get plugin stats:: "label": "Ambient", "type": "temperature_core", "unit": "C", - "value": 34, + "value": 37, "warning": 0}, {"critical": None, "key": "label", "label": "Ambient 3", "type": "temperature_core", "unit": "C", - "value": 29, + "value": 30, "warning": 0}] Fields descriptions: @@ -1776,7 +1738,7 @@ Get a specific item when field matches the given value:: "label": "Ambient", "type": "temperature_core", "unit": "C", - "value": 34, + "value": 37, "warning": 0}]} GET smart @@ -1794,10 +1756,10 @@ Get plugin stats:: # curl http://localhost:61208/api/4/system {"hostname": "nicolargo-xps15", - "hr_name": "Ubuntu 24.04 64bit / Linux 6.11.0-21-generic", + "hr_name": "Ubuntu 24.04 64bit / Linux 6.11.0-24-generic", "linux_distro": "Ubuntu 24.04", "os_name": "Linux", - "os_version": "6.11.0-21-generic", + "os_version": "6.11.0-24-generic", "platform": "64bit"} Fields descriptions: @@ -1820,7 +1782,7 @@ GET uptime Get plugin stats:: # curl http://localhost:61208/api/4/uptime - "2 days, 1:33:36" + "1 day, 17:51:58" GET version ----------- @@ -1865,8 +1827,8 @@ Get plugin stats:: # curl http://localhost:61208/api/4/wifi [{"key": "ssid", - "quality_level": -61.0, - "quality_link": 49.0, + "quality_level": -59.0, + "quality_link": 51.0, "ssid": "wlp0s20f3"}] Get a specific field:: @@ -1878,8 +1840,8 @@ Get a specific item when field matches the given value:: # curl http://localhost:61208/api/4/wifi/ssid/value/wlp0s20f3 {"wlp0s20f3": [{"key": "ssid", - "quality_level": -61.0, - "quality_link": 49.0, + "quality_level": -59.0, + "quality_link": 51.0, "ssid": "wlp0s20f3"}]} GET all stats @@ -1943,34 +1905,34 @@ GET stats history History of a plugin:: # curl http://localhost:61208/api/4/cpu/history - {"system": [["2025-04-21T10:37:33.438604", 4.1], - ["2025-04-21T10:37:34.567313", 1.5], - ["2025-04-21T10:37:35.592424", 1.5]], - "user": [["2025-04-21T10:37:33.438600", 16.2], - ["2025-04-21T10:37:34.567309", 2.5], - ["2025-04-21T10:37:35.592420", 2.5]]} + {"system": [["2025-05-27T10:44:35.577810", 2.7], + ["2025-05-27T10:44:36.687892", 0.9], + ["2025-05-27T10:44:37.721905", 0.9]], + "user": [["2025-05-27T10:44:35.577806", 3.9], + ["2025-05-27T10:44:36.687889", 1.0], + ["2025-05-27T10:44:37.721902", 1.0]]} Limit history to last 2 values:: # curl http://localhost:61208/api/4/cpu/history/2 - {"system": [["2025-04-21T10:37:34.567313", 1.5], - ["2025-04-21T10:37:35.592424", 1.5]], - "user": [["2025-04-21T10:37:34.567309", 2.5], - ["2025-04-21T10:37:35.592420", 2.5]]} + {"system": [["2025-05-27T10:44:36.687892", 0.9], + ["2025-05-27T10:44:37.721905", 0.9]], + "user": [["2025-05-27T10:44:36.687889", 1.0], + ["2025-05-27T10:44:37.721902", 1.0]]} History for a specific field:: # curl http://localhost:61208/api/4/cpu/system/history - {"system": [["2025-04-21T10:37:32.236268", 4.1], - ["2025-04-21T10:37:33.438604", 4.1], - ["2025-04-21T10:37:34.567313", 1.5], - ["2025-04-21T10:37:35.592424", 1.5]]} + {"system": [["2025-05-27T10:44:34.399773", 2.7], + ["2025-05-27T10:44:35.577810", 2.7], + ["2025-05-27T10:44:36.687892", 0.9], + ["2025-05-27T10:44:37.721905", 0.9]]} Limit history for a specific field to last 2 values:: # curl http://localhost:61208/api/4/cpu/system/history - {"system": [["2025-04-21T10:37:34.567313", 1.5], - ["2025-04-21T10:37:35.592424", 1.5]]} + {"system": [["2025-05-27T10:44:36.687892", 0.9], + ["2025-05-27T10:44:37.721905", 0.9]]} GET limits (used for thresholds) -------------------------------- @@ -2130,7 +2092,9 @@ All limits/thresholds:: "16", "17", "18", - "19"]}, + "19"], + "processlist_status_critical": ["Z", "D"], + "processlist_status_ok": ["R", "W", "P", "I"]}, "programlist": {"history_size": 1200.0}, "psutilversion": {"history_size": 1200.0}, "quicklook": {"history_size": 1200.0, diff --git a/docs/gw/influxdb.rst b/docs/gw/influxdb.rst index 0f0e5de3..21d520da 100644 --- a/docs/gw/influxdb.rst +++ b/docs/gw/influxdb.rst @@ -17,19 +17,19 @@ Glances InfluxDB data model: +---------------+-----------------------+-----------------------+ | Measurement | Fields | Tags | +===============+=======================+=======================+ -| cpu | user | hostname | +| cpu | user | hostname | | | system | | | | iowait... | | +---------------+-----------------------+-----------------------+ | network | read_bytes | hostname | | | write_bytes | disk_name | | | time_since_update... | | -|  | | | +| | | | +---------------+-----------------------+-----------------------+ | diskio | rx | hostname | | | tx | interface_name | | | time_since_update... | | -|  | | | +| | | | +---------------+-----------------------+-----------------------+ | docker | cpu_percent | hostname | | | memory_usage... | name | @@ -78,7 +78,7 @@ configuration file (no limit on columns number). Note: if you want to use SSL, please set 'protocol=https'. -InfluxDB v2 (from InfluxDB v1.8.x/Flux and InfluxDB v2.x) +InfluxDB v2 (from InfluxDB v1.8.x/Flux and InfluxDB foo.cpu + # => foo.mem + # You can also use dynamic values + #prefix=foo + # Following tags will be added for all measurements + # You can also use dynamic values. + # Note: hostname and name (for process) are always added as a tag + #tags=foo:bar,spam:eggs,domain:`domainname` + +and run Glances with: + +.. code-block:: console + + $ glances --export influxdb3 + +Note: if you want to use SSL, please set host with 'https' scheme instead of 'http'. + Grafana ------- diff --git a/docs/man/glances.1 b/docs/man/glances.1 index 08d946cf..894e71ab 100644 --- a/docs/man/glances.1 +++ b/docs/man/glances.1 @@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "GLANCES" "1" "Apr 21, 2025" "4.3.2_dev01" "Glances" +.TH "GLANCES" "1" "May 27, 2025" "4.3.2_dev01" "Glances" .SH NAME glances \- An eye on your system .SH SYNOPSIS diff --git a/glances/exports/export.py b/glances/exports/export.py index f0e08afb..40b4f555 100644 --- a/glances/exports/export.py +++ b/glances/exports/export.py @@ -11,7 +11,13 @@ I am your father... ...for all Glances exports IF. """ -from glances.globals import NoOptionError, NoSectionError, iteritems, iterkeys, json_dumps +from glances.globals import ( + NoOptionError, + NoSectionError, + iteritems, + iterkeys, + json_dumps, +) from glances.logger import logger from glances.timer import Counter @@ -21,12 +27,12 @@ class GlancesExport: # List of non exportable internal plugins non_exportable_plugins = [ - 'alert', - 'help', - 'plugin', - 'psutilversion', - 'quicklook', - 'version', + "alert", + "help", + "plugin", + "psutilversion", + "quicklook", + "version", ] def __init__(self, config=None, args=None): @@ -71,7 +77,7 @@ class GlancesExport: """Close the export module.""" logger.debug(f"Finalise export interface {self.export_name}") - def load_conf(self, section, mandatories=['host', 'port'], options=None): + def load_conf(self, section, mandatories=["host", "port"], options=None): """Load the export
configuration in the Glances configuration file. :param section: name of the export section to load @@ -112,7 +118,7 @@ class GlancesExport: """Return the value of the item 'key'.""" ret = None try: - ret = item[item['key']] + ret = item[item["key"]] except KeyError: logger.error(f"No 'key' available in {item}") if isinstance(ret, list): @@ -128,14 +134,77 @@ class GlancesExport: d_tags = {} if tags: try: - d_tags = dict([x.split(':') for x in tags.split(',')]) + d_tags = dict([x.split(":") for x in tags.split(",")]) except ValueError: # one of the 'key:value' pairs was missing - logger.info('Invalid tags passed: %s', tags) + logger.info("Invalid tags passed: %s", tags) d_tags = {} return d_tags + def normalize_for_influxdb(self, name, columns, points): + """Normalize data for the InfluxDB's data model. + + :return: a list of measurements. + """ + FIELD_TO_TAG = ["name", "cmdline", "type"] + ret = [] + + # Build initial dict by crossing columns and point + data_dict = dict(zip(columns, points)) + + # issue1871 - Check if a key exist. If a key exist, the value of + # the key should be used as a tag to identify the measurement. + keys_list = [k.split(".")[0] for k in columns if k.endswith(".key")] + if not keys_list: + keys_list = [None] + + for measurement in keys_list: + # Manage field + if measurement is not None: + fields = { + k.replace(f"{measurement}.", ""): data_dict[k] for k in data_dict if k.startswith(f"{measurement}.") + } + else: + fields = data_dict + # Transform to InfluxDB data model + # https://docs.influxdata.com/influxdb/v1.8/write_protocols/line_protocol_reference/ + for k in fields: + # Do not export empty (None) value + if fields[k] is None: + continue + # Convert numerical to float + try: + fields[k] = float(fields[k]) + except (TypeError, ValueError): + # Convert others to string + try: + fields[k] = str(fields[k]) + except (TypeError, ValueError): + pass + # Manage tags + tags = self.parse_tags(self.tags) + # Add the hostname as a tag + tags["hostname"] = self.hostname + if "hostname" in fields: + fields.pop("hostname") + # Others tags... + if "key" in fields and fields["key"] in fields: + # Create a tag from the key + # Tag should be an string (see InfluxDB data model) + tags[fields["key"]] = str(fields[fields["key"]]) + # Remove it from the field list (can not be a field and a tag) + fields.pop(fields["key"]) + # Add name as a tag (example for the process list) + for k in FIELD_TO_TAG: + if k in fields: + tags[k] = str(fields[k]) + # Remove it from the field list (can not be a field and a tag) + fields.pop(k) + # Add the measurement to the list + ret.append({"measurement": name, "tags": tags, "fields": fields}) + return ret + def plugins_to_export(self, stats): """Return the list of plugins to export. @@ -177,7 +246,7 @@ class GlancesExport: if isinstance(all_stats[plugin], dict): all_stats[plugin].update(all_limits[plugin]) # Remove the _disable field - all_stats[plugin].pop(f'{plugin}_disable', None) + all_stats[plugin].pop(f"{plugin}_disable", None) elif isinstance(all_stats[plugin], list): # TypeError: string indices must be integers (Network plugin) #1054 for i in all_stats[plugin]: @@ -197,17 +266,17 @@ class GlancesExport: if isinstance(stats, dict): # Stats is a dict # Is there a key ? - if 'key' in iterkeys(stats) and stats['key'] in iterkeys(stats): - pre_key = '{}.'.format(stats[stats['key']]) + if "key" in iterkeys(stats) and stats["key"] in iterkeys(stats): + pre_key = "{}.".format(stats[stats["key"]]) else: - pre_key = '' + pre_key = "" # Walk through the dict for key, value in sorted(iteritems(stats)): if isinstance(value, bool): value = json_dumps(value).decode() if isinstance(value, list): - value = ' '.join([str(v) for v in value]) + value = " ".join([str(v) for v in value]) if isinstance(value, dict): item_names, item_values = self.build_export(value) diff --git a/glances/exports/glances_influxdb/__init__.py b/glances/exports/glances_influxdb/__init__.py index 0de532c9..33a87148 100644 --- a/glances/exports/glances_influxdb/__init__.py +++ b/glances/exports/glances_influxdb/__init__.py @@ -17,8 +17,6 @@ from influxdb.client import InfluxDBClientError from glances.exports.export import GlancesExport from glances.logger import logger -FIELD_TO_TAG = ['name', 'cmdline', 'type'] - class Export(GlancesExport): """This class manages the InfluxDB export module.""" @@ -33,20 +31,22 @@ class Export(GlancesExport): self.db = None # Optional configuration keys - self.protocol = 'http' + self.protocol = "http" self.prefix = None self.tags = None self.hostname = None # Load the InfluxDB configuration file self.export_enable = self.load_conf( - 'influxdb', mandatories=['host', 'port', 'user', 'password', 'db'], options=['protocol', 'prefix', 'tags'] + "influxdb", + mandatories=["host", "port", "user", "password", "db"], + options=["protocol", "prefix", "tags"], ) if not self.export_enable: - exit('Missing INFLUXDB version 1 config') + exit("Missing influxdb config") # The hostname is always add as a tag - self.hostname = node().split('.')[0] + self.hostname = node().split(".")[0] # Init the InfluxDB client self.client = self.init() @@ -57,7 +57,7 @@ class Export(GlancesExport): return None # Correct issue #1530 - if self.protocol is not None and (self.protocol.lower() == 'https'): + if self.protocol is not None and (self.protocol.lower() == "https"): ssl = True else: ssl = False @@ -72,7 +72,7 @@ class Export(GlancesExport): password=self.password, database=self.db, ) - get_all_db = [i['name'] for i in db.get_list_database()] + get_all_db = [i["name"] for i in db.get_list_database()] except InfluxDBClientError as e: logger.critical(f"Cannot connect to InfluxDB database '{self.db}' ({e})") sys.exit(2) @@ -85,76 +85,20 @@ class Export(GlancesExport): return db - def _normalize(self, name, columns, points): - """Normalize data for the InfluxDB's data model. - - :return: a list of measurements. - """ - ret = [] - - # Build initial dict by crossing columns and point - data_dict = dict(zip(columns, points)) - - # issue1871 - Check if a key exist. If a key exist, the value of - # the key should be used as a tag to identify the measurement. - keys_list = [k.split('.')[0] for k in columns if k.endswith('.key')] - if not keys_list: - keys_list = [None] - - for measurement in keys_list: - # Manage field - if measurement is not None: - fields = { - k.replace(f'{measurement}.', ''): data_dict[k] for k in data_dict if k.startswith(f'{measurement}.') - } - else: - fields = data_dict - # Transform to InfluxDB data model - # https://docs.influxdata.com/influxdb/v1.8/write_protocols/line_protocol_reference/ - for k in fields: - # Do not export empty (None) value - if fields[k] is None: - continue - # Convert numerical to float - try: - fields[k] = float(fields[k]) - except (TypeError, ValueError): - # Convert others to string - try: - fields[k] = str(fields[k]) - except (TypeError, ValueError): - pass - # Manage tags - tags = self.parse_tags(self.tags) - if 'key' in fields and fields['key'] in fields: - # Create a tag from the key - # Tag should be an string (see InfluxDB data model) - tags[fields['key']] = str(fields[fields['key']]) - # Remove it from the field list (can not be a field and a tag) - fields.pop(fields['key']) - # Add the hostname as a tag - tags['hostname'] = self.hostname - # Add name as a tag (example for the process list) - for k in FIELD_TO_TAG: - if k in fields: - tags[k] = str(fields[k]) - # Remove it from the field list (can not be a field and a tag) - fields.pop(k) - # Add the measurement to the list - ret.append({'measurement': name, 'tags': tags, 'fields': fields}) - return ret - def export(self, name, columns, points): """Write the points to the InfluxDB server.""" # Manage prefix if self.prefix is not None: - name = self.prefix + '.' + name + name = self.prefix + "." + name # Write input to the InfluxDB database if not points: logger.debug(f"Cannot export empty {name} stats to InfluxDB") else: try: - self.client.write_points(self._normalize(name, columns, points), time_precision="s") + self.client.write_points( + self.normalize_for_influxdb(name, columns, points), + time_precision="s", + ) except Exception as e: # Log level set to warning instead of error (see: issue #1561) logger.warning(f"Cannot export {name} stats to InfluxDB ({e})") diff --git a/glances/exports/glances_influxdb2/__init__.py b/glances/exports/glances_influxdb2/__init__.py index 0dc3b345..048d4634 100644 --- a/glances/exports/glances_influxdb2/__init__.py +++ b/glances/exports/glances_influxdb2/__init__.py @@ -6,7 +6,7 @@ # SPDX-License-Identifier: LGPL-3.0-only # -"""InfluxDB (from to InfluxDB 1.8+) interface class.""" +"""InfluxDB (from to InfluxDB 1.8+ to <3.0) interface class.""" import sys from platform import node @@ -16,8 +16,6 @@ from influxdb_client import InfluxDBClient, WriteOptions from glances.exports.export import GlancesExport from glances.logger import logger -FIELD_TO_TAG = ['name', 'cmdline', 'type'] - class Export(GlancesExport): """This class manages the InfluxDB export module.""" @@ -32,7 +30,7 @@ class Export(GlancesExport): self.token = None # Optional configuration keys - self.protocol = 'http' + self.protocol = "http" self.prefix = None self.tags = None self.hostname = None @@ -40,12 +38,12 @@ class Export(GlancesExport): # Load the InfluxDB configuration file self.export_enable = self.load_conf( - 'influxdb2', - mandatories=['host', 'port', 'user', 'password', 'org', 'bucket', 'token'], - options=['protocol', 'prefix', 'tags', 'interval'], + "influxdb2", + mandatories=["host", "port", "user", "password", "org", "bucket", "token"], + options=["protocol", "prefix", "tags", "interval"], ) if not self.export_enable: - exit('Missing influxdb2 config') + exit("Missing influxdb2 config") # Interval between two exports (in seconds) if self.interval is None: @@ -60,7 +58,7 @@ class Export(GlancesExport): logger.debug(f"InfluxDB export interval is set to {self.interval} seconds") # The hostname is always add as a tag - self.hostname = node().split('.')[0] + self.hostname = node().split(".")[0] # Init the InfluxDB client self.client = self.init() @@ -70,10 +68,16 @@ class Export(GlancesExport): if not self.export_enable: return None - url = f'{self.protocol}://{self.host}:{self.port}' + url = f"{self.protocol}://{self.host}:{self.port}" try: # See docs: https://influxdb-client.readthedocs.io/en/stable/api.html#influxdbclient - client = InfluxDBClient(url=url, enable_gzip=False, verify_ssl=False, org=self.org, token=self.token) + client = InfluxDBClient( + url=url, + enable_gzip=False, + verify_ssl=False, + org=self.org, + token=self.token, + ) except Exception as e: logger.critical(f"Cannot connect to InfluxDB server '{url}' ({e})") sys.exit(2) @@ -93,76 +97,22 @@ class Export(GlancesExport): ) ) - def _normalize(self, name, columns, points): - """Normalize data for the InfluxDB's data model. - - :return: a list of measurements. - """ - ret = [] - - # Build initial dict by crossing columns and point - data_dict = dict(zip(columns, points)) - - # issue1871 - Check if a key exist. If a key exist, the value of - # the key should be used as a tag to identify the measurement. - keys_list = [k.split('.')[0] for k in columns if k.endswith('.key')] - if not keys_list: - keys_list = [None] - - for measurement in keys_list: - # Manage field - if measurement is not None: - fields = { - k.replace(f'{measurement}.', ''): data_dict[k] for k in data_dict if k.startswith(f'{measurement}.') - } - else: - fields = data_dict - # Transform to InfluxDB datamodel - # https://docs.influxdata.com/influxdb/v2.0/reference/syntax/line-protocol/ - for k in fields: - # Do not export empty (None) value - if fields[k] is None: - continue - # Convert numerical to float - try: - fields[k] = float(fields[k]) - except (TypeError, ValueError): - # Convert others to string - try: - fields[k] = str(fields[k]) - except (TypeError, ValueError): - pass - # Manage tags - tags = self.parse_tags(self.tags) - if 'key' in fields and fields['key'] in fields: - # Create a tag from the key - # Tag should be an string (see InfluxDB data model) - tags[fields['key']] = str(fields[fields['key']]) - # Remove it from the field list (can not be a field and a tag) - fields.pop(fields['key']) - # Add the hostname as a tag - tags['hostname'] = self.hostname - # Add name as a tag (example for the process list) - for k in FIELD_TO_TAG: - if k in fields: - tags[k] = str(fields[k]) - # Remove it from the field list (can not be a field and a tag) - fields.pop(k) - # Add the measurement to the list - ret.append({'measurement': name, 'tags': tags, 'fields': fields}) - return ret - def export(self, name, columns, points): """Write the points to the InfluxDB server.""" # Manage prefix if self.prefix is not None: - name = self.prefix + '.' + name + name = self.prefix + "." + name # Write input to the InfluxDB database if not points: logger.debug(f"Cannot export empty {name} stats to InfluxDB") else: try: - self.client.write(self.bucket, self.org, self._normalize(name, columns, points), time_precision="s") + self.client.write( + self.bucket, + self.org, + self.normalize_for_influxdb(name, columns, points), + time_precision="s", + ) except Exception as e: # Log level set to warning instead of error (see: issue #1561) logger.warning(f"Cannot export {name} stats to InfluxDB ({e})") diff --git a/glances/exports/glances_influxdb3/__init__.py b/glances/exports/glances_influxdb3/__init__.py new file mode 100644 index 00000000..70e5123d --- /dev/null +++ b/glances/exports/glances_influxdb3/__init__.py @@ -0,0 +1,98 @@ +# +# This file is part of Glances. +# +# SPDX-FileCopyrightText: 2025 Nicolas Hennion +# +# SPDX-License-Identifier: LGPL-3.0-only +# + +"""InfluxDB (for InfluxDB 3.x) interface class.""" + +import sys +from platform import node + +from influxdb_client_3 import InfluxDBClient3 + +from glances.exports.export import GlancesExport +from glances.logger import logger + + +class Export(GlancesExport): + """This class manages the InfluxDB export module.""" + + def __init__(self, config=None, args=None): + """Init the InfluxDB export IF.""" + super().__init__(config=config, args=args) + + # Mandatory configuration keys (additional to host and port) + self.host = None + self.port = None + self.org = None + self.database = None + self.token = None + + # Optional configuration keys + self.prefix = None + self.tags = None + self.hostname = None + + # Load the InfluxDB configuration file + self.export_enable = self.load_conf( + "influxdb3", + mandatories=["host", "port", "org", "database", "token"], + options=["prefix", "tags"], + ) + if not self.export_enable: + exit("Missing influxdb3 config") + + # The hostname is always add as a tag + self.hostname = node().split(".")[0] + + # Init the InfluxDB client + self.client = self.init() + + def init(self): + """Init the connection to the InfluxDB server.""" + if not self.export_enable: + return None + + try: + db = InfluxDBClient3( + host=self.host, + org=self.org, + database=self.database, + token=self.token, + ) + except Exception as e: + logger.critical(f"Cannot connect to InfluxDB database '{self.database}' ({e})") + sys.exit(2) + + if self.database == db._database: + logger.info( + f"Stats will be exported to InfluxDB server {self.host}:{self.port} in {self.database} database" + ) + else: + logger.critical(f"InfluxDB database '{self.database}' did not exist. Please create it") + sys.exit(2) + + return db + + def export(self, name, columns, points): + """Write the points to the InfluxDB server.""" + # Manage prefix + if self.prefix is not None: + name = self.prefix + "." + name + # Write input to the InfluxDB database + if not points: + logger.debug(f"Cannot export empty {name} stats to InfluxDB") + else: + try: + self.client.write( + record=self.normalize_for_influxdb(name, columns, points), + time_precision="s", + ) + except Exception as e: + # Log level set to warning instead of error (see: issue #1561) + logger.warning(f"Cannot export {name} stats to InfluxDB ({e})") + else: + logger.debug(f"Export {name} stats to InfluxDB") diff --git a/optional-requirements.txt b/optional-requirements.txt index dc3fc345..87c8380e 100644 --- a/optional-requirements.txt +++ b/optional-requirements.txt @@ -11,7 +11,8 @@ fastapi>=0.82.0 graphitesender hddtemp influxdb>=1.0.0 # For InfluxDB < 1.8 -influxdb-client # For InfluxDB >= 1.8 +influxdb-client # For InfluxDB >= 1.8 and < 3.x +influxdb3-python # For InfluxDB 3.x jinja2 kafka-python netifaces2 diff --git a/pyproject.toml b/pyproject.toml index 271178fe..3c986be1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -75,6 +75,7 @@ export = [ "ibmcloudant", "influxdb-client", "influxdb>=1.0.0", + "influxdb3-python", "kafka-python", "paho-mqtt", "pika", diff --git a/tests/test_export_influxdb.sh b/tests/test_export_influxdb_v1.sh similarity index 62% rename from tests/test_export_influxdb.sh rename to tests/test_export_influxdb_v1.sh index d5757d7b..ebfadc71 100755 --- a/tests/test_export_influxdb.sh +++ b/tests/test_export_influxdb_v1.sh @@ -6,8 +6,8 @@ # Exit on error set -e -echo "Starting InfluxDB container..." -docker run -d --name influxdb-for-glances \ +echo "Starting InfluxDB version 1 container..." +docker run -d --name influxdb-v1-for-glances \ -p 8086:8086 \ influxdb:1.11 @@ -21,8 +21,8 @@ for i in {1..30}; do if [ $i -eq 30 ]; then echo "Error: Timed out waiting for InfluxDB to start" - docker stop influxdb-for-glances - docker rm influxdb-for-glances + docker stop influxdb-v1-for-glances + docker rm influxdb-v1-for-glances exit 1 fi @@ -32,8 +32,8 @@ done # Create the glances database echo "Creating 'glances' database..." -docker exec influxdb-for-glances influx -execute 'DROP DATABASE glances' -docker exec influxdb-for-glances influx -execute 'CREATE DATABASE glances' +docker exec influxdb-v1-for-glances influx -execute 'DROP DATABASE glances' +docker exec influxdb-v1-for-glances influx -execute 'CREATE DATABASE glances' # Run glances with export to InfluxDB, stopping after 10 writes # This will run synchronously now since we're using --stop-after @@ -42,30 +42,30 @@ echo "Glances to export system stats to InfluxDB (duration: ~ 20 seconds)" echo "Checking if Glances data was successfully exported to InfluxDB..." # Query to check if data exists in the glances database -MEASUREMENT_COUNT=$(docker exec influxdb-for-glances influx -database 'glances' -format json -execute 'SHOW MEASUREMENTS' | jq '.results[0].series[0].values' | jq length) +MEASUREMENT_COUNT=$(docker exec influxdb-v1-for-glances influx -database 'glances' -format json -execute 'SHOW MEASUREMENTS' | jq '.results[0].series[0].values' | jq length) if [ "$MEASUREMENT_COUNT" -eq 0 ]; then echo "Error: No Glances measurement found in the InfluxDB database" - docker stop influxdb-for-glances - docker rm influxdb-for-glances + docker stop influxdb-v1-for-glances + docker rm influxdb-v1-for-glances exit 1 else echo "Success! Found $MEASUREMENT_COUNT measurements in the Glances database." fi # Query to check if data exists in the glances database -SERIE_COUNT=$(docker exec influxdb-for-glances influx -database 'glances' -format json -execute 'SELECT * FROM cpu' | jq '.results[0].series[0].values' | jq length) +SERIE_COUNT=$(docker exec influxdb-v1-for-glances influx -database 'glances' -format json -execute 'SELECT * FROM cpu' | jq '.results[0].series[0].values' | jq length) if [ "$SERIE_COUNT" -eq 9 ]; then echo "Success! Found $SERIE_COUNT series in the Glances database (CPU plugin)." else echo "Error: Found $SERIE_COUNT series instead of 9" - docker stop influxdb-for-glances - docker rm influxdb-for-glances + docker stop influxdb-v1-for-glances + docker rm influxdb-v1-for-glances exit 1 fi # Stop and remove the InfluxDB container echo "Stopping and removing InfluxDB container..." -docker stop influxdb-for-glances -docker rm influxdb-for-glances +docker stop influxdb-v1-for-glances +docker rm influxdb-v1-for-glances echo "Script completed successfully!" \ No newline at end of file diff --git a/tests/test_export_influxdb_v3.sh b/tests/test_export_influxdb_v3.sh new file mode 100755 index 00000000..32fba08d --- /dev/null +++ b/tests/test_export_influxdb_v3.sh @@ -0,0 +1,75 @@ +#!/bin/bash +# Pre-requisites: +# - docker +# - jq + +# Exit on error +set -e + +echo "Starting InfluxDB version 3 (Core) container..." +docker run -d --name influxdb-v3-for-glances \ + -p 8181:8181 \ + influxdb:3-core --node-id host01 --object-store memory + +# Wait for InfluxDB to be ready (5 seconds) +echo "Waiting for InfluxDB to start..." +sleep 5 + +# Create the token +echo "Creating InfluxDB token..." +TOKEN_RETURN=$(docker exec influxdb-v3-for-glances influxdb3 create token --admin) +TOKEN=$(echo -n $TOKEN_RETURN | awk '{ print $6 }') +echo "Token: $TOKEN" + +# Create a new configuration for the test +echo "Creating a temporary Glances configuration file with the token in /tmp/glances.conf..." +sed "s/PUT_YOUR_INFLUXDB3_TOKEN_HERE/$TOKEN/g" ./conf/glances.conf > /tmp/glances.conf + +# Create the glances database +echo "Creating 'glances' database..." +docker exec -e "INFLUXDB3_AUTH_TOKEN=$TOKEN" influxdb-v3-for-glances influxdb3 create database glances +docker exec -e "INFLUXDB3_AUTH_TOKEN=$TOKEN" influxdb-v3-for-glances influxdb3 show databases + +# Get the list of tables in the glances database after creation +TABLES_INIT=$(docker exec -e "INFLUXDB3_AUTH_TOKEN=$TOKEN" influxdb-v3-for-glances influxdb3 query --database glances --format json 'SHOW TABLES') +TABLES_INIT_COUNT=$(echo "$TABLES_INIT" | jq length) + +# Run glances with export to InfluxDB, stopping after 10 writes +# This will run synchronously now since we're using --stop-after +echo "Glances to export system stats to InfluxDB (duration: ~ 20 seconds)" +./venv/bin/python -m glances --config /tmp/glances.conf --export influxdb3 --stop-after 10 --quiet + +echo "Checking if Glances data was successfully exported to InfluxDB..." +# Query to check if data exists in the glances database +TABLES=$(docker exec -e "INFLUXDB3_AUTH_TOKEN=$TOKEN" influxdb-v3-for-glances influxdb3 query --database glances --format json 'SHOW TABLES') +TABLES_COUNT=$(echo "$TABLES" | jq length) +if [ "$TABLES_COUNT" -eq "$TABLES_INIT_COUNT" ]; then + echo "Error: No Glances measurement found in the InfluxDB database" + docker stop influxdb-v3-for-glances + docker rm influxdb-v3-for-glances + exit 1 +else + echo "Success! Found $TABLES_COUNT measurements in the Glances database." +fi + +# Query to check if data exists in the glances database +SERIE=$(docker exec -e "INFLUXDB3_AUTH_TOKEN=$TOKEN" influxdb-v3-for-glances influxdb3 query --database glances --format json 'SELECT * FROM cpu') +SERIE_COUNT=$(echo "$SERIE" | jq length) +if [ "$SERIE_COUNT" -eq 9 ]; then + echo "Success! Found $SERIE_COUNT series in the Glances database (CPU plugin)." +else + echo "Error: Found $SERIE_COUNT series instead of 9" + docker stop influxdb-v3-for-glances + docker rm influxdb-v3-for-glances + exit 1 +fi + +# Stop and remove the InfluxDB container +echo "Stopping and removing InfluxDB container..." +docker stop influxdb-v3-for-glances +docker rm influxdb-v3-for-glances + +# Remove the temporary configuration file +rm -f /tmp/glances.conf + +echo "Script completed successfully!" \ No newline at end of file