Add system_info role for comprehensive infrastructure inventory

New role for gathering detailed system information including CPU, GPU,
RAM, disk, network, and hypervisor details with JSON export capabilities.

Role capabilities:
- Comprehensive hardware detection (CPU, GPU, RAM, disk, network)
- Hypervisor detection (KVM, Proxmox, LXD, Docker, Podman, VMware, Hyper-V)
- System information gathering (OS, kernel, uptime, security modules)
- Health checks and validation tasks
- JSON export with timestamped backups
- Human-readable summary generation
- Support for multiple Linux distributions

Features:
- Modular task organization by information type
- Feature toggles for selective gathering
- CLAUDE.md compliant validation tasks including:
  * Disk usage monitoring (>80% warnings)
  * Memory usage statistics
  * Top CPU and memory processes
  * System uptime tracking
  * Logged users reporting
- OS-specific variable handling
- DMI/SMBIOS hardware information
- SMART disk health status
- Network interface statistics

File structure:
roles/system_info/
├── README.md              # Comprehensive documentation
├── defaults/main.yml      # Configurable defaults
├── vars/main.yml          # Role variables
├── meta/main.yml          # Galaxy metadata
├── tasks/
│   ├── main.yml          # Main task coordinator
│   ├── install.yml       # Package installation
│   ├── gather_system.yml # OS and system info
│   ├── gather_cpu.yml    # CPU details
│   ├── gather_gpu.yml    # GPU detection
│   ├── gather_memory.yml # RAM information
│   ├── gather_disk.yml   # Disk and LVM info
│   ├── gather_network.yml # Network configuration
│   ├── detect_hypervisor.yml # Virtualization detection
│   ├── export_stats.yml  # JSON export
│   └── validate.yml      # Health checks (CLAUDE.md compliant)
├── templates/
│   └── summary.txt.j2    # Human-readable summary
├── handlers/
│   └── main.yml          # Service handlers
└── tests/
    └── test.yml          # Basic test playbook

Use cases:
- Infrastructure inventory for CMDB integration
- Capacity planning and resource optimization
- Hardware audit and compliance reporting
- Hypervisor and VM tracking
- System health monitoring
- Documentation generation

Output:
- JSON: ./stats/machines/<fqdn>/system_info.json
- Backup: ./stats/machines/<fqdn>/system_info_<timestamp>.json
- Summary: ./stats/machines/<fqdn>/summary.txt

Requirements:
- Ansible >= 2.9
- Root/sudo access for hardware information
- Packages: lshw, dmidecode, pciutils, usbutils, smartmontools, ethtool

Compliance:
- CLAUDE.md health check requirements implemented
- CIS Benchmark support for system auditing
- NIST compliance documentation support
- Security-first design with minimal system impact

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-11 01:36:01 +01:00
parent 0231144d87
commit 70b57d223f
19 changed files with 1877 additions and 0 deletions

View File

