#!/bin/bash # Use bash since ash can only do math on 32bit numbers, which is not sufficient for ID44 calculations # also for negative length to ${x:0:-1} # This script gathers information about the hardware and configuration and reports it back to the server . /opt/openslx/config export LANG=C export LC_ALL=C export PATH=$PATH:/opt/openslx/sbin:/opt/openslx/bin touch "/tmp/hw-delete-list" || exit 10 chmod 0600 "/tmp/hw-delete-list" || exit 11 echo -n "" > "/tmp/hw-delete-list" || exit 12 mktemp() { local FILE= if [ -x /bin/mktemp ]; then FILE=$(/bin/mktemp) fi if [ -z "$FILE" ] && [ -x /opt/openslx/bin/mktemp ]; then FILE=$(/opt/openslx/bin/mktemp) fi if [ -z "$FILE" ]; then local FN DIR for DIR in "tmp" "tmp" "tmp" "run/user/$UID" "run"; do FN="/${DIR}/${RANDOM}-${$}-${UID}-$(date +%N)" [ -e "$FN" ] && continue touch "$FN" || continue chmod 0600 "$FN" || continue echo -n "" > "$FN" || continue FILE="$FN" break done fi if [ -z "$FILE" ]; then # Uhm... echo "/dev/null" exit 1 fi echo "$FILE" >> "/tmp/hw-delete-list" echo "$FILE" } # Can't just place all temp file names in a variable since mktemp usually runs in a subshell cleanup() { local FILE= while read -r FILE _ || [ -n "$FILE" ]; do [ -f "$FILE" ] && rm -f -- "$FILE" done < "/tmp/hw-delete-list" } slxfdisk() { local binary RET for binary in "/opt/openslx/sbin/fdisk -u" "busybox fdisk -u" "fdisk"; do $binary "$@" RET=$? [ "$RET" = "127" ] && continue # command not found [ "$RET" = "1" ] && [ "${binary%% *}" = "busybox" ] && continue # maybe applet not found return $RET done return 127 } if [ -z "$SLX_REMOTE_LOG" ]; then echo "No remote log url given, will not report" exit 1 fi # 1) Get MAC Address used for booting eval $(grep -Eo BOOTIF=\\S+ /proc/cmdline) if [ "${#BOOTIF}" -ne "20" ]; then echo "Getting MAC from /proc/cmdline failed, using 'ip a'..." BOOTIF=01-$(ip a | grep -A 1 ': br0' | grep -o 'ether ..:..:..:..:..:..' | cut -d' ' -f2 | sed s/:/-/g) if [ "${#BOOTIF}" -ne "20" ]; then echo "FAIL FAIL FAIL" BOOTIF="99-88-77-66-55-44-33" fi fi MAC=${BOOTIF:3} echo "Determined MAC=$MAC" # 2) Get machine UUID written in stage3.1 UUID=$(cat /etc/system-uuid) if [ -z "$UUID" ] || [ "${#UUID}" -ne "36" ]; then echo "No/malformed UUID, aborting" >&2 exit 1 fi echo "UUID=$UUID" # 3) Uptime in seconds UPTIME=$(grep -o -E '^[0-9]+' /proc/uptime) # 4) Number of real and virtual CPU cores # Virtual, cheap way VCORES=$(grep '^processor\s' /proc/cpuinfo | sort -u | wc -l) # Real cores CPUCORES=$(cat /sys/devices/system/cpu/cpu*/topology/thread_siblings_list | sort -u | wc -l) # Fallback 1... if [ -z "$CPUCORES" ] || [ "$CPUCORES" = "0" ]; then for c in $(dmidecode -t 4 | grep 'Core Enabled' | awk -F ':' '{print $2}'); do CPUCORES=$(( $CPUCORES + $c )) done fi # Fallback 2... if [ -z "$CPUCORES" ] || [ "$CPUCORES" = "0" ]; then CPUCORES=$(grep -E -e '^core id\s*:' -e '^physical\s*:' /proc/cpuinfo | xargs -l2 echo | sort -u | wc -l) fi echo "$CPUCORES real cores, $VCORES with HT" # 5) CPU model name CPUMODEL=$(grep -m1 '^model name\s*:' /proc/cpuinfo | sed 's/^model name\s*:\s*//;s/\s\s*/ /g;s/^ //;s/ $//') echo "$CPUMODEL" # 6) RAM RAM=$(grep -m1 '^MemTotal:' /proc/meminfo | awk '{print $2}') RAM=$(( $RAM / 1024 )) if [ -z "$RAM" ] || [ "$RAM" -lt 500 ]; then # Fallback to dmidecode RAM=0 for c in $(dmidecode -t 17 | grep -o 'Size:.*MB$' | awk '{print $2}'); do RAM=$(( $RAM + $c )) done fi echo "$RAM MB RAM" # 7) 64bit virtualization support VT="UNSUPPORTED" VIRTTYPE=$(grep -m1 '^flags\s*:' /proc/cpuinfo | grep -wo -e svm -e vmx) [ -n "$VIRTTYPE" ] && modprobe msr echo "Virtualization technology: $VIRTTYPE" if [ "$VIRTTYPE" = "vmx" ]; then # intel BIT1=$(rdmsr --bitfield 0:0 0x3a 2>/dev/null || echo "fail") BIT2=$(rdmsr --bitfield 2:2 0x3a 2>/dev/null || echo "fail") if [ "$BIT1" = "fail" -o "$BIT2" = "fail" ]; then VT="UNKNOWN" elif [ "$BIT1" = "0" -o "$BIT2" = "1" ]; then VT="ENABLED" else VT="DISABLED" fi elif [ "$VIRTTYPE" = "svm" ]; then # amd BIT=$(rdmsr --bitfield 4:4 0xc0010114 2>/dev/null || echo "fail") if [ "$BIT" = "fail" ]; then VT="UNKNOWN" elif [ "$BIT" = "0" ]; then VT="ENABLED" else VT="DISABLED" fi fi echo "$VIRTTYPE is $VT" # 8) ID44 partition size ID44=0 if ! slx-tools fs_path_isvolatile "/tmp" ; then ID44_SPACE=($(slx-tools fs_path_space "/tmp")) [ -n "${ID44_SPACE[1]}" ] && ID44="$(( ${ID44_SPACE[1]} / 1024 ))" if [ "$ID44" -eq 0 ]; then # Fallback to old way to detect it # Try df first, make sure device starts with /dev read -r df_dev df_size df_used df_avail df_usepercent df_mountpoint df_crap < <(df -P /tmp | grep ' /tmp$') if [ -n "${df_size}" ] && [ "${df_dev:0:5}" = "/dev/" ]; then # df reports kbytes, convert to MB ID44=$(( df_size / 1024 )) fi if [ "$ID44" = 0 ]; then # slxfdisk fallback for c in $(slxfdisk -l | grep -E '[0-9]+[\-\+]?\s+44\s+' | awk '{print $1}'); do val=$(blockdev --getsize64 "$c") [ -z "$val" ] && continue [ "$val" -gt "$ID44" ] && ID44=$val done # blockdev reports bytes, convert to MB ID44=$(( $ID44 / 1058576 )) # we'd rather underreport fi fi fi echo "Temp partition: $ID44 MB" # 9) check smart values FDISK=$(mktemp) declare -a DISKS shopt -s extglob for disk in /dev/disk/by-path/!(*-part*|*-usb-*); do [ -L "$disk" ] || continue disk="$( readlink -f "$disk" )" DISKS+=("$disk") slxfdisk -l "$disk" done > "$FDISK" shopt -u extglob BADSECTORS=0 if which smartctl; then ALLSMART=$(mktemp) FILE=$(mktemp) [ -z "$FILE" ] && FILE="/tmp/smartctl.$$.$RANDOM.$RANDOM" for dev in "${DISKS[@]}"; do smartctl -i -H -A -f "brief" "$dev" > "$FILE" # Exit code might be nonzero to indicate fail status, or because some (but not all) ATA commands # failed. So instead, see if we at least got basic device information and go on if so. grep -q -E -e '^Device Model:' -e '^\s*5\s.+\s.+\s.+\s[0-9]' "$FILE" || continue echo "NEXTHDD=$dev" >> "$ALLSMART" cat "$FILE" >> "$ALLSMART" # parse OVERALL=$(grep -o "test result: .*$" "$FILE" | cut -c 14-) [ "x$OVERALL" = "xPASSED" ] && OVERALL="" REALLOC=$(grep "^ *5 " "$FILE" | awk '{print $8}') PENDING=$(grep "^ *197 " "$FILE" | awk '{print $8}') SPINRETRY_VAL=$(grep "^ *10 " "$FILE" | awk '{print $4}') SPINRETRY_THR=$(grep "^ *10 " "$FILE" | awk '{print $6}') [ -n "$OVERALL" ] && BADSECTORS=$(( $BADSECTORS + 100 )) if [ -n "$REALLOC" ] && [ "$REALLOC" -gt "0" ]; then BADSECTORS=$(( $BADSECTORS + $REALLOC )) fi if [ -n "$PENDING" ] && [ "$PENDING" -gt "5" ]; then BADSECTORS=$(( $BADSECTORS + $PENDING )) fi if [ -n "$SPINRETRY_VAL" ] && [ "$SPINRETRY_VAL" -le "$SPINRETRY_THR" ]; then BADSECTORS=$(( $BADSECTORS + 100 )) fi done rm -f -- "$FILE" fi echo "SMART: $OVERALL - $REALLOC reallocated, $PENDING pending" # A) Read system model and manufacturer dmidec() { local RETVAL=$(dmidecode "$@" 2>/dev/null | grep -v '^#' | grep -v '^Invalid' | sed 's/\s\s*/ /g;s/^ //;s/ $//') case "$RETVAL" in ""|*"Product Name"*|*"be filled"*|"unknown"|*"efault string"*|*"efault String"*|*"product name"*|*"anufacturer"*|*"ystem model"*) RETVAL="Unknown" ;; esac echo "$RETVAL" } HW_MODEL=$(dmidec -q -s system-product-name) HW_MANUF=$(dmidec -q -s system-manufacturer) # Try fallback to baseboard if [ "$HW_MODEL" = "Unknown" ]; then HW_MODEL=$(dmidec -q -s baseboard-product-name) HW_MANUF=$(dmidec -q -s baseboard-manufacturer) fi MODEL="$HW_MODEL" if [ "$HW_MANUF" != "Unknown" ]; then MODEL="$MODEL ($HW_MANUF)" fi echo "System model: $MODEL" # n) Dump raw data to a file DATAFILE=$(mktemp) cat > "$DATAFILE" <<-EOF ############################### CPU ##################################### Sockets: $(grep '^physical id' /proc/cpuinfo | sort -u | wc -l) Real cores: $CPUCORES Virtual cores: $VCORES ######################## Partition tables ############################### EOF cat "$FDISK" >> "$DATAFILE" cat >> "$DATAFILE" <<-EOF ############################ PCI ID ##################################### EOF lspci -n -m >> "$DATAFILE" cat >> "$DATAFILE" <<-EOF ########################## dmidecode #################################### EOF dmidecode >> "$DATAFILE" if [ -n "$ALLSMART" ] && [ -s "$ALLSMART" ]; then cat >> "$DATAFILE" <<-EOF ########################### smartctl #################################### EOF cat "$ALLSMART" >> "$DATAFILE" fi cat >> "$DATAFILE" <<-EOF ######################### EOF echo "Created report file" [ -n "$ALLSMART" ] && rm -f -- "$ALLSMART" # Put some info in local file for later use HDDCOUNT="${#DISKS[@]}" bashesc () { sed s/\'/\'\"\'\"\'/g <<<$* } HW_MANUF=$(bashesc "$HW_MANUF") HW_MODEL=$(bashesc "$HW_MODEL") cat > "/run/hwinfo" < /run/hw-warnings.log CONTACT_RZ= if [ "$HW_KVM" = "DISABLED" ]; then echo "ff0000" "* 64Bit-Gast-Support (VT-x oder AMD-V) ist im BIOS deaktiviert. 64Bit VMs können nicht gestartet werden." >&4 CONTACT_RZ=jau elif [ "$HW_KVM" = "UNSUPPORTED" ]; then echo "000000" "* CPU hat keinen 64Bit-Gast-Support (VT-x oder AMD-V). 64Bit VMs können nicht gestartet werden." >&4 fi if [ -n "$HW_MBRAM" ] && [ "$HW_MBRAM" -lt 3400 ]; then local GB=$(( ( HW_MBRAM + 300 ) / 1024 )) echo "000000" "* Dieser PC hat wenig RAM (${GB}GB). Die Leistung von VM-Sitzungen wird nicht optimal sein." >&4 fi if [ "$HW_ID44" = "0" ]; then echo "000000" "* Keine ID44-Partition gefunden. VMs bekommen wenig RAM zugewiesen." >&4 if [ "$HW_HDDCOUNT" = "0" ]; then echo "000000" " Keine Festplatte erkannt; eine Festplatte wird empfohlen, wenn Sie VMs nutzen wollen." >&4 elif [ -n "$HW_HDDCOUNT" ]; then CONTACT_RZ=klar fi if [ -n "$HW_MBRAM" ] && [ "$HW_MBRAM" -lt 4500 ]; then echo "ff0000" " Da der PC wenig RAM hat, ist die Einrichtung einer ID44-Partition dringend zu empfehlen." >&4 fi elif [ -n "$HW_ID44" ] && [ "$HW_ID44" -lt 10000 ]; then echo "000000" "* Die ID44-Partition ist sehr klein. VM-Sitzungen könnten nach einiger Zeit aus Speichermangel abstürzen." >&4 CONTACT_RZ=fjeden fi if [ -n "$SLX_VM_NFS" ] && ! systemctl status mount-vm-store >/dev/null; then echo "ff0000" "* Der VM-Store konnte nicht eingehängt werden. VMs können nicht gestartet werden." >&4 echo "ff0000" " Versuchen Sie das Problem zu lösen, indem Sie den Computer neu starten." >&4 fi if grep -q '^nouveau ' "/proc/modules"; then echo "ff5500" "* Die nVidia-Karte in diesem Rechner wird nur von den quelloffenen Treibern (nouveau) unterstützt, und daher mit verminderter Leistung laufen." >&4 fi if [ -n "$CONTACT_RZ" ]; then echo "000000" " -- " >&4 echo "000000" " -- Wenden Sie sich ggf. an den bwLehrpool-Support Ihres Rechenzentrums -- " >&4 fi } buildlogfile & # Fire away echo "Submitting to $SLX_REMOTE_LOG" if curl --retry 4 --retry-connrefused --max-time 5 --retry-max-time 15 \ --data-urlencode "type=~poweron" --data-urlencode "uuid=$UUID" --data-urlencode "macaddr=$MAC" \ --data-urlencode "uptime=$UPTIME" --data-urlencode "realcores=$CPUCORES" --data-urlencode "mbram=$RAM" \ --data-urlencode "kvmstate=$VT" --data-urlencode "cpumodel=$CPUMODEL" --data-urlencode "id44mb=$ID44" \ --data-urlencode "badsectors=$BADSECTORS" --data-urlencode "systemmodel=$MODEL" \ --data-urlencode "data@$DATAFILE" "$SLX_REMOTE_LOG" | grep -q "RESULT=0"; then echo "Success" rm -f -- "$DATAFILE" START=$(( $RANDOM % 5 )) DELAY=$(( $RANDOM % 20 )) cat > "/etc/cron.d/usage_stats" <<-EOF # Update usage statistics on server SHELL=/bin/sh PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/opt/openslx/sbin:/opt/openslx/bin ${START}-59/5 * * * * root sleep ${DELAY}; /opt/openslx/scripts/cron-system_usage_update EOF touch "/etc/cron.d" # Sometimes, aufs doesn't update the mtime of dirs when creating files, # so cron would not rescan the cron directory cleanup # Trigger right now so resource usage gets updated /opt/openslx/scripts/cron-system_usage_update exit 0 else echo "Failed..." fi echo "Server doesn't seem to support hardware/usage stats - disabling logging" rm -f -- "/etc/cron.d/usage_stats" cleanup exit 1