From c214543b5ffdf3080ef282b251d62490719cb65d Mon Sep 17 00:00:00 2001 From: Keith Nash Date: Wed, 26 Jul 2017 10:12:19 -0500 Subject: [PATCH] Initial submission Perl script for reporting CPU and drive temperatures --- get-system-temps.pl | 329 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 329 insertions(+) create mode 100644 get-system-temps.pl diff --git a/get-system-temps.pl b/get-system-temps.pl new file mode 100644 index 0000000..3b95f24 --- /dev/null +++ b/get-system-temps.pl @@ -0,0 +1,329 @@ +#!/usr/local/bin/perl +############################################################################### +# +# get-system-temps.pl +# +# Displays CPU and drive temperatures +# +# Drive information reported includes the device ID, temperature, capacity, type +# (SDD or HDD), serial number, model, and, if available, the model family. +# +# Optionally uses IPMI to report CPU temperatures. Otherwise, these are pulled +# from sysctl. IPMI is more accurate in that it reports the temperature of each +# socketed CPU in the system, even on virtualized instances, whereas the CPU +# temperatures typically aren't available from sysctl in this case. +# +# Requires the smartmontools, available at: https://www.smartmontools.org/ +# +# Keith Nash, July 2017 +# +############################################################################### + +use strict; +use warnings; + +# Get system's hostname: + +my $hostname = qx(hostname); +chomp($hostname); + +# Full path to the smartctl program: + +my $smartctl = "/usr/local/sbin/smartctl"; + +# IPMI setup: + +# Toggle IPMI support on or off: +# 1 = on: use IPMI +# 0 = off: use sysctl instead of IPMI + +my $useipmi = 1; + +# IPMI username and password file. The password file is a text file with the +# IPMI user's password on the first line. Be sure to set permissions to 0600 +# on the password file. +# +# You may not need credentials on some systems. In this case, ignore these +# variables and modify the ipmitool variable below to suit your environment, +# removing the '-I lanplus' and user credential options (-U and -f) as needed. + +my $ipmiuser = "root"; +my $ipmipwfile = "/root/ipmi_password"; + +# The IPMI host must be either an IP address or a DNS-resolvable hostname. If you +# have multiple systems, leave the variable blank and edit the conditional below +# to specify the IPMI host according to the host on which you are running the script: + +my $ipmihost = ""; + +if ($useipmi && $ipmihost eq "") + { + if ($hostname =~ /bandit/) + { + $ipmihost="falcon.ipmi.spearfoot.net" + } + elsif ($hostname =~ /boomer/) + { + $ipmihost="felix.ipmi.spearfoot.net" + } + elsif ($hostname =~ /bacon/) + { + $ipmihost="fritz.ipmi.spearfoot.net" + } + else + { + die "No IPMI host specified!\n" + } + } + +# Full path to ipmitool program, including options and credentials: + +my $ipmitool = "/usr/local/bin/ipmitool -I lanplus -H $ipmihost -U $ipmiuser -f $ipmipwfile"; + +main(); + +############################################################################### +# +# main +# +############################################################################### +sub main +{ + printf("==========\n\n"); + + if ($useipmi) + { + printf("%s (IPMI host: %s)\n\n",$hostname,$ipmihost); + } + else + { + printf("%s\n\n",$hostname); + } + + display_cpu_temps(); + display_drive_info(); +} + +############################################################################### +# +# display_cpu_temps +# +############################################################################### +sub display_cpu_temps +{ + my $temp; + my $cpucores=0; + + if ($useipmi) + { + $cpucores = qx($ipmitool sdr | grep -c -i "cpu.*temp"); + } + else + { + $cpucores = qx(sysctl -n hw.ncpu); + } + + printf("=== CPU (%d) ===\n",$cpucores); + + if ($useipmi) + { + if ($cpucores > 1) + { + for (my $core=1; $core <= $cpucores; $core++) + { + $temp=qx($ipmitool sdr | grep -i "CPU$core Temp" | awk '{print \$4}'); + chomp($temp); + printf("CPU %2u: %3sC\n",$core,$temp); + } + } + else + { + $temp=qx($ipmitool sdr | grep -i "CPU Temp" | awk '{print \$4}'); + chomp($temp); + printf("CPU %2u: %3sC\n",1,$temp); + } + } + else + { + for (my $core=0; $core < $cpucores; $core++) + { + $temp = qx(sysctl -n dev.cpu.$core.temperature); + chomp($temp); + if ($temp <= 0) + { + printf("CPU %2u: -N/A-\n",$core); + } + else + { + printf("CPU %2u: %3sC\n",$core,$temp); + } + } + } +} + +############################################################################### +# +# display_drive_info +# +############################################################################### +sub display_drive_info +{ + my $drive_id; + my $drive_model; + my $drive_family; + my $drive_serial; + my $drive_capacity; + my $drive_temp; + my $drive_is_ssd; + my $drive_family_display; + + printf("\n=== Drives ===\n"); + + my @smart_drive_list = get_smart_drives(); + + foreach my $drive (@smart_drive_list) + { + ($drive_model, $drive_family, $drive_serial, $drive_capacity, $drive_temp, $drive_is_ssd) = get_drive_info($drive); + + if ($drive =~ /\/dev\/(.*)/) + { + $drive_id = $1; + } + else + { + $drive_id = $drive; + } + + if ($drive_family eq "") + { + $drive_family_display = ""; + } + else + { + $drive_family_display = "(" . $drive_family . ")"; + } + + printf("%6.6s: %3uC [%8.8s %s] %-20.20s %s %s\n", + $drive_id, + $drive_temp, + $drive_capacity, + $drive_is_ssd ? "SSD" : "HDD", + $drive_serial, + $drive_model, + $drive_family_display); + } +} + +############################################################################### +# +# get_smart_drives +# +############################################################################### +sub get_smart_drives +{ + my @retval = (); + my @drive_list = split(" ", qx($smartctl --scan | awk '{print \$1}')); + + foreach my $drive (@drive_list) + { + my $smart_enabled = qx($smartctl -i $drive | grep "SMART support is: Enabled" | awk '{print \$4}'); + chomp($smart_enabled); + if ($smart_enabled eq "Enabled") + { + push @retval, $drive; + } + } + + return @retval; +} + +############################################################################### +# +# get_drive_info +# +############################################################################### +sub get_drive_info +{ + my $drive = shift; + my $smart_data = qx($smartctl -a $drive); + + my $drive_model = ""; + my $drive_family = ""; + my $drive_serial = ""; + my $drive_capacity = ""; + my $drive_temp = 0; + my $drive_is_ssd = 0; + + $drive_temp = get_drive_temp($drive); + + # Serial number + if ($smart_data =~ /^Serial Number:\s*(.*)\s/m) + { + $drive_serial = $1; + } + + # Device model + if ($smart_data =~ /^Device Model:\s*(.*)\s/m) + { + $drive_model = $1; + } + + # Model family + if ($smart_data =~ /^Model Family:\s*(.*)\s/m) + { + $drive_family = $1; + } + + # User capacity + if ($smart_data =~ /^User Capacity:.*\[(.*)\]\s/m) + { + $drive_capacity = $1; + } + + # Determine if drive is a SSD + if ($smart_data =~ /^Rotation Rate:[ ]*Solid State Device/m) + { + $drive_is_ssd = 1; + } + elsif ($smart_data =~ /^[ 0-9]{3} Unknown_SSD_Attribute/m) + { + $drive_is_ssd = 1; + } + elsif ($smart_data =~ /^[ 0-9]{3} Wear_Leveling_Count/m) + { + $drive_is_ssd = 1; + } + elsif ($smart_data =~ /^[ 0-9]{3} Media_Wearout_Indicator/m) + { + $drive_is_ssd = 1; + } + elsif ($drive_family =~ /SSD/) + { + # Model family indicates SSD + $drive_is_ssd = 1; + } + + return ($drive_model, $drive_family, $drive_serial, $drive_capacity, $drive_temp, $drive_is_ssd); +} + +############################################################################### +# +# get_drive_temp +# +############################################################################### +sub get_drive_temp +{ + my $drive = shift; + my $retval = 0; + + $retval = qx($smartctl -A $drive | grep "194 Temperature" | awk '{print \$10}'); + + if (!$retval) + { + $retval = qx($smartctl -A $drive | grep "190 Airflow_Temperature" | awk '{print \$10}'); + } + + return $retval; +} + +