@@ -0,0 +1,284 @@
---
# Hypervisor detection tasks
- name: Check if running in a virtual environment
set_fact:
system_info_virtualization_type: "{{ ansible_virtualization_type | default('physical') }}"
system_info_virtualization_role: "{{ ansible_virtualization_role | default('NA') }}"
tags: [gather, hypervisor]
- name: Detect virtualization using systemd-detect-virt
shell: systemd-detect-virt
register: system_info_detect_virt_raw
changed_when: false
failed_when: false
tags: [gather, hypervisor]
- name: Set systemd virtualization detection
set_fact:
system_info_systemd_virt: "{{ system_info_detect_virt_raw.stdout | default('none') }}"
tags: [gather, hypervisor]
- name: Check for KVM/QEMU hypervisor capability
shell: |
if command -v virsh &> /dev/null; then
echo "virsh: available"
virsh version 2>/dev/null || echo "virsh not accessible"
else
echo "virsh: not installed"
fi
register: system_info_virsh_check_raw
changed_when: false
failed_when: false
tags: [gather, hypervisor, kvm]
- name: Check libvirt service status
shell: systemctl is-active libvirtd 2>/dev/null || echo "not running"
register: system_info_libvirtd_status_raw
changed_when: false
failed_when: false
tags: [gather, hypervisor, libvirt]
- name: Gather libvirt details (if available)
block:
- name: Check libvirt version
shell: virsh version
register: system_info_libvirt_version_raw
changed_when: false
become: true
- name: List libvirt networks
shell: virsh net-list --all
register: system_info_libvirt_networks_raw
changed_when: false
become: true
- name: List libvirt storage pools
shell: virsh pool-list --all
register: system_info_libvirt_pools_raw
changed_when: false
become: true
- name: Count running VMs
shell: virsh list --state-running | grep -c running || echo "0"
register: system_info_libvirt_running_vms_raw
changed_when: false
become: true
- name: Count total VMs
shell: virsh list --all | tail -n +3 | grep -v "^$" | wc -l
register: system_info_libvirt_total_vms_raw
changed_when: false
become: true
when: "'available' in system_info_virsh_check_raw.stdout"
failed_when: false
tags: [gather, hypervisor, libvirt]
- name: Check for Proxmox VE
shell: |
if command -v pveversion &> /dev/null; then
pveversion
else
echo "Proxmox VE not installed"
fi
register: system_info_proxmox_check_raw
changed_when: false
failed_when: false
tags: [gather, hypervisor, proxmox]
- name: Gather Proxmox details (if available)
block:
- name: Get Proxmox cluster status
shell: pvecm status 2>/dev/null || echo "Not in a cluster"
register: system_info_proxmox_cluster_raw
changed_when: false
- name: List Proxmox VMs
shell: qm list 2>/dev/null || echo "No VMs or qm not available"
register: system_info_proxmox_vms_raw
changed_when: false
- name: List Proxmox containers
shell: pct list 2>/dev/null || echo "No containers or pct not available"
register: system_info_proxmox_containers_raw
changed_when: false
- name: Get Proxmox storage status
shell: pvesm status 2>/dev/null || echo "Storage information not available"
register: system_info_proxmox_storage_raw
changed_when: false
when: "'pveversion' in system_info_proxmox_check_raw.stdout"
failed_when: false
tags: [gather, hypervisor, proxmox]
- name: Check for LXD/LXC
shell: |
if command -v lxc &> /dev/null; then
lxc version
else
echo "LXD not installed"
fi
register: system_info_lxd_check_raw
changed_when: false
failed_when: false
tags: [gather, hypervisor, lxd]
- name: Gather LXD details (if available)
block:
- name: List LXD containers
shell: lxc list --format json
register: system_info_lxd_containers_raw
changed_when: false
- name: Get LXD storage pools
shell: lxc storage list --format json
register: system_info_lxd_storage_raw
changed_when: false
- name: Get LXD networks
shell: lxc network list --format json
register: system_info_lxd_networks_raw
changed_when: false
- name: Check LXD cluster status
shell: lxc cluster list --format json 2>/dev/null || echo "[]"
register: system_info_lxd_cluster_raw
changed_when: false
when: "'Client version' in system_info_lxd_check_raw.stdout or 'Server version' in system_info_lxd_check_raw.stdout"
failed_when: false
tags: [gather, hypervisor, lxd]
- name: Check for Docker
shell: |
if command -v docker &> /dev/null; then
docker version --format '{{.Server.Version}}' 2>/dev/null || echo "Docker installed but not running"
else
echo "Docker not installed"
fi
register: system_info_docker_check_raw
changed_when: false
failed_when: false
tags: [gather, hypervisor, docker]
- name: Gather Docker details (if available)
block:
- name: Count running containers
shell: docker ps -q | wc -l
register: system_info_docker_running_raw
changed_when: false
- name: Count total containers
shell: docker ps -aq | wc -l
register: system_info_docker_total_raw
changed_when: false
- name: List Docker images
shell: docker images --format "{{.Repository}}:{{.Tag}}" | wc -l
register: system_info_docker_images_raw
changed_when: false
when:
- "'not installed' not in system_info_docker_check_raw.stdout"
- "'not running' not in system_info_docker_check_raw.stdout"
failed_when: false
tags: [gather, hypervisor, docker]
- name: Check for Podman
shell: |
if command -v podman &> /dev/null; then
podman version --format '{{.Version}}'
else
echo "Podman not installed"
fi
register: system_info_podman_check_raw
changed_when: false
failed_when: false
tags: [gather, hypervisor, podman]
- name: Gather VMware ESXi/vSphere information
shell: |
if [ -f /etc/vmware-release ]; then
cat /etc/vmware-release
else
echo "Not VMware ESXi"
fi
register: system_info_vmware_check_raw
changed_when: false
failed_when: false
tags: [gather, hypervisor, vmware]
- name: Check for Hyper-V Linux Integration Services
shell: |
if lsmod | grep -q hv_vmbus; then
echo "Hyper-V detected"
lsmod | grep ^hv_
else
echo "Not Hyper-V"
fi
register: system_info_hyperv_check_raw
changed_when: false
failed_when: false
tags: [gather, hypervisor, hyperv]
- name: Determine if system is a hypervisor
set_fact:
system_info_is_hypervisor: >-
{{
('available' in system_info_virsh_check_raw.stdout) or
('pveversion' in system_info_proxmox_check_raw.stdout) or
('Client version' in system_info_lxd_check_raw.stdout) or
('Server version' in system_info_lxd_check_raw.stdout) or
('VMware ESXi' in system_info_vmware_check_raw.stdout) or
(system_info_docker_check_raw.stdout | regex_search('\\d+\\.\\d+'))
}}
tags: [gather, hypervisor]
- name: Aggregate hypervisor information
set_fact:
system_info_hypervisor:
is_virtual: "{{ system_info_virtualization_role == 'guest' }}"
is_hypervisor: "{{ system_info_is_hypervisor }}"
virtualization_type: "{{ system_info_virtualization_type }}"
virtualization_role: "{{ system_info_virtualization_role }}"
systemd_detection: "{{ system_info_systemd_virt }}"
kvm_libvirt:
installed: "{{ 'available' in system_info_virsh_check_raw.stdout }}"
service_status: "{{ system_info_libvirtd_status_raw.stdout | default('N/A') }}"
version: "{{ system_info_libvirt_version_raw.stdout_lines | default([]) if 'available' in system_info_virsh_check_raw.stdout else [] }}"
running_vms: "{{ system_info_libvirt_running_vms_raw.stdout | default('0') if 'available' in system_info_virsh_check_raw.stdout else '0' }}"
total_vms: "{{ system_info_libvirt_total_vms_raw.stdout | default('0') if 'available' in system_info_virsh_check_raw.stdout else '0' }}"
networks: "{{ system_info_libvirt_networks_raw.stdout_lines | default([]) if 'available' in system_info_virsh_check_raw.stdout else [] }}"
storage_pools: "{{ system_info_libvirt_pools_raw.stdout_lines | default([]) if 'available' in system_info_virsh_check_raw.stdout else [] }}"
proxmox:
installed: "{{ 'pveversion' in system_info_proxmox_check_raw.stdout }}"
version: "{{ system_info_proxmox_check_raw.stdout | default('N/A') }}"
cluster_status: "{{ system_info_proxmox_cluster_raw.stdout_lines | default([]) if 'pveversion' in system_info_proxmox_check_raw.stdout else [] }}"
vms: "{{ system_info_proxmox_vms_raw.stdout_lines | default([]) if 'pveversion' in system_info_proxmox_check_raw.stdout else [] }}"
containers: "{{ system_info_proxmox_containers_raw.stdout_lines | default([]) if 'pveversion' in system_info_proxmox_check_raw.stdout else [] }}"
storage: "{{ system_info_proxmox_storage_raw.stdout_lines | default([]) if 'pveversion' in system_info_proxmox_check_raw.stdout else [] }}"
lxd:
installed: "{{ 'version' in system_info_lxd_check_raw.stdout }}"
version: "{{ system_info_lxd_check_raw.stdout | default('N/A') }}"
containers: "{{ system_info_lxd_containers_raw.stdout | default('[]') if 'version' in system_info_lxd_check_raw.stdout else '[]' }}"
storage: "{{ system_info_lxd_storage_raw.stdout | default('[]') if 'version' in system_info_lxd_check_raw.stdout else '[]' }}"
networks: "{{ system_info_lxd_networks_raw.stdout | default('[]') if 'version' in system_info_lxd_check_raw.stdout else '[]' }}"
cluster: "{{ system_info_lxd_cluster_raw.stdout | default('[]') if 'version' in system_info_lxd_check_raw.stdout else '[]' }}"
docker:
installed: "{{ 'not installed' not in system_info_docker_check_raw.stdout }}"
version: "{{ system_info_docker_check_raw.stdout | default('N/A') }}"
running_containers: "{{ system_info_docker_running_raw.stdout | default('0') if 'not installed' not in system_info_docker_check_raw.stdout and 'not running' not in system_info_docker_check_raw.stdout else '0' }}"
total_containers: "{{ system_info_docker_total_raw.stdout | default('0') if 'not installed' not in system_info_docker_check_raw.stdout and 'not running' not in system_info_docker_check_raw.stdout else '0' }}"
images_count: "{{ system_info_docker_images_raw.stdout | default('0') if 'not installed' not in system_info_docker_check_raw.stdout and 'not running' not in system_info_docker_check_raw.stdout else '0' }}"
podman:
installed: "{{ 'not installed' not in system_info_podman_check_raw.stdout }}"
version: "{{ system_info_podman_check_raw.stdout | default('N/A') }}"
vmware:
is_esxi: "{{ 'VMware ESXi' in system_info_vmware_check_raw.stdout }}"
version: "{{ system_info_vmware_check_raw.stdout | default('N/A') }}"
hyperv:
detected: "{{ 'Hyper-V detected' in system_info_hyperv_check_raw.stdout }}"
modules: "{{ system_info_hyperv_check_raw.stdout_lines | default([]) }}"
tags: [gather, hypervisor]

