Compare commits

...

3 Commits

Author SHA1 Message Date
daafb6c4fb Fix DIMM parsing, memory slot count, and service status bugs
- Fix BASH_REMATCH being wiped by second =~ in Bank Locator exclusion
  check, causing DIMM table to always show empty. Use glob == *pattern*
  instead of =~ for the exclusion so BASH_REMATCH is preserved.
- Remove incorrect bank_locators subtraction from total_slots count
  (grep pattern already excluded Bank Locator lines, so subtracting
  them again produced 0 total slots).
- Fix check_services showing stray "not-found" line after "inactive"
  status by replacing `|| echo "not-found"` with empty-string check.
- Add bc to README requirements list (script checks for it).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 18:50:28 -05:00
76f7aaa64c Bump version to 1.2.0 and update changelog
Update version number and document all changes from the
issue fixes including new features, bug fixes, and
performance improvements.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 10:56:29 -05:00
ef004c621e Fix CPU MHz parsing and memory DIMM display issues
- Fix CPU MHz showing multiple lines by using head -1 and proper regex
- Add CPU max MHz display when available
- Fix memory DIMM table not showing data by improving regex patterns
- Fix Type: field matching other fields like "Type Detail:"
- Filter out bonding_masters from network interfaces list
- Add fallback message when DIMM details cannot be parsed

#17

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 10:55:55 -05:00
2 changed files with 90 additions and 65 deletions

View File

@@ -32,6 +32,7 @@ The script requires the following tools to be installed:
- smartctl
- sensors
- lspci
- bc
Optional tools for enhanced diagnostics:
- ethtool (for detailed NIC information including link speed and firmware)
@@ -73,6 +74,8 @@ curl -sL "http://10.10.10.63:3000/LotusGuild/proxDoc/raw/branch/main/proxDoc.sh"
- `--vm-list`: Check VM vitals
- `--ct-list`: Check container vitals
- `--backup`: Review backup health
- `--checks=LIST`: Run only specific checks (comma-separated)
- Valid checks: cpu, ram, memory, storage, disk, network, hardware, temps, services, ceph, vms, containers
## Output Information
@@ -101,10 +104,26 @@ The script provides detailed information about:
## Version
Current Version: 1.1.0
Current Version: 1.2.0
### Changelog
#### v1.2.0
- Fixed variable quoting in disk iteration loops (security)
- Added input validation with whitelist of valid options
- Added examples to help documentation
- Added timeout protection to smartctl and ceph commands
- Added `--checks=` option for selective diagnostics
- Extracted magic strings into named constants
- Added validation for potentially empty variables
- Standardized error handling with cleanup trap
- Added optional logging infrastructure (PROXDOC_LOGFILE)
- Cached disk list and unit files to reduce command overhead
- Added efficient process wait utility function
- Fixed CPU MHz parsing showing multiple values
- Fixed memory DIMM table not displaying data
- Fixed bonding_masters showing as network interface
#### v1.1.0
- Added DriveAtlas integration (`--drives`) for physical drive bay mapping
- Added Ceph cluster health monitoring (`--ceph`)

View File

