Add enhanced terminal history panel UI with animations, keyboard navigation, advanced filtering, search highlighting, and improved storage metrics display with detailed filesystem tables and ZFS/LVM support
Tests / Backend Tests (Python) (3.10) (push) Has been cancelled
Tests / Backend Tests (Python) (3.11) (push) Has been cancelled
Tests / Backend Tests (Python) (3.12) (push) Has been cancelled
Tests / Frontend Tests (JS) (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / All Tests Passed (push) Has been cancelled

This commit is contained in:
2025-12-21 12:31:08 -05:00
parent 5ff7017aaa
commit 6d8432169b
137 changed files with 30407 additions and 651 deletions
@@ -0,0 +1,288 @@
---
# Builtin Playbook: Collecte détaillée du stockage
# Ce playbook collecte les informations de stockage détaillées (disques, partitions, mounts, ZFS, LVM)
# Compatible: Linux (Alpine/Debian), TrueNAS CORE (FreeBSD), TrueNAS SCALE (Linux+ZFS)
- name: Collect Detailed Storage Information
hosts: all
become: false
gather_facts: true
vars:
_builtin_playbook: true
_builtin_id: collect_storage_details
_collect_metrics: true
tasks:
- name: Detect OS type
ansible.builtin.set_fact:
detected_os_type: >-
{%- if ansible_system == 'FreeBSD' -%}truenas-core
{%- elif ansible_distribution | lower == 'debian' and 'truenas' in (ansible_product_name | default('') | lower) -%}truenas-scale
{%- elif ansible_distribution | lower == 'alpine' -%}linux-alpine
{%- elif ansible_os_family | lower == 'debian' -%}linux-debian
{%- elif ansible_os_family | lower == 'redhat' -%}linux-redhat
{%- else -%}linux-generic{%- endif -%}
- name: Initialize collection metadata
ansible.builtin.set_fact:
storage_commands_run: []
storage_partial_failures: []
storage_collection_start: "{{ ansible_date_time.iso8601 }}"
- name: Get block devices via lsblk (Linux)
ansible.builtin.shell: |
if command -v lsblk >/dev/null 2>&1; then
lsblk -J -b -o NAME,TYPE,SIZE,FSTYPE,MOUNTPOINT,MODEL,SERIAL,UUID,LABEL,ROTA 2>/dev/null || \
lsblk -J -b -o NAME,TYPE,SIZE,FSTYPE,MOUNTPOINT 2>/dev/null || \
echo '{"blockdevices":[]}'
else
echo '{"blockdevices":[]}'
fi
register: lsblk_output
changed_when: false
ignore_errors: true
when: ansible_system == 'Linux'
- name: Parse lsblk JSON
block:
- name: Parse lsblk
ansible.builtin.set_fact:
block_devices_raw: "{{ (lsblk_output.stdout | default('{}') | from_json).blockdevices | default([]) }}"
storage_commands_run: "{{ storage_commands_run + [{'cmd': 'lsblk', 'status': 'ok'}] }}"
rescue:
- name: Fallback block_devices
ansible.builtin.set_fact:
block_devices_raw: []
storage_partial_failures: "{{ storage_partial_failures + ['lsblk parse failed'] }}"
when: ansible_system == 'Linux'
- name: Get mounts via findmnt (Linux)
ansible.builtin.shell: |
if command -v findmnt >/dev/null 2>&1; then
findmnt -J -b -o TARGET,SOURCE,FSTYPE,OPTIONS,SIZE,USED,AVAIL,USE% 2>/dev/null || echo '{"filesystems":[]}'
else
echo '{"filesystems":[]}'
fi
register: findmnt_output
changed_when: false
ignore_errors: true
when: ansible_system == 'Linux'
- name: Parse findmnt JSON
block:
- name: Parse findmnt
ansible.builtin.set_fact:
mounts_raw: "{{ (findmnt_output.stdout | default('{}') | from_json).filesystems | default([]) }}"
storage_commands_run: "{{ storage_commands_run + [{'cmd': 'findmnt', 'status': 'ok'}] }}"
rescue:
- name: Fallback mounts
ansible.builtin.set_fact:
mounts_raw: []
storage_partial_failures: "{{ storage_partial_failures + ['findmnt parse failed'] }}"
when: ansible_system == 'Linux'
- name: Get filesystems via df (Linux)
ansible.builtin.shell: |
df -B1 -PT -x tmpfs -x devtmpfs -x squashfs -x overlay 2>/dev/null | tail -n +2 | awk '{
gsub(/%/,"",$6);
printf "{\"device\":\"%s\",\"fstype\":\"%s\",\"size_bytes\":%s,\"used_bytes\":%s,\"avail_bytes\":%s,\"use_pct\":%s,\"mountpoint\":\"%s\"}\n", $1, $2, $3, $4, $5, $6, $7
}' | paste -sd "," | awk '{print "["$0"]"}' 2>/dev/null || echo '[]'
register: df_output
changed_when: false
ignore_errors: true
when: ansible_system == 'Linux'
- name: Parse df JSON
block:
- name: Parse df
ansible.builtin.set_fact:
filesystems_raw: "{{ df_output.stdout | default('[]') | from_json }}"
storage_commands_run: "{{ storage_commands_run + [{'cmd': 'df', 'status': 'ok'}] }}"
rescue:
- name: Fallback filesystems
ansible.builtin.set_fact:
filesystems_raw: []
storage_partial_failures: "{{ storage_partial_failures + ['df parse failed'] }}"
when: ansible_system == 'Linux'
- name: Get filesystems via df (FreeBSD)
ansible.builtin.shell: |
df -k -T 2>/dev/null | tail -n +2 | awk '{
size_bytes=$3*1024; used_bytes=$4*1024; avail_bytes=$5*1024;
gsub(/%/,"",$6);
printf "{\"device\":\"%s\",\"fstype\":\"%s\",\"size_bytes\":%d,\"used_bytes\":%d,\"avail_bytes\":%d,\"use_pct\":%d,\"mountpoint\":\"%s\"}\n", $1, $2, size_bytes, used_bytes, avail_bytes, $6, $7
}' | paste -sd "," | awk '{print "["$0"]"}' 2>/dev/null || echo '[]'
register: df_bsd_output
changed_when: false
ignore_errors: true
when: ansible_system == 'FreeBSD'
- name: Parse df FreeBSD JSON
block:
- name: Parse df FreeBSD
ansible.builtin.set_fact:
filesystems_raw: "{{ df_bsd_output.stdout | default('[]') | from_json }}"
storage_commands_run: "{{ storage_commands_run + [{'cmd': 'df', 'status': 'ok'}] }}"
rescue:
- name: Fallback filesystems FreeBSD
ansible.builtin.set_fact:
filesystems_raw: []
storage_partial_failures: "{{ storage_partial_failures + ['df (FreeBSD) parse failed'] }}"
when: ansible_system == 'FreeBSD'
- name: Get LVM info
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_json=$(pvs --reportformat json --units b 2>/dev/null | tr -d '\n' || echo '{"report":[{"pv":[]}]}')
vgs_json=$(vgs --reportformat json --units b 2>/dev/null | tr -d '\n' || echo '{"report":[{"vg":[]}]}')
lvs_json=$(lvs --reportformat json --units b 2>/dev/null | tr -d '\n' || echo '{"report":[{"lv":[]}]}')
echo "{\"pvs_raw\":$pvs_json,\"vgs_raw\":$vgs_json,\"lvs_raw\":$lvs_json}"
else
echo '{"pvs_raw":{"report":[{"pv":[]}]},"vgs_raw":{"report":[{"vg":[]}]},"lvs_raw":{"report":[{"lv":[]}]}}'
fi
register: lvm_output
changed_when: false
ignore_errors: true
when: ansible_system == 'Linux'
- name: Parse LVM JSON
block:
- name: Parse LVM
ansible.builtin.set_fact:
lvm_parsed: "{{ lvm_output.stdout | default('{}') | from_json }}"
- name: Extract LVM components
ansible.builtin.set_fact:
lvm_info_raw:
pvs: "{{ (lvm_parsed.pvs_raw.report[0].pv | default([])) if lvm_parsed.pvs_raw is defined else [] }}"
vgs: "{{ (lvm_parsed.vgs_raw.report[0].vg | default([])) if lvm_parsed.vgs_raw is defined else [] }}"
lvs: "{{ (lvm_parsed.lvs_raw.report[0].lv | default([])) if lvm_parsed.lvs_raw is defined else [] }}"
storage_commands_run: "{{ storage_commands_run + [{'cmd': 'lvm', 'status': 'ok'}] }}"
rescue:
- name: Fallback LVM
ansible.builtin.set_fact:
lvm_info_raw:
pvs: []
vgs: []
lvs: []
storage_partial_failures: "{{ storage_partial_failures + ['lvm parse failed'] }}"
when: ansible_system == 'Linux'
- name: Get ZFS pools
ansible.builtin.shell: |
if command -v zpool >/dev/null 2>&1; then
zpool list -H -p -o name,size,alloc,free,cap,health 2>/dev/null | awk -F'\t' '{
printf "{\"name\":\"%s\",\"size_bytes\":%s,\"alloc_bytes\":%s,\"free_bytes\":%s,\"cap_pct\":%s,\"health\":\"%s\"}\n", $1, $2, $3, $4, $5, $6
}' | paste -sd "," | awk '{print "["$0"]"}' 2>/dev/null || echo '[]'
else
echo '[]'
fi
register: zpool_output
changed_when: false
ignore_errors: true
- name: Parse ZFS pools JSON
block:
- name: Parse zpool
ansible.builtin.set_fact:
zfs_pools_raw: "{{ zpool_output.stdout | default('[]') | from_json }}"
storage_commands_run: "{{ storage_commands_run + [{'cmd': 'zpool list', 'status': 'ok'}] }}"
rescue:
- name: Fallback zfs pools
ansible.builtin.set_fact:
zfs_pools_raw: []
storage_partial_failures: "{{ storage_partial_failures + ['zpool parse failed'] }}"
- name: Get ZFS datasets
ansible.builtin.shell: |
if command -v zfs >/dev/null 2>&1; then
zfs list -H -p -o name,used,avail,refer,mountpoint,type 2>/dev/null | awk -F'\t' '{
mp = ($5 == "-") ? "null" : sprintf("\"%s\"", $5);
printf "{\"name\":\"%s\",\"used_bytes\":%s,\"avail_bytes\":%s,\"refer_bytes\":%s,\"mountpoint\":%s,\"type\":\"%s\"}\n", $1, $2, $3, $4, mp, $6
}' | paste -sd "," | awk '{print "["$0"]"}' 2>/dev/null || echo '[]'
else
echo '[]'
fi
register: zfs_datasets_output
changed_when: false
ignore_errors: true
- name: Parse ZFS datasets JSON
block:
- name: Parse zfs list
ansible.builtin.set_fact:
zfs_datasets_raw: "{{ zfs_datasets_output.stdout | default('[]') | from_json }}"
storage_commands_run: "{{ storage_commands_run + [{'cmd': 'zfs list', 'status': 'ok'}] }}"
rescue:
- name: Fallback zfs datasets
ansible.builtin.set_fact:
zfs_datasets_raw: []
storage_partial_failures: "{{ storage_partial_failures + ['zfs list parse failed'] }}"
- name: Initialize missing Linux variables (FreeBSD)
ansible.builtin.set_fact:
block_devices_raw: []
mounts_raw: []
lvm_info_raw:
pvs: []
vgs: []
lvs: []
when: ansible_system == 'FreeBSD'
- name: Initialize missing variables (fallbacks)
ansible.builtin.set_fact:
block_devices_raw: "{{ block_devices_raw | default([]) }}"
mounts_raw: "{{ mounts_raw | default([]) }}"
filesystems_raw: "{{ filesystems_raw | default([]) }}"
lvm_info_raw: "{{ lvm_info_raw | default({'pvs':[], 'vgs':[], 'lvs':[]}) }}"
zfs_pools_raw: "{{ zfs_pools_raw | default([]) }}"
zfs_datasets_raw: "{{ zfs_datasets_raw | default([]) }}"
- name: Compute storage summary
ansible.builtin.set_fact:
storage_summary:
total_bytes: "{{ filesystems_raw | map(attribute='size_bytes') | select('number') | sum | int }}"
used_bytes: "{{ filesystems_raw | map(attribute='used_bytes') | select('number') | sum | int }}"
free_bytes: "{{ filesystems_raw | map(attribute='avail_bytes') | select('number') | sum | int }}"
- name: Determine collection status
ansible.builtin.set_fact:
storage_status: >-
{%- if storage_partial_failures | length > 0 -%}partial
{%- elif (block_devices_raw | length > 0) or (filesystems_raw | length > 0) or (zfs_pools_raw | length > 0) -%}ok
{%- else -%}error{%- endif -%}
storage_collection_end: "{{ ansible_date_time.iso8601 }}"
- name: Build storage metrics output
ansible.builtin.set_fact:
metrics_output:
host: "{{ inventory_hostname }}"
data:
storage_details:
os_type: "{{ detected_os_type | trim }}"
status: "{{ storage_status | trim }}"
collected_at: "{{ storage_collection_start }}"
commands_run: "{{ storage_commands_run }}"
partial_failures: "{{ storage_partial_failures }}"
summary:
total_bytes: "{{ storage_summary.total_bytes | int }}"
used_bytes: "{{ storage_summary.used_bytes | int }}"
free_bytes: "{{ storage_summary.free_bytes | int }}"
used_pct: "{{ ((storage_summary.used_bytes | int) / (storage_summary.total_bytes | int) * 100) | round(1) if (storage_summary.total_bytes | int) > 0 else 0 }}"
block_devices: "{{ block_devices_raw }}"
mounts: "{{ mounts_raw }}"
filesystems: "{{ filesystems_raw }}"
lvm:
pvs: "{{ lvm_info_raw.pvs | default([]) }}"
vgs: "{{ lvm_info_raw.vgs | default([]) }}"
lvs: "{{ lvm_info_raw.lvs | default([]) }}"
zfs:
pools: "{{ zfs_pools_raw }}"
datasets: "{{ zfs_datasets_raw }}"
feature_flags:
has_lvm: "{{ (lvm_info_raw.vgs | default([]) | length) > 0 }}"
has_zfs: "{{ (zfs_pools_raw | length) > 0 }}"
has_lsblk: "{{ (block_devices_raw | length) > 0 }}"
has_findmnt: "{{ (mounts_raw | length) > 0 }}"
- name: Output metrics in parseable format
ansible.builtin.debug:
msg: "METRICS_JSON_START:{{ metrics_output | to_json }}:METRICS_JSON_END"