View File

@@ -0,0 +1,73 @@
---
# Statistics aggregation and export tasks
- name: Set collection timestamp
set_fact:
system_info_timestamp: "{{ ansible_date_time.iso8601 }}"
system_info_timestamp_epoch: "{{ ansible_date_time.epoch }}"
tags: [export, statistics]
- name: Aggregate all system information
set_fact:
system_info_complete:
collection_info:
timestamp: "{{ system_info_timestamp }}"
timestamp_epoch: "{{ system_info_timestamp_epoch }}"
collected_by: "ansible"
role_version: "{{ system_info_role_version }}"
ansible_version: "{{ ansible_version.full }}"
host_info:
hostname: "{{ system_info_hostname }}"
fqdn: "{{ system_info_fqdn }}"
uptime: "{{ system_info_uptime }}"
boot_time: "{{ system_info_boot_time }}"
system: "{{ system_info_os | default({}) }}"
kernel: "{{ system_info_kernel | default({}) }}"
hardware: "{{ system_info_hardware | default({}) }}"
security:
selinux: "{{ system_info_selinux_status | default('N/A') }}"
apparmor: "{{ system_info_apparmor_status | default('N/A') }}"
cpu: "{{ system_info_cpu | default({}) }}"
gpu: "{{ system_info_gpu | default({}) }}"
memory: "{{ system_info_memory | default({}) }}"
swap: "{{ system_info_swap | default({}) }}"
disk: "{{ system_info_disk | default({}) }}"
network: "{{ system_info_network | default({}) }}"
hypervisor: "{{ system_info_hypervisor | default({}) }}"
tags: [export, statistics]
- name: Create JSON statistics file on control node
copy:
content: "{{ system_info_complete | to_nice_json(indent=system_info_json_indent) }}"
dest: "{{ system_info_stats_dir }}/system_info.json"
mode: '0644'
delegate_to: localhost
become: false
tags: [export, statistics]
- name: Create timestamped JSON backup
copy:
content: "{{ system_info_complete | to_nice_json(indent=system_info_json_indent) }}"
dest: "{{ system_info_stats_dir }}/system_info_{{ system_info_timestamp_epoch }}.json"
mode: '0644'
delegate_to: localhost
become: false
tags: [export, statistics, backup]
- name: Create human-readable summary file
template:
src: summary.txt.j2
dest: "{{ system_info_stats_dir }}/summary.txt"
mode: '0644'
delegate_to: localhost
become: false
tags: [export, statistics, summary]
- name: Display statistics file location
debug:
msg:
- "System information collected successfully"
- "Statistics saved to: {{ system_info_stats_dir }}/system_info.json"
- "Summary saved to: {{ system_info_stats_dir }}/summary.txt"
- "Timestamped backup: {{ system_info_stats_dir }}/system_info_{{ system_info_timestamp_epoch }}.json"
tags: [export, statistics]

View File

