Skip to content

Instantly share code, notes, and snippets.

@ixs
Last active November 21, 2025 16:09
Show Gist options
  • Select an option

  • Save ixs/77d45e036146a872a82852c8732d51d6 to your computer and use it in GitHub Desktop.

Select an option

Save ixs/77d45e036146a872a82852c8732d51d6 to your computer and use it in GitHub Desktop.
Small script to collect system information for a machine in TWiki Syntax
#!/bin/bash
#
# Collect system info from servers
#
STORCLI=/opt/MegaRAID/storcli/storcli64
HAS_RAID=0
VERSION=v2.0
echo "<noautolink>"
echo "Hardware inventory generated using collect_system_info.sh $VERSION from https://gist.github.com/ixs/77d45e036146a872a82852c8732d51d6"
# Check prerequisites
if ! command -v lspci &> /dev/null; then
echo "Error: 'lspci' command not found. Please install pciutils."
exit 1
fi
if ! command -v ethtool &> /dev/null; then
echo "Error: 'ethtool' command not found. Please install ethtool."
exit 1
fi
fetch_nics() {
read -r -d '' AWK_PARSER << 'EOF'
BEGIN {
RS = "" # Paragraph mode (records separated by blank lines)
FS = "\n" # Fields separated by newlines
}
{
path = ""; name = ""; sub_sys = ""
for (i=1; i<=NF; i++) {
line = $i
# Extract Path and Name from the first line
if (i == 1) {
split(line, parts, " ")
path = parts[1]
idx = index(line, "controller: ")
if (idx > 0) {
name = substr(line, idx + 12)
} else {
name = line
}
}
# Extract Subsystem
idx = index(line, "Subsystem: ")
if (idx > 0) {
sub_sys = substr(line, idx + 11)
}
}
if (path != "") {
printf "%s|%s|%s\n", path, name, sub_sys
}
}
EOF
# 3. Run lspci and parse basic info
RAW_DATA=$(lspci -PP -d ::0200 -v 2>/dev/null)
if [ -z "$RAW_DATA" ]; then
echo "No Ethernet controllers found."
exit 0
fi
# 4. Process data and fetch MACs via ethtool
declare -a paths names subsystems macs
declare -A subsys_counts processed_groups
while IFS='|' read -r p n s; do
paths+=("$p")
names+=("$n")
subsystems+=("$s")
# --- MAC Address Resolution ---
# 1. Extract the leaf PCI ID (e.g., "03:00.0") from the full path
pci_id="${p##*/}"
resolved_mac=""
# 2. Find the corresponding directory in sysfs
# We use a wildcard because sysfs typically includes the domain (e.g., 0000:03:00.0)
sys_dir=$(ls -d /sys/bus/pci/devices/*"$pci_id" 2>/dev/null | head -n 1)
if [ -n "$sys_dir" ] && [ -d "$sys_dir/net" ]; then
# 3. Find the interface name (e.g., "enp3s0f0") inside the 'net' folder
iface=$(ls "$sys_dir/net" 2>/dev/null | head -n 1)
if [ -n "$iface" ]; then
# 4. Query ethtool for the Permanent address
# Output format: "Permanent address: 00:1b:21:9c:43:f4"
ethtool_out=$(ethtool -P "$iface" 2>/dev/null)
# Extract just the MAC address (3rd column)
resolved_mac=$(echo "$ethtool_out" | awk '{print $3}')
fi
fi
macs+=("$resolved_mac")
# Count subsystems for grouping logic
if [ -n "$s" ]; then
curr=${subsys_counts["$s"]}
subsys_counts["$s"]=$((curr + 1))
fi
done < <(echo "$RAW_DATA" | awk "$AWK_PARSER")
# 5. Display the Topology
total=${#paths[@]}
for (( i=0; i<total; i++ )); do
t_sub="${subsystems[$i]}"
t_name="${names[$i]}"
t_mac="${macs[$i]}"
# Determine if this should be grouped
count=${subsys_counts["$t_sub"]}
is_card_group=0
# Group if count > 1 AND name looks like an Adapter
if [[ $count -gt 1 && ("$t_sub" == *"Adapter"* || "$t_sub" == *"Quad"* || "$t_sub" == *"Dual"*) ]]; then
is_card_group=1
fi
if [ $is_card_group -eq 1 ]; then
# If this group hasn't been printed yet
if [ -z "${processed_groups["$t_sub"]}" ]; then
echo " * $t_sub"
# Print all members of the group
for (( j=0; j<total; j++ )); do
if [ "${subsystems[$j]}" == "$t_sub" ]; then
s_disp=""
[ -n "${macs[$j]}" ] && s_disp=" [${macs[$j]}]"
echo " * ${names[$j]}$s_disp"
fi
done
processed_groups["$t_sub"]=1
fi
else
# Standalone device (print if not already handled by a group)
if [ -z "${processed_groups["$t_sub"]}" ]; then
s_disp=""
[ -n "$t_mac" ] && s_disp=" [$t_mac]"
echo " * $t_name$s_disp"
fi
fi
done
}
get_dimm_mfg() {
local id="$1"
case "$id" in
0x2C00) echo "Micron Technology, Inc." ;;
0x5105) echo "Qimonda AG i. In." ;;
0x802C) echo "Micron Technology, Inc." ;;
0x80AD) echo "Hynix Semiconductor Inc." ;;
0x80CE) echo "Samsung Electronics, Inc." ;;
0x8551) echo "Qimonda AG i. In." ;;
0xAD00) echo "Hynix Semiconductor Inc." ;;
0xCE00) echo "Samsung Electronics, Inc." ;;
*) echo "Unknown ($id)" ;;
esac
}
get_dmi_handle() {
local handle="$1"
if [[ -z "$handle" ]]; then
echo "Usage: get_handle <handle_id> (example: 0x0029)" >&2
return 1
fi
# Try dmidecode -H first
if dmidecode -H "$handle" &>/dev/null; then
dmidecode -H "$handle"
return 0
fi
# Fallback: parse full dmidecode output
dmidecode |
awk -v h="$handle" '
$1 == "Handle" && $2 == h "," {printing=1}
printing
/^Handle / && $2 != h "," && printing {exit}
'
}
echo " * System"
echo " * Hersteller: $(dmidecode -t system | grep Manufacturer | cut -d : -f 2 | xargs)"
echo " * Bezeichnung: $(dmidecode -t system | grep "Product Name" | cut -d : -f 2 | xargs)"
echo " * Seriennummer: $(dmidecode -t system | grep "Serial Number" | cut -d : -f 2 | xargs)"
echo " * Mainboard"
if dmidecode -t baseboard | grep Manufacturer | grep -q Cisco; then
echo " * Produktnummer: $(dmidecode -t baseboard | grep Version | cut -d : -f 2 | xargs)"
else
echo " * Produktnummer: $(dmidecode -t baseboard | grep "Product Name" | cut -d : -f 2 | xargs)"
fi
echo " * Seriennummer: $(dmidecode -t baseboard | grep "Serial Number" | cut -d : -f 2 | xargs)"
echo " * $(dmidecode -t processor | grep -c Version)x CPUs"
dmidecode -t processor | grep Version | while read line; do
echo " * $(echo "$line" | cut -d : -f 2 | xargs)"
done
echo " * $(dmidecode -t memory | grep 'Part Number' | grep -v "NO DIMM" | wc -l)x DIMMs"
dmidecode -t memory | grep '^Handle' | cut -d " " -f 2 | cut -c -6 | while read handle; do
get_dmi_handle $handle | grep -q "Part Number" || continue
get_dmi_handle $handle | grep -q "NO DIMM" && continue
echo " * $(get_dmi_handle $handle | grep -P "\tLocator" | cut -d : -f 2 | xargs) ($(get_dmi_handle $handle | grep -P "\tBank Locator" | cut -d : -f 2 | xargs))"
echo " * Speichergroesse $(get_dmi_handle $handle | grep "Size" | cut -d : -f 2 | xargs)"
echo " * Typ: $(get_dmi_handle $handle | grep "Type" | cut -d : -f 2 | xargs)"
echo " * Geschwindigkeit: $(get_dmi_handle $handle | grep -P "\tSpeed" | cut -d : -f 2 | xargs)"
echo " * Hersteller: $(get_dimm_mfg $(get_dmi_handle $handle | grep "Manufacturer" | cut -d : -f 2 | xargs))"
echo " * Bezeichnung: $(get_dmi_handle $handle | grep "Part Number" | cut -d : -f 2 | xargs)"
echo " * Seriennummer: $(get_dmi_handle $handle | grep "Serial Number" | cut -d : -f 2 | xargs)"
done
echo " * PCI Karten"
dmidecode -t 202 | grep '^Handle' | cut -d " " -f 2 | cut -c -6 | while read handle; do
get_dmi_handle $handle | awk '
/Strings:/ {
getline; card=$0;
getline; loc=$0; sub(/[\t ]*SlotID:/, "", loc);
getline; fw=$0
}
END {
gsub(/^[ \t]+/, "", card);
gsub(/^[ \t]+/, "", fw);
print " * Slot " loc;
print " * " card
print " * Firmware: " fw;
}'
done
if lspci -d ::0200 | grep -q Ethernet; then
echo " * Netzwerkkarten"
fetch_nics
fi
echo " * Grafikkarten"
while read -r line; do
echo " * $(echo $line | cut -d : -f 3- | xargs)"
done < <(lspci -d ::0300)
if lspci -d ::0c04 | grep -q Fibre; then
echo " * FibreChannel"
while read -r line; do
echo " * $(echo $line | cut -d : -f 3- | xargs)"
done < <(lspci -d ::0c04)
fi
if lspci | grep -q RAID; then
HAS_RAID=0
echo " * RAID"
while read -r line; do
HAS_RAID=$((HAS_RAID + 1))
echo " * $(echo $line | cut -d : -f 3- | xargs)"
done < <(lspci -d ::0104)
else
HAS_RAID=0
echo " * RAID nicht gefunden"
fi
if [ "$HAS_RAID" -gt "0" ]; then
if lspci -d ::0104 | grep -q LSI; then
echo " * LSI RAID"
data=$("$STORCLI" /c0 show nolog J)
echo " * Produktbezeichnung: $(echo "$data" | jq -r '.Controllers[]."Response Data"."Product Name"')"
echo " * Seriennummer: $(echo "$data" | jq -r '.Controllers[]."Response Data"."Serial Number"')"
echo " * CacheVault Modul: $(echo "$data" | jq -r '.Controllers[]."Response Data"."Cachevault_Info"[].Model')"
echo " * $(echo "$data" | jq -r '.Controllers[]."Response Data"."Physical Drives"')x Festplatten"
data=$("$STORCLI" /c0 /eall /sall show all nolog J)
"$STORCLI" /c0 /eall /sall show all nolog J | jq -r '
.Controllers[]."Response Data" as $rd |
$rd | to_entries[]
| select(.key | type == "string" and test("^Drive /c[0-9]+/e[0-9]+/s[0-9]+$"))
| .key as $drive
| .value[0] as $info
| ($drive + " - Detailed Information") as $detailKey
| ($rd[$detailKey][$drive + " Device attributes"]) as $attr
| ($rd[$detailKey][$drive + " Device attributes"]) as $attr
| ($info["EID:Slt"]) as $slot
| ($info.Size | split(" ")[0] | tonumber | round) as $size
| ($info.Model | gsub("^[[:space:]]+|[[:space:]]+$"; "")) as $model
| ($attr.SN | gsub("^[[:space:]]+|[[:space:]]+$"; "")) as $sn
| ($attr."Firmware Revision" | gsub("^[[:space:]]+|[[:space:]]+$"; "")) as $fw
| " * \($slot) \($size)GB \($info.Intf) \($model) SN: \($sn) FW: \($fw)"'
elif lspci -d ::0104 | grep -q 3ware; then
echo " * 3ware RAID"
data=$(tw_cli /c0 show all)
echo " * Produktbezeichnung: $(echo "$data" | grep '/c0 Model' | cut -d = -f 2 | xargs)"
echo " * Seriennummer: $(echo "$data" | grep '/c0 Serial Number' | cut -d = -f 2 | xargs)"
echo " * BBU Modul: $(tw_cli /c0/bbu show all | grep '/c0/bbu Serial Number' | cut -d = -f 2 | xargs)"
for port in $(echo "$data" | grep -o -E '^(p[0-9]+)'); do
type=$(echo "$data" | grep -E "^$port[[:space:]]" | awk '{print $6}')
disk=$(tw_cli /c0/${port} show all)
size=$(echo "$disk" | grep "Capacity" | cut -d "=" -f 2 | xargs | cut -d " " -f 1)
serial=$(echo "$disk" | grep "Serial" | cut -d "=" -f 2 | xargs | cut -d " " -f 1)
firmware=$(echo "$disk" | grep "Firmware Version" | cut -d "=" -f 2 | xargs)
echo " * ${port} $size GB $type SN: $serial FW: $firmware"
done
elif lspci -d ::0104 | grep -q Intel; then
echo " * Intel Matrix RAID (RST / IMSM)"
mdadm --detail --scan \
| grep -oE '/dev/md/[^ ]+' \
| grep -v '/dev/md/imsm' \
| sort -u \
| while read -r md; do
echo " * Array: $md"
# ---- RAID array details (filtered) ----
# mdadm --detail "$md" |
# awk '
# /^ *Raid Level/ {print " * " $0}
# /^ *Array Size/ {print " * " $0}
# /^ *State/ {print " * " $0}
# /^ *Active Devices/ {print " * " $0}
# /^ *Working Devices/{print " * " $0}
# /^ *Failed Devices/ {print " * " $0}
# '
# ---- List member devices ----
devices=$(mdadm --detail "$md" \
| awk '/ active sync | spare | faulty | removed |[0-9]+[ ]+[0-9]+[ ]+[0-9]+[ ]+[0-9]/ {print $NF}')
# echo " * Member Disks:"
# ---- Disk details like LSI block ----
for dev in $devices; do
base=$(basename "$dev")
sys="/sys/block/$base/device"
# Model / Serial / Firmware
model=$(cat "$sys/model" 2>/dev/null | xargs)
#serial=$(cat "$sys/serial" 2>/dev/null | xargs)
serial=$(smartctl -i ${dev} -j | jq -r '.serial_number' | xargs)
fwrev=$(cat "$sys/rev" 2>/dev/null | xargs)
# Size (convert sectors → GiB)
sectors=$(cat "/sys/block/$base/size" 2>/dev/null)
size_gb=$(( sectors / 1953125 ))
# Interface (SATA/NVMe/SAS)
transport=$(cat "$sys"/transport 2>/dev/null || echo SATA)
printf " * %s %sGB %s %s SN: %s FW: %s\n" \
"$base" "$size_gb" "$transport" "$model" "$serial" "$fwrev"
done
done
fi
fi
echo "</noautolink>"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment