397 lines
18 KiB
YAML
397 lines
18 KiB
YAML
---
|
|
# Builtin Playbook: Collecte d'informations système complètes
|
|
# Ce playbook collecte CPU, mémoire, disque, OS et réseau en une seule exécution
|
|
# Les résultats sont formatés en JSON pour être parsés par l'application
|
|
|
|
- name: Collect Complete System Information
|
|
hosts: all
|
|
become: false
|
|
gather_facts: true
|
|
vars:
|
|
_builtin_playbook: true
|
|
_builtin_id: collect_system_info
|
|
_collect_metrics: true
|
|
tasks:
|
|
- name: Gather additional facts
|
|
ansible.builtin.setup:
|
|
gather_subset:
|
|
- hardware
|
|
- network
|
|
- virtual
|
|
|
|
- name: Get CPU load averages
|
|
ansible.builtin.shell: cat /proc/loadavg | awk '{print $1, $2, $3}'
|
|
register: cpu_load
|
|
changed_when: false
|
|
ignore_errors: true
|
|
|
|
- name: Parse CPU load parts safely
|
|
ansible.builtin.set_fact:
|
|
cpu_load_parts: "{{ cpu_load.stdout.split() if (cpu_load is defined and cpu_load.stdout is defined) else [] }}"
|
|
|
|
- name: Get CPU temperature (if available)
|
|
ansible.builtin.shell: |
|
|
temp=""
|
|
|
|
# 1) Essayer les capteurs standard du noyau (/sys/class/thermal)
|
|
for z in /sys/class/thermal/thermal_zone*/temp; do
|
|
if [ -f "$z" ]; then
|
|
raw=$(cat "$z" 2>/dev/null || echo "")
|
|
if [ -n "$raw" ]; then
|
|
temp=$(awk -v v="$raw" 'BEGIN { if (v != "" && v != "0") printf "%.1f", v/1000; }')
|
|
if [ -n "$temp" ]; then
|
|
break
|
|
fi
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# 2) Spécifique Raspberry Pi : vcgencmd measure_temp
|
|
if [ -z "$temp" ] && command -v vcgencmd >/dev/null 2>&1; then
|
|
raw=$(vcgencmd measure_temp 2>/dev/null | sed 's/[^0-9\.]*//g')
|
|
if [ -n "$raw" ]; then
|
|
temp=$raw
|
|
fi
|
|
fi
|
|
|
|
# 3) Utiliser lm-sensors si disponible
|
|
if [ -z "$temp" ] && command -v sensors >/dev/null 2>&1; then
|
|
raw=$(sensors 2>/dev/null | awk '/^Package id 0:|^Tctl:|^CPU Temp:|^temp1:/{gsub("+","",$2); gsub("°C","",$2); print $2; exit}')
|
|
if [ -n "$raw" ]; then
|
|
temp=$raw
|
|
fi
|
|
fi
|
|
|
|
if [ -z "$temp" ]; then
|
|
echo "null"
|
|
else
|
|
printf "%.1f" "$temp"
|
|
fi
|
|
register: cpu_temp
|
|
changed_when: false
|
|
ignore_errors: true
|
|
|
|
- name: Get memory info from /proc/meminfo (Linux)
|
|
ansible.builtin.shell: |
|
|
awk '/MemTotal/{total=$2} /MemFree/{free=$2} /MemAvailable/{avail=$2} /Buffers/{buf=$2} /^Cached/{cache=$2} /SwapTotal/{stotal=$2} /SwapFree/{sfree=$2} END{
|
|
used=total-avail;
|
|
usage=used/total*100;
|
|
sused=stotal-sfree;
|
|
susage=(stotal>0)?sused/stotal*100:0;
|
|
printf "{\"total_mb\":%d,\"used_mb\":%d,\"free_mb\":%d,\"usage_percent\":%.1f,\"swap_total_mb\":%d,\"swap_used_mb\":%d,\"swap_usage_percent\":%.1f}",
|
|
total/1024, used/1024, avail/1024, usage, stotal/1024, sused/1024, susage
|
|
}' /proc/meminfo 2>/dev/null || echo ''
|
|
register: memory_info_linux
|
|
changed_when: false
|
|
ignore_errors: true
|
|
|
|
- name: Get memory info using sysctl (FreeBSD/TrueNAS)
|
|
ansible.builtin.shell: |
|
|
total=$(sysctl -n hw.physmem 2>/dev/null || echo 0)
|
|
free=$(sysctl -n vm.stats.vm.v_free_count 2>/dev/null || echo 0)
|
|
pagesize=$(sysctl -n hw.pagesize 2>/dev/null || echo 4096)
|
|
swap_total=$(swapinfo -k 2>/dev/null | awk 'NR==2 {print $2}' || echo 0)
|
|
swap_used=$(swapinfo -k 2>/dev/null | awk 'NR==2 {print $3}' || echo 0)
|
|
|
|
total_mb=$((total / 1024 / 1024))
|
|
free_mb=$((free * pagesize / 1024 / 1024))
|
|
used_mb=$((total_mb - free_mb))
|
|
usage=$(awk -v used=$used_mb -v total=$total_mb 'BEGIN {if(total>0) printf "%.1f", used/total*100; else print "0.0"}')
|
|
|
|
swap_total_mb=$((swap_total / 1024))
|
|
swap_used_mb=$((swap_used / 1024))
|
|
swap_usage=$(awk -v used=$swap_used_mb -v total=$swap_total_mb 'BEGIN {if(total>0) printf "%.1f", used/total*100; else print "0.0"}')
|
|
|
|
printf '{"total_mb":%d,"used_mb":%d,"free_mb":%d,"usage_percent":%s,"swap_total_mb":%d,"swap_used_mb":%d,"swap_usage_percent":%s}' \
|
|
$total_mb $used_mb $free_mb $usage $swap_total_mb $swap_used_mb $swap_usage
|
|
register: memory_info_bsd
|
|
changed_when: false
|
|
ignore_errors: true
|
|
when: memory_info_linux.stdout == ''
|
|
|
|
- name: Set memory info variable
|
|
ansible.builtin.set_fact:
|
|
memory_info_json: "{{ memory_info_linux.stdout if memory_info_linux.stdout != '' else (memory_info_bsd.stdout | default('{\"total_mb\":0,\"used_mb\":0,\"free_mb\":0,\"usage_percent\":0.0,\"swap_total_mb\":0,\"swap_used_mb\":0,\"swap_usage_percent\":0.0}')) }}"
|
|
|
|
- name: Parse memory info JSON
|
|
ansible.builtin.set_fact:
|
|
memory_info_parsed: "{{ memory_info_json if (memory_info_json is mapping) else (memory_info_json | from_json) }}"
|
|
|
|
- name: Get disk usage for all mount points
|
|
ansible.builtin.shell: |
|
|
if df --help 2>/dev/null | grep -q -- '--output'; then
|
|
df -BG --output=source,fstype,target,size,used,avail,pcent -x tmpfs -x devtmpfs -x squashfs 2>/dev/null | tail -n +2 | awk '{
|
|
gsub("G",""); gsub("%","");
|
|
dev=$1; fs=$2; mnt=$3;
|
|
gsub(/\\/,"\\\\",dev); gsub(/\"/,"\\\"",dev);
|
|
gsub(/\\/,"\\\\",fs); gsub(/\"/,"\\\"",fs);
|
|
gsub(/\\/,"\\\\",mnt); gsub(/\"/,"\\\"",mnt);
|
|
printf "{\"device\":\"%s\",\"filesystem\":\"%s\",\"mount\":\"%s\",\"total_gb\":%s,\"used_gb\":%s,\"free_gb\":%s,\"usage_percent\":%s}\n", dev, fs, mnt, $4, $5, $6, $7
|
|
}' | paste -sd "," | awk '{print "["$0"]"}'
|
|
else
|
|
echo "[]"
|
|
fi
|
|
register: disk_info
|
|
changed_when: false
|
|
ignore_errors: true
|
|
|
|
- name: Parse disk usage JSON safely
|
|
block:
|
|
- name: Parse disk usage JSON
|
|
ansible.builtin.set_fact:
|
|
disk_info_parsed: "{{ ((disk_info.stdout | default('[]', true) | trim) if ((disk_info.stdout | default('', true) | trim) | regex_search('^\\[')) else '[]') | from_json }}"
|
|
rescue:
|
|
- name: Fallback disk_info_parsed
|
|
ansible.builtin.set_fact:
|
|
disk_info_parsed: []
|
|
|
|
- name: Get disk devices layout (lsblk JSON if available)
|
|
ansible.builtin.shell: |
|
|
if command -v lsblk >/dev/null 2>&1; then
|
|
tmp="/tmp/lsblk_${$}.json"
|
|
if lsblk -J -b -o NAME,TYPE,SIZE,FSTYPE,MOUNTPOINT,MODEL,SERIAL,UUID >"$tmp" 2>/dev/null; then
|
|
cat "$tmp"
|
|
else
|
|
echo '{"blockdevices":[]}'
|
|
fi
|
|
rm -f "$tmp" >/dev/null 2>&1 || true
|
|
else
|
|
echo '{"blockdevices":[]}'
|
|
fi
|
|
register: disk_devices
|
|
changed_when: false
|
|
ignore_errors: true
|
|
|
|
- name: Parse disk devices JSON safely
|
|
block:
|
|
- name: Parse disk devices JSON
|
|
ansible.builtin.set_fact:
|
|
disk_devices_parsed: "{{ ((((disk_devices.stdout | default('{\"blockdevices\":[]}', true) | trim) if ((disk_devices.stdout | default('', true) | trim) | regex_search('^\\{')) else '{\"blockdevices\":[]}') | from_json).blockdevices) | default([], true) }}"
|
|
rescue:
|
|
- name: Fallback disk_devices_parsed
|
|
ansible.builtin.set_fact:
|
|
disk_devices_parsed: []
|
|
|
|
- name: Get LVM info (pvs/vgs/lvs) as JSON (best-effort)
|
|
ansible.builtin.shell: |
|
|
if command -v pvs >/dev/null 2>&1 && command -v vgs >/dev/null 2>&1 && command -v lvs >/dev/null 2>&1; then
|
|
pvs --reportformat json 2>/dev/null || echo '{"report":[]}'
|
|
echo '---'
|
|
vgs --reportformat json 2>/dev/null || echo '{"report":[]}'
|
|
echo '---'
|
|
lvs --reportformat json 2>/dev/null || echo '{"report":[]}'
|
|
else
|
|
echo '{"report":[]}'
|
|
echo '---'
|
|
echo '{"report":[]}'
|
|
echo '---'
|
|
echo '{"report":[]}'
|
|
fi
|
|
register: lvm_info
|
|
changed_when: false
|
|
ignore_errors: true
|
|
|
|
- name: Parse LVM JSON safely
|
|
block:
|
|
- name: Parse LVM JSON
|
|
ansible.builtin.set_fact:
|
|
_lvm_chunks: "{{ (lvm_info.stdout | default('{\"report\":[]}\n---\n{\"report\":[]}\n---\n{\"report\":[]}', true)).split('---') }}"
|
|
lvm_info_parsed:
|
|
pvs: "{{ (_lvm_chunks[0] | trim | from_json).report[0].pv | default([], true) if (_lvm_chunks | length) > 0 else [] }}"
|
|
vgs: "{{ (_lvm_chunks[1] | trim | from_json).report[0].vg | default([], true) if (_lvm_chunks | length) > 1 else [] }}"
|
|
lvs: "{{ (_lvm_chunks[2] | trim | from_json).report[0].lv | default([], true) if (_lvm_chunks | length) > 2 else [] }}"
|
|
rescue:
|
|
- name: Fallback lvm_info_parsed
|
|
ansible.builtin.set_fact:
|
|
lvm_info_parsed:
|
|
pvs: []
|
|
vgs: []
|
|
lvs: []
|
|
|
|
- name: Get ZFS pools info as JSON (best-effort)
|
|
ansible.builtin.shell: |
|
|
if command -v zpool >/dev/null 2>&1; then
|
|
zpool list -H -o name,size,alloc,free,cap 2>/dev/null | python3 -c 'import sys,json; rows=[]; [rows.append({"name":p[0],"size":p[1],"alloc":p[2],"free":p[3],"cap":p[4]}) for p in (l.strip().split() for l in sys.stdin) if len(p)>=5]; print(json.dumps(rows))' 2>/dev/null || echo '[]'
|
|
else
|
|
echo '[]'
|
|
fi
|
|
register: zfs_pools
|
|
changed_when: false
|
|
ignore_errors: true
|
|
|
|
- name: Get ZFS datasets info as JSON (best-effort)
|
|
ansible.builtin.shell: |
|
|
if command -v zfs >/dev/null 2>&1; then
|
|
zfs list -H -o name,used,avail,refer,mountpoint 2>/dev/null | python3 -c 'import sys,json; rows=[]; [rows.append({"name":p[0],"used":p[1],"avail":p[2],"refer":p[3],"mountpoint":p[4]}) for p in ((line.rstrip("\\n").split("\\t") if "\\t" in line else line.strip().split()) for line in sys.stdin) if len(p)>=5]; print(json.dumps(rows))' 2>/dev/null || echo '[]'
|
|
else
|
|
echo '[]'
|
|
fi
|
|
register: zfs_datasets
|
|
changed_when: false
|
|
ignore_errors: true
|
|
|
|
- name: Parse ZFS JSON safely
|
|
block:
|
|
- name: Parse ZFS JSON
|
|
ansible.builtin.set_fact:
|
|
zfs_info_parsed:
|
|
pools: "{{ ((zfs_pools.stdout | default('[]', true) | trim) if ((zfs_pools.stdout | default('', true) | trim) | regex_search('^\\[')) else '[]') | from_json }}"
|
|
datasets: "{{ ((zfs_datasets.stdout | default('[]', true) | trim) if ((zfs_datasets.stdout | default('', true) | trim) | regex_search('^\\[')) else '[]') | from_json }}"
|
|
rescue:
|
|
- name: Fallback zfs_info_parsed
|
|
ansible.builtin.set_fact:
|
|
zfs_info_parsed:
|
|
pools: []
|
|
datasets: []
|
|
|
|
- name: Get root partition info specifically
|
|
ansible.builtin.shell: |
|
|
if df -BG / >/dev/null 2>&1; then
|
|
df -BG / 2>/dev/null | tail -1 | awk '{gsub("G",""); gsub("%",""); printf "{\"total_gb\":%s,\"used_gb\":%s,\"usage_percent\":%s}", $2, $3, $5}'
|
|
elif df -kP / >/dev/null 2>&1; then
|
|
df -kP / 2>/dev/null | tail -1 | awk '{total_gb=$2/1024/1024; used_gb=$3/1024/1024; gsub("%",""); printf "{\"total_gb\":%.1f,\"used_gb\":%.1f,\"usage_percent\":%s}", total_gb, used_gb, $5}'
|
|
else
|
|
echo '{"total_gb":0,"used_gb":0,"usage_percent":0}'
|
|
fi
|
|
register: disk_root
|
|
changed_when: false
|
|
ignore_errors: true
|
|
|
|
- name: Parse root partition JSON safely
|
|
block:
|
|
- name: Parse root partition JSON
|
|
ansible.builtin.set_fact:
|
|
disk_root_parsed: "{{ ((disk_root.stdout | default('{\"total_gb\":0,\"used_gb\":0,\"usage_percent\":0}', true) | trim) if ((disk_root.stdout | default('', true) | trim) | regex_search('^\\{')) else '{\"total_gb\":0,\"used_gb\":0,\"usage_percent\":0}') | from_json }}"
|
|
rescue:
|
|
- name: Fallback disk_root_parsed
|
|
ansible.builtin.set_fact:
|
|
disk_root_parsed:
|
|
total_gb: 0
|
|
used_gb: 0
|
|
usage_percent: 0
|
|
|
|
- name: Get CPU current frequency in MHz (best-effort)
|
|
ansible.builtin.shell: |
|
|
(awk -F: '/cpu MHz/{gsub(/ /, "", $2); print $2; exit}' /proc/cpuinfo 2>/dev/null || echo '')
|
|
register: cpu_mhz
|
|
changed_when: false
|
|
ignore_errors: true
|
|
|
|
- name: Get CPU max frequency in MHz (best-effort)
|
|
ansible.builtin.shell: |
|
|
if [ -f /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq ]; then
|
|
awk '{printf "%.1f", $1/1000}' /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq 2>/dev/null
|
|
else
|
|
echo ""
|
|
fi
|
|
register: cpu_max_mhz
|
|
changed_when: false
|
|
ignore_errors: true
|
|
|
|
- name: Get CPU min frequency in MHz (best-effort)
|
|
ansible.builtin.shell: |
|
|
if [ -f /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq ]; then
|
|
awk '{printf "%.1f", $1/1000}' /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq 2>/dev/null
|
|
else
|
|
echo ""
|
|
fi
|
|
register: cpu_min_mhz
|
|
changed_when: false
|
|
ignore_errors: true
|
|
|
|
- name: Get system uptime in seconds
|
|
ansible.builtin.shell: cat /proc/uptime | awk '{print int($1)}'
|
|
register: uptime_seconds
|
|
changed_when: false
|
|
ignore_errors: true
|
|
|
|
- name: Get human-readable uptime
|
|
ansible.builtin.shell: uptime -p 2>/dev/null || uptime | sed 's/.*up //' | sed 's/,.*//'
|
|
register: uptime_human
|
|
changed_when: false
|
|
ignore_errors: true
|
|
|
|
- name: Get network interfaces info
|
|
ansible.builtin.shell: |
|
|
ip -j addr show 2>/dev/null | python3 -c "
|
|
import sys, json
|
|
try:
|
|
data = json.load(sys.stdin)
|
|
result = []
|
|
for iface in data:
|
|
if iface.get('ifname') not in ['lo']:
|
|
info = {'name': iface.get('ifname'), 'state': iface.get('operstate', 'unknown')}
|
|
for addr in iface.get('addr_info', []):
|
|
if addr.get('family') == 'inet':
|
|
info['ip_address'] = addr.get('local')
|
|
if iface.get('address'):
|
|
info['mac_address'] = iface.get('address')
|
|
result.append(info)
|
|
print(json.dumps(result))
|
|
except:
|
|
print('[]')
|
|
" 2>/dev/null || echo "[]"
|
|
register: network_info
|
|
changed_when: false
|
|
ignore_errors: true
|
|
|
|
- name: Parse network info JSON safely
|
|
block:
|
|
- name: Parse network info JSON
|
|
ansible.builtin.set_fact:
|
|
network_info_parsed: "{{ ((network_info.stdout | default('[]', true) | trim) if ((network_info.stdout | default('', true) | trim) | regex_search('^\\[')) else '[]') | from_json }}"
|
|
rescue:
|
|
- name: Fallback network_info_parsed
|
|
ansible.builtin.set_fact:
|
|
network_info_parsed: []
|
|
|
|
- name: Build metrics JSON output
|
|
ansible.builtin.set_fact:
|
|
metrics_output:
|
|
host: "{{ inventory_hostname }}"
|
|
data:
|
|
# CPU info
|
|
cpu_count: "{{ ansible_processor_vcpus | default(ansible_processor_count, true) | default(1) }}"
|
|
cpu_model: "{{ ansible_processor[2] | default('Unknown', true) if (ansible_processor is defined and ansible_processor | length > 2) else 'Unknown' }}"
|
|
cpu_sockets: "{{ ansible_processor_count | default(1, true) | int }}"
|
|
cpu_threads_per_core: "{{ ansible_processor_threads_per_core | default('', true) }}"
|
|
cpu_cores: "{{ (ansible_processor_cores | default(0, true) | int) * (ansible_processor_count | default(1, true) | int) if (ansible_processor_cores is defined and ansible_processor_cores | int > 0) else '' }}"
|
|
cpu_threads: "{{ ansible_processor_vcpus | default('', true) }}"
|
|
cpu_mhz: "{{ cpu_mhz.stdout | default('', true) }}"
|
|
cpu_max_mhz: "{{ cpu_max_mhz.stdout | default('', true) }}"
|
|
cpu_min_mhz: "{{ cpu_min_mhz.stdout | default('', true) }}"
|
|
cpu_load_1m: "{{ (cpu_load_parts[0] if (cpu_load_parts | length > 0) else '0') | float }}"
|
|
cpu_load_5m: "{{ (cpu_load_parts[1] if (cpu_load_parts | length > 1) else '0') | float }}"
|
|
cpu_load_15m: "{{ (cpu_load_parts[2] if (cpu_load_parts | length > 2) else '0') | float }}"
|
|
cpu_temperature: "{{ cpu_temp.stdout if (cpu_temp.stdout is defined and cpu_temp.stdout != 'null') else '' }}"
|
|
# Memory info
|
|
memory_total_mb: "{{ memory_info_parsed.total_mb | default(0, true) | int }}"
|
|
memory_used_mb: "{{ memory_info_parsed.used_mb | default(0, true) | int }}"
|
|
memory_free_mb: "{{ memory_info_parsed.free_mb | default(0, true) | int }}"
|
|
memory_usage_percent: "{{ memory_info_parsed.usage_percent | default(0, true) | float }}"
|
|
swap_total_mb: "{{ memory_info_parsed.swap_total_mb | default(0, true) | int }}"
|
|
swap_used_mb: "{{ memory_info_parsed.swap_used_mb | default(0, true) | int }}"
|
|
swap_usage_percent: "{{ memory_info_parsed.swap_usage_percent | default(0, true) | float }}"
|
|
# Disk info
|
|
disk_info: "{{ disk_info_parsed | default([], true) }}"
|
|
disk_devices: "{{ disk_devices_parsed | default([], true) }}"
|
|
disk_root_total_gb: "{{ disk_root_parsed.total_gb | default(0, true) | float }}"
|
|
disk_root_used_gb: "{{ disk_root_parsed.used_gb | default(0, true) | float }}"
|
|
disk_root_usage_percent: "{{ disk_root_parsed.usage_percent | default(0, true) | float }}"
|
|
# Storage stacks
|
|
lvm_info: "{{ lvm_info_parsed | default({'pvs':[], 'vgs':[], 'lvs':[]}, true) }}"
|
|
zfs_info: "{{ zfs_info_parsed | default({'pools':[], 'datasets':[]}, true) }}"
|
|
# System info
|
|
os_name: "{{ ansible_distribution | default('Unknown', true) }}"
|
|
os_version: "{{ ansible_distribution_version | default('', true) }}"
|
|
kernel_version: "{{ ansible_kernel | default('', true) }}"
|
|
hostname: "{{ ansible_hostname | default(inventory_hostname, true) }}"
|
|
uptime_seconds: "{{ uptime_seconds.stdout | default('0', true) | int }}"
|
|
uptime_human: "{{ uptime_human.stdout | default('unknown', true) }}"
|
|
# Network info
|
|
network_info: "{{ network_info_parsed | default([], true) }}"
|
|
|
|
- name: Output metrics in parseable format
|
|
ansible.builtin.debug:
|
|
msg: "METRICS_JSON_START:{{ metrics_output | to_json }}:METRICS_JSON_END"
|