@@ -0,0 +1,105 @@
---
# CPU information gathering tasks
- name: Gather CPU information from /proc/cpuinfo
shell: |
cat /proc/cpuinfo | grep -E "model name|processor|cpu MHz|cache size|physical id|cpu cores|flags" | head -20
register: system_info_cpu_proc_raw
changed_when: false
tags: [gather, cpu]
- name: Gather CPU count information
set_fact:
system_info_cpu_count:
physical: "{{ ansible_processor_count }}"
cores_per_socket: "{{ ansible_processor_cores }}"
threads_per_core: "{{ ansible_processor_threads_per_core }}"
vcpus: "{{ ansible_processor_vcpus }}"
total_cores: "{{ ansible_processor_count * ansible_processor_cores }}"
tags: [gather, cpu]
- name: Gather CPU model information
set_fact:
system_info_cpu_model: "{{ ansible_processor[2] | default(ansible_processor[0]) }}"
tags: [gather, cpu]
- name: Gather detailed CPU information using lscpu
shell: lscpu
register: system_info_lscpu_raw
changed_when: false
tags: [gather, cpu]
- name: Parse lscpu output
set_fact:
system_info_cpu_architecture: "{{ system_info_lscpu_raw.stdout | regex_search('Architecture:\\s+(.+)', '\\1') | default(['Unknown'], true) | first | trim }}"
system_info_cpu_op_modes: "{{ system_info_lscpu_raw.stdout | regex_search('CPU op-mode\\(s\\):\\s+(.+)', '\\1') | default(['Unknown'], true) | first | trim }}"
system_info_cpu_vendor: "{{ system_info_lscpu_raw.stdout | regex_search('Vendor ID:\\s+(.+)', '\\1') | default(['Unknown'], true) | first | trim }}"
system_info_cpu_family: "{{ system_info_lscpu_raw.stdout | regex_search('CPU family:\\s+(.+)', '\\1') | default(['Unknown'], true) | first | trim }}"
system_info_cpu_model_name: "{{ system_info_lscpu_raw.stdout | regex_search('Model name:\\s+(.+)', '\\1') | default(['Unknown'], true) | first | trim }}"
system_info_cpu_mhz: "{{ system_info_lscpu_raw.stdout | regex_search('CPU MHz:\\s+(.+)', '\\1') | default(['Unknown'], true) | first | trim }}"
system_info_cpu_max_mhz: "{{ system_info_lscpu_raw.stdout | regex_search('CPU max MHz:\\s+(.+)', '\\1') | default(['Unknown'], true) | first | trim }}"
system_info_cpu_min_mhz: "{{ system_info_lscpu_raw.stdout | regex_search('CPU min MHz:\\s+(.+)', '\\1') | default(['Unknown'], true) | first | trim }}"
tags: [gather, cpu]
- name: Check for CPU vulnerability mitigations
shell: lscpu | grep -i vulnerab || echo "No vulnerability information available"
register: system_info_cpu_vulnerabilities_raw
changed_when: false
tags: [gather, cpu, security]
- name: Gather CPU flags/features
shell: |
grep -m1 "^flags" /proc/cpuinfo | cut -d: -f2 | tr ' ' '\n' | sort | tr '\n' ' '
register: system_info_cpu_flags_raw
changed_when: false
tags: [gather, cpu]
- name: Set CPU flags fact
set_fact:
system_info_cpu_flags: "{{ system_info_cpu_flags_raw.stdout.split() | default([]) }}"
tags: [gather, cpu]
- name: Check for virtualization support
set_fact:
system_info_cpu_virtualization:
vmx: "{{ 'vmx' in system_info_cpu_flags }}"
svm: "{{ 'svm' in system_info_cpu_flags }}"
support: "{{ 'vmx' in system_info_cpu_flags or 'svm' in system_info_cpu_flags }}"
type: "{{ 'Intel VT-x' if 'vmx' in system_info_cpu_flags else ('AMD-V' if 'svm' in system_info_cpu_flags else 'None') }}"
tags: [gather, cpu]
- name: Gather CPU cache information
shell: lscpu | grep -i cache
register: system_info_cpu_cache_raw
changed_when: false
tags: [gather, cpu]
- name: Gather current CPU load
shell: |
uptime | awk -F'load average:' '{print $2}' | sed 's/^ *//'
register: system_info_cpu_load_raw
changed_when: false
tags: [gather, cpu]
- name: Set CPU load fact
set_fact:
system_info_cpu_load: "{{ system_info_cpu_load_raw.stdout | trim }}"
tags: [gather, cpu]
- name: Aggregate CPU information
set_fact:
system_info_cpu:
model: "{{ system_info_cpu_model_name }}"
vendor: "{{ system_info_cpu_vendor }}"
architecture: "{{ system_info_cpu_architecture }}"
family: "{{ system_info_cpu_family }}"
count: "{{ system_info_cpu_count }}"
current_mhz: "{{ system_info_cpu_mhz }}"
max_mhz: "{{ system_info_cpu_max_mhz }}"
min_mhz: "{{ system_info_cpu_min_mhz }}"
cache: "{{ system_info_cpu_cache_raw.stdout_lines | default([]) }}"
flags: "{{ system_info_cpu_flags[:50] }}"
virtualization: "{{ system_info_cpu_virtualization }}"
current_load: "{{ system_info_cpu_load }}"
vulnerabilities: "{{ system_info_cpu_vulnerabilities_raw.stdout_lines | default([]) }}"
tags: [gather, cpu]

View File

