Last active
November 21, 2025 16:09
-
-
Save ixs/77d45e036146a872a82852c8732d51d6 to your computer and use it in GitHub Desktop.
Small script to collect system information for a machine in TWiki Syntax
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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