@@ -1,6 +1,6 @@
#!/bin/bash
VERSION="1.1.0"
VERSION="1.2.0"
###################
# Timeout Configuration
@@ -210,15 +210,21 @@ get_disk_health() {
}
get_cpu_info() {
local cpu_info cpu_cores cpu_mhz
local cpu_info cpu_cores cpu_mhz cpu_max_mhz
cpu_info=$(grep -m 1 -w 'model name' /proc/cpuinfo 2>/dev/null | awk -F: '{print $2}' | xargs)
cpu_cores=$(lscpu 2>/dev/null | grep '^CPU(s):' | awk '{print $2}')
cpu_mhz=$(lscpu 2>/dev/null | grep 'MHz' | awk '{print $4}')
cpu_cores=$(lscpu 2>/dev/null | grep -E '^CPU\(s\):' | awk '{print $2}')
# Get current CPU MHz (first match only) - handle both "CPU MHz:" and "CPU(s) MHz:" formats
cpu_mhz=$(lscpu 2>/dev/null | grep -E '^CPU( )?(max )?MHz:' | head -1 | awk -F: '{print $2}' | xargs)
cpu_max_mhz=$(lscpu 2>/dev/null | grep -E '^CPU max MHz:' | awk -F: '{print $2}' | xargs)
echo -e "${GREEN}CPU Model:${NC} ${cpu_info:-Unknown}"
echo -e "${GREEN}CPU Cores:${NC} ${cpu_cores:-Unknown}"
echo -e "${GREEN}CPU MHz:${NC} ${cpu_mhz:-Unknown}"
if [[ -n "$cpu_max_mhz" ]]; then
echo -e "${GREEN}CPU MHz:${NC} ${cpu_mhz:-Unknown} (max: ${cpu_max_mhz})"
else
echo -e "${GREEN}CPU MHz:${NC} ${cpu_mhz:-Unknown}"
fi
}
get_ram_info() {
@@ -292,97 +298,93 @@ get_motherboard_info() {
get_memory_details() {
echo -e "\n${GREEN}=== Memory DIMM Information ===${NC}"
# Use a more robust parsing approach
local locator size type speed manufacturer
local in_device=false
# Print header
printf "%-12s %-12s %-10s %-12s %-20s\n" "Slot" "Size" "Type" "Speed" "Manufacturer"
printf "%-12s %-12s %-10s %-12s %-20s\n" "----" "----" "----" "-----" "------------"
local locator="" size="" type="" speed="" manufacturer=""
local in_device=false
local dimm_count=0
while IFS= read -r line; do
# Detect start of a memory device section
if [[ "$line" =~ ^Memory[[:space:]]Device ]]; then
# If we have data from previous device, print it
if [[ -n "$locator" && -n "$size" && ! "$size" =~ (No|Not|Installed) ]]; then
# Detect start of a memory device section (may have leading whitespace or not)
if [[ "$line" =~ Memory[[:space:]]+Device$ ]] || [[ "$line" == "Memory Device" ]]; then
# Print previous device if it had valid data
if [[ -n "$locator" && -n "$size" && ! "$size" =~ ^(No|Not)[[:space:]] ]]; then
printf "%-12s %-12s %-10s %-12s %-20s\n" \
"${locator:-N/A}" \
"${size:-N/A}" \
"${type:-N/A}" \
"${speed:-N/A}" \
"${manufacturer:-N/A}"
((dimm_count++))
fi
# Reset variables for new device
locator=""
size=""
type=""
speed=""
manufacturer=""
# Reset for new device
locator="" size="" type="" speed="" manufacturer=""
in_device=true
continue
fi
# Skip if not in a device section
[[ "$in_device" != true ]] && continue
# Parse fields (case-insensitive, flexible whitespace)
if [[ "$line" =~ ^[[:space:]]*Locator:[[:space:]]*(.+)$ ]] && [[ ! "$line" =~ Bank ]]; then
# Parse fields - be very specific to avoid matching wrong lines
# Locator (but not Bank Locator)
if [[ "$line" =~ ^[[:space:]]*Locator:[[:space:]]*(.+)$ ]] && [[ ! "$line" == *Bank*Locator* ]]; then
locator="${BASH_REMATCH[1]}"
locator="${locator// /_}" # Replace spaces with underscores
locator="${locator// /_}"
# Size
elif [[ "$line" =~ ^[[:space:]]*Size:[[:space:]]*(.+)$ ]]; then
size="${BASH_REMATCH[1]}"
elif [[ "$line" =~ ^[[:space:]]*Type:[[:space:]]*(.+)$ ]]; then
# Type (exact match, not Type Detail or other Type fields)
elif [[ "$line" =~ ^[[:space:]]*Type:[[:space:]]*([A-Za-z0-9]+)$ ]]; then
type="${BASH_REMATCH[1]}"
# Skip if it's an error or unknown type
[[ "$type" =~ (Unknown|Error|Correction) ]] && type=""
# Only clear if it's truly unknown
[[ "$type" == "Unknown" ]] && type=""
# Speed (exact field)
elif [[ "$line" =~ ^[[:space:]]*Speed:[[:space:]]*(.+)$ ]]; then
speed="${BASH_REMATCH[1]}"
[[ "$speed" =~ Unknown ]] && speed=""
[[ "$speed" == "Unknown" ]] && speed=""
# Manufacturer
elif [[ "$line" =~ ^[[:space:]]*Manufacturer:[[:space:]]*(.+)$ ]]; then
manufacturer="${BASH_REMATCH[1]}"
[[ "$manufacturer" =~ (Unknown|NO DIMM) ]] && manufacturer=""
# Clear common placeholder values
[[ "$manufacturer" =~ ^(Unknown|NO[[:space:]]DIMM|Not[[:space:]]Specified)$ ]] && manufacturer=""
fi
# Empty line marks end of device section
if [[ -z "$line" ]]; then
# Empty line or new section marks end
if [[ -z "$line" ]] || [[ "$line" =~ ^Handle ]]; then
in_device=false
fi
done < <(dmidecode -t memory 2>/dev/null)
# Print last device if it has data
if [[ -n "$locator" && -n "$size" && ! "$size" =~ (No|Not|Installed) ]]; then
# Print last device if valid
if [[ -n "$locator" && -n "$size" && ! "$size" =~ ^(No|Not)[[:space:]] ]]; then
printf "%-12s %-12s %-10s %-12s %-20s\n" \
"${locator:-N/A}" \
"${size:-N/A}" \
"${type:-N/A}" \
"${speed:-N/A}" \
"${manufacturer:-N/A}"
((dimm_count++))
fi
# If no DIMMs were printed, show a message
if [[ $dimm_count -eq 0 ]]; then
echo " (Unable to parse DIMM details from dmidecode)"
fi
# Memory summary
echo -e "\n${GREEN}Memory Summary:${NC}"
# Count slots more reliably
local total_slots=0
local populated=0
while IFS= read -r line; do
if [[ "$line" =~ ^[[:space:]]*Locator: ]] && [[ ! "$line" =~ Bank ]]; then
((total_slots++))
fi
done < <(dmidecode -t memory 2>/dev/null)
while IFS= read -r line; do
if [[ "$line" =~ ^[[:space:]]*Size:[[:space:]]*(.+)$ ]]; then
local size_val="${BASH_REMATCH[1]}"
if [[ ! "$size_val" =~ (No|Not|Installed) ]]; then
((populated++))
fi
fi
done < <(dmidecode -t memory 2>/dev/null)
# Count slots and populated using simpler grep approach
# Pattern ^[[:space:]]*Locator: already excludes "Bank Locator:" lines
local total_slots populated
total_slots=$(dmidecode -t memory 2>/dev/null | grep -c "^[[:space:]]*Locator:")
populated=$(dmidecode -t memory 2>/dev/null | grep "^[[:space:]]*Size:" | grep -cv "No Module\|Not Installed")
echo -e " Total Slots: $total_slots"
echo -e " Populated: $populated"
@@ -445,16 +447,19 @@ get_physical_interfaces() {
for iface in /sys/class/net/*; do
# Skip if glob didn't match anything
[[ -e "$iface" ]] || continue
# Get just the interface name
iface=$(basename "$iface")
# Skip loopback
[[ "$iface" == "lo" ]] && continue
# Skip bonding_masters (virtual file, not an interface)
[[ "$iface" == "bonding_masters" ]] && continue
# Skip virtual/firewall interfaces
[[ "$iface" =~ $VIRTUAL_IFACE_PATTERN ]] && continue
# This is a physical interface
echo "$iface"
done
@@ -662,7 +667,8 @@ check_services() {
local services=("pvedaemon" "pveproxy" "pvecluster" "pve-cluster" "corosync")
for service in "${services[@]}"; do
local status
status=$(systemctl is-active "$service" 2>/dev/null || echo "not-found")
status=$(systemctl is-active "$service" 2>/dev/null)
[[ -z "$status" ]] && status="not-found"
echo -e "${GREEN}$service:${NC} $status"
done
}