@@ -0,0 +1,139 @@
---
# Disk information gathering tasks
- name: Gather disk usage information
shell: df -h | grep -vE '^Filesystem|tmpfs|cdrom'
register: system_info_disk_usage_raw
changed_when: false
failed_when: false
tags: [gather, disk]
- name: Gather disk usage in machine-readable format
shell: df -B1 | grep -vE '^Filesystem|tmpfs|cdrom'
register: system_info_disk_usage_bytes_raw
changed_when: false
failed_when: false
tags: [gather, disk]
- name: List block devices
shell: lsblk -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE,MODEL,SERIAL
register: system_info_lsblk_raw
changed_when: false
tags: [gather, disk]
- name: Gather detailed block device information
shell: lsblk -J -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE,UUID,PARTUUID,MODEL,SERIAL,STATE,ROTA
register: system_info_lsblk_json_raw
changed_when: false
tags: [gather, disk]
- name: Check for LVM usage
shell: |
if command -v pvs &> /dev/null; then
echo "=== Physical Volumes ==="
pvs
echo "=== Volume Groups ==="
vgs
echo "=== Logical Volumes ==="
lvs
else
echo "LVM not configured or not available"
fi
register: system_info_lvm_raw
changed_when: false
become: true
failed_when: false
tags: [gather, disk]
- name: Detect if LVM is in use
set_fact:
system_info_lvm_detected: "{{ 'Physical Volumes' in system_info_lvm_raw.stdout }}"
tags: [gather, disk]
- name: Gather mount points information
shell: mount | grep -vE "tmpfs|devtmpfs|sysfs|proc|cgroup"
register: system_info_mounts_raw
changed_when: false
failed_when: false
tags: [gather, disk]
- name: Check for RAID arrays
shell: |
if [ -f /proc/mdstat ]; then
cat /proc/mdstat
else
echo "No software RAID detected"
fi
register: system_info_mdstat_raw
changed_when: false
tags: [gather, disk]
- name: Detect hardware RAID controllers
shell: lspci | grep -iE "raid|storage controller" || echo "No hardware RAID controllers detected"
register: system_info_hw_raid_raw
changed_when: false
tags: [gather, disk]
- name: Gather disk I/O statistics
shell: iostat -x 1 2 | tail -n +4
register: system_info_iostat_raw
changed_when: false
failed_when: false
tags: [gather, disk]
- name: List physical disks
shell: lsblk -d -o NAME,SIZE,TYPE,ROTA,MODEL | grep disk
register: system_info_physical_disks_raw
changed_when: false
tags: [gather, disk]
- name: Check for SSD vs HDD
shell: |
for disk in $(lsblk -d -n -o NAME,TYPE | grep disk | awk '{print $1}'); do
rota=$(cat /sys/block/$disk/queue/rotational 2>/dev/null || echo "unknown")
if [ "$rota" = "0" ]; then
echo "$disk: SSD"
elif [ "$rota" = "1" ]; then
echo "$disk: HDD"
else
echo "$disk: Unknown"
fi
done
register: system_info_disk_types_raw
changed_when: false
failed_when: false
tags: [gather, disk]
- name: Gather SMART status (if available)
shell: |
if command -v smartctl &> /dev/null; then
for disk in $(lsblk -d -n -o NAME,TYPE | grep disk | awk '{print $1}'); do
echo "=== /dev/$disk ==="
smartctl -H /dev/$disk 2>/dev/null || echo "SMART not available for /dev/$disk"
done
else
echo "smartctl not available"
fi
register: system_info_smart_raw
changed_when: false
become: true
failed_when: false
tags: [gather, disk]
- name: Aggregate disk information
set_fact:
system_info_disk:
usage_human: "{{ system_info_disk_usage_raw.stdout_lines | default([]) }}"
block_devices: "{{ system_info_lsblk_raw.stdout_lines }}"
lvm:
enabled: "{{ system_info_lvm_detected }}"
details: "{{ system_info_lvm_raw.stdout_lines | default([]) }}"
mounts: "{{ system_info_mounts_raw.stdout_lines | default([]) }}"
raid:
software: "{{ system_info_mdstat_raw.stdout_lines }}"
hardware: "{{ system_info_hw_raid_raw.stdout_lines }}"
physical_disks: "{{ system_info_physical_disks_raw.stdout_lines | default([]) }}"
disk_types: "{{ system_info_disk_types_raw.stdout_lines | default([]) }}"
smart_status: "{{ system_info_smart_raw.stdout_lines | default([]) }}"
io_stats: "{{ system_info_iostat_raw.stdout_lines | default([]) }}"
tags: [gather, disk]

View File

@@ -0,0 +1,96 @@
---
# GPU information gathering tasks
- name: Detect GPU devices using lspci
shell: lspci | grep -iE "VGA|3D|Display" || echo "No GPU detected"
register: system_info_gpu_lspci_raw
changed_when: false
tags: [gather, gpu]
- name: Gather detailed GPU information
shell: lspci -v -s $(lspci | grep -iE "VGA|3D" | cut -d' ' -f1) 2>/dev/null || echo "No detailed GPU info available"
register: system_info_gpu_detailed_raw
changed_when: false
tags: [gather, gpu]
- name: Check for NVIDIA GPU
shell: lspci | grep -i nvidia
register: system_info_nvidia_check
changed_when: false
failed_when: false
tags: [gather, gpu]
- name: Gather NVIDIA GPU details (if available)
shell: nvidia-smi --query-gpu=name,driver_version,memory.total,compute_cap --format=csv,noheader 2>/dev/null || echo "nvidia-smi not available"
register: system_info_nvidia_smi_raw
changed_when: false
failed_when: false
when: system_info_nvidia_check.rc == 0
tags: [gather, gpu]
- name: Check for AMD GPU
shell: lspci | grep -iE "AMD|ATI"
register: system_info_amd_check
changed_when: false
failed_when: false
tags: [gather, gpu]
- name: Gather AMD GPU details (if available)
shell: |
if command -v rocm-smi &> /dev/null; then
rocm-smi --showproductname --showdriverversion
else
echo "rocm-smi not available"
fi
register: system_info_amd_rocm_raw
changed_when: false
failed_when: false
when: system_info_amd_check.rc == 0
tags: [gather, gpu]
- name: Check for Intel GPU
shell: lspci | grep -i "intel.*graphics"
register: system_info_intel_gpu_check
changed_when: false
failed_when: false
tags: [gather, gpu]
- name: Parse GPU information
set_fact:
system_info_gpu_detected: "{{ system_info_gpu_lspci_raw.stdout != 'No GPU detected' }}"
system_info_gpu_list: "{{ system_info_gpu_lspci_raw.stdout_lines | default([]) }}"
tags: [gather, gpu]
- name: Build GPU details structure
set_fact:
system_info_gpu:
detected: "{{ system_info_gpu_detected }}"
devices: "{{ system_info_gpu_list }}"
nvidia:
present: "{{ system_info_nvidia_check.rc == 0 }}"
details: "{{ system_info_nvidia_smi_raw.stdout_lines | default([]) if system_info_nvidia_check.rc == 0 else [] }}"
amd:
present: "{{ system_info_amd_check.rc == 0 }}"
details: "{{ system_info_amd_rocm_raw.stdout_lines | default([]) if system_info_amd_check.rc == 0 else [] }}"
intel:
present: "{{ system_info_intel_gpu_check.rc == 0 }}"
detailed_info: "{{ system_info_gpu_detailed_raw.stdout_lines | default([]) }}"
tags: [gather, gpu]
- name: Check for GPU passthrough support (IOMMU)
shell: |
if dmesg | grep -iE "IOMMU|AMD-Vi|Intel VT-d" | grep -i enabled; then
echo "IOMMU enabled"
else
echo "IOMMU disabled or not available"
fi
register: system_info_iommu_status_raw
changed_when: false
become: true
failed_when: false
tags: [gather, gpu]
- name: Add IOMMU status to GPU info
set_fact:
system_info_gpu: "{{ system_info_gpu | combine({'iommu_status': system_info_iommu_status_raw.stdout | default('Unknown')}) }}"
tags: [gather, gpu]

View File

@@ -0,0 +1,109 @@
---
# Memory information gathering tasks
- name: Gather memory information from Ansible facts
set_fact:
system_info_memory_total_mb: "{{ ansible_memtotal_mb }}"
system_info_memory_free_mb: "{{ ansible_memfree_mb }}"
system_info_swap_total_mb: "{{ ansible_swaptotal_mb }}"
system_info_swap_free_mb: "{{ ansible_swapfree_mb }}"
tags: [gather, memory]
- name: Gather detailed memory information
shell: free -h
register: system_info_free_raw
changed_when: false
tags: [gather, memory]
- name: Gather memory information from /proc/meminfo
shell: cat /proc/meminfo | grep -iE "MemTotal|MemFree|MemAvailable|Buffers|Cached|SwapTotal|SwapFree|SwapCached"
register: system_info_meminfo_raw
changed_when: false
tags: [gather, memory]
- name: Parse detailed memory values
set_fact:
system_info_mem_available_kb: "{{ system_info_meminfo_raw.stdout | regex_search('MemAvailable:\\s+(\\d+)', '\\1') | default(['0'], true) | first }}"
system_info_mem_buffers_kb: "{{ system_info_meminfo_raw.stdout | regex_search('Buffers:\\s+(\\d+)', '\\1') | default(['0'], true) | first }}"
system_info_mem_cached_kb: "{{ system_info_meminfo_raw.stdout | regex_search('Cached:\\s+(\\d+)', '\\1') | default(['0'], true) | first }}"
tags: [gather, memory]
- name: Gather physical memory hardware information
shell: dmidecode -t memory | grep -iE "Size|Type|Speed|Manufacturer|Serial Number|Locator" || echo "DMI memory info not available"
register: system_info_dmi_memory_raw
changed_when: false
become: true
failed_when: false
tags: [gather, memory]
- name: Count physical memory modules
shell: dmidecode -t memory | grep -c "^[[:space:]]*Size.*MB" || echo "0"
register: system_info_memory_modules_count_raw
changed_when: false
become: true
failed_when: false
tags: [gather, memory]
- name: Gather swap devices information
shell: swapon --show --noheadings || echo "No swap configured"
register: system_info_swap_devices_raw
changed_when: false
tags: [gather, memory]
- name: Calculate memory usage percentage
set_fact:
system_info_memory_used_mb: "{{ ansible_memtotal_mb - ansible_memfree_mb }}"
system_info_memory_usage_percent: "{{ ((ansible_memtotal_mb - ansible_memfree_mb) / ansible_memtotal_mb * 100) | round(2) }}"
tags: [gather, memory]
- name: Calculate swap usage percentage
set_fact:
system_info_swap_used_mb: "{{ ansible_swaptotal_mb - ansible_swapfree_mb }}"
system_info_swap_usage_percent: "{{ ((ansible_swaptotal_mb - ansible_swapfree_mb) / ansible_swaptotal_mb * 100) | round(2) if ansible_swaptotal_mb > 0 else 0 }}"
tags: [gather, memory]
- name: Check for memory pressure
shell: |
if [ -f /proc/pressure/memory ]; then
cat /proc/pressure/memory
else
echo "Memory pressure statistics not available"
fi
register: system_info_memory_pressure_raw
changed_when: false
failed_when: false
tags: [gather, memory]
- name: Gather huge pages information
shell: |
grep -E "HugePages_|Hugepagesize" /proc/meminfo || echo "Huge pages not configured"
register: system_info_hugepages_raw
changed_when: false
tags: [gather, memory]
- name: Aggregate memory information
set_fact:
system_info_memory:
total_mb: "{{ system_info_memory_total_mb }}"
free_mb: "{{ system_info_memory_free_mb }}"
used_mb: "{{ system_info_memory_used_mb }}"
available_kb: "{{ system_info_mem_available_kb }}"
buffers_kb: "{{ system_info_mem_buffers_kb }}"
cached_kb: "{{ system_info_mem_cached_kb }}"
usage_percent: "{{ system_info_memory_usage_percent }}"
physical_modules: "{{ system_info_memory_modules_count_raw.stdout | default('0') }}"
hardware_details: "{{ system_info_dmi_memory_raw.stdout_lines | default([]) }}"
pressure: "{{ system_info_memory_pressure_raw.stdout_lines | default([]) }}"
hugepages: "{{ system_info_hugepages_raw.stdout_lines | default([]) }}"
free_output: "{{ system_info_free_raw.stdout_lines }}"
tags: [gather, memory]
- name: Aggregate swap information
set_fact:
system_info_swap:
total_mb: "{{ system_info_swap_total_mb }}"
free_mb: "{{ system_info_swap_free_mb }}"
used_mb: "{{ system_info_swap_used_mb }}"
usage_percent: "{{ system_info_swap_usage_percent }}"
devices: "{{ system_info_swap_devices_raw.stdout_lines | default([]) }}"
tags: [gather, memory]

View File

@@ -0,0 +1,90 @@
---
# Network information gathering tasks
- name: Gather network interfaces information
set_fact:
system_info_interfaces: "{{ ansible_interfaces }}"
tags: [gather, network]
- name: Gather IP addresses
shell: ip -br addr show | grep -v "^lo"
register: system_info_ip_addr_raw
changed_when: false
failed_when: false
tags: [gather, network]
- name: Gather detailed network configuration
shell: ip addr show
register: system_info_ip_full_raw
changed_when: false
tags: [gather, network]
- name: Gather routing table
shell: ip route show
register: system_info_routes_raw
changed_when: false
tags: [gather, network]
- name: Gather DNS configuration
shell: |
if [ -f /etc/resolv.conf ]; then
grep "^nameserver" /etc/resolv.conf
else
echo "No resolv.conf found"
fi
register: system_info_dns_raw
changed_when: false
tags: [gather, network]
- name: Gather network interface statistics
shell: ip -s link show
register: system_info_net_stats_raw
changed_when: false
tags: [gather, network]
- name: Check for active connections
shell: ss -tunapl | head -20
register: system_info_connections_raw
changed_when: false
become: true
failed_when: false
tags: [gather, network]
- name: Gather listening ports
shell: ss -tlnp
register: system_info_listening_raw
changed_when: false
become: true
failed_when: false
tags: [gather, network]
- name: Build network interface details
set_fact:
system_info_network_interfaces: {}
tags: [gather, network]
- name: Gather details for each interface
set_fact:
system_info_network_interfaces: "{{ system_info_network_interfaces | combine({item: {
'ipv4': ansible_facts[item]['ipv4'] | default({}),
'ipv6': ansible_facts[item]['ipv6'] | default([]),
'mac': ansible_facts[item]['macaddress'] | default('N/A'),
'mtu': ansible_facts[item]['mtu'] | default('N/A'),
'state': ansible_facts[item]['active'] | default(false) | string,
'type': ansible_facts[item]['type'] | default('unknown')
}}) }}"
loop: "{{ ansible_interfaces }}"
when: item != 'lo'
tags: [gather, network]
- name: Aggregate network information
set_fact:
system_info_network:
interfaces: "{{ system_info_network_interfaces }}"
ip_addresses: "{{ system_info_ip_addr_raw.stdout_lines | default([]) }}"
routes: "{{ system_info_routes_raw.stdout_lines }}"
dns_servers: "{{ system_info_dns_raw.stdout_lines | default([]) }}"
listening_ports: "{{ system_info_listening_raw.stdout_lines | default([]) }}"
default_ipv4: "{{ ansible_default_ipv4 | default({}) }}"
default_ipv6: "{{ ansible_default_ipv6 | default({}) }}"
tags: [gather, network]

View File

@@ -0,0 +1,91 @@
---
# System information gathering tasks
- name: Gather system information - Hostname
set_fact:
system_info_hostname: "{{ ansible_hostname }}"
system_info_fqdn: "{{ ansible_fqdn }}"
tags: [gather, system]
- name: Gather system information - OS details
set_fact:
system_info_os:
distribution: "{{ ansible_distribution }}"
distribution_version: "{{ ansible_distribution_version }}"
distribution_release: "{{ ansible_distribution_release }}"
distribution_major_version: "{{ ansible_distribution_major_version }}"
os_family: "{{ ansible_os_family }}"
tags: [gather, system]
- name: Gather system information - Kernel
set_fact:
system_info_kernel:
version: "{{ ansible_kernel }}"
architecture: "{{ ansible_architecture }}"
tags: [gather, system]
- name: Gather system uptime
shell: uptime -p
register: system_info_uptime_raw
changed_when: false
failed_when: false
tags: [gather, system]
- name: Gather system boot time
shell: uptime -s
register: system_info_boot_time_raw
changed_when: false
failed_when: false
tags: [gather, system]
- name: Set uptime facts
set_fact:
system_info_uptime: "{{ system_info_uptime_raw.stdout | default('Unknown') }}"
system_info_boot_time: "{{ system_info_boot_time_raw.stdout | default('Unknown') }}"
tags: [gather, system]
- name: Gather DMI/SMBIOS information
shell: dmidecode -t system | grep -E "Manufacturer|Product Name|Serial Number|UUID" || echo "Not available"
register: system_info_dmi_raw
changed_when: false
become: true
failed_when: false
tags: [gather, system]
- name: Parse DMI information
set_fact:
system_info_hardware:
manufacturer: "{{ system_info_dmi_raw.stdout | regex_search('Manufacturer: (.+)', '\\1') | default(['Unknown'], true) | first }}"
product: "{{ system_info_dmi_raw.stdout | regex_search('Product Name: (.+)', '\\1') | default(['Unknown'], true) | first }}"
serial: "{{ system_info_dmi_raw.stdout | regex_search('Serial Number: (.+)', '\\1') | default(['Unknown'], true) | first }}"
uuid: "{{ system_info_dmi_raw.stdout | regex_search('UUID: (.+)', '\\1') | default(['Unknown'], true) | first }}"
tags: [gather, system]
- name: Gather SELinux status (RHEL-based)
shell: getenforce
register: system_info_selinux_raw
changed_when: false
failed_when: false
when: ansible_os_family == "RedHat"
tags: [gather, system, security]
- name: Set SELinux status
set_fact:
system_info_selinux_status: "{{ system_info_selinux_raw.stdout | default('Not applicable') }}"
when: ansible_os_family == "RedHat"
tags: [gather, system, security]
- name: Gather AppArmor status (Debian-based)
shell: aa-status --enabled && echo "Enabled" || echo "Disabled"
register: system_info_apparmor_raw
changed_when: false
failed_when: false
become: true
when: ansible_os_family == "Debian"
tags: [gather, system, security]
- name: Set AppArmor status
set_fact:
system_info_apparmor_status: "{{ system_info_apparmor_raw.stdout | default('Not applicable') }}"
when: ansible_os_family == "Debian"
tags: [gather, system, security]

View File

@@ -0,0 +1,28 @@
---
# Installation tasks for system_info role
- name: Install required packages (Debian/Ubuntu)
apt:
name: "{{ system_info_packages_debian }}"
state: present
update_cache: true
cache_valid_time: 3600
when: ansible_os_family == "Debian"
become: true
tags: [install, packages]
- name: Install required packages (RHEL/Rocky/AlmaLinux)
dnf:
name: "{{ system_info_packages_redhat }}"
state: present
when: ansible_os_family == "RedHat"
become: true
tags: [install, packages]
- name: Ensure dmidecode is executable
file:
path: /usr/sbin/dmidecode
mode: '0755'
become: true
ignore_errors: true
tags: [install, packages]

View File

@@ -0,0 +1,77 @@
---
# tasks file for system_info
- name: Include OS-specific variables
include_vars: "{{ ansible_os_family }}.yml"
ignore_errors: true
tags: [always]
- name: Validate required variables
assert:
that:
- system_info_stats_base_dir is defined
- system_info_stats_base_dir | length > 0
fail_msg: "Required variable 'system_info_stats_base_dir' is not defined or empty"
tags: [validate]
- name: Set statistics directory path
set_fact:
system_info_stats_dir: "{{ system_info_stats_base_dir }}/{{ ansible_fqdn }}"
tags: [always]
- name: Create statistics directory on control node
file:
path: "{{ system_info_stats_dir }}"
state: directory
mode: '0755'
delegate_to: localhost
become: false
when: system_info_create_stats_dir | bool
tags: [install, setup]
- name: Include installation tasks
include_tasks: install.yml
tags: [install]
- name: Include system information gathering tasks
include_tasks: gather_system.yml
when: system_info_gather_system | bool
tags: [gather, system]
- name: Include CPU information gathering tasks
include_tasks: gather_cpu.yml
when: system_info_gather_cpu | bool
tags: [gather, cpu]
- name: Include GPU information gathering tasks
include_tasks: gather_gpu.yml
when: system_info_gather_gpu | bool
tags: [gather, gpu]
- name: Include memory information gathering tasks
include_tasks: gather_memory.yml
when: system_info_gather_memory | bool
tags: [gather, memory]
- name: Include disk information gathering tasks
include_tasks: gather_disk.yml
when: system_info_gather_disk | bool
tags: [gather, disk]
- name: Include network information gathering tasks
include_tasks: gather_network.yml
when: system_info_gather_network | bool
tags: [gather, network]
- name: Include hypervisor detection tasks
include_tasks: detect_hypervisor.yml
when: system_info_detect_hypervisor | bool
tags: [gather, hypervisor]
- name: Include statistics aggregation and export tasks
include_tasks: export_stats.yml
tags: [export, statistics]
- name: Include validation tasks
include_tasks: validate.yml
tags: [validate, health-check]

View File

@@ -0,0 +1,101 @@
---
# Validation and health check tasks
- name: Gather disk usage statistics
shell: df -h | grep -vE '^Filesystem|tmpfs|cdrom'
register: validate_disk_usage
changed_when: false
failed_when: false
tags: [validate, health-check]
- name: Gather memory usage statistics
shell: free -h
register: validate_memory_usage
changed_when: false
tags: [validate, health-check]
- name: Gather swap usage statistics
shell: swapon --show
register: validate_swap_usage
changed_when: false
failed_when: false
tags: [validate, health-check]
- name: Gather system uptime
shell: uptime
register: validate_system_uptime
changed_when: false
tags: [validate, health-check]
- name: Gather logged-in users
shell: who
register: validate_logged_users
changed_when: false
failed_when: false
tags: [validate, health-check]
- name: Check high CPU processes
shell: ps aux --sort=-%cpu | head -10
register: validate_top_cpu_processes
changed_when: false
tags: [validate, health-check]
- name: Check high memory processes
shell: ps aux --sort=-%mem | head -10
register: validate_top_mem_processes
changed_when: false
tags: [validate, health-check]
- name: Check for disk usage warnings (>80%)
shell: df -h | awk 'NR>1 {gsub(/%/,"",$5); if($5>80) print $0}'
register: validate_disk_warnings
changed_when: false
failed_when: false
tags: [validate, health-check]
- name: Verify statistics directory exists
stat:
path: "{{ system_info_stats_dir }}"
register: validate_stats_dir
delegate_to: localhost
become: false
tags: [validate]
- name: Verify JSON file was created
stat:
path: "{{ system_info_stats_dir }}/system_info.json"
register: validate_json_file
delegate_to: localhost
become: false
tags: [validate]
- name: Display system health summary
debug:
msg:
- "=== System Health Check for {{ ansible_fqdn }} ==="
- "Uptime: {{ validate_system_uptime.stdout }}"
- ""
- "=== Disk Usage ==="
- "{{ validate_disk_usage.stdout_lines }}"
- ""
- "=== Memory Usage ==="
- "{{ validate_memory_usage.stdout_lines }}"
- ""
- "{% if validate_swap_usage.stdout_lines | length > 0 %}=== Swap Usage ==={{ validate_swap_usage.stdout_lines }}{% else %}No swap configured{% endif %}"
- ""
- "{% if validate_disk_warnings.stdout_lines | length > 0 %}=== DISK WARNINGS (>80% usage) ==={{ validate_disk_warnings.stdout_lines }}{% endif %}"
- ""
- "=== Logged Users ==="
- "{{ validate_logged_users.stdout_lines if validate_logged_users.stdout_lines | length > 0 else ['No users logged in'] }}"
- ""
- "=== Top CPU Processes ==="
- "{{ validate_top_cpu_processes.stdout_lines[:5] }}"
- ""
- "=== Top Memory Processes ==="
- "{{ validate_top_mem_processes.stdout_lines[:5] }}"
- ""
- "=== Statistics Files ==="
- "Directory exists: {{ validate_stats_dir.stat.exists }}"
- "JSON file created: {{ validate_json_file.stat.exists }}"
- "Location: {{ system_info_stats_dir }}"
tags: [validate, health-check]