#!/bin/bash # LANG="en_US.UTF-8" # TrueNASConfigEmailEncryption="" # Set this to "" for no encryption, MUST REMAIN ON LINE 5. # NOTE: Some email providers will not send some encrypted file types, such as GMAIL, but .zip files are okay. # NOTE: 7zip is used for the compression and encryption. # Use [-help] to read the Help Section. For a short list of commands use [-h]. # Use [-config] to create a configuration file in the directory this script is run from. ###### ZPool & SMART status report with FreeNAS/TrueNAS config backup ### Original script by joeschmuck, modified by Bidule0hm, then by melp (me) ### Version: v1.3 TrueNAS Scale (Jeff Alperin 12-6-21) ### Version v1.4 to v2.4 FreeNAS/TrueNAS (Core & Scale) (joeschmuck) ### Changelog: # V2.4.4-Special (25 Aug 2023) # - Special version to collect multipath drive data and remove it from the report. # # V2.4.4 (19 August 2023) # - Incorporated Spencer.py script by calling the external script. # - Changed TrueNAS Backup Configuration file name to include the TrueNAS version number. # - Fixed ability to edit/add Warranty Drive Data so it does not erase previously entered data. # - Fixed SSD/NVMe Capacity Title in chart. # - Fixed NVMe Media Errors column display, it now appears centered and has lines around it. # - Added more Wear Level & TBW compatibility. # - Updated -h and -help commands. # - Updated text section 'Drives for this pool' to list non-GPTID entries. # # V2.4.3 (16 June 2023) # - Minor Update to recognize more SCSI drive Offline Uncorrectable Errors and Total Data Written. # - Minor Update to recognize UDMA CRC Errors for some older Intel SSD's. # # V2.4.2 (19 May 2023) # - Bug Fix to properly recognize Samsung HD103UJ HDD. # - Bug fix to properly recognize/display more than 26 drives in Scale. # # V2.4.1 (29 April 2023) # - Bugfix to allow script to be run outside the script directory. # - Updated chmod 755 to the new script file. # - Updated cleanup routine for upgrade files left behind. # - Updated TrueNAS Config Backup name to reference the NAS name. # - Added Automatic Update Feature. # - Added Update Script File Checksum Check. # # V2.4 (21 April 2023) # - Added NVMe Columns for Media Errors and Total Data Written. # - Added Total Data Written for all drives. # - Updated User Guide. # # V2.3 (14 April 2023) # - Simplified User Installation # - Bugfix for Custom Drive Configuration - Wear Level, Helium Level. # # V2.2 (10 April 2023) # - Bugfix for Test Age always being = "2". # - Bugfix for SSD Wear Level for certain drives. # - Bugfix for Scale, no TrueNAS Config Backup without 7-zip. # - Added Automatic Update Notification for newer version. # - Added Message from the Creator. # - Added Symlink - "multi_report.sh" is the file name to run from this point forward. Read the User Guide for more details. # - Changed TrueNASConfig Backup to zip file type attachment. # - Adjusted for drive that passes SMART Test but reports the test hour as '0'. # # See Changelog file on github.com/JoeSchmuck ######### INSTRUCTIONS ON USE OF THIS SCRIPT # # This script has been designed to run using an external reusable configuration file which is # configured by running the script with the -config parameter. # # To run the program from the command line, ensure you are in the directory in which the script resides, # then use ./multi_report.sh [-h] for additional help instructions, and [-config] to run the configuration. # # You may need to make the script executable using "chmod +x multi_report.sh" or chmod 777. # I prefer to have my directory permission setup to 777, but mine is a small home system. # Your security may demand more strick control. # ###### User-definable Parameters (IF YOU DO NOT WANT TO USE THE EXTERNAL CONFIGURATION FILE) ####### # # Modifying the script below should ONLY be done if you are not using an External Configuration File. # # The External Configuration File allows the end user to not be required to update the script every time a # new change comes out. It is highly recommended to use the external configuration file, in Version 3 # I plan to make the external configuration file a requirement unless there is argument against this move. # # The sections below configures the script to your needs. Please follow the instructions as it will matter, you cannot # just "wing it". Configurations are exact. We use generally three different formats, Variables = true/false, # Variables = NUMBER, and Variables = Comma Separated Variable (CSV) Strings. Each variable will have a description # associated with it, read it carefully. # # The default configuration will work right out of the box however one item must be changed, your email address. # I highly recommend you try out the default setup first and then make changes as desired. The only two changes # I recommend is of course your email address, the second is the location of the 'statistical_data_file.cvs'. # # Pay attention to any changes you make, accidentally deleting a quote will cause the entire script to fail. # Do not continue editing the script after the User Definable Section unless you know what you are doing. # # This script will not harm your drives. We are mostly only collecting drive status data. All file writes are # to /tmp space with one exception: statistical_data_file.cvs is stored in /tmp by default however if you desire # to maintain this data it must be stored in a dataset (user selected). # NOTE: The below parameters are all the "Default" values and are used to create new blank configuration files. ###### Email Address(s) ###### Email="YourEmail@Address.com" # Normal report email address From="TrueNAS@local.com" # From address (default works for many) ### Alert Email Configuration ### AlertEmail="YourAlertEmail@Address.com" # Alert email address used with the '-m' switch. AlertOnWarningTemp="true" # Send alert on Warning Temp. Default="true" AlertOnCriticalError="true" # Send alert on Critical Error. Default="true" ### Config File Name and Location ### SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) Config_File_Name="$SCRIPT_DIR/multi_report_config.txt" ########## Script Update ########## # Ensure you understand these options. CheckForUpdates="true" # Will check GitHub for updates and include message in next email. AutomaticUpdate="false" # WARNING !!! This option will automatically update the script if a newer version exists on GitHub with no user interaction. ###### Spencer Integration # Warning Levels are: None, Warning, Critical -- This only affects the Email Subject Line, if any errors are present, an attachment will occur. spencer_new_warning_level="Warning" # What to do if a "new" error occurs. spencer_existing_warning_level="None" # What to do for an existing error. spencer_enable="true" # To call the Spencer.py script if "true" or "false" to not run the Spencer.py script. spencer_script_name="$SCRIPT_DIR/spencer.py" # The default is "spencer.py" located in the default script directory. ###### Zpool Status Summary Table Settings PoolUsedWarn=80 # Pool used percentage for CRITICAL color to be used. ScrubAgeWarn=37 # Maximum age (in days) of last pool scrub before CRITICAL color will be used. Default=37. ###### Temperature Settings HDDtempWarn=45 # HDD Drive Warning Temp (in C) when a WARNING message will be used. HDDtempCrit=50 # HDD Drive Critical Temp (in C) when a CRITICAL message will be used. SSDtempWarn=45 # SSD Drive Warning Temp (in C) when a WARNING message will be used. SSDtempCrit=50 # SSD Drive Critical Temp (in C) when a CRITICAL message will be used. NVMtempWarn=50 # NVM Drive Warning Temp (in C) when a WARNING message will be used. NVMtempCrit=60 # NVM Drive Critical Temp (in C) when a CRITICAL message will be used. HDD_Cur_Pwr_Max_Temp_Ovrd="true" # HDD Max Drive Temp Override. This value when "true" will NOT alarm on any Current Power Cycle Max Temperature Limit. SSD_Cur_Pwr_Max_Temp_Ovrd="true" # SSD Max Drive Temp Override. This value when "true" will NOT alarm on any Current Power Cycle Max Temperature Limit. NVM_Cur_Pwr_Max_Temp_Ovrd="true" # NVM Max Drive Temp Override. This value when "true" will NOT alarm on any Current Power Cycle Max Temperature Limit. ###### SSD/NVMe Specific Settings WearLevelCrit=9 # Wear Level Alarm Setpoint when a WARNING message. 9% is the default. ###### General Settings # Output Formats PowerTimeFormat="h" # Format for power-on hours string, valid options are "ymdh", "ymd", "ym", "y", or "h" (year month day hour). TempDisplay="*C" # The format you desire the temperature to be displayed. Common formats are: "*C", "^C", or "^c". Choose your own. Non_Exist_Value="---" # How do you desire non-existent data to be displayed. The Default is "---", popular options are "N/A" or " ". Pool_Capacity_Type="zfs" # Select "zfs" or "zpool" for Zpool Status Report - Pool Size and Free Space capacities. "zfs" is default. # Ignore or Activate Alarms IgnoreUDMA="false" # Set to "true" to ignore all UltraDMA CRC Errors for the summary alarm (Email Header) only, errors will appear in the graphical chart. Default is "false". IgnoreSeekError="true" # Set to "true" to ignore all Seek Error Rate/Health errors. Default is true. IgnoreReadError="true" # Set to "true" to ignore all Seek Error Rate/Health errors. Default is true. IgnoreMultiZone="false" # Set to "true" to ignore all MultiZone Errors. Default is false. DisableWarranty="true" # Set to "true to disable Email Subject line alerts for any expired warranty alert. The Email body will still report the alert. Default is "true". # Disable or Activate Input/Output File Settings IncludeSSD="true" # Set to "true" will engage SSD Automatic Detection and Reporting, false = Disable SSD Automatic Detection and Reporting. IncludeNVM="true" # Set to "true" will engage NVM Automatic Detection and Reporting, false = Disable NVM Automatic Detection and Reporting. ReportNonSMART="true" # Will force even non-SMART devices to be reported, "true" = normal operation to report non-SMART devices. DisableRAWdata="false" # Set to "true" to remove the smartctl -a data and non-smart data appended to the normal report. Default is false. ATA_Auto_Enable="false" # Set to "true" to automatically update Log Error count to only display a log error when a new one occurs. # Media Alarms SectorsWarn=0 # Number of sectors per drive when a WARNING message will be used, this value should be less than SectorsCrit. SectorsCrit=9 # Number of sectors per drive when a CRITICAL message will be used. ReAllocWarn=0 # Number of Reallocated sector events allowed. MultiZoneWarn=0 # Number of MultiZone Errors to allow when a Warning message will be used. Default is 0. MultiZoneCrit=5 # Number of MultiZone Errors to allow when a Warning message will be used. Default is 5. DeviceRedFlag="true" # Set to "true" to have the Device Column indicate RED for ANY alarm condition. Default is true. HeliumAlarm="true" # Set to "true" to set for a critical alarm any He value below "HeliumMin" value. Default is true. HeliumMin=100 # Set to 100 for a zero leak helium result. An alert will occur below this value. RawReadWarn=5 # Number of read errors to allow when a WARNING message will be used, this value should be less than RawReadCrit. RawReadCrit=100 # Number of read errors to allow when a CRITICAL message will be used. SeekErrorsWarn=5 # Number of seek errors to allow when a WARNING message will be used, this value should be less than SeekErrorsCrit. SeekErrorsCrit=100 # Number of seek errors to allow when a CRITICAL message will be used. NVM_Media_Errors=1 # Number of Media Errors to alarm with a CRITICAL message. # Time-Limited Error Recovery (TLER) SCT_Enable="false" # Set to "true" to send a command to enable SCT on your drives for user defined timeout. SCT_Warning_Level="TLER_No_Msg" # Set to "all" will generate a Warning Message for all devices not reporting SCT enabled. "TLER" reports only drive which support TLER. # "TLER_No_Msg" will only report for TLER drives and not report a Warning Message if the drive can set TLER on. SCT_Read_Timeout=70 # Set to the read threshold. Default = 70 = 7.0 seconds. SCT_Write_Timeout=70 # Set to the write threshold. Default = 70 = 7.0 seconds. # SMART Testing Alarm TestWarnAge=2 # Maximum age (in days) of last SMART test before CRITICAL color/message will be used. # Zpool Fragmentation Alarm ZpoolFragWarn=80 # Percent of fragmentation before a Warning message occurs. ###### Statistical Data File statistical_data_file="$SCRIPT_DIR/statisticalsmartdata.csv" # Default location is where the script is located. SDF_DataRecordEnable="true" # Set to "true" will save all drive data into a CSV file defined by "statistical_data_file" below. SDF_DataEmail="true" # Set to "true" to have an attachment of the file emailed to you. Default is true. SDF_DataPurgeDays=730 # Set to the number of day you wish to keep in the data. Older data will be purged. Default is 730 days (2 years). 0=Disable. SDF_DataEmailDay="Mon" # Set to the day of the week the statistical report is emailed. (All, Mon, Tue, Wed, Thu, Fri, Sat, Sun, Month) ###### FreeNAS config backup settings TrueNASConfigEmailEnable="true" # Set to "true" to save config backup (which renders next two options operational); "false" to keep disable config backups. TrueNASConfigEmailDay="Mon" # Set to the day of the week the config is emailed. (All, Mon, Tue, Wed, Thu, Fri, Sat, Sun, Month) TrueNASConfigBackupSave="false" # Set to "false" to delete FreeNAS config backup after mail is sent; "true" to keep it in dir below. TrueNASConfigBackupLocation="/tmp/" # Directory in which to store the backup FreeNAS config files. ###### Attach multi_report_config.txt to email ###### MRConfigEmailEnable="true" # Set to "true" to enable periodic email (which renders next two options operational). MRChangedEmailSend="true" # If "true" will attach the updated/changed file to the email. MRConfigEmailDay="Mon" # Set to the day of the week the multi_report_config.txt is emailed. (All, Mon, Tue, Wed, Thu, Fri, Sat, Sun, Month, Never) ########## REPORT CHART CONFIGURATION ############## ###### REPORT HEADER TITLE ###### HDDreportTitle="Spinning Rust Summary Report" # This is the title of the HDD report, change as you desire. SSDreportTitle="SSD Summary Report" # This is the title of the SSD report, change as you desire. NVMreportTitle="NVMe Summary Report" # This is the title of the NVMe report, change as you desire. ### CUSTOM REPORT CONFIGURATION ### # By default most items are selected. Change the item to false to have it not displayed in the graph, true to have it displayed. # NOTE: Alarm setpoints are not affected by these settings, this is only what columns of data are to be displayed on the graph. # I would recommend that you remove columns of data that you don't really care about to make the graph less busy. # For Zpool Status Summary Zpool_Pool_Name_Title="Pool Name" Zpool_Status_Title="Status" Zpool_Pool_Size_Title="Pool Size" Zpool_Free_Space_Title="Free Space" Zpool_Used_Space_Title="Used Space" Zfs_Pool_Size_Title="^Pool Size" Zfs_Free_Space_Title="^Free Space" Zfs_Used_Space_Title="^Used Space" Zpool_Frag_Title="Frag" Zpool_Read_Errors_Title="Read Errors" Zpool_Write_Errors_Title="Write Errors" Zpool_Checksum_Errors_Title="Cksum Errors" Zpool_Scrub_Repaired_Title="Scrub Repaired Bytes" Zpool_Scrub_Errors_Title="Scrub Errors" Zpool_Scrub_Age_Title="Last Scrub Age" Zpool_Scrub_Duration_Title="Last Scrub Duration" # For Hard Drive Section HDD_Device_ID="true" HDD_Device_ID_Title="Device ID" HDD_Serial_Number="true" HDD_Serial_Number_Title="Serial Number" HDD_Model_Number="true" HDD_Model_Number_Title="Model Number" HDD_Capacity="true" HDD_Capacity_Title="HDD Capacity" HDD_Rotational_Rate="true" HDD_Rotational_Rate_Title="RPM" HDD_SMART_Status="true" HDD_SMART_Status_Title="SMART Status" HDD_Warranty_Title="Warr- anty" HDD_Warranty="true" HDD_Raw_Read_Error_Rate="true" HDD_Raw_Read_Error_Rate_Title="Raw Error Rate" HDD_Drive_Temp="true" HDD_Drive_Temp_Title="Curr Temp" HDD_Drive_Temp_Min="true" HDD_Drive_Temp_Min_Title="Temp Min" HDD_Drive_Temp_Max="true" HDD_Drive_Temp_Max_Title="Temp Max" HDD_Power_On_Hours="true" HDD_Power_On_Hours_Title="Power On Time" HDD_Start_Stop_Count="true" HDD_Start_Stop_Count_Title="Start Stop Count" HDD_Load_Cycle="true" HDD_Load_Cycle_Title="Load Cycle Count" HDD_Spin_Retry="true" HDD_Spin_Retry_Title="Spin Retry Count" HDD_Reallocated_Sectors="true" HDD_Reallocated_Sectors_Title="Re-alloc Sects" HDD_Reallocated_Events="true" HDD_Reallocated_Events_Title="Re-alloc Evnt" HDD_Pending_Sectors="true" HDD_Pending_Sectors_Title="Curr Pend Sects" HDD_Offline_Uncorrectable="true" HDD_Offline_Uncorrectable_Title="Offl Unc Sects" HDD_UDMA_CRC_Errors_List="true" HDD_UDMA_CRC_Errors_List_Title="UDMA CRC Error" HDD_Seek_Error_Rate="true" HDD_Seek_Error_Rate_Title="Seek Error Rate" HDD_MultiZone_Errors="true" HDD_MultiZone_Errors_Title="Multi Zone Error" HDD_Helium_Level="true" HDD_Helium_Level_Title="He Level" HDD_Last_Test_Age="true" HDD_Last_Test_Age_Title="Last Test Age" HDD_Last_Test_Type="true" HDD_Last_Test_Type_Title="Last Test Type" HDD_Total_Data_Written="true" HDD_Total_Data_Written_Title="Total Data Written" # For Solid State Drive Section SSD_Device_ID="true" SSD_Device_ID_Title="Device ID" SSD_Serial_Number="true" SSD_Serial_Number_Title="Serial Number" SSD_Model_Number="true" SSD_Model_Number_Title="Model Number" SSD_Capacity="true" SSD_Capacity_Title="SSD Capacity" SSD_SMART_Status="true" SSD_SMART_Status_Title="SMART Status" SSD_Warranty_Title="Warr- anty" SSD_Warranty="true" SSD_Drive_Temp="true" SSD_Drive_Temp_Title="Curr Temp" SSD_Drive_Temp_Min="true" SSD_Drive_Temp_Min_Title="Temp Min" SSD_Drive_Temp_Max="true" SSD_Drive_Temp_Max_Title="Temp Max" SSD_Power_On_Hours="true" SSD_Power_On_Hours_Title="Power On Time" SSD_Wear_Level="true" SSD_Wear_Level_Title="Wear Level" SSD_Reallocated_Sectors="true" SSD_Reallocated_Sectors_Title="Re-alloc Sects" SSD_Reallocated_Events="true" SSD_Reallocated_Events_Title="Re-alloc Evnt" SSD_Pending_Sectors="true" SSD_Pending_Sectors_Title="Curr Pend Sects" SSD_Offline_Uncorrectable="true" SSD_Offline_Uncorrectable_Title="Offl Unc Sects" SSD_UDMA_CRC_Errors_List="true" SSD_UDMA_CRC_Errors_List_Title="UDMA CRC Error" SSD_Last_Test_Age="true" SSD_Last_Test_Age_Title="Last Test Age" SSD_Last_Test_Type="true" SSD_Last_Test_Type_Title="Last Test Type" SSD_Total_Data_Written="true" SSD_Total_Data_Written_Title="Total Data Written" # For NVMe Drive Section NVM_Device_ID="true" NVM_Device_ID_Title="Device ID" NVM_Serial_Number="true" NVM_Serial_Number_Title="Serial Number" NVM_Model_Number="true" NVM_Model_Number_Title="Model Number" NVM_Capacity="true" NVM_Capacity_Title="NVMe Capacity" NVM_SMART_Status="true" NVM_SMART_Status_Title="SMART Status" NVM_Warranty_Title="Warr- anty" NVM_Warranty="true" NVM_Critical_Warning="true" NVM_Critical_Warning_Title="Critical Warning" NVM_Drive_Temp="true" NVM_Drive_Temp_Title="Curr Temp" NVM_Drive_Temp_Min="false" # I have not found this on an NVMe drive yet, so set to false NVM_Drive_Temp_Min_Title="Temp Min" NVM_Drive_Temp_Max="false" # I have not found this on an NVMe drive yet, so set to false NVM_Drive_Temp_Max_Title="Temp Max" NVM_Power_On_Hours="true" NVM_Power_On_Hours_Title="Power On Time" NVM_Wear_Level="true" NVM_Wear_Level_Title="Wear Level" NVM_Media_Error="true" NVM_Media_Error_Title="Media Errors" NVM_Total_Data_Written="true" NVM_Total_Data_Written_Title="Total Data Written" ###### Mouseover # This will display the original value of an overridden value (in yellow) # This is a tri-state value as explained below. # "true" will displaying the actual value via a mouseover html link. Still working to make this a true Mouseover. # "false" will not generate any special chart and will run as previous versions. # "alt" will place the actual value within parentheses. An option if your email client can't display mouseover. Mouseover="alt" ###### Drive Ignore List # What does it do: # Use this to list any drives to ignore and remove from the report. This is very useful for ignoring USB Flash Drives # or other drives for which good data is not able to be collected (non-standard). # # How to use it: # We are using a comma delimited file to identify the drive serial numbers. You MUST use the exact and full serial # number smartctl reports, if there is no identical match then it will not ignore the drive. You may list drives # from other systems and they will not have any effect on a system where the drive does not exist. This is great # to have one configuration file that can be used on several systems. # # Example: Ignore_Drives_List="VMWare,1JUMLBD,21HNSAFC21410E" Ignore_Drives_List="none" ###### Drive UDMA_CRC_Error_Count List # What does it do: # If you have a drive which has an UDMA count other than 0 (zero), this setting will offset the # value back to zero for the concerns of monitoring future increases of this specific error. Any match will # subtract the given value to report a 0 (zero) value and highlight it in yellow to denote it was overridden. # The Warning Title will not be flagged if this is zero'd out in this manner. # NOTE: UDMA_CRC_Errors_List are typically permanently stored in the drive and cannot be reset to zero even though # they are frequently caused by a data cable communications error. # # How to use it: # List each drive by serial number and include the current UDMA_CRC_Error_Count value. # The format is very specific and will not work if you wing it, use the Example. # # Set the FLAG in the FLAGS Section IgnoreUDMA to false (the default setting). # # If the error count exceeds the limit minus the offset then a warning message will be generated. # On the Status Report the UDMA CRC Errors block will be YELLOW with a value of 0 for an overridden value. # -- NOTE: We are using the colon : as the separator between the drive serial number and the value to subtract. # # Format: variable=Drive_Serial_Number:Current_UDMA_Error_Count and add a comma if you have more than one drive. # # The below example shows drive WD-WMC4N2578099 has 1 UDMA_CRC_Error, drive S2X1J90CA48799 has 2 errors. # # Example: "WD-WMC4N2578099:1,S2X1J90CA48799:2,P02618119268:1" CRC_Errors_List="none" ###### MultiZone_List_Errors List # What does it do: # This identifies drives with MultiZone_List_Errors which may be irritating people. # # How to use it: # Use same format as CRC_Errors_List, [drive serial number:error count] MultiZone_List="none" ####### Reallocated Sectors Exceptions # What does it do: # This will offset any Reallocated Sectors count by the value provided. # # I do not recommend using this feature as I'm a believer in if you have over 5 bad sectors, odds are the drive will get worse. # I'd recommend replacing the drive before complete failure, but that is your decision. # # Why is it even an option? # I use it for testing purposes only but you may want to use it. # # How to use it: # Use same format as CRC_Errors_List, [drive serial number:error count] ReAllocated_Sector_List="none" ReAllocated_Sector_Events_List="none" ######## ATA Error Log Silencing ################## # What does it do: # This will ignore error log messages equal to or less than the threshold. # # How to use: # Same as the CRC_Errors_List, [drive serial number:error count] ATA_Errors="none" ####### Custom Drive Configuration # Used to define specific alarm values for specific drives by serial number. # This should only be used for drives where the default alarm settings # are not proper or you need to reverse some values where they may be listed # opposite, for example WearLevel may be listed as 0% vice 100%. # Up to 24 unique drive values may be stored. # # Use -config to set these values. Custom_Drives_List="" ####### Warranty Expiration Date # What does it do: # This section is used to add warranty expirations for designated drives and to create an alert when they expire. # The date format is YYYY-MM-DD. # # Below is an example for the format using my own drives, which yes, are expired. # As previously stated above, drive serial numbers must be an exact match to what smartctl reports to function. # # If the drive does not exist, for example my drives are not on your system, then nothing will happen. # # How to use it: # Use the format ="Drive_Serial_Number:YYYY-MM-DD" and add a comma if you have more than one drive. # Example: Drive_Warranty_List="K1JUMLBD:2020-09-30,K1JRSWLD:2020-09-30,K1JUMW4D:2020-09-30,K1GVD84B:2020-10-12" Drive_Warranty_List="" ######## Expired Drive Warranty Setup expiredWarrantyBoxColor="#000000" # "#000000" = normal box perimeter color. WarrantyBoxPixels="1" # Box line thickness. 1 = normal, 2 = thick, 3 = Very Thick, used for expired drives only. WarrantyBackgndColor="#f1ffad" # Hex code or "none" = normal background, Only for expired drives. ######## Enable-Disable Text Portion ######## Enable_Text_Section="true" # This will display the Text Section when = "true" or remove it when not "true". Default="true" ###### Global table of colors # The colors selected you can change but you will need to look up the proper HEX code for a color. okColor="#b5fcb9" # Hex code for color to use in SMART Status column if drives pass (default is darker light green, #b5fcb9). warnColor="#F38B16" # Hex code for WARN color (default is orange, #F38B16). critColor="#f44336" # Hex code for CRITICAL color (default is red, #f44336). altColor="#f4f4f4" # Table background alternates row colors between white and this color (default is light gray, #f4f4f4). whtColor="#ffffff" # Hex for White background. ovrdColor="#ffffe4" # Hex code for Override Yellow. blueColor="#87ceeb" # Hex code for Sky Blue, used for the SCRUB/SMART Test In Progress/background. yellowColor="#f1ffad" # Hex code for pale yellow. ########################## ########################## ### ### ### STOP EDITING THE ### ### SCRIPT HERE ### ### ### ########################## ########################## ###### Auto-generated Parameters softver=$(uname -s) host=$(hostname -s) truenas_ver=$(cat /etc/version) testdata_path="data" Dump_Loop="0" re='^[0-9]+$' runfilename="multi_report.sh" ### temp files have been converted to variable stored, not stored in /tmp/ as a file. ### tempfilepath=$(( 10 + $RANDOM % 1000 )) logfile="/tmp/${tempfilepath}smart_report_body.tmp" logfile_header="/tmp/${tempfilepath}smart_report_header.tmp" boundary="gc0p4Jq0M2Yt08jU534c0p" CurrentFilename="multi_report_v2.4.4-Multipath_2023_08_25.txt" valid_config_version_date="2023-08-13" # Configuration file valid date progverdate="$(echo $CurrentFilename | cut -d '_' -f4,5,6 | cut -d '.' -f1 | sed -r 's/[_]+/-/g')" progname="Multi-Report "$(echo $CurrentFilename | cut -d '_' -f3)" dtd:" if [[ $softver != "Linux" ]]; then if [[ "$(cat /etc/version | grep "FreeNAS")" ]]; then programver=progname$progverdate" (FreeNAS "$(cat /etc/version | cut -d " " -f1 | sed 's/FreeNAS-//')")" programver2="$(cat /etc/version | cut -d"-" -f1)" programver3="$(cat /etc/version | cut -d " " -f1 | sed 's/FreeNAS-//')" else programver=$progname$progverdate" (TrueNAS Core "$(cat /etc/version | cut -d " " -f1 | sed 's/TrueNAS-//')")" programver2="$(cat /etc/version | cut -d"-" -f1)_Core" programver3="$(cat /etc/version | cut -d " " -f1 | sed 's/TrueNAS-//')" fi else programver=$progname$progverdate" (TrueNAS Scale "$(cat /etc/version)")" programver2="TrueNAS_Scale_$(cat /etc/version | cut -d" " -f1)" programver3="$(cat /etc/version)" fi declare -a testfilenames declare -a testfilenamesHDD declare -a smartdrives ########################## ########################## ### ### ### PROGRAMMING / ### ### TROUBLESHOOTING ### ### HACKS ### ### ### ########################## ########################## #Unique programming hacks to properly emulate other hardware that is not actually on the system. VMWareNVME="off" # Set to "off" normally, "on" to assist in incorrect VMWare reporting. Joes_System="false" # Custom settings for my system and to remove these from your system. Sample_Test="false" # Setup static test values for testing. Develop="false" # Set to 'true' for development output. GitHubSimulate="false" # Use test section of GitHub. ########################## ########################## ### ### ### DEFINE FUNCTIONS ### ### ### ########################## ########################## ########## EXIT IF MULTIPLE INSTANCES ARE RUNNING ########## # Let's stop a second instance from running. mefull=`basename "$0"` me="$(echo $mefull | cut -d '.' -f 1)" if [[ $softver != "Linux" ]]; then # For FreeBSD pstestpid="$(pgrep -o -f ${me})" if [[ $pstestpid -gt 0 ]]; then pstest="$(ps -p ${pstestpid} | awk '{print $4}' | cut -d ':' -f 2 | cut -d '.' -f 2)" pstest="$(echo $pstest | cut -d ' ' -f 2)" fi else # For Debian pstest="$(ps -e -o comm,etime | grep ${me} | head -1 | awk '{print $2}' | cut -d ':' -f 2)" fi pstest=${pstest#0} if [[ $pstest -gt "0" ]]; then echo "Script is already Running... Exiting"; exit 1; fi #################### FUNCTIONS #################### ########## CHECK MESSAGES FILE FOR ERROR MESSAGES ########## # We will check the /var/log/messages file for any iscsi, cam, ctl, or cdb error messages # # Use the CSV that is used in the data recording to record each instance of an alarm. # Search each instance for an exact match for repeat offenders. # Remove entries if they no longer exits in the messages log file. # # Count up the duplicate errors and put them as "6x error message" for example. # Create a Message file to add to the Warning list so we can punt this out. # spencer () { # Check status of "/tmp/spencer_report.txt" file, then if we have errors process it. if [ -f "/tmp/spencer_report.txt" ]; then if [ $(grep -wic "New Error Messages" "/tmp/spencer_report.txt") -gt 0 ]; then # Error Levels are: None, Warning, Critical if [[ $spencer_new_warning_level == "Warning" ]]; then logfile_warning=$logfile_warning"$(printf "Spencer New Error Data - See Attachment")" elif [[ $spencer_new_warning_level == "Critical" ]]; then logfile_critical=$logfile_critical"$(printf "Spencer New Error Data - See Attachment")" fi fi if [ $(grep -wic "Previous" "/tmp/spencer_report.txt") -gt 0 ]; then # Error Levels are: None, Warning, Critical if [[ $spencer_existing_warning_level == "Warning" ]]; then logfile_warning=$logfile_warning"$(printf "Spencer Existing Error Data - See Attachment")" elif [[ $spencer_existing_warning_level == "Critical" ]]; then logfile_critical=$logfile_critical"$(printf "Spencer Existing Error Data - See Attachment")" fi fi fi } ########## SEE IF 7ZIP IS LOADED AND IF NOT, INSTALL IT ########## # NOTE: We check for 7zip installed and if not install it. check_7zip () { if type "7z" &> /dev/null; then return; fi # No 7zip is installed, lets go get it. echo "Installing 7-Zip..." wget https://www.7-zip.org/a/7z2201-linux-x64.tar.xz > /dev/null 2>&1 # Get 7-zip tar xf 7z*-linux-x64.tar.xz 7zzs > /dev/null 2>&1 # Extract the executable file only cp 7zzs /usr/local/bin # Copy to /bin ln -s /usr/local/bin/7zzs /usr/local/bin/7z # Symlink it to "7z" echo "7-Zip Installed" # Cleanup if test -e "7z2201-linux-x64.tar.xz"; then rm "7z2201-linux-x64.tar.xz"; fi if test -e "7zzs"; then rm "7zzs"; fi } ########## AUTOMATIC SCRIPT UPDATE ########## # This will update the script with any newer script on GitHub and is manually envoked [-update] # # How it works or might work # Check GitHub for Date on script, if that is possible. # If date/time is more current than this current script is, download the new script. # Next write the new file over the current file name. # Edit line #5 with a password if one was present. update_script () { echo "Update Script Routine" echo "Removing Old Script Source if it exists" if test -e "/tmp/Multi-Report"; then rm -R "/tmp/Multi-Report"; fi if test -e "/tmp/multi_report_update.txt"; then rm "/tmp/multi_report_update.txt"; fi echo "Downloading new script files" if [[ "$(curl -is https://github.com | head -n 1)" ]]; then # Go git the file ( cd /tmp git clone -q https://github.com/JoeSchmuck/Multi-Report.git ) # For the Public distribution GitVersion="$(cat "/tmp/Multi-Report/current_script")" validate="$(cat "/tmp/Multi-Report/current_cksum")" checksum="$(cat "/tmp/Multi-Report/current_script")" # For the Development distribution if [[ $GitHubSimulate == "true" ]]; then GitVersion="$(cat "/tmp/Multi-Report/current_script1")" validate="$(cat "/tmp/Multi-Report/current_cksum1")" checksum="$(cat "/tmp/Multi-Report/current_script1")" fi echo " " echo "Your current version is: "$CurrentFilename echo " The new version is: "$GitVersion echo " " if [ $softver != "Linux" ]; then checksum="$(md5 /tmp/Multi-Report/$checksum | cut -d '=' -f 2 | cut -d ' ' -f 2)" else checksum="$(md5sum --tag /tmp/Multi-Report/$checksum | cut -d '=' -f 2 | cut -d ' ' -f 2)" fi if [[ $checksum == $validate ]]; then echo "File is valid" else echo "Downloaded file is corrupt, does not match CRC." if [[ $AutomaticUpdate == "true" ]]; then AutomaticUpdate="false" return fi fi if [[ $AutomaticUpdate != "true" ]]; then echo "Enter 'y' to commit or any other key to abort." read Keyboard_yn else echo "Automatic Update is Enabled..." Keyboard_yn="y" fi if [[ $Keyboard_yn == "y" ]] || [[ $Keyboard_yn == "Y" ]]; then echo "Updating Script..." echo "3" sed '5s/.*/TrueNASConfigEmailEncryption="'$TrueNASConfigEmailEncryption'" # Set this to "" for no encryption, MUST REMAIN ON LINE 5./' /tmp/Multi-Report/$GitVersion > /tmp/multi_report_update.txt sleep .5 # Generate the full file name minus the .txt VersionFilename="$(echo $CurrentFilename | rev | cut -d '.' -f 2,3,4,5 | rev)" echo "2" sleep .5 # Backup the original multi_report.sh file and config file to save if test -e $SCRIPT_DIR"/multi_report.sh"; then cp $SCRIPT_DIR"/multi_report.sh" $SCRIPT_DIR"/"$CurrentFilename; fi cp $SCRIPT_DIR"/multi_report_config.txt" $SCRIPT_DIR"/"$VersionFilename"_config.txt" # Check if old multi_report.sh file exists and delete if test -e $SCRIPT_DIR"/"$runfilename; then rm $SCRIPT_DIR"/"${runfilename} # Remove actual script fi # Copy the new multi_report.sh file and set permissions cp /tmp/multi_report_update.txt $SCRIPT_DIR"/"$runfilename chmod 755 $SCRIPT_DIR"/"$runfilename # Copy the User Guide echo "1" sleep .5 cp "/tmp/Multi-Report/Multi_Report_User_Guide.pdf" $SCRIPT_DIR"/." cp "/tmp/Multi-Report/changelog.txt" $SCRIPT_DIR"/." echo " " echo "Your script has been updated and a new copy of the User Guide and changelog is in your directory." sleep 1 # Cleanup Leftover Files if test -e "/tmp/Multi-Report"; then rm -R "/tmp/Multi-Report"; fi if test -e "/tmp/multi_report_update.txt"; then rm "/tmp/multi_report_update.txt"; fi else echo "Aborted" if test -e "/tmp/Multi-Report"; then rm -R "/tmp/Multi-Report"; fi exit 0 fi else echo "GitHub is Not Available" fi if [[ $AutomaticUpdate != "true" ]]; then echo "Exiting..." exit 0 fi } ########## CHECK FOR NEWER SCRIPT ########## # This will check GitHub to find out if the script is newer. # If it is newer then it will send a message to the user via email. # It's up to the user if htye want the update or not. checkforupdate () { echo "Checking for Updates" if test -e "/tmp/Multi-Report"; then rm -R "/tmp/Multi-Report"; fi if [[ "$(curl -is https://github.com | head -n 1)" ]]; then # Go git the file ( cd /tmp git clone -q https://github.com/JoeSchmuck/Multi-Report.git ) # Examine the file name in 'current_script' for version and date. if test -e "/tmp/Multi-Report/current_script"; then # if test -e "/tmp/Multi-Report/current_script1"; then GitVersion="$(cat "/tmp/Multi-Report/current_script" | cut -d 'v' -f 2 | cut -d '_' -f 1)" # GitVersion="$(cat "/tmp/Multi-Report/current_script1" | cut -d 'v' -f 2 | cut -d '_' -f 1)" VersionFilename="$(echo $CurrentFilename | cut -d 'v' -f 2 | cut -d '_' -f 1)" echo "Current Version "$VersionFilename" -- GitHub Version "$GitVersion #GitVersion="2.4.1" #VersionFilename="2.4" if [[ $GitVersion > $VersionFilename ]] && [[ $VersionFilename != "" ]]; then UpdateAvailable="true" echo "Update Available -- Use the '-update' switch to update the script." else echo "No Update Required" fi else echo "No GitHub Version File Available" fi else echo "GitHub.com Not Available" fi if test -e "/tmp/Multi-Report/messages.txt"; then Messages="$(cat "/tmp/Multi-Report/messages.txt")" if [[ $Messages != "" ]]; then echo "Message from the Creator"; fi else echo "No GitHub Message Available" fi } ########## LOAD EXTERNAL CONFIGURATION FILE ########## load_config () { if test -e "$Config_File_Name"; then . "$Config_File_Name" # Lets test if the config file needs to be updated first. config_version_date="$(cat "$Config_File_Name" | grep "dtd" | cut -d ':' -f 2 | cut -d ' ' -f 1 )" if [[ $config_version_date < $valid_config_version_date ]]; then echo "Found Old Configuration File"; echo "Automatically updating configuration file..."; update_config_file; echo "Continuing to run script"; fi . "$Config_File_Name" else echo " " echo " No Config File Exists --- Checking for a valid email within the script..." if [[ $Email == "YourEmail@Address.com" ]]; then echo " " echo " No Valid Email Address..." echo " Recommend running script with the '-config' switch and selecting" echo " the N)ew Configuration option." echo " " echo "... Exiting" echo " " exit 1 else echo "Valid Email within the script = "$Email", using script parameters..." echo " " External_Config="no" return fi fi } ########## CLEAR VARIABLES ########## # Setup variables for each drive pass. # ALPHABETIZE THESE SO THEY LOOK BETTER clear_variables () { altlastTestHours="" altlastTestType="" capacity="" chkreadfailure="" crcErrors="" lastTestHours="" lastTestType="" loadCycle="" modelnumber="" multiZone="" NVMcriticalWarning="" tdw="" offlineUnc="" onHours="" pending="" reAlloc="" reAllocEvent="" rotation="" seekErrorHealth="" seekErrorHealth2="" serial="" smartStatus="" smarttesting="" startStop="" spinRetry="" temp="" temp_max="" temp_min="" wearLevel="" onTime="" testAge="" SER="" Helium="" He2="" seekErrorRate="" rawReadErrorRate="" rawReadErrorRate2="" seek="" test_ata_error="" WarrantyClock="" warrantytemp="" wearLevelAdj="" crcErrorsOrig="" reAllocOrig="" reAllocEventOrig="" multiZoneOrig="" serial1="" devicetype="" # And Reset bgColors if [[ "$bgColor" == "$altColor" ]]; then bgColor="#ffffff"; else bgColor="$altColor"; fi deviceStatusColor=$bgColor smartStatusColor=$bgColor tempColor=$bgColor temp_maxColor=$bgColor onTimeColor=$bgColor spinRetryColor=$bgColor reAllocColor=$bgColor reAllocEventColor=$bgColor pendingColor=$bgColor offlineUncColor=$bgColor crcErrorsColor=$bgColor seekErrorHealthColor=$bgColor rawReadErrorRateColor=$bgColor multiZoneColor=$bgColor testAgeColor=$bgColor lastTestTypeColor=$bgColor wearLevelColor=$bgColor NVMcriticalWarningColor=$bgColor mediaErrorsColor=$bgColor HeliumColor=$bgColor WarrantyBoxColor="black" WarrantyBackgroundColor=$bgColor lastTestTypeColor=$bgColor } ########## CONVERT TO DECIMAL ########## # Convert any number into decimal format convert_to_decimal () { if [[ "$1" == "" ]]; then return; fi Converting_Value=${1#0} Converting_Value="${Converting_Value//,}" Return_Value=$Converting_Value if [[ $1 == "0" ]]; then Return_Value=0; fi } ########## SORT DRIVES ROUTINE ########## # Sort drives into alphabetical order. sort_drives () { sort_list=$(for i in `echo $sort_list`; do echo "$i" done | sort -V) } ########## SORT DRIVES ROUTINE for SCALE ########## # Sort drives into alphabetical order. sort_drives_scale () { sort_list_short=$(for i in `echo $sort_list`; do echo "$i" done | awk 'length<4' | sort -V) sort_list_long=$(for i in `echo $sort_list`; do echo "$i" done | awk 'length>3' | sort -V) sort_list=$sort_list_short" "$sort_list_long } ########## REMOVE DUPLICATE DRIVE SERIAL NUMBERS - MULTIPATH FIX ########## remove_duplicate () { # Variable is 'duplicate_list' not_duplicate_serial="" not_duplicate_drive="" # Get drive serial number #echo "IN - duplicate_list="$duplicate_list for i in ${duplicate_list}; do # Drive ID's smartdata5="$(smartctl -x --json=u /dev/"$i")" dup_test="$(echo "${smartdata5}" | jq -Mre '.serial_number | values')" # Drive Serial Number #echo "Serial="$dup_test #echo "not_duplicate_serial="$not_duplicate_serial #echo "test" if [[ ! "$not_duplicate_serial" == *"$dup_test"* ]]; then not_duplicate_serial=$not_duplicate_serial" "$dup_test # echo "SN not in not_duplicate_serial, add to not_duplicate_drive" if [[ $not_duplicate_drive == "" ]]; then not_duplicate_drive=$i # echo "Loop 3" else not_duplicate_drive=$not_duplicate_drive" "$i # echo "Loop 4" fi # else # echo "Drive SN already listed, skip." # echo "Loop 2" fi #echo "not_duplicate_serial="$not_duplicate_serial duplicate_list=$not_duplicate_drive done duplicate_list=$not_duplicate_drive #echo "OUT - duplicate_list="$duplicate_list } ########## CONVERT TEST DRIVE NUMBERS INTO SCALE DRIVE NAMES ########## # This will convert ada"1" into sd"a", ada"26" into sd"z", etc. # Incoming and outgoing variable is "numbertoletters" number_to_letters () { declare -i j=$numbertoletters+1 if [[ $j -gt 52 && $j -lt 79 ]]; then # First Character 'b' declare -i scaleid=$(($j-52)) firstletter="b" declare -i k=$[ 96+${scaleid} ] secondletter=$(printf \\$(printf '%03o' $k)) fi if [[ $j -gt 26 && $j -lt 53 ]]; then # First Character 'a' declare -i scaleid=$(($j-26)) firstletter="a" declare -i k=$[ 96+${scaleid} ] secondletter=$(printf \\$(printf '%03o' $k)) fi numbertoletters=$firstletter$secondletter if [[ $j -lt 27 ]]; then declare -i k=$[ 96+${j} ] firstletter=$(printf \\$(printf '%03o' $k)) numbertoletters=$firstletter fi } ########## PURGE OLD DATA FROM CSV FILE ########## # This routine will purge the "statistical_data_file" of data older then "SDF_DataPurgeDays". purge_exportdata () { # Delete temp file if it exists if test -e "/tmp/temp_purge_file.csv"; then rm "/tmp/temp_purge_file.csv"; fi if [ $softver != "Linux" ]; then expireDate=$(date -v -"$SDF_DataPurgeDays"d +%Y/%m/%d) else expireDate=$(date -d "$SDF_DataPurgeDays days ago" +%Y/%m/%d) fi awk -v expireDate="$expireDate" -F, '{ if($1 >= expireDate) print $0;}' "$statistical_data_file" > "/tmp/temp_purge_file.csv" cp -R "/tmp/temp_purge_file.csv" "$statistical_data_file" } ########## PURGE TEST DATA FROM CSV FILE ########## # This routine will purge the "statistical_data_file" of test data matching "TEST". purge_testdata () { echo "Purging Statistical Database of Test Data" # Delete temp file if it exists if test -e "/tmp/temp_purge_file.csv"; then rm "/tmp/temp_purge_file.csv"; fi awk -F, '{ if($3 != "TEST") print $0;}' "$statistical_data_file" > "/tmp/temp_purge_file.csv" cp -R "/tmp/temp_purge_file.csv" "$statistical_data_file" } ########## CLEAN UP TEMPORARY FILES ########## cleanup_files () { ### Clean up our temporary files if test -e "/tmp/temp_purge_file.csv"; then rm "/tmp/temp_purge_file.csv"; fi rm /tmp/${tempfilepath}* > /dev/null 2>&1 ### Clean up drive data files f=(/tmp/*_a.txt) if [[ -f "${f[0]}" ]]; then rm /tmp/*_a.txt; fi f=(/tmp/*_x.txt) if [[ -f "${f[0]}" ]]; then rm /tmp/*_x.txt; fi f=(/tmp/*.pool) if [[ -f "${f[0]}" ]]; then rm /tmp/*.pool; fi f=(/tmp/*.json) if [[ -f "${f[0]}" ]]; then rm /tmp/*.json; fi ### Clean up TrueNAS Configuration files if test -e "/tmp/${Config_Name}.db"; then rm "/tmp/${Config_Name}.db"; fi if test -e /tmp/config_backup.md5; then rm /tmp/config_backup.md5; fi if test -e /tmp/config_backup.sha256; then rm /tmp/config_backup.sha256; fi if test -e "/tmp/${Config_Name}.zip"; then rm "/tmp/$Config_Name.zip"; fi if test -e "/tmp/freenas-v1.db"; then rm "/tmp/freenas-v1.db"; fi if test -e "/tmp/freenas-v1.md5"; then rm "/tmp/freenas-v1.md5"; fi if test -e "/tmp/freenas-v1.sha256"; then rm "/tmp/freenas-v1.sha256"; fi if test -e "/tmp/pwenc_secret"; then rm "/tmp/pwenc_secret"; fi if test -e "/tmp/pwenc_secret.md5"; then rm "/tmp/pwenc_secret.md5"; fi if test -e "/tmp/pwenc_secret.sha256"; then rm "/tmp/pwenc_secret.sha256"; fi ### Clean up Spencer File if test -e "/tmp/spencer_report.txt"; then rm "/tmp/spencer_report.txt"; fi ### Clean up complete! } #################### EMAIL FUNCTIONS #################### ########## EMAIL EXPORT DATA CVS FILE ########## # Attach statistical data file Email_datafile () { if [ "$SDF_DataEmail" == "true" ]; then Now=$(date +"%a") doit="false" case $SDF_DataEmailDay in All) doit="true" ;; Mon|Tue|Wed|Thu|Fri|Sat|Sun) if [[ "$SDF_DataEmailDay" == "$Now" ]]; then doit="true"; fi ;; Month) if [[ $(date +"%d") == "01" ]]; then doit="true"; fi ;; *) ;; esac if [[ "$doit" == "true" ]]; then Attach_Statistical_File="true"; Attach_Files1="true"; else Attach_Statistical_File="false"; fi doit="" fi if [[ "$MRConfigEmailEnable" == "true" ]]; then if [[ "$MR_Attach_Config" == "1" ]]; then doit="true"; fi Now=$(date +"%a") case $MRConfigEmailDay in All) doit="true" ;; Mon|Tue|Wed|Thu|Fri|Sat|Sun) if [[ "$MRConfigEmailDay" == "$Now" ]]; then doit="true"; fi ;; Month) if [[ $(date +"%d") == "01" ]]; then doit="true"; fi ;; Never) ;; *) ;; esac if [[ "$doit" == "true" ]] || [[ "$dump_all" != "0" ]]; then Attach_Multi_Report="true"; Attach_Files1="true"; else Attach_Multi_Report="false"; fi fi } ########## COMBINE ALL DATA INTO A FORMAL EMAIL MESSAGE AND SEND IT ########## # Create Email Header and Send Email create_Email () { # Test if there is a Warning Message and Setup Subject Line if [[ $logfile_critical != "" ]]; then subject="*CRITICAL ERROR* SMART Testing Results for ${host} *CRITICAL ERROR*" elif [[ $logfile_warning != "" ]]; then subject="*WARNING* SMART Testing Results for ${host} *WARNING*" elif [[ $DisableWarranty == "false" ]]; then if [[ ! $logfile_warranty == "" ]]; then subject="*Drive Warranty Expired* - SMART Testing Results for ${host}" else subject="SMART Testing Results for ${host} - All is Good" fi else subject="SMART Testing Results for ${host} - All is Good" fi if [[ $Monitor == "true" ]]; then Email=$AlertEmail subject="${host} -> ALERT" fi #Keyboard_Message="Test Message" ### Set email headers ### ( echo "MIME-Version: 1.0" echo "Content-Type: multipart/mixed; boundary=${boundary}" echo "From: ${From}" echo "To: ${Email}" echo "Subject: ${subject}" echo "--${boundary}" echo "Content-Type: text/html" if [[ $Monitor == "false" ]]; then if [[ "$(echo $programver | grep -i "beta")" ]] || [[ $UpdateAvailable == "true" ]]; then echo ""; fi echo $programver"
Report Run "$(date +%d-%b-%Y)" @ "$timestamp"
" duration=$SECONDS if [[ $duration < "60" ]]; then echo "Execution Time: $(($duration % 60)) Seconds" else echo "Execution Time: $(($duration / 60)) Minutes : $(($duration % 60)) Seconds" fi if [[ $UpdateAvailable == "true" ]]; then echo "
UPDATE AVAILABLE --> "$GitVersion; fi if [[ $Messages != "" ]]; then echo "
Message from the Creator: "$Messages"
"; fi if [[ "$(echo $programver | grep -i "beta")" ]] || [[ $UpdateAvailable == "true" ]]; then echo "
"; fi echo "

" # Keyboard_Message comes from the '-dump email' command. if [[ $Keyboard_Message != "" ]]; then echo ""$Keyboard_Message"

" fi fi ) > ${logfile_header} cat $logfile >> $logfile_header ### Send report sendmail -t -oi < "$logfile_header" } ########## TRUENAS CONFIGURATION BACKUP ########## # TrueNAS Configuration Backup (if enabled) config_backup () { if [[ "$Monitor" == "true" ]]; then return; fi ###### Config backup (if enabled) Config_Name="$(date "+"$host"_Config_"$programver3"_%Y-%m-%d.zip")" if [ "$TrueNASConfigEmailEnable" == "true" ]; then Now=$(date +"%a") doit="false" case $TrueNASConfigEmailDay in All) doit="true" ;; Mon|Tue|Wed|Thu|Fri|Sat|Sun) if [[ "$TrueNASConfigEmailDay" == "$Now" ]]; then doit="true" fi ;; Month) if [[ $(date +"%d") == "01" ]]; then doit="true" fi ;; *) ;; esac if [[ "$doit" == "true" ]]; then # Set up file names, etc for later tarfile="/tmp/config_backup.zip" ### Test config integrity if ! [ "$(sqlite3 /data/freenas-v1.db "pragma integrity_check;")" == "ok" ]; then # Config integrity check failed, set MIME content type to html and print warning ( echo "--${boundary}" echo "Content-Type: text/html" echo "Automatic backup of FreeNAS configuration has failed! The configuration file is corrupted!" echo "You should correct this problem as soon as possible!" echo "
" ) >> "$logfile" else Attach_TrueNAS_Config="true" Attach_Files1="true" # Config integrity check passed; copy config db, generate checksums, make .zip archive cp /data/freenas-v1.db "/tmp/freenas-v1.db" cp /data/pwenc_secret "/tmp/pwenc_secret" if [ $softver != "Linux" ]; then md5 "/tmp/freenas-v1.db" > /tmp/freenas-v1.md5 sha256 "/tmp/freenas-v1.db" > /tmp/freenas-v1.sha256 md5 "/tmp/pwenc_secret" > /tmp/pwenc_secret.md5 sha256 "/tmp/pwenc_secret" > /tmp/pwenc_secret.sha256 else md5sum "/tmp/freenas-v1.db" > /tmp/freenas-v1.md5 sha256sum "/tmp/freenas-v1.db" > /tmp/freenas-v1.sha256 md5sum "/tmp/pwenc_secret" > /tmp/pwenc_secret.md5 sha256sum "/tmp/pwenc_secret" > /tmp/pwenc_secret.sha256 fi ( cd "/tmp/" || exit; if [[ $TrueNASConfigEmailEncryption != "" ]]; then if [ $softver != "Linux" ]; then # Core - Encrypt 7z a "${Config_Name}" ./freenas-v1.db ./freenas-v1.md5 ./freenas-v1.sha256 ./pwenc_secret ./pwenc_secret.md5 ./pwenc_secret.sha256 -tzip -mem=AES256 -mx1 -p${TrueNASConfigEmailEncryption} > /dev/null 2<&1 else # Scale - Encrypt (using local copy) 7z a "${Config_Name}" ./freenas-v1.db ./freenas-v1.md5 ./freenas-v1.sha256 ./pwenc_secret ./pwenc_secret.md5 ./pwenc_secret.sha256 -tzip -mem=AES256 -mx1 -p${TrueNASConfigEmailEncryption} > /dev/null 2<&1 fi else if [ $softver != "Linux" ]; then # Core - UnEncrypted 7z a "${Config_Name}" ./freenas-v1.db ./freenas-v1.md5 ./freenas-v1.sha256 ./pwenc_secret ./pwenc_secret.md5 ./pwenc_secret.sha256 -tzip -mem=AES256 -mx1 > /dev/null 2<&1 else # Scale - UnEncrypted 7z a "${Config_Name}" ./freenas-v1.db ./freenas-v1.md5 ./freenas-v1.sha256 ./pwenc_secret ./pwenc_secret.md5 ./pwenc_secret.sha256 -tzip -mem=AES256 -mx1 > /dev/null 2<&1 fi fi ) # If logfile saving is enabled, copy .zip file to specified location before it (and everything else) is removed below if [ "$TrueNASConfigBackupSave" == "true" ]; then TrueNASConfigBackupLocation="$(echo $TrueNASConfigBackupLocation | sed 's:/*$::')" cp "/tmp/${Config_Name}" "${TrueNASConfigBackupLocation}/${Config_Name}" fi fi fi fi } ########## DUMP DRIVE DATA ########## # This routine will dump the selected drive data into individual files for troubleshooting. dump_drive_data () { if [[ "$drive" != "cd0" ]]; then tempjson="$(smartctl -x --json /dev/${drive})" serial1="$(echo "${tempjson}" | jq -Mre '.serial_number | values')" rpm="$(echo "${tempjson}" | jq -Mre '.rotation_rate | values')" if [[ "$rpm" == "0" ]]; then drivetype="SSD"; else drivetype="HDD"; fi if [[ "$(echo $tempjson | grep -i "nvm")" ]]; then drivetype="NVM"; fi ( if [[ $json_error_log != "" ]]; then if test -e "/tmp/${tempfilepath}${drive}_${serial1}_a.txt"; then echo "--${boundary}" echo "Content-Type: text/html" echo "Content-Transfer-Encoding: base64" echo "Content-Disposition: attachment; filename=${drive}_${serial1}_a.txt" base64 "/tmp/${tempfilepath}${drive}_${serial1}_a.txt" fi if test -e "/tmp/${tempfilepath}${drive}_${serial1}_x.txt"; then echo "--${boundary}" echo "Content-Type: text/html" echo "Content-Transfer-Encoding: base64" echo "Content-Disposition: attachment; filename=${drive}_${serial1}_x.txt" base64 "/tmp/${tempfilepath}${drive}_${serial1}_x.txt" fi fi if test -e "/tmp/${tempfilepath}${drivetype}_${drive}_${serial1}.json"; then echo "--${boundary}" echo "Content-Type: text/html" echo "Content-Transfer-Encoding: base64" echo "Content-Disposition: attachment; filename=${drivetype}_${drive}_${serial1}.json" base64 "/tmp/${tempfilepath}${drivetype}_${drive}_${serial1}.json" fi ) >> "$logfile" fi } #################### ZPOOL FUNCTIONS #################### ########## GENERATE ZPOOL REPORT ########## # Report Summary Section (html tables) zpool_report () { if [[ "$Monitor" == "false" ]]; then ( echo "" if [[ $Pool_Capacity_Type == "zfs" ]]; then echo "" else echo "" fi echo "" echo " " echo " " echo " " echo " " echo " " echo " " echo " " echo " " echo " " echo " " echo " " echo " " echo " " echo "" ) > "$logfile" fi # Let's find out the name of the pools and sort them. if [[ "$testdata" == "-t" ]]; then # Use test data echo "Zpool Does not Function Yet. Next Revision." ### Until is does function, use the system at hand for the data. pools=$(zpool list -H -o name) else pools=$(zpool list -H -o name) fi sort_list=$pools sort_drives pools=$sort_list poolNum=0 # Alternate background in pools table for pool in $pools; do # zpool health summary status="$(zpool list -H -o health "$pool")" # Total all read, write, and checksum errors per pool errors="$(zpool status "$pool" | grep -E "(ONLINE|DEGRADED|FAULTED|UNAVAIL|REMOVED)[ \\t]+[0-9]+")" readErrors=0 for err in $(echo "$errors" | awk '{print $3}'); do if echo "$err" | grep -E -q "[^0-9]+"; then readErrors=1000 break fi readErrors=$((readErrors + err)) done writeErrors=0 for err in $(echo "$errors" | awk '{print $4}'); do if echo "$err" | grep -E -q "[^0-9]+"; then writeErrors=1000 break fi writeErrors=$((writeErrors + err)) done cksumErrors=0 for err in $(echo "$errors" | awk '{print $5}'); do if echo "$err" | grep -E -q "[^0-9]+"; then cksumErrors=1000 break fi cksumErrors=$((cksumErrors + err)) done if [ "$readErrors" -gt 999 ]; then readErrors=">1K"; fi if [ "$writeErrors" -gt 999 ]; then writeErrors=">1K"; fi if [ "$cksumErrors" -gt 999 ]; then cksumErrors=">1K"; fi # Get ZFS capacity (the real capacity) zfs_pool_used="$(zfs list $pool | awk '{print $2}' | sed -e '/USED/d')" zfs_pool_avail="$(zfs list $pool | awk '{print $3}' | sed -e '/AVAIL/d')" if [[ $zfs_pool_used == *"T"* ]]; then zfs_pool_used1="$(awk -v a="$zfs_pool_used" 'BEGIN { printf a*1000 }' ")"; else statusColor="$bgColor"; fi if [ "$readErrors" != "0" ]; then readErrorsColor="$warnColor"; logfile_warning=$logfile_warning"$(echo "$pool - Scrub Read Errors
")"; else readErrorsColor="$bgColor"; fi if [ "$writeErrors" != "0" ]; then writeErrorsColor="$warnColor"; logfile_warning=$logfile_warning"$(echo "$pool - Scrub Write Errors
")"; else writeErrorsColor="$bgColor"; fi if [ "$cksumErrors" != "0" ]; then cksumErrorsColor="$warnColor"; logfile_warning=$logfile_warning"$(echo "$pool - Scrub Cksum Errors
")"; else cksumErrorsColor="$bgColor"; fi if [ "$used" -ge "$PoolUsedWarn" ]; then usedColor="$warnColor"; logfile_warning=$logfile_warning"$(echo "$pool - Scrub Used
")"; else usedColor="$bgColor"; fi if [ "$scrubRepBytes" != "$Non_Exist_Value" ] && [ "$scrubRepBytes" != "0" ] && ! [ "$(echo "$scrubRepBytesfull" | grep "Resilvered")" ]; then scrubRepBytesColor="$warnColor"; logfile_warning=$logfile_warning"$(echo "$pool - Scrub Rep Bytes
")"; else scrubRepBytesColor="$bgColor"; fi if [ "$scrubErrors" != "$Non_Exist_Value" ] && [ "$scrubErrors" != "0" ]; then scrubErrorsColor="$warnColor"; logfile_critical=$logfile_critical"$(echo "$pool - Scrub Errors
")"; else scrubErrorsColor="$bgColor"; fi if [ "$(echo "$scrubAge" | awk '{print int($1)}')" -ge "$ScrubAgeWarn" ]; then scrubAgeColor="$warnColor"; logfile_warning=$logfile_warning"$(echo "$pool - Scrub Age")"; else scrubAgeColor="$bgColor"; fi if [ "$scrubAge" == "In Progress" ]; then scrubAgeColor="$blueColor"; fi if [ "$frag" != "$Non_Exist_Value" ]; then if [ "$frag" -ge "$ZpoolFragWarn" ]; then fragColor="$warnColor"; logfile_warning=$logfile_warning"$(echo "$pool - Fragmentation above Threshold - $frag%
")"; else fragColor="$bgColor"; fi frag=" "$frag"%" fi if [[ $Pool_Capacity_Type == "zfs" ]]; then pool_size=$zfs_pool_size pool_free=$zfs_pool_avail used=$zfs_pool_used" ("$used"%)" else used=$pool_used" ("$used"%)" fi if [[ "$Monitor" == "false" ]]; then ( # Use the information gathered above to write the date to the current table row printf "\\n" "$bgColor" "$pool" "$statusColor" "$status" "$pool_size" "$pool_free" "$usedColor" "$used" "$fragColor" "$frag" "$readErrorsColor" "$readErrors" "$writeErrorsColor" "$writeErrors" "$cksumErrorsColor" \ "$cksumErrors" "$scrubRepBytesColor" "$scrubRepBytesfull" "$scrubErrorsColor" "$scrubErrors" "$scrubAgeColor" "$scrubAge" "$scrubTime" ) >> "$logfile" fi done # End of zpool status table if [[ "$Monitor" == "false" ]]; then echo "
*ZPool/ZFS Status Report Summary
*ZPool Status Report Summary
"$Zpool_Pool_Name_Title""$Zpool_Status_Title""$Zpool_Pool_Size_Title""$Zpool_Free_Space_Title""$Zpool_Used_Space_Title""$Zpool_Frag_Title""$Zpool_Read_Errors_Title""$Zpool_Write_Errors_Title""$Zpool_Checksum_Errors_Title""$Zpool_Scrub_Repaired_Title""$Zpool_Scrub_Errors_Title""$Zpool_Scrub_Age_Title""$Zpool_Scrub_Duration_Title"
%s %s %s %s %s %s %s %s %s %s %s %s %s
" >> "$logfile" if [[ $Pool_Capacity_Type == "zfs" ]]; then echo "
*Data obtained from zpool and zfs commands." >> "$logfile" else echo "
*Data obtained from zpool command. Capacities include Parity Data." >> "$logfile" fi fi } #################### DRIVE DATA FUNCTIONS #################### ########## IGNORE DRIVES ROUTINE ########## # Examine the $Ignore_Drives_List variable and remove any matches from the $drive variable. process_ignore_drives () { targument="$(smartctl -i /dev/"${drive}" | grep "Serial Number:" | awk '{print $3}')"; s="0" IFS=',' read -ra ADDR <<< "$Ignore_Drives_List" for i in "${ADDR[@]}"; do if [[ $i == $targument ]]; then s="1"; continue; fi done if [[ $s == "0" ]]; then printf "%s " "${drive}"; fi } ########## GET SMART HARD DRIVES ########## # Get listing of the SMART HDD's and put into $smartdrives variable - example: 'ada0 ada1 ada2 ada3' get_smartHDD_listings () { if [ $softver != "Linux" ]; then smartdrives=$(for drive in $(sysctl -n kern.disks); do if [ "$(smartctl -i /dev/"${drive}" | grep "SMART support is:.\s*Enabled")" ] && ! [ "$(smartctl -i /dev/"${drive}" | grep "Solid State Device")" ]; then process_ignore_drives; fi done | awk '{for (i=NF; i!=0 ; i--) print $i }' | tr ' ' '\n' | sort | tr '\n' ' ') else smartdrives=$(for drive in $(fdisk -l | grep "Disk /dev/sd" | cut -c 11-15 | cut -d ":" -f1 | tr '\n' ' '); do if [ "$(smartctl -i /dev/"${drive}" | grep "SMART support is:.\s*Enabled")" ] && ! [ "$(smartctl -i /dev/"${drive}" | grep "Solid State Device")" ]; then process_ignore_drives; fi done | awk '{for (i=NF; i!=0 ; i--) print $i }' | tr ' ' '\n' | sort | tr '\n' ' ') fi # Call Sort Routine with the drive string. if [[ "$smartdrives" != "" ]]; then duplicate_list=$smartdrives remove_duplicate # Remove duplicate serial numbers for MultiPath smartdrives=$duplicate_list sort_list=$smartdrives if [ $softver != "Linux" ]; then sort_drives else sort_drives_scale fi smartdrives=$sort_list fi } ########## GET SMART SOLID DISK DRIVES ########## # Get listing of the SMART SSD's and put into $smartdrivesSSD variable get_smartSSD_listings () { if [ $softver != "Linux" ]; then smartdrivesSSD=$(for drive in $(sysctl -n kern.disks); do if [ "$(smartctl -i /dev/"${drive}" | grep "SMART support is:.\s*Enabled")" ] && [ "$(smartctl -i /dev/"${drive}" | grep "Solid State Device")" ]; then process_ignore_drives; fi done | awk '{for (i=NF; i!=0 ; i--) print $i }' | tr ' ' '\n' | sort | tr '\n' ' ') else smartdrivesSSD=$(for drive in $(fdisk -l | grep "Disk /dev/sd" | cut -c 11-15 | cut -d ":" -f1 | tr '\n' ' '); do if [ "$(smartctl -i /dev/"${drive}" | grep "SMART support is:.\s*Enabled")" ] && [ "$(smartctl -i /dev/"${drive}" | grep "Solid State Device")" ]; then process_ignore_drives; fi done | awk '{for (i=NF; i!=0 ; i--) print $i }' | tr ' ' '\n' | sort | tr '\n' ' ') fi # Call Sort Routine with the drive string. if [[ "$smartdrivesSSD" != "" ]]; then duplicate_list=$smartdrivesSSD remove_duplicate # Remove duplicate serial numbers smartdrivesSSD=$duplicate_list sort_list=$smartdrivesSSD if [ $softver != "Linux" ]; then sort_drives else sort_drives_scale fi smartdrivesSSD=$sort_list fi } ########## GET NVMe DRIVES ########## # Get listing of the SMART NVMe's and put into $smartdrivesNVM variable get_smartNVM_listings () { if [ $softver != "Linux" ]; then smartdrivesNVM=$(for drive in $(sysctl -n kern.disks); do if [ "$(smartctl -i /dev/"${drive}" | grep "NVM")" ]; then process_ignore_drives; fi done | awk '{for (i=NF; i!=0 ; i--) print $i }' | tr ' ' '\n' | sort | tr '\n' ' ') else smartdrivesNVM=$(for drive in $(fdisk -l | grep "Disk /dev/nvm" | cut -d ':' -f 1 | cut -d '/' -f 3 | tr '\n' ' '); do if [ "$(smartctl -i /dev/"${drive}" | grep "NVM")" ]; then process_ignore_drives; fi done | awk '{for (i=NF; i!=0 ; i--) print $i }' | tr ' ' '\n' | sort | tr '\n' ' ') fi ### Convert nvdx to nvmexx in smartdrivesNVM ### smartdrivesNVM=$( echo "$smartdrivesNVM" | sed 's/nvd/nvme/g' ) # Call Sort Routine with the drive string. if [[ "$smartdrivesNVM" != "" ]]; then duplicate_list=$smartdrivesNVM remove_duplicate # Remove duplicate serial numbers smartdrivesNVM=$duplicate_list sort_list=$smartdrivesNVM if [ $softver != "Linux" ]; then sort_drives else sort_drives_scale fi smartdrivesNVM=$sort_list fi } ########## GET OTHER SMART DEVICES ########## # Get listing of the OTHER-SMART devices and put into $non-smartdrives variable get_smartOther_listings () { ### Get the non-SSD listing - MUST support SMART # variable nonsmartdrives if [ $softver != "Linux" ]; then nonsmartdrives=$(for drive in $(sysctl -n kern.disks); do if [ ! "$(smartctl -i /dev/"${drive}" | grep "SMART support is:.\s*Enabled")" ] && [ ! "$(smartctl -i /dev/"${drive}" | grep "NVM")" ]; then process_ignore_drives; fi done | awk '{for (i=NF; i!=0 ; i--) print $i }' | tr ' ' '\n' | sort | tr '\n' ' ') else nonsmartdrives=$(for drive in $(fdisk -l | grep "Disk /dev/sd" | cut -c 11-13 | tr '\n' ' '); do if [ ! "$(smartctl -i /dev/"${drive}" | grep "SMART support is: Enabled")" ]; then process_ignore_drives; fi done | awk '{for (i=NF; i!=0 ; i--) print $i }' | tr ' ' '\n' | sort | tr '\n' ' ') fi # Call Sort Routine with the drive string. if [[ "$nonsmartdrives" != "" ]]; then duplicate_list=$nonsmartdrives remove_duplicate # Remove duplicate serial numbers nonsmartdrives=$duplicate_list sort_list=$nonsmartdrives if [ $softver != "Linux" ]; then sort_drives else sort_drives_scale fi nonsmartdrives=$sort_list fi } ########## SAVE JSON DATA ########## # Rename function to "save_json_data" get_json_data () { # Save drive JSON data if -dump command used # Uses $drive parameter. if [[ "$drive" != "cd0" ]]; then if [[ "$dump_all" != "0" ]]; then # Pull a fresh JSON listing tempjson="$(smartctl -x --json=u /dev/${drive})" serial1="$(echo "${tempjson}" | jq -Mre '.serial_number | values')" rpm="$(echo "${tempjson}" | jq -Mre '.rotation_rate | values')" if [[ "$rpm" == "0" ]]; then drivetype="SSD"; else drivetype="HDD"; fi if [[ "$(echo $tempjson | grep -i "nvm")" ]]; then drivetype="NVM"; fi echo "$tempjson" > "/tmp/${tempfilepath}${drivetype}_${drive}_${serial1}.json" fi fi } ########## GET DRIVE DATA ########## # Gets drive data for HDD/SSD/NVM drives. # $1 = HDD/SSD/NVM, $smartdrives* must have data as well. get_drive_data () { # File /tmp/json_errors.txt is used to write what data did not come from a json file. if [[ $Develop == "true" ]]; then echo " " echo "Looking for "$drive echo " " fi if [[ "$testfilepath" != "" ]]; then # Import simulated drive data. # Find matching "$drive" to file in $smartdrives ${testfilenames[i]} smartdata5="" if [[ "$1" == "HDD" ]]; then smartdriveswc="$(echo ${smartdrives[*]} | wc -w)" if [[ $Develop == "true" ]]; then echo "Number of words in this run to scan = "$smartdriveswc; fi for i in $(seq 0 $smartdriveswc); do if [[ "${smartdrives[$i]}" == "${drive}" ]]; then if [[ $Develop == "true" ]]; then echo "We have a match"; fi smartdata5="$(cat ${testfilenamesHDD[$i]})" echo "/dev/$drive -- ${testfilenamesHDD[i]}" break fi done fi if [[ "$1" == "SSD" ]]; then smartdriveswc="$(echo ${smartdrivesSSD[*]} | wc -w)" if [[ $Develop == "true" ]]; then echo "Number of words in this run to scan = "$smartdriveswc; fi for i in $(seq 0 $smartdriveswc); do if [[ "${smartdrivesSSD[$i]}" == "${drive}" ]]; then if [[ $Develop == "true" ]]; then echo "We have a match"; fi smartdata5="$(cat ${testfilenamesSSD[$i]})" echo "/dev/$drive -- ${testfilenamesSSD[i]}" break fi done fi if [[ "$1" == "NVM" ]]; then smartdriveswc="$(echo ${smartdrivesNVM[*]} | wc -w)" if [[ $Develop == "true" ]]; then echo "Number of words in this run to scan = "$smartdriveswc; fi for i in $(seq 0 $smartdriveswc); do if [[ "${smartdrivesNVM[$i]}" == "${drive}" ]]; then if [[ $Develop == "true" ]]; then echo "We have a match"; fi smartdata5="$(cat ${testfilenamesNVM[$i]})" echo "/dev/$drive -- ${testfilenamesNVM[i]}" break fi done fi if [[ "$testfile2" != "" ]]; then smartdata="$(cat "$testfile2")" else smartdata="" fi else # Else let's read the Real Drive Data if [[ $Develop == "true" ]]; then echo "Looking at Real Drive Data, Not Simulated"; fi # Get drive data for json processing smartdata5="$(smartctl -x --json=u /dev/"$drive")" # Get drive data old school for what isn't in json yet smartdata="$(smartctl -a /dev/"$drive")" # Save data for -dump routine fi # If NON Drives - Return if [[ "$1" == "NON" ]]; then return; fi # Get Serial Number if [[ "$(echo "${smartdata5}" | jq -Mre '.serial_number | values')" ]]; then serial="$(echo "${smartdata5}" | jq -Mre '.serial_number | values')" else if [[ "$(echo "${smartdata5}" | grep -i "vmware")" ]]; then serial="VMWare"; fi json_error_log=$json_error_log"\n$(echo $drive" .serial_number not in json")" > /dev/null 2<&1 fi > /dev/null 2<&1 # Get Model Number if [[ "$(echo "${smartdata5}" | jq -Mre '.model_name | values')" ]]; then modelnumber="$(echo "${smartdata5}" | jq -Mre '.model_name | values')"; else json_error_log=$json_error_log$'\n'"$(echo $1" "$serial" .model_name not in json")"; fi > /dev/null 2<&1 # Get Capacity if [[ "$(echo "${smartdata5}" | jq -Mre '.logical_block_size | values')" ]]; then capacity1="$(echo "${smartdata5}" | jq -Mre '.logical_block_size | values')"; else json_error_log=$json_error_log$'\n'"$(echo $1" "$serial" .logical_block_size not in json")"; fi > /dev/null 2<&1 if [[ "$(echo "${smartdata5}" | jq -Mre '.user_capacity.blocks | values')" ]]; then capacity2="$(echo "${smartdata5}" | jq -Mre '.user_capacity.blocks | values')"; else json_error_log=$json_error_log$'\n'"$(echo $1" "$serial" .user_capacity.blocks not in json")"; fi > /dev/null 2<&1 # Calculate Capacity and adjust for GB/TB capacity=$(( ${capacity1} * ${capacity2} )) capacity="$(( capacity/1000000000 ))" if [[ $capacity -gt 999 ]]; then capacity="$(awk -v a="$capacity" 'BEGIN { printf "%.2f", a/1000 }' /dev/null 2<&1 # Get SMART Status if [[ "$(echo "${smartdata5}" | jq -Mre '.smart_status.passed | values')" ]]; then if [ "$(echo "${smartdata5}" | jq -Mre '.smart_status.passed | values')" = "true" ]; then smartStatus="PASSED" else smartStatus="FAILED" fi else json_error_log=$json_error_log$'\n'"$(echo $1" "$serial" .smart_status.passed not in json")" fi > /dev/null 2<&1 # Get Temperature - Normal if [[ "$(echo "${smartdata5}" | jq -Mre '.temperature.current | values')" ]]; then temp="$(echo "${smartdata5}" | jq -Mre '.temperature.current | values')" elif [[ "$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 194) | .raw.value | values')" ]]; then temp="$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 194) | .raw.value | values')" else json_error_log=$json_error_log$'\n'"$(echo $1" "$serial" .temperature.current not in json")" if [[ "$(smartctl -x /dev/"$drive" | grep "Current Temperature:" | awk '{print $3}' | cut -d '/' -f2)" != "?" ]]; then temp=$(smartctl -x /dev/"$drive" | grep "Current Temperature:" | awk '{print $3}' | cut -d '/' -f2) else temp=$Non_Exist_Value fi fi > /dev/null 2<&1 # Get Temperature - Minimum for Power Cycle if [[ "$(echo "${smartdata5}" | jq -Mre '.temperature.power_cycle_min | values')" ]]; then temp_min="$(echo "${smartdata5}" | jq -Mre '.temperature.power_cycle_min | values')" elif [[ "$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 194) | .raw.string | values')" ]]; then tempstring="$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 194) | .raw.string | values')" temp_min=$(echo "${tempstring}" | awk '{print $3}' | cut -d '/' -f1) temp_max=$(echo "${tempstring}" | awk '{print $3}' | cut -d '/' -f2 | tr -d ')') else temp_min=$Non_Exist_Value if [[ $1 == "HDD" ]]; then json_error_log=$json_error_log$'\n'"$(echo $1" "$serial" .temperature.power_cycle_min not in json")"; fi > /dev/null 2<&1 fi > /dev/null 2<&1 # Get Temperature - Maximum for Power Cycle if [[ "$(echo "${smartdata5}" | jq -Mre '.temperature.power_cycle_max | values')" ]]; then temp_max="$(echo "${smartdata5}" | jq -Mre '.temperature.power_cycle_max | values')" fi if [[ $temp_max == "" ]]; then temp_max=$Non_Exist_Value if [[ $1 == "HDD" ]]; then json_error_log=$json_error_log$'\n'"$(echo $1" "$serial" .temperature.power_cycle_max not in json")"; fi > /dev/null 2<&1 fi ### SETUP FOR NO VALUE OBTAINED if [[ $TempDisplaytemp != "" ]]; then TempDisplay=$TempDisplaytemp; fi TempDisplaymin=$TempDisplay TempDisplaymax=$TempDisplay if ! [[ $temp =~ $re ]]; then temp=$Non_Exist_Value; TempDisplaytemp=$TempDisplay; TempDisplay=""; fi if ! [[ $temp_min =~ $re ]]; then temp_min=$Non_Exist_Value; TempDisplaymin=""; fi if ! [[ $temp_max =~ $re ]]; then temp_max=$Non_Exist_Value; TempDisplaymax=""; fi # Get Power On Hours if [[ "$(echo "${smartdata5}" | jq -Mre '.power_on_time.hours | values')" ]]; then onHours="$(echo "${smartdata5}" | jq -Mre '.power_on_time.hours | values')" else json_error_log=$json_error_log$'\n'"$(echo $1" "$serial" .power_on_time.hours not in json")"; fi > /dev/null 2<&1 # Setup for a Zero Hour Report, Yes in case someone has a zero hour value, the division will fail. if [[ $onHours == "0" ]]; then onHours=1; json_error_log=$json_error_log$'\n'"$(echo $1" "$serial" .power_on_time.hours = 0")"; logfile_warning=$logfile_warning"$(printf "Drive "$serial" actual Power On Hours = 0, added 1 hour so the script doesn't fail, if this persists it may be a script failure or unrecognized drive.
")"; fi > /dev/null 2<&1 # Get Start Stop Count if [[ "$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 4) | .raw.value | values')" ]]; then startStop="$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 4) | .raw.value | values')"; fi > /dev/null 2<&1 if [[ $startStop == "" ]] && [[ $1 == "HDD" ]]; then startStop="$(echo "${smartdata5}" | grep -i "Accumulated start-stop cycles:" | cut -d ':' -f 3 | tr -d ' ",')" json_error_log=$json_error_log$'\n'"$(echo $1" "$serial" .id == 4 Start_Stop_Count not in json")"; fi > /dev/null 2<&1 # Get Load Cycle Count if [[ "$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 193) | .raw.value | values')" ]]; then loadCycle="$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 193) | .raw.value | values')"; fi > /dev/null 2<&1 if [[ $loadCycle == "" ]] && [[ $1 == "HDD" ]]; then json_error_log=$json_error_log$'\n'"$(echo $1" "$serial" .id == 193 Load_Cycle_Count not in json")"; fi > /dev/null 2<&1 # Get Spin Retry Count if [[ "$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 10) | .raw.value | values')" ]]; then spinRetry="$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 10) | .raw.value | values')"; fi > /dev/null 2<&1 if [[ $spinRetry == "" ]] && [[ $1 == "HDD" ]]; then json_error_log=$json_error_log$'\n'"$(echo $1" "$serial" .id == 10 Spin_Retry_Count not in json")"; fi > /dev/null 2<&1 # Get Reallocated Sectors Count if [[ "$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 5) | .raw.value | values')" ]]; then reAlloc="$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 5) | .raw.value | values')"; fi > /dev/null 2<&1 if [[ $reAlloc == "" ]]; then json_error_log=$json_error_log$'\n'"$(echo $1" "$serial" .id == 5 Reallocated_Sector_Ct not in json")"; fi > /dev/null 2<&1 # Get Reallocated Sector Events Count if [[ "$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 196) | .raw.value | values')" ]]; then reAllocEvent="$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 196) | .raw.value | values')"; fi > /dev/null 2<&1 if [[ $reAllocEvent == "" ]]; then json_error_log=$json_error_log$'\n'"$(echo $1" "$serial" .id == 196 Reallocated_Event_Count not in json")"; fi > /dev/null 2<&1 # Get Current Pending Sectors Count if [[ "$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 197) | .raw.value | values')" ]]; then pending="$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 197) | .raw.value | values')"; fi > /dev/null 2<&1 if [[ $pending == "" ]]; then json_error_log=$json_error_log$'\n'"$(echo $1" "$serial" .id == 197 Current_Pending_Sector not in json")"; fi > /dev/null 2<&1 # Get Offline Uncorrectable Errors if [[ "$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 198) | .raw.value | values')" ]]; then offlineUnc="$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 198) | .raw.value | values')"; fi > /dev/null 2<&1 if [[ $offlineUnc == "" ]]; then json_error_log=$json_error_log$'\n'"$(echo $1" "$serial" .id == 198 Offline_Uncorrectable not in json")"; fi > /dev/null 2<&1 if [[ "$(echo "${smartdata5}" | jq -Mre '.scsi_error_counter_log.write.total_uncorrected_errors | values')" ]]; then offlineUnc1="$(echo "${smartdata5}" | jq -Mre '.scsi_error_counter_log.write.total_uncorrected_errors | values')"; offlineUnc2="$(echo "${smartdata5}" | jq -Mre '.scsi_error_counter_log.read.total_uncorrected_errors | values')"; offlineUnc=$((( $offlineUnc1 + $offlineUnc2 ))); fi > /dev/null 2<&1 # Get UDMA CRC Errors if [[ "$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 199) | .raw.value | values')" ]]; then crcErrors="$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 199) | .raw.value | values')"; fi > /dev/null 2<&1 if [[ $crcErrors == "" ]]; then json_error_log=$json_error_log$'\n'"$(echo $1" "$serial" .id == 199 UDMA_CRC_Error_Count not in json")"; fi > /dev/null 2<&1 # Get Read Error Rate if [[ "$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 1) | .raw.value | values')" ]]; then rawReadErrorRate="$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 1) | .raw.value | values')"; fi > /dev/null 2<&1 if [[ $rawReadErrorRate == "" ]] && [[ $1 == "HDD" ]]; then json_error_log=$json_error_log$'\n'"$(echo $1" "$serial" .id == 1 Raw_Read_Error_Rate not in json")"; fi > /dev/null 2<&1 # Get Seek Error Rate if [[ "$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 7) | .raw.value | values')" ]]; then seekErrorHealth="$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 7) | .raw.value | values')"; fi > /dev/null 2<&1 if [[ $seekErrorHealth == "" ]] && [[ $1 == "HDD" ]]; then json_error_log=$json_error_log$'\n'"$(echo $1" "$serial" .id == 7 Seek_Error_Rate not in json")"; fi > /dev/null 2<&1 # Get MultiZone Error Rate if [[ "$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 200) | .raw.value | values')" ]]; then multiZone="$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 200) | .raw.value | values')"; fi > /dev/null 2<&1 if [[ "$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 200) | .name | values')" == "Pressure_Limit" ]]; then multiZone=""; fi > /dev/null 2<&1 if [[ $multiZone == "" ]]; then if [[ "$(echo "${smartdata5}" | grep "Multi_Zone_Error_Rate")" ]]; then json_error_log=$json_error_log$'\n'"$(echo $1" "$serial" .id == 200 Multi_Zone_Error not decoded properly")" fi fi > /dev/null 2<&1 # Get Helium Level he2="" # Are these two variables needed here? lastTestHours="" he2="$(echo "${smartdata5}" | grep -i "helium" | awk '{print $2}' | tr -d '",' | head -1)" if [[ "$he2" != "" ]]; then Helium="$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.name == "'${he2}'") | .raw.value | values')" heliumthresh="$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.name == "'${he2}'") | .thresh | values')" # Normalize Helium to 100. if [[ $heliumthresh > 50 ]]; then Helium=$(( 100 - $Helium )) # Helium is now Normalized where "100" is good. fi else if [[ "$(echo "${smartdata5}" | grep -i "helium")" ]]; then json_error_log=$json_error_log$'\n'"$(echo $1" "$serial" Helium_Level not decoded properly")" #> /dev/null 2<&1 fi fi > /dev/null 2<&1 # Get SSD Total Data Written ssdtdw="" ssdblocksize="" ID241="" if [[ "$(echo "${smartdata5}" | jq -Mre '.logical_block_size | values')" ]]; then ssdblocksize="$(echo "${smartdata5}" | jq -Mre '.logical_block_size | values')" fi > /dev/null 2<&1 if [[ "$(echo "${smartdata5}" | jq -Mre '.ata_device_statistics.pages[].table[] | select(.name == "Logical Sectors Written") | .value | values')" ]]; then ssdtdw="$(echo "${smartdata5}" | jq -Mre '.ata_device_statistics.pages[].table[] | select(.name == "Logical Sectors Written") | .value | values')" fi > /dev/null 2<&1 if [[ $ssdtdw == "" ]]; then if [[ "$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.name == "Total_Writes_GiB") | .raw.value | values')" ]]; then ID241="$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.name == "Total_Writes_GiB") | .raw.value | values')" elif [[ "$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 241) | .raw.value | values')" ]]; then ssdtdw="$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 241) | .raw.value | values')" fi > /dev/null 2<&1 fi # Get Wear Level wearLevelThreshold="" wearLevel="" if [[ "$(echo "${smartdata5}" | jq -Mre '.ata_device_statistics.pages[].table[] | select(.name == "Percentage Used Endurance Indicator") | .value | values')" ]]; then wearLevel="$(echo "${smartdata5}" | jq -Mre '.ata_device_statistics.pages[].table[] | select(.name == "Percentage Used Endurance Indicator") | .value | values')" wearLevelThreshold=100 if [[ $wearLevel -gt 100 ]]; then wearLevel=""; fi fi > /dev/null 2<&1 if [[ $wearLevel == "" ]]; then if [[ "$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 231) | .value | values')" ]]; then wearLevel="$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 231) | .value | values')" wearLevelThreshold="$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.id == 231) | .thresh | values')" elif [[ "$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.name == "Percent_Lifetime_Remain") | .value | values')" ]]; then wearLevel="$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.name == "Percent_Lifetime_Remain") | .value | values')" wearLevelThreshold="$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.name == "Percent_Lifetime_Remain") | .thresh | values')" elif [[ "$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.name == "Media_Wearout_Indicator") | .value | values')" ]]; then wearLevel="$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.name == "Media_Wearout_Indicator") | .value | values')" wearLevelThreshold="$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.name == "Media_Wearout_Indicator") | .thresh | values')" elif [[ "$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.name == "Wear_Leveling_Count") | .value | values')" ]]; then wearLevel="$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.name == "Wear_Leveling_Count") | .value | values')" wearLevelThreshold="$(echo "${smartdata5}" | jq -Mre '.ata_smart_attributes.table[] | select(.name == "Wear_Leveling_Count") | .thresh | values')" fi > /dev/null 2<&1 if [[ $wearLevel == "" ]] && [[ $1 != "HDD" ]]; then if [[ "$(echo ${smartdata5} | grep -i "wear")" ]] || [[ "$(echo ${smartdata5} | grep -i "life")" ]]; then json_error_log=$json_error_log$'\n'"$(echo $1" "$serial" .id == 231 SSD_Life_Left not properly decoded")"; fi fi > /dev/null 2<&1 fi > /dev/null 2<&1 # Adjust wearLevel for negative values. if [[ $wearLevelThreshold != "" ]] && [[ $wearLevel != "" ]]; then if [[ $wearLevelThreshold -gt 50 ]]; then wearLevel=$((100 - $wearLevel)) fi fi # Check SSD/NVMe Wear Level - THIS WORKS for NVMe Where 100 = 100 if [[ $wearLevel == "" ]]; then if [[ "$(echo "${smartdata5}" | jq -Mre '.nvme_smart_health_information_log.percentage_used | values')" ]]; then wearLevel="$(echo "${smartdata5}" | jq -Mre '.nvme_smart_health_information_log.percentage_used | values')" wearLevel=$((100 - $wearLevel)) elif [[ "$(echo "${smartdata5}" | jq -Mre '.nvme_smart_health_information_log.available_spare | values')" ]]; then wearLevel="$(echo "${smartdata5}" | jq -Mre '.nvme_smart_health_information_log.available_spare | values')" fi > /dev/null 2<&1 fi # WORKS FOR SCSI Where 0 = 100 if [[ "$(echo "${smartdata5}" | jq -Mre '.scsi_percentage_used_endurance_indicator | values')" ]]; then wearLevel=$((100 - "$(echo "${smartdata5}" | jq -Mre '.scsi_percentage_used_endurance_indicator | values')")) fi > /dev/null 2<&1 # Get Device Type - SCSI if [[ "$(echo "${smartdata5}" | jq -Mre '.device.type | values')" ]]; then devicetype="$(echo "${smartdata5}" | jq -Mre '.device.type | values')" else json_error_log=$json_error_log$'\n'"$(echo $1 " "$serial" .device.type not in json")" > /dev/null 2<&1 fi > /dev/null 2<&1 if [[ $devicetype == "scsi" ]]; then modelnumber=$modelnumber" (SCSI)" fi # Get NVMe Critical Warning Value if [[ "$(echo "${smartdata5}" | jq -Mre '.nvme_smart_health_information_log.critical_warning | values')" ]]; then NVMcriticalWarning="$(echo "${smartdata5}" | jq -Mre '.nvme_smart_health_information_log.critical_warning | values')" fi > /dev/null 2<&1 # Get NVMe Media Errors if [[ "$(echo "${smartdata5}" | jq -Mre '.nvme_smart_health_information_log.media_errors | values')" ]]; then mediaErrors="$(echo "${smartdata5}" | jq -Mre '.nvme_smart_health_information_log.media_errors | values')" fi > /dev/null 2<&1 # Get NVMe Total Data Written if [[ "$(echo "${smartdata5}" | jq -Mre '.nvme_smart_health_information_log.data_units_written | values')" ]]; then tdw="$(echo "${smartdata5}" | jq -Mre '.nvme_smart_health_information_log.data_units_written | values')" fi > /dev/null 2<&1 # Get SCSI Total Data Written if [[ $tdw == "" ]]; then if [[ "$(echo "${smartdata5}" | jq -Mre '.scsi_error_counter_log.write.gigabytes_processed | values')" ]]; then tdw="$(echo "${smartdata5}" | jq -Mre '.scsi_error_counter_log.write.gigabytes_processed | values')" fi > /dev/null 2<&1 fi sas=0 if [[ "$(echo "$smartdata5" | grep "SAS")" ]]; then sas=1; fi if [[ "$(echo "${smartdata5}" | grep ".ata_smart_attributes")" ]]; then if [[ $Develop == "true" ]]; then echo "SMART Attributes are Present Look Up By Number Add common ID's here."; fi else json_error_log=$json_error_log$'\n'"$(echo $1" "$serial" .ata_smart_attributes not in json")" > /dev/null 2<&1 # SCSI Unique if [[ $Develop == "true" ]]; then echo "++++++++ SMART Attributes are NOT present ++++++++"; fi loadCycle="$(echo "${smartdata5}" | grep -i "Accumulated load-unload cycles:" | cut -d ':' -f 3 | tr -d ' ",')" reAllocEvent="$(echo "${smartdata5}" | jq -Mre '.scsi_grown_defect_list | values')" startStop="$(echo "${smartdata5}" | grep -i "Accumulated start-stop cycles:" | cut -d ':' -f 3 | tr -d ' ",')" # Data from 'smartctl -x' if [[ "$(echo "$smartdata" | grep "Spin_Retry_Count" | awk '{print $10}')" ]]; then spinRetry="$(echo "$smartdata" | grep "Spin_Retry_Count" | awk '{print $10 + 0}')"; fi if [[ "$(echo "$smartdata" | grep "Current_Pending_Sector" | awk '{print $10}')" ]]; then pending="$(echo "$smartdata" | grep "Current_Pending_Sector" | awk '{print $10}')"; fi if [[ "$(echo "$smartdata" | grep "Offline_Uncorrectable" | awk '{print $10}')" ]]; then offlineUnc="$(echo "$smartdata" | grep "Offline_Uncorrectable" | awk '{print $10}')"; fi if [[ "$(echo "$smartdata" | grep "Uncorrectable_Error_Cnt" | awk '{print $10}')" ]]; then offlineUnc="$(echo "$smartdata" | grep "Uncorrectable_Error_Cnt" | awk '{print $10}')"; fi if [[ "$(echo "$smartdata" | grep "UDMA_CRC_Error_Count" | awk '{print $10}')" ]]; then crcErrors="$(echo "$smartdata" | grep "UDMA_CRC_Error_Count" | awk '{print $10 + 0}')"; fi if [[ "$(echo "$smartdata" | grep "CRC_Error_Count" | awk '{print $10}')" ]]; then crcErrors="$(echo "$smartdata" | grep "CRC_Error_Count" | awk '{print $10}')"; fi if [[ "$(echo "$smartdata" | grep "Multi_Zone_Error_Rate" | awk '{print $10}')" ]]; then multiZone="$(echo "$smartdata" | grep "Multi_Zone_Error_Rate" | awk '{print $10 + 0}')"; fi # On hold because of step above... if [[ "$(echo "$smartdata" | grep "Reallocated_Event_Count" | awk '{print $10}')" ]]; then # reAllocEvent="$(echo "$smartdata" | grep "Reallocated_Event_Count" | awk '{print $10}')"; fi fi if [[ $crcErrors == "" ]]; then if [[ "$(echo "$smartdata" | grep "Interface CRC Errors" | awk '{print $4}')" ]]; then crcErrors="$(echo "$smartdata" | grep "Interface CRC Errors" | awk '{print $4}')"; fi fi if [[ "$(echo "${smartdata5}" | jq -Mre '.ata_smart_data.self_test.status.remaining_percent | values')" ]]; then smarttesting="$(echo "${smartdata5}" | jq -Mre '.ata_smart_data.self_test.status.remaining_percent | values')" fi lastTestStatusPass="true" # Setup default, a failure can change it. if [[ "$(echo "${smartdata5}" | jq -Mre '.ata_smart_self_test_log.extended.table | values')" ]]; then lastTestHours="$(echo "${smartdata5}" | jq -Mre '.ata_smart_self_test_log.extended.table[0].lifetime_hours | values')" lastTestType="$(echo "${smartdata5}" | jq -Mre '.ata_smart_self_test_log.extended.table[0].type.string | values')" lastTestStatus="$(echo "${smartdata5}" | jq -Mre '.ata_smart_self_test_log.extended.table[0].status.string | values')" lastTestStatusPass="$(echo "${smartdata5}" | jq -Mre '.ata_smart_self_test_log.extended.table[0].status.passed | values')" elif [[ "$(echo "${smartdata5}" | jq -Mre '.ata_smart_self_test_log.standard.table | values')" ]]; then lastTestHours="$(echo "${smartdata5}" | jq -Mre '.ata_smart_self_test_log.standard.table[0].lifetime_hours | values')" lastTestType="$(echo "${smartdata5}" | jq -Mre '.ata_smart_self_test_log.standard.table[0].type.string | values')" lastTestStatus="$(echo "${smartdata5}" | jq -Mre '.ata_smart_self_test_log.standard.table[0].status.string | values')" lastTestStatusPass="$(echo "${smartdata5}" | jq -Mre '.ata_smart_self_test_log.standard.table[0].status.passed | values')" elif [[ "$(echo "${smartdata5}" | jq -Mre '.ata_smart_selective_self_test_log.table | values')" ]]; then lastTestHours="$(echo "${smartdata5}" | jq -Mre '.ata_smart_selective_self_test_log.table[0].lifetime_hours | values')" lastTestType="$(echo "${smartdata5}" | jq -Mre '.ata_smart_selective_self_test_log.table[0].status.string | values')" lastTestStatus="$(echo "${smartdata5}" | jq -Mre '.ata_smart_selective_self_test_log.table[0].status.string | values')" lastTestStatusPass="$(echo "${smartdata5}" | jq -Mre '.ata_smart_selective_self_test_log.table[0].status.passed | values')" fi if [[ $lastTestStatus == "" ]]; then if [[ "$(echo "${smartdata5}" | jq -Mre '.ata_smart_data.self_test.status.string | values')" ]]; then lastTestStatus="$(echo "${smartdata5}" | jq -Mre '.ata_smart_data.self_test.status.string | values')" fi fi # This looks for "smartctl_xxxx_u": when json value is not available if [[ $lastTestHours == "" ]]; then if [[ "$(echo "${smartdata5}" | grep "NOW")" ]]; then lastTestHours="$(echo "${smartdata5}" | grep "# 2" | awk '{print $8}' )" lastTestStatus="$(echo "${smartdata5}" | grep "# 1" | awk '{print $6" "$7" "$8" "$9}' )" # chkreadfailure="$(echo "${smartdata5}" | grep "# 1" | awk '{print $5}') smarttesting="$(echo "${smartdata5}" | grep -i "Self-test execution status:" | cut -d ':' -f 3 | tr -d '[a-z]% ,\\"')" # Alternate for this value... smarttesting="$(echo "${smartdata5}" | grep -i "remaining" | cut -d% -f1 | rev | cut -b -2 | rev)" else lastTestHours="$(echo "${smartdata5}" | grep "# 1" | awk '{print $8}' )" lastTestStatus="$(echo "${smartdata5}" | grep "# 1" | awk '{print $6}' )" # chkreadfailure="$(echo "${smartdata}" | grep "# 1" | awk '{print $5}') smarttesting="$(echo "${smartdata5}" | grep -i "Self-test execution status:" | cut -d ':' -f 3 | tr -d '[a-z]% ,\\"')" fi lastTestType="$(echo "${smartdata5}" | grep "# 1" | awk '{print $4" "$5}' )" fi # Adjust for a drive that does report a passed SMART Test but not the hour. if [[ $lastTestHours == "0" ]] && [[ $lastTestStatusPass == "true" ]]; then lastTestHours=$onHours fi if [[ $lastTestStatusPass == "" ]]; then lastTestStatusPass="true"; fi # Fix for lack of data, default to pass. # if [[ lastTestStatus == "" ]]; then if [[ "$(echo "${smartdata}" | grep "# 1" | awk '{print $5}')" ]]; then chkreadfailure="$(echo "${smartdata}" | grep "# 1" | awk '{print $5}')" fi # fi if [[ $lastTestType == "" ]]; then lastTestType=$Non_Exist_Value; fi ### Try looking for Custom_Drive data here. Need it for wearLevelAdj. IFS=',' read -ra ADDR <<< "$Custom_Drives_List" for i in "${ADDR[@]}"; do cdrivesn1="$(echo $i | cut -d':' -f 1)" if [[ $cdrivesn1 == $serial ]]; then if [[ "$(echo $i | cut -d':' -f 16)" == "d" || "$(echo $i | cut -d':' -f 16)" == "r" ]] ; then wearLevelAdj="$(echo $i | cut -d':' -f 16)"; fi fi done if [[ "$rotation" > 0 ]]; then Custom_Drives_ListDrive="HDD" fi if [[ "$rotation" == "0" ]]; then Custom_Drives_ListDrive="SSD" if [[ $wearLevelAdj == "r" ]]; then wearLevel=$(( 100 - $wearLevel )) fi fi if [[ "$rotation" == "" ]] && [[ $1 == "HDD" ]]; then Custom_Drives_ListDrive="HDD" fi if [[ "$(echo "$smartdata5" | grep "NVM")" ]]; then Custom_Drives_ListDrive="NVM" fi if [[ $Develop == "true" ]]; then echo " " echo "Drive Type: "$1" | Serial Number: "$serial" | Model: "$modelnumber echo "Capacity: "$capacity" | RPM: "$rotation" | SMART Status: "$smartStatus echo "Curr Temp: "$temp" | Temp Min: "$temp_min" | Temp Max: "$temp_max echo "onHours="$onHours" | Start Stop Count: "$startStop" | Load Cycle: "$loadCycle echo "Spin Retry: "$spinRetry" | Reallocated Sectors: "$reAlloc" | Reallocated Events: "$reAllocEvent echo "Curr Pending Sectors: "$pending" | Offline uncorrectable: "$offlineUnc" | UDMA CRC Errors: "$crcErrors echo "Read Error Rate: "$rawReadErrorRate" | Seek Error Rate: "$seekErrorHealth" | MultiZone: "$multiZone echo "Helium: "$Helium" | HeliumThreshold="$heliumthresh" | Wear Level: "$wearLevel echo "Last Test Age: "$lastTestHours" | Last Test Type: "$lastTestType" | lastTestStatus="$lastTestStatus echo "testStatus="$smarttesting" | lastTestStatusPass="$lastTestStatusPass echo #echo "SAS="$sas #echo "lastTestHours="$lastTestHours", altlastTestHours="$altlastTestHours echo "==========================================" fi # Definitions of variables: # # $1 = Drive Type (HDD/SSD/NVM) # $lastTestType = The last/current test (background short/long, Short/Long offline, Conveyance) # $lastTestStatus = "Completed without error", "Completed", "Self test in progress", etc... # $testStatus = Percent completed of a test in progress "35%" with "%" trimmed. # $lastTestStatusPass = json passed = "true" or "false". if [[ $Sample_Test == "true" ]]; then echo "In Testing Mode" # Change any value below to override the actual drive values. # These are critical monitored values #temp_min=10 #temp_max=50 temp=44 #spinRetry=0 reAlloc=2 reAllocEvent=2 pending=1 offlineUnc=10 crcErrors=0 multiZone=3 Helium=91 #wearLevel=16 #mediaErrors=0 # Below here are non-critical (No alarm generated) seekErrorHealth=6 seekErrorRate=6 rawReadErrorRate=5 #startStop=490 #loadCycle=500 #onHours=50026 #lastTestHours=50000 tdw=175234 fi ########## CALL CONVERT VARIABLES TO DECIMAL ########## if [[ "$temp_min" != "" ]] && [[ "$temp_min" != "0" ]] && [[ "$temp_min" != "$Non_Exist_Value" ]]; then convert_to_decimal $temp_min; temp_min=$Return_Value; fi if [[ "$temp_max" != "" ]] && [[ "$temp_max" != "0" ]] && [[ "$temp_max" != "$Non_Exist_Value" ]]; then convert_to_decimal $temp_max; temp_max=$Return_Value; fi if [[ "$temp" != "" ]] && [[ "$temp" != "0" ]] && [[ "$temp" != "$Non_Exist_Value" ]]; then convert_to_decimal $temp; temp=$Return_Value; fi if [[ "$spinRetry" != "" ]] && [[ "$spinRetry" != "0" ]]; then convert_to_decimal $spinRetry; spinRetry=$Return_Value; fi if [[ "$reAllocEvent" != "" ]] && [[ "$reAllocEvent" != "0" ]]; then convert_to_decimal $reAllocEvent; reAllocEvent=$Return_Value; fi if [[ "$pending" != "" ]] && [[ "$pending" != "0" ]]; then convert_to_decimal $pending; pending=$Return_Value; fi if [[ "$offlineUnc" != "" ]] && [[ "$offlineUnc" != "0" ]]; then convert_to_decimal $offlineUnc; offlineUnc=$Return_Value; fi if [[ "$crcErrors" != "" ]] && [[ "$crcErrors" != "0" ]]; then convert_to_decimal $crcErrors; crcErrors=$Return_Value; fi if [[ "$seekErrorHealth2" != "" ]] && [[ "$seekErrorHealth2" != "0" ]]; then convert_to_decimal $seekErrorHealth2; seekErrorHealth2=$Return_Value; fi if [[ "$seekErrorHealth" != "" ]] && [[ "$seekErrorHealth" != "0" ]]; then convert_to_decimal $seekErrorHealth; seekErrorHealth=$Return_Value; fi if [[ "$rawReadErrorRate2" != "" ]] && [[ "$rawReadErrorRate2" != "0" ]]; then convert_to_decimal $rawReadErrorRate2; rawReadErrorRate2=$Return_Value; fi if [[ "$rawReadErrorRate" != "" ]] && [[ "$rawReadErrorRate" != "0" ]]; then convert_to_decimal $rawReadErrorRate; rawReadErrorRate=$Return_Value; fi if [[ "$multiZone" != "" ]] && [[ "$multiZone" != "0" ]]; then convert_to_decimal $multiZone; multiZone=$Return_Value; fi if [[ "$wearLevel" != "" ]] && [[ "$wearLevel" != "0" ]]; then convert_to_decimal $wearLevel; wearLevel=$Return_Value; fi if [[ "$mediaErrors" != "" ]] && [[ "$mediaErrors" != "0" ]] && [[ "$mediaErrors" != "$Non_Exist_Value" ]]; then convert_to_decimal $mediaErrors; mediaErrors=$Return_Value; fi if [[ "$tdw" != "" ]] && [[ "$tdw" != "0" ]] && [[ "$tdw" != "$Non_Exist_Value" ]]; then convert_to_decimal $tdw; tdw=$Return_Value; fi if [[ "$temp" != "" ]] && [[ "$temp" != "0" ]]; then convert_to_decimal $temp; temp=$Return_Value; fi if [[ "$startStop" != "" ]] && [[ "$startStop" != "0" ]]; then convert_to_decimal $startStop; startStop=$Return_Value; fi if [[ "$loadCycle" != "" ]] && [[ "$loadCycle" != "0" ]]; then convert_to_decimal $loadCycle; loadCycle=$Return_Value; fi if [[ "$reAlloc" != "" ]] && [[ "$reAlloc" != "0" ]]; then convert_to_decimal $reAlloc; reAlloc=$Return_Value; fi if [[ "$onHours" != "" ]] && [[ "$onHours" != "0" ]]; then convert_to_decimal $onHours; onHours=$Return_Value; fi if [[ "$Helium" != "" ]] && [[ "$Helium" != "0" ]]; then convert_to_decimal $Helium; Helium=$Return_Value; fi lastTestHours="$(echo $lastTestHours | tr -d "()%/")" if [[ "$lastTestHours" != "" ]] && [[ "$lastTestHours" != "0" ]]; then convert_to_decimal $lastTestHours; lastTestHours=$Return_Value; fi altlastTestHours="$(echo $altlastTestHours | tr -d "()%/")" if [[ "$altlastTestHours" -gt "0" ]]; then convert_to_decimal $altlastTestHours; altlastTestHours=$Return_Value; fi # Some drives do not report test age after 65536 hours. if [[ $onHours -gt "65536" ]] && [[ $lastTestHours -gt "0" && $lastTestHours -lt "65536" ]]; then lastTestHours=$(($lastTestHours + 65536)); fi ######## VMWare Hack to fix NVMe bad variables ##### if [[ "$VMWareNVME" == "on" ]]; then if [[ "$serial" == "VMWare" ]]; then onHours="17,200" wearLevel="98" temp="38" temp_min="20" temp_max="43" fi fi # read -n 1 Keyboard # Pause Execution for Writing Script # Save data for -dump routine if [[ "$dump_all" != "0" ]]; then get_json_data "$(echo "$smartdata" > /tmp/${tempfilepath}${drive}_${serial}_a.txt)" 2> /dev/null "$(smartctl -x /dev/"$drive" > /tmp/${tempfilepath}${drive}_${serial}_x.txt)" 2> /dev/null fi # Add a line return between drives json_error_log=$json_error_log$'\n' } ########## GENERATE TABLE ########## # Call function with generate_table "HDD|SSD|NVM" generate_table () { if [[ "$Monitor" == "true" ]]; then return; fi detail_level="$1" # Lets add up how many columns we will need. if [[ "$Drive_Warranty_List" == "none" || "$Drive_Warranty_List" == "" ]]; then HDD_Warranty="false" SSD_Warranty="false" NVM_Warranty="false" fi Columns=0; if [[ "$1" == "HDD" ]] && [[ "$HDD_Device_ID" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "HDD" ]] && [[ "$HDD_Serial_Number" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "HDD" ]] && [[ "$HDD_Model_Number" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "HDD" ]] && [[ "$HDD_Capacity" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "HDD" ]] && [[ "$HDD_Rotational_Rate" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "HDD" ]] && [[ "$HDD_SMART_Status" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "HDD" ]] && [[ "$HDD_Warranty" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "HDD" ]] && [[ "$HDD_Drive_Temp" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "HDD" ]] && [[ "$HDD_Drive_Temp_Min" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "HDD" ]] && [[ "$HDD_Drive_Temp_Max" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "HDD" ]] && [[ "$HDD_Power_On_Hours" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "HDD" ]] && [[ "$HDD_Start_Stop_Count" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "HDD" ]] && [[ "$HDD_Load_Cycle" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "HDD" ]] && [[ "$HDD_Spin_Retry" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "HDD" ]] && [[ "$HDD_Reallocated_Sectors" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "HDD" ]] && [[ "$HDD_Reallocated_Events" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "HDD" ]] && [[ "$HDD_Pending_Sectors" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "HDD" ]] && [[ "$HDD_Offline_Uncorrectable" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "HDD" ]] && [[ "$HDD_UDMA_CRC_Errors_List" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "HDD" ]] && [[ "$HDD_Raw_Read_Error_Rate" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "HDD" ]] && [[ "$HDD_Seek_Error_Rate" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "HDD" ]] && [[ "$HDD_MultiZone_Errors" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "HDD" ]] && [[ "$HDD_Helium_Level" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "HDD" ]] && [[ "$HDD_Total_Data_Written" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "HDD" ]] && [[ "$HDD_Last_Test_Age" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "HDD" ]] && [[ "$HDD_Last_Test_Type" == "true" ]]; then ((Columns=Columns+1)); fi; # Count for SSD if [[ "$1" == "SSD" ]] && [[ "$SSD_Device_ID" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "SSD" ]] && [[ "$SSD_Serial_Number" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "SSD" ]] && [[ "$SSD_Model_Number" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "SSD" ]] && [[ "$SSD_Capacity" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "SSD" ]] && [[ "$SSD_SMART_Status" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "SSD" ]] && [[ "$SSD_Warranty" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "SSD" ]] && [[ "$SSD_Drive_Temp" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "SSD" ]] && [[ "$SSD_Drive_Temp_Min" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "SSD" ]] && [[ "$SSD_Drive_Temp_Max" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "SSD" ]] && [[ "$SSD_Power_On_Hours" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "SSD" ]] && [[ "$SSD_Wear_Level" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "SSD" ]] && [[ "$SSD_Reallocated_Sectors" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "SSD" ]] && [[ "$SSD_Reallocated_Events" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "SSD" ]] && [[ "$SSD_Pending_Sectors" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "SSD" ]] && [[ "$SSD_Offline_Uncorrectable" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "SSD" ]] && [[ "$SSD_UDMA_CRC_Errors_List" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "SSD" ]] && [[ "$SSD_Total_Data_Written" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "SSD" ]] && [[ "$SSD_Last_Test_Age" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "SSD" ]] && [[ "$SSD_Last_Test_Type" == "true" ]]; then ((Columns=Columns+1)); fi; # Count for NVMe if [[ "$1" == "NVM" ]] && [[ "$NVM_Device_ID" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "NVM" ]] && [[ "$NVM_Serial_Number" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "NVM" ]] && [[ "$NVM_Model_Number" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "NVM" ]] && [[ "$NVM_Capacity" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "NVM" ]] && [[ "$NVM_SMART_Status" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "NVM" ]] && [[ "$NVM_Warranty" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "NVM" ]] && [[ "$NVM_Critical_Warning" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "NVM" ]] && [[ "$NVM_Drive_Temp" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "NVM" ]] && [[ "$NVM_Drive_Temp_Min" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "NVM" ]] && [[ "$NVM_Drive_Temp_Max" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "NVM" ]] && [[ "$NVM_Power_On_Hours" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "NVM" ]] && [[ "$NVM_Wear_Level" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "NVM" ]] && [[ "$NVM_Media_Error" == "true" ]]; then ((Columns=Columns+1)); fi; if [[ "$1" == "NVM" ]] && [[ "$NVM_Total_Data_Written" == "true" ]]; then ((Columns=Columns+1)); fi; ( # Write HTML table headers to log file echo "

" echo "" if [[ "$1" == "HDD" ]]; then echo ""; fi if [[ "$1" == "SSD" ]]; then echo ""; fi if [[ "$1" == "NVM" ]]; then echo ""; fi echo "" if [[ "$1" == "HDD" ]] && [[ "$HDD_Device_ID" == "true" ]]; then echo " "; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Device_ID" == "true" ]]; then echo " "; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_Device_ID" == "true" ]]; then echo " "; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Serial_Number" == "true" ]]; then echo " "; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Serial_Number" == "true" ]]; then echo " "; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_Serial_Number" == "true" ]]; then echo " "; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Model_Number" == "true" ]]; then echo " "; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Model_Number" == "true" ]]; then echo " "; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_Model_Number" == "true" ]]; then echo " "; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Capacity" == "true" ]]; then echo " "; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Capacity" == "true" ]]; then echo " "; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_Capacity" == "true" ]]; then echo " "; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Rotational_Rate" == "true" ]]; then echo " "; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_SMART_Status" == "true" ]]; then echo " "; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_SMART_Status" == "true" ]]; then echo " "; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_SMART_Status" == "true" ]]; then echo " "; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Warranty" == "true" ]]; then echo " "; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Warranty" == "true" ]]; then echo " "; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_Warranty" == "true" ]]; then echo " "; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_Critical_Warning" == "true" ]]; then echo " "; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Drive_Temp" == "true" ]]; then echo " "; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Drive_Temp" == "true" ]]; then echo " "; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_Drive_Temp" == "true" ]]; then echo " "; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Drive_Temp_Min" == "true" ]]; then echo " "; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Drive_Temp_Min" == "true" ]]; then echo " "; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_Drive_Temp_Min" == "true" ]]; then echo " "; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Drive_Temp_Max" == "true" ]]; then echo " "; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Drive_Temp_Max" == "true" ]]; then echo " "; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_Drive_Temp_Max" == "true" ]]; then echo " "; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Power_On_Hours" == "true" ]]; then echo " "; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Power_On_Hours" == "true" ]]; then echo " "; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_Power_On_Hours" == "true" ]]; then echo " "; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Wear_Level" == "true" ]]; then echo " "; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_Wear_Level" == "true" ]]; then echo " "; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Start_Stop_Count" == "true" ]]; then echo " "; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Load_Cycle" == "true" ]]; then echo " "; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Spin_Retry" == "true" ]]; then echo " "; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Reallocated_Sectors" == "true" ]]; then echo " "; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Reallocated_Sectors" == "true" ]]; then echo " "; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Reallocated_Events" == "true" ]]; then echo " "; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Reallocated_Events" == "true" ]]; then echo " "; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Pending_Sectors" == "true" ]]; then echo " "; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Pending_Sectors" == "true" ]]; then echo " "; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Offline_Uncorrectable" == "true" ]]; then echo " "; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Offline_Uncorrectable" == "true" ]]; then echo " "; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_UDMA_CRC_Errors_List" == "true" ]]; then echo " "; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_UDMA_CRC_Errors_List" == "true" ]]; then echo " "; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Raw_Read_Error_Rate" == "true" ]]; then echo " "; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Seek_Error_Rate" == "true" ]]; then echo " "; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_MultiZone_Errors" == "true" ]]; then echo " "; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Helium_Level" == "true" ]]; then echo " "; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Last_Test_Age" == "true" ]]; then echo " "; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Last_Test_Age" == "true" ]]; then echo " "; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Last_Test_Type" == "true" ]]; then echo " "; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Last_Test_Type" == "true" ]]; then echo " "; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Total_Data_Written" == "true" ]]; then echo " "; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Total_Data_Written" == "true" ]]; then echo " "; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_Media_Error" == "true" ]]; then echo " "; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_Total_Data_Written" == "true" ]]; then echo " "; fi echo "" ) >> "$logfile" } ########## WRITE TABLE ########## # Call function with end_table "HDD|SSD|NVM" write_table () { if [[ "$Monitor" == "true" ]]; then return; fi ( printf "\n" $bgColor; if [[ "$1" == "HDD" ]] && [[ "$HDD_Device_ID" == "true" ]]; then printf "\n" "$deviceStatusColor" "$drive"; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Device_ID" == "true" ]]; then printf "\n" "$deviceStatusColor" "$drive"; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_Device_ID" == "true" ]]; then printf "\n" "$deviceStatusColor" "$drive"; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Serial_Number" == "true" ]]; then printf "\n" "$bgColor" "$serial"; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Serial_Number" == "true" ]]; then printf "\n" "$bgColor" "$serial"; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_Serial_Number" == "true" ]]; then printf "\n" "$bgColor" "$serial"; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Model_Number" == "true" ]]; then printf "\n" "$modelnumber"; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Model_Number" == "true" ]]; then printf "\n" "$modelnumber"; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_Model_Number" == "true" ]]; then printf "\n" "$modelnumber"; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Capacity" == "true" ]]; then printf "\n" "$capacity"; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Capacity" == "true" ]]; then printf "\n" "$capacity"; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_Capacity" == "true" ]]; then printf "\n" "$capacity"; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Rotational_Rate" == "true" ]]; then printf "\n" "$rotation"; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_SMART_Status" == "true" ]]; then printf "\n" "$smartStatusColor" "$smartStatus"; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_SMART_Status" == "true" ]]; then printf "\n" "$smartStatusColor" "$smartStatus"; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_SMART_Status" == "true" ]]; then printf "\n" "$smartStatusColor" "$smartStatus"; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Warranty" == "true" ]] && [[ "$WarrantyBoxColor" == "$expiredWarrantyBoxColor" ]]; then printf "\n" "$WarrantyBackgroundColor" "$WarrantyBoxPixels" "$WarrantyBoxColor" "$WarrantyClock"; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Warranty" == "true" ]] && [[ "$WarrantyBoxColor" != "$expiredWarrantyBoxColor" ]]; then printf "\n" "$WarrantyBackgroundColor" "$WarrantyBoxColor" "$WarrantyClock"; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Warranty" == "true" ]] && [[ "$WarrantyBoxColor" == "$expiredWarrantyBoxColor" ]]; then printf "\n" "$WarrantyBackgroundColor" "$WarrantyBoxPixels" "$WarrantyBoxColor" "$WarrantyClock"; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Warranty" == "true" ]] && [[ "$WarrantyBoxColor" != "$expiredWarrantyBoxColor" ]]; then printf "\n" "$WarrantyBackgroundColor" "$WarrantyBoxColor" "$WarrantyClock"; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_Warranty" == "true" ]] && [[ "$WarrantyBoxColor" == "$expiredWarrantyBoxColor" ]]; then printf "\n" "$WarrantyBackgroundColor" "$WarrantyBoxPixels" "$WarrantyBoxColor" "$WarrantyClock"; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_Warranty" == "true" ]] && [[ "$WarrantyBoxColor" != "$expiredWarrantyBoxColor" ]]; then printf "\n" "$WarrantyBackgroundColor" "$WarrantyBoxColor" "$WarrantyClock"; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_Critical_Warning" == "true" ]]; then printf "\n" "$NVMcriticalWarningColor" "$NVMcriticalWarning"; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Drive_Temp" == "true" ]]; then printf "\n" "$tempColor" "$temp"; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Drive_Temp" == "true" ]]; then printf "\n" "$tempColor" "$temp"; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_Drive_Temp" == "true" ]]; then printf "\n" "$tempColor" "$temp"; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Drive_Temp_Min" == "true" ]]; then printf "\n" "$temp_min"; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Drive_Temp_Min" == "true" ]]; then printf "\n" "$temp_min"; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_Drive_Temp_Min" == "true" ]]; then printf "\n" "$temp_min"; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Drive_Temp_Max" == "true" ]]; then printf "\n" "$temp_maxColor" "$temp_max"; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Drive_Temp_Max" == "true" ]]; then printf "\n" "$temp_maxColor" "$temp_max"; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_Drive_Temp_Max" == "true" ]]; then printf "\n" "$temp_maxColor" "$temp_max"; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Power_On_Hours" == "true" ]]; then printf "\n" "$onTimeColor" "$onTime"; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Power_On_Hours" == "true" ]]; then printf "\n" "$onTimeColor" "$onTime"; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_Power_On_Hours" == "true" ]]; then printf "\n" "$onTimeColor" "$onTime"; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Wear_Level" == "true" ]]; then printf "\n" "$wearLevelColor" "$wearLevel"; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_Wear_Level" == "true" ]]; then printf "\n" "$wearLevelColor" "$wearLevel"; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_Media_Error" == "true" ]]; then printf "\n" "$mediaErrorsColor" "$mediaErrors"; fi if [[ "$1" == "NVM" ]] && [[ "$NVM_Total_Data_Written" == "true" ]]; then printf "\n" "$bgColor" "$tdw"; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Start_Stop_Count" == "true" ]]; then printf "\n" "$startStop"; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Load_Cycle" == "true" ]]; then printf "\n" "$loadCycle"; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Spin_Retry" == "true" ]]; then printf "\n" "$spinRetryColor" "$spinRetry"; fi if [[ $reAllocColor != $ovrdColor ]] || [[ $Mouseover == "false" ]]; then if [[ "$1" == "HDD" ]] && [[ "$HDD_Reallocated_Sectors" == "true" ]]; then printf "\n" "$reAllocColor" "$reAlloc"; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Reallocated_Sectors" == "true" ]]; then printf "\n" "$reAllocColor" "$reAlloc"; fi else if [[ $Mouseover == "alt" ]]; then if [[ "$1" == "HDD" ]] && [[ "$HDD_Reallocated_Sectors" == "true" ]]; then printf "\n" "$reAllocColor" "$reAlloc" "$reAllocOrig"; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Reallocated_Sectors" == "true" ]]; then printf "\n" "$reAllocColor" "$reAlloc" "$reAllocOrig"; fi fi if [[ $Mouseover == "true" ]]; then if [[ "$1" == "HDD" ]] && [[ "$HDD_Reallocated_Sectors" == "true" ]]; then printf "\n" "$reAllocColor" "$reAllocOrig" "$reAlloc"; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Reallocated_Sectors" == "true" ]]; then printf "\n" "$reAllocColor" "$reAllocOrig" "$reAlloc"; fi fi fi if [[ $reAllocEventColor != $ovrdColor ]] || [[ $Mouseover == "false" ]]; then if [[ "$1" == "HDD" ]] && [[ "$HDD_Reallocated_Events" == "true" ]]; then printf "\n" "$reAllocEventColor" "$reAllocEvent"; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Reallocated_Events" == "true" ]]; then printf "\n" "$reAllocEventColor" "$reAllocEvent"; fi else if [[ $Mouseover == "alt" ]]; then if [[ "$1" == "HDD" ]] && [[ "$HDD_Reallocated_Events" == "true" ]]; then printf "\n" "$reAllocEventColor" "$reAllocEvent" "$reAllocEventOrig"; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Reallocated_Events" == "true" ]]; then printf "\n" "$reAllocEventColor" "$reAllocEvent" "$reAllocEventOrig"; fi fi if [[ $Mouseover == "true" ]]; then if [[ "$1" == "HDD" ]] && [[ "$HDD_Reallocated_Events" == "true" ]]; then printf "\n" "$reAllocEventColor" "$reAllocEventOrig" "$reAllocEvent"; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Reallocated_Events" == "true" ]]; then printf "\n" "$reAllocEventColor" "$reAllocEventOrig" "$reAllocEvent"; fi fi fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Pending_Sectors" == "true" ]]; then printf "\n" "$pendingColor" "$pending"; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Pending_Sectors" == "true" ]]; then printf "\n" "$pendingColor" "$pending"; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Offline_Uncorrectable" == "true" ]]; then printf "\n" "$offlineUncColor" "$offlineUnc"; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Offline_Uncorrectable" == "true" ]]; then printf "\n" "$offlineUncColor" "$offlineUnc"; fi if [[ $crcErrorsColor != $ovrdColor ]] || [[ $Mouseover == "false" ]]; then if [[ "$1" == "HDD" ]] && [[ "$HDD_UDMA_CRC_Errors_List" == "true" ]]; then printf "\n" "$crcErrorsColor" "$crcErrors"; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_UDMA_CRC_Errors_List" == "true" ]]; then printf "\n" "$crcErrorsColor" "$crcErrors"; fi else if [[ $Mouseover == "alt" ]]; then if [[ "$1" == "HDD" ]] && [[ "$HDD_UDMA_CRC_Errors_List" == "true" ]]; then printf "\n" "$crcErrorsColor" "$crcErrors" "$crcErrorsOrig"; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_UDMA_CRC_Errors_List" == "true" ]]; then printf "\n" "$crcErrorsColor" "$crcErrors" "$crcErrorsOrig"; fi fi if [[ $Mouseover == "true" ]]; then if [[ "$1" == "HDD" ]] && [[ "$HDD_UDMA_CRC_Errors_List" == "true" ]]; then printf "\n" "$crcErrorsColor" "$crcErrorsOrig" "$crcErrors"; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_UDMA_CRC_Errors_List" == "true" ]]; then printf "\n" "$crcErrorsColor" "$crcErrorsOrig" "$crcErrors"; fi fi fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Raw_Read_Error_Rate" == "true" ]]; then printf "\n" "$rawReadErrorRateColor" "$rawReadErrorRate"; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Seek_Error_Rate" == "true" ]]; then printf "\n" "$seekErrorHealthColor" "$seekErrorHealth"; fi if [[ $multiZoneColor != $ovrdColor ]] || [[ $Mouseover == "false" ]]; then if [[ "$1" == "HDD" ]] && [[ "$HDD_MultiZone_Errors" == "true" ]]; then printf "\n" "$multiZoneColor" "$multiZone"; fi else if [[ $Mouseover == "alt" ]]; then if [[ "$1" == "HDD" ]] && [[ "$HDD_MultiZone_Errors" == "true" ]]; then printf "\n" "$multiZoneColor" "$multiZone" "$multiZoneOrig"; fi fi if [[ $Mouseover == "true" ]]; then if [[ "$1" == "HDD" ]] && [[ "$HDD_MultiZone_Errors" == "true" ]]; then printf "\n" "$multiZoneColor" "$multiZoneOrig" "$multiZone"; fi fi fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Helium_Level" == "true" ]]; then printf "\n" "$HeliumColor" "$Helium"; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Last_Test_Age" == "true" ]]; then printf "\n" "$testAgeColor" "$testAge"; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Last_Test_Age" == "true" ]]; then printf "\n" "$testAgeColor" "$testAge"; fi if [[ $lastTestTypeColor == $bgColor ]] && [[ "$(echo $lastTestStatus | grep -i "progress" )" ]]; then lastTestTypeColor=$blueColor; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Last_Test_Type" == "true" ]]; then printf "\n" "$lastTestTypeColor" "$lastTestType"; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Last_Test_Type" == "true" ]]; then printf "\n" "$lastTestTypeColor" "$lastTestType"; fi if [[ "$1" == "HDD" ]] && [[ "$HDD_Total_Data_Written" == "true" ]]; then printf "\n" "$bgColor" "$tdw"; fi if [[ "$1" == "SSD" ]] && [[ "$SSD_Total_Data_Written" == "true" ]]; then printf "\n" "$bgColor" "$tdw"; fi echo "" ) | tr -d "[]" >> "$logfile" } ########## END THE TABLE ########## end_table () { if [[ "$Monitor" == "true" ]]; then return; fi ( echo "" echo "
"$HDDreportTitle"
"$SSDreportTitle"
"$NVMreportTitle"
"$HDD_Device_ID_Title""$SSD_Device_ID_Title""$NVM_Device_ID_Title""$HDD_Serial_Number_Title""$SSD_Serial_Number_Title""$NVM_Serial_Number_Title""$HDD_Model_Number_Title""$SSD_Model_Number_Title""$NVM_Model_Number_Title""$HDD_Capacity_Title""$SSD_Capacity_Title""$NVM_Capacity_Title""$HDD_Rotational_Rate_Title""$HDD_SMART_Status_Title""$SSD_SMART_Status_Title""$NVM_SMART_Status_Title""$HDD_Warranty_Title""$SSD_Warranty_Title""$NVM_Warranty_Title""$NVM_Critical_Warning_Title""$HDD_Drive_Temp_Title""$SSD_Drive_Temp_Title""$NVM_Drive_Temp_Title""$HDD_Drive_Temp_Min_Title""$SSD_Drive_Temp_Min_Title""$NVM_Drive_Temp_Min_Title""$HDD_Drive_Temp_Max_Title""$SSD_Drive_Temp_Max_Title""$NVM_Drive_Temp_Max_Title""$HDD_Power_On_Hours_Title""$SSD_Power_On_Hours_Title""$NVM_Power_On_Hours_Title""$SSD_Wear_Level_Title""$NVM_Wear_Level_Title""$HDD_Start_Stop_Count_Title""$HDD_Load_Cycle_Title""$HDD_Spin_Retry_Title""$HDD_Reallocated_Sectors_Title""$SSD_Reallocated_Sectors_Title""$HDD_Reallocated_Events_Title""$SSD_Reallocated_Events_Title""$HDD_Pending_Sectors_Title""$SSD_Pending_Sectors_Title""$HDD_Offline_Uncorrectable_Title""$SSD_Offline_Uncorrectable_Title""$HDD_UDMA_CRC_Errors_List_Title""$SSD_UDMA_CRC_Errors_List_Title""$HDD_Raw_Read_Error_Rate_Title""$HDD_Seek_Error_Rate_Title""$HDD_MultiZone_Errors_Title""$HDD_Helium_Level_Title""$HDD_Last_Test_Age_Title""$SSD_Last_Test_Age_Title""$HDD_Last_Test_Type_Title""$SSD_Last_Test_Type_Title""$HDD_Total_Data_Written_Title""$SSD_Total_Data_Written_Title""$NVM_Media_Error_Title""$NVM_Total_Data_Written_Title"
/dev/%s/dev/%s/dev/%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s$TempDisplay%s$TempDisplay%s$TempDisplay%s$TempDisplaymin%s$TempDisplaymin%s$TempDisplaymin%s$TempDisplaymax%s$TempDisplaymax%s$TempDisplaymax%s%s%s%s%s%s%s%s%s%s%s%s%s(%s)%s(%s)%s%s%s%s%s(%s)%s(%s)%s%s%s%s%s%s%s%s%s(%s)%s(%s)%s%s$SER%s$SER%s%s%s(%s)%s%s%s%s%s%s%s%s
" if [[ "$SER1" == "1" ]]; then echo "* = Seek Error Rate is Normalized. Higher is better."; fi echo "
" ) >> "$logfile" } ########## COMPILE DETAILED REPORT ########## # Detailed Report Section (monospace text) detailed_report () { testfile=$1 if [[ $Monitor == "false" ]]; then ( echo "
"
			echo "Multi-Report Text Section"
			echo "
"
		) >> "$logfile"

		if test -e "$Config_File_Name"; then
			echo "External Configuration File in use dtd:$config_version_date " >> "$logfile"
		else
			echo "No External Configuration File Exists" >> "$logfile"
		fi

		if [[ "$dump_all" == "3" ]]; then
			echo "YOU have requested '-dump email' and thus an email was sent to Joe Schmuck for analysis.
He will try to contact you on the sending email address. If this is an invalid
email address then please send a followup email to joeschmuck2023@hotmail.com
Your personal information will not be shared.

" >> "$logfile" fi if [[ $TrueNASConfigEmailEnable == "true" ]]; then echo "TrueNAS Configuration File --> Emailed every: $TrueNASConfigEmailDay" >> "$logfile"; fi if [[ $SDF_DataRecordEnable == "true" ]]; then if [[ "$(echo $statistical_data_file | grep "/tmp/")" ]]; then echo "The Statistical Data File is located in the /tmp directory and is not permanent.
Recommend changing to a proper dataset.

" >> "$logfile" fi if [[ $statistical_data_file_created == "1" ]]; then echo "Statistical Data File Created.
" >> "$logfile"; fi if [[ $SDF_DataEmail == "true" ]]; then echo "Statistical Export Log Located: $statistical_data_file --> Emailed every: $SDF_DataEmailDay
" >> "$logfile" else echo "Statistical Export Log Located at: $statistical_data_file
" >> "$logfile" fi fi fi ### Lets write out the error messages if there are any, Critical first followed by Warning Monitor_Send_Email="false" if [[ $AlertOnCriticalError == "true" ]] && [[ $Monitor == "true" ]]; then if [[ $logfile_critical != "" ]]; then Monitor_Send_Email="true" fi fi if [[ $AlertOnWarningTemp == "true" ]] && [[ $Monitor == "true" ]]; then if [[ $Temperature_Log != "" ]]; then Monitor_Send_Email="true" fi fi ( if [[ $Monitor == "false" ]]; then if [[ ! $logfile_messages == "" ]]; then echo "MESSAGES LOG FILE" echo $logfile_messages echo "
END
" fi if [[ $logfile_critical != "" ]]; then echo "CRITICAL LOG FILE" echo $logfile_critical echo "
END
" fi if [[ $logfile_warning != "" ]]; then echo "WARNING LOG FILE" echo $logfile_warning echo "
END
" fi if [[ ! $Ignore_Drives_List == "none" ]]; then echo "Ignored Drives = "$Ignore_Drives_List echo "
END
" fi if [[ ! $logfile_warranty == "" ]]; then echo $logfile_warranty echo "
" fi else if [[ $logfile_critical != "" ]]; then echo $logfile_critical fi if [[ $Temperature_Log != "" ]]; then echo $Temperature_Log fi fi ) >> "$logfile" if [[ $Monitor == "false" ]]; then if [[ $DisableRAWdata != "true" ]]; then ### zpool status for each pool for pool in $pools; do if [ $softver != "Linux" ]; then drives_in_gptid=$(zpool status "$pool" | grep "gptid" | awk '{print $1}') else drives_in_gptid=$(zpool status -P "$pool" | grep "/dev/disk" | awk -F '[/]' '{print $5}' | cut -d " " -f1) fi driveit=0 ( # Create a simple header and drop the output of zpool status -v echo "########## ZPool status report for ${pool} ##########" zpool status -v "$pool" zpool_list=$(zpool status -v "$pool") for longgptid in $drives_in_gptid; do if [ $softver != "Linux" ]; then drive_ident=$(glabel status | tail -n +2 | grep "$longgptid" | awk '{print $1 " -> " $3}' | cut -d '/' -f2 | cut -d 'p' -f1) else drive_ident=$longgptid" -> "$(/sbin/blkid | grep "$longgptid" | cut -d ":" -f1 | cut -d "/" -f3) fi ### Check if GPTID or not and display correct one # if [[ $drive_ident != "" ]]; then if [[ $driveit == "0" ]]; then echo "
Drives for this pool are listed below:"; driveit="1"; fi echo $drive_ident fi done for zpoollist in $zpool_list; do # Check $zpoollist for 'ada', 'da' or 'sd' and if either, this is not a GPTID. zpool_drive_test=$(echo $zpoollist | cut -c1-3) if [[ $zpool_drive_test == "ada" ]]; then # Match to 'ada' drive_ident=$(echo $zpoollist | cut -f1 -d" ")" -> "$(echo $zpoollist | cut -f1 -d"p") echo $drive_ident fi zpoollist_drive_test=$(echo $zpoollist | cut -c1-2) if [[ $zpoollist_drive_test == "da" ]] || [[ $zpoollist_drive_test == "sd" ]] && [[ $zpoollist != "data" ]]; then # Match to 'da' or 'sd' drive_ident=$(echo $zpoollist | cut -f1 -d" ")" -> "$(echo $zpoollist | cut -f1 -d"p") echo $drive_ident fi done echo "
" ) >> "$logfile" done if [[ $IncludeSSD == "true" ]] && [[ $IncludeNVM == "true" ]]; then drives="${smartdrives} ${smartdrivesSSD} ${smartdrivesNVM}" elif [[ $IncludeSSD == "true" ]]; then drives="${smartdrives} ${smartdrivesSSD}" else drives="${smartdrives}" fi ### SMART status for each drive - SMART Enabled write_ATA_Errors="0" # What is happening here, ada50 and nvme50? for drive in $drives; do if [[ $drive == "TEST" || $drive == "nvme50" ]] ; then testfileX="$( cat "${testfile}" )" modelnumber="$(echo "${testfileX}" | jq -Mre '.model_name | values')" serial="$(echo "${testfileX}" | jq -Mre '.serial_number | values')" ( echo "
########## FULL TESTFILE -- SMART status report for ${drive} drive (${modelnumber}: ${serial}) ##########" echo "
Data not available in test mode" ) >> "$logfile" else # Gather brand and serial number of each drive smartdata="$(smartctl -a /dev/"$drive")" smartdata5="$(smartctl -x --json=u /dev/"$drive")" modelnumber="$(echo "${smartdata5}" | jq -Mre '.model_name | values')" serial="$(echo "${smartdata5}" | jq -Mre '.serial_number | values')" test_ata_error="$(smartctl -H -A -l error /dev/"$drive" | grep "ATA Error Count" | awk '{print $4}')" modelnumber="$(echo "${modelnumber}" | sed -e 's/\ *$//g')" if [[ $serial == "" ]]; then serial="N/A"; fi if [[ $modelnumber == "" ]]; then modelnumber="N/A"; fi # If no data in ATA_Errors then lets gather data if needed. if [[ $ATA_Errors == "" ]]; then ATA_Errors="none"; fi ### ATA ERROR LOG ### Let's find a match to string ATA_Errors IFS=',' read -ra ADDR <<< "$ATA_Errors" for i in "${ADDR[@]}"; do ataerrorssn1="$(echo $i | cut -d':' -f 1)" ataerrorsdt1="$(echo $i | cut -d':' -f 2)" if [[ $ataerrorssn1 == "none" ]]; then if [[ ! $test_ata_error == "" ]]; then if [[ $ATA_Auto_Enable == "true" ]]; then temp_ATA_Errors=$temp_ATA_Errors$serial:$test_ata_error"," write_ATA_Errors="1" fi fi fi if [[ "$ataerrorssn1" == "$serial" ]]; then ataerrors=$ataerrorsdt1 if [[ $ATA_Errors == "" ]]; then write_ATA_Errors="1" fi temp_ATA_Errors=$temp_ATA_Errors$serial:$test_ata_error"," if [[ $test_ata_error -gt $ataerrors ]]; then write_ATA_Errors="1" logfile_warning=$logfile_warning"$(printf "Drive "$serial" ATA Error Count: "$test_ata_error" - Value Increased
")" fi fi continue done ( # Create a simple header and drop the output of some basic smartctl commands echo "########## SMART status report for ${drive} drive (${modelnumber} : ${serial}) ##########" if [[ $test_ata_error -gt "0" ]]; then if [[ $test_ata_error -gt $ataerrors ]]; then smartctl -H -A -l error /dev/"$drive" else smartctl -H -A /dev/"$drive" echo "ATA Error Count: "$test_ata_error echo " " fi else smartctl -H -A -l error /dev/"$drive" fi # Create Recent Tests Report echo "Num Test_Description (Most recent Short & Extended Tests - Listed by test number)" lasttest1="$(smartctl -x /dev/"$drive" | egrep "# 1")" echo $lasttest1 if [[ $lasttest1 == *"Short offline"* ]]; then lastfind="Extended offline"; fi; if [[ $lasttest1 == *"Extended offline"* ]]; then lastfind="Short offline"; fi; lasttest2="$(smartctl -x /dev/"$drive" | egrep "$lastfind" | head -1)" echo $lasttest2 echo "
" ) >> "$logfile" # SCT Error Recovery Control Report scterc="$(smartctl -l scterc /dev/"$drive" | tail -3 | head -2)" ( echo "SCT Error Recovery Control: "$scterc echo "
" ) >> "$logfile" fi done fi fi } ########## COMPILE NON-SMART REPORT ########## ### NON-SMART status report section # I don't particularly use this but some folks might find it useful. # To activate it, in the variables set ReportNonSMART=true. # It will list all drives where Non-SMART is true and remove devices starting with "cd", for example "cd0" non_smart_report () { if [[ "$Monitor" == "true" ]]; then return; fi drives=$nonsmartdrives if [ $ReportNonSMART == "true" ]; then for drive in $drives; do if [ ! "$(echo "$drive" | grep "cd")" ]; then # Gather model number and serial number of each drive modelnumber="" serial="" smartdata="$(smartctl -a /dev/"$drive")" smartdata5="$(smartctl -x --json=u /dev/"$drive")" serial="$(echo "${smartdata5}" | jq -Mre '.serial_number | values')" if [[ $serial == "" ]]; then serial="$(echo "$smartdata" | grep "Serial Number" | awk '{print $3}')" fi modelnumber="$(echo "${smartdata5}" | jq -Mre '.model_name | values')" modelnumber="$(echo "${modelnumber}" | sed -e 's/\ *$//g')" if [[ $serial == "" ]]; then serial="N/A"; fi if [[ $modelnumber == "" ]]; then modelnumber="N/A"; fi ( echo "
" echo "########## NON-SMART status report for ${drive} drive (${modelnumber} : ${serial}) ##########" # And we will dump everything since it's not a standard SMART device. echo "SMARTCTL DATA" smartctl -a /dev/"$drive" echo "
" if [ $softver == "Linux" ]; then echo "FDISK DATA" fdisk -l /dev/"$drive" fi ) >> "$logfile" fi done fi } ########## REMOVE UN-NEEDED JUNK AND FINALIZE EMAIL MESSAGE END ########## remove_junk_report () { ### Remove some un-needed junk from the output sed -i -e '/smartctl/d' "$logfile" sed -i -e '/Copyright/d' "$logfile" sed -i -e '/=== START OF READ/d' "$logfile" sed -i -e '/SMART Attributes Data/d' "$logfile" sed -i -e '/Vendor Specific SMART/d' "$logfile" sed -i -e '/SMART Error Log Version/d' "$logfile" } attach_files () { #### CAN THE REST OF THIS FUNCTION THIS BE MOVED INTO A SINGLE SECTION FOR ATTACHMENTS ????? ###### doit="false" if [[ "$dump_all" != "0" ]]; then output_x=0 # Now attach all the attachments. output_html="$(cat $logfile)" output_x=1 ( # Write MIME section header for file attachment (encoded with base64) if [[ $output_html != "" ]]; then echo "--${boundary}" echo "Content-Type: text/html; charset=utf-8" echo "Content-Transfer-Encoding: base64" echo "Content-Disposition: attachment; filename=email_body.html" base64 <<< $output_html fi ) >> "$logfile" # Attach dump files first for drive in $smartdrives; do dump_drive_data done for drive in $smartdrivesSSD; do dump_drive_data done for drive in $smartdrivesNVM; do dump_drive_data done for drive in $nonsmartdrives; do dump_drive_data done doit="true" fi if [[ $Attach_Files1 == "true" ]]; then ( if [[ $Attach_Multi_Report == "true" ]]; then if test -e "/tmp/Old_multi_report_config.txt"; then # Write MIME section header for file attachment (encoded with base64) echo "--${boundary}" echo "Content-Type: text/html" echo "Content-Transfer-Encoding: base64" echo "Content-Disposition: attachment; filename=Old_multi_report_config.txt" base64 "/tmp/Old_multi_report_config.txt" rm /tmp/Old_multi_report_config.txt fi if test -e $Config_File_Name; then # Write MIME section header for file attachment (encoded with base64) echo "--${boundary}" echo "Content-Type: text/html" echo "Content-Transfer-Encoding: base64" echo "Content-Disposition: attachment; filename=multi_report_config.txt" base64 $Config_File_Name fi fi if [[ $Attach_Statistical_File == "true" ]]; then if test -e $statistical_data_file; then # Write MIME section header for file attachment (encoded with base64) echo "--${boundary}" echo "Content-Type: text/csv" echo "Content-Transfer-Encoding: base64" echo "Content-Disposition: attachment; filename=Statistical_Data.csv" base64 "$statistical_data_file" fi fi if [[ $Attach_TrueNAS_Config == "true" ]]; then if test -e /tmp/$Config_Name; then echo "--${boundary}" echo "Content-Type: application/zip" echo "Content-Transfer-Encoding: base64" echo "Content-Disposition: attachment; filename=${Config_Name}" base64 /tmp/$Config_Name rm /tmp/$Config_Name fi fi if [[ $dump_all != "0" ]]; then if test -e "/tmp/zpoollist.pool"; then echo "--${boundary}" echo "Content-Type: text/html" echo "Content-Transfer-Encoding: base64" echo "Content-Disposition: attachment; filename=zpoollist.pool" base64 "/tmp/zpoollist.pool" fi if test -e "/tmp/zpoolstatus.pool"; then echo "--${boundary}" echo "Content-Type: text/html" echo "Content-Transfer-Encoding: base64" echo "Content-Disposition: attachment; filename=zpoolstatus.pool" base64 "/tmp/zpoolstatus.pool" fi if [[ $json_error_log != "" ]]; then echo "--${boundary}" echo "Content-Type: text/plain" echo "Content-Transfer-Encoding: base64" echo "Content-Disposition: attachment; filename=json_error_log.txt" base64 <<< $json_error_log fi fi ) >> "$logfile" fi if test -e "/tmp/spencer_report.txt"; then ( if [ $(grep -wic "New Error Messages" "/tmp/spencer_report.txt") -gt 0 ] || [ $(grep -wic "Previous" "/tmp/spencer_report.txt") -gt 0 ]; then echo "--${boundary}" echo "Content-Type: text/html" echo "Content-Transfer-Encoding: base64" echo "Content-Disposition: attachment; filename=spencer_report.txt" base64 "/tmp/spencer_report.txt" fi ) >> "$logfile" fi } ########## CRUNCH THE NUMBERS and FORMAT MESSAGES and COLORS ########## ##### Call with HDD|SSD|NVM ############## crunch_numbers () { detail_level=$1 ### Lets adjust for all the Media Alarms, Temp Alarms, and Wear Level for the new Custom_Drives_List # We need to change the values in the running script to use slight different variables # for example SectorsWarn will now be SectorsWarnx # Do this for all the pertinent variables and add a section to scan the Custom_Drives_List variable # and if a serial number matches, then use the variables there vs the defaults. ### Order of data -- $serial":"$tempwarn":"$tempcrit":"$sectorswarn":"$sectorscrit":"$reallocwarn":"$multizonewarn":"$multizonecrit":"$rawreadwarn":"$rawreadcrit":"$seekerrorswarn":"$seekerrorscrit":"$testage":"$testAgeOvrd":"$heliummin":"$wearleveladj" # Predefine default variables if [[ $Custom_Drives_ListDrive == "HDD" ]]; then HDDtempWarnx=$HDDtempWarn; HDDtempCritx=$HDDtempCrit fi if [[ $Custom_Drives_ListDrive == "SSD" ]]; then SSDtempWarnx=$SSDtempWarn; SSDtempCritx=$SSDtempCrit fi if [[ $Custom_Drives_ListDrive == "NVM" ]]; then NVMtempWarnx=$NVMtempWarn; NVMtempCritx=$NVMtempCrit fi SectorsWarnx=$SectorsWarn SectorsCritx=$SectorsCrit ReAllocWarnx=$ReAllocWarn MultiZoneWarnx=$MultiZoneWarn MultiZoneCritx=$MultiZoneCrit RawReadWarnx=$RawReadWarn RawReadCritx=$RawReadCrit SeekErrorsWarnx=$SeekErrorsWarn SeekErrorsCritx=$SeekErrorsCrit TestWarnAgex=$TestWarnAge testAgeOvrd="0" HeliumMinx=$HeliumMin ### 'd' = Default Values IFS=',' read -ra ADDR <<< "$Custom_Drives_List" for i in "${ADDR[@]}"; do cdrivesn1="$(echo $i | cut -d':' -f 1)" if [[ $cdrivesn1 == $serial ]]; then if [[ $Custom_Drives_ListDrive == "HDD" ]]; then if [[ "$(echo $i | cut -d':' -f 2)" != "d" ]]; then HDDtempWarnx="$(echo $i | cut -d':' -f 2)" fi if [[ "$(echo $i | cut -d':' -f 3)" != "d" ]]; then HDDtempCritx="$(echo $i | cut -d':' -f 3)" fi fi if [[ $Custom_Drives_ListDrive == "SSD" ]]; then if [[ "$(echo $i | cut -d':' -f 2)" != "d" ]]; then SSDtempWarnx="$(echo $i | cut -d':' -f 2)" fi if [[ "$(echo $i | cut -d':' -f 3)" != "d" ]]; then SSDtempCritx="$(echo $i | cut -d':' -f 3)" fi fi if [[ $Custom_Drives_ListDrive == "NVM" ]]; then if [[ "$(echo $i | cut -d':' -f 2)" != "d" ]]; then NVMtempWarnx="$(echo $i | cut -d':' -f 2)" fi if [[ "$(echo $i | cut -d':' -f 3)" != "d" ]]; then NVMtempCritx="$(echo $i | cut -d':' -f 3)" fi fi if [[ "$(echo $i | cut -d':' -f 4)" != "d" ]]; then SectorsWarnx="$(echo $i | cut -d':' -f 4)"; fi if [[ "$(echo $i | cut -d':' -f 5)" != "d" ]]; then SectorsCritx="$(echo $i | cut -d':' -f 5)"; fi if [[ "$(echo $i | cut -d':' -f 6)" != "d" ]]; then ReAllocWarnx="$(echo $i | cut -d':' -f 6)"; fi if [[ "$(echo $i | cut -d':' -f 7)" != "d" ]]; then MultiZoneWarnx="$(echo $i | cut -d':' -f 7)"; fi if [[ "$(echo $i | cut -d':' -f 8)" != "d" ]]; then MultiZoneCritx="$(echo $i | cut -d':' -f 8)"; fi if [[ "$(echo $i | cut -d':' -f 9)" != "d" ]]; then RawReadWarnx="$(echo $i | cut -d':' -f 9)"; fi if [[ "$(echo $i | cut -d':' -f 10)" != "d" ]]; then RawReadCritx="$(echo $i | cut -d':' -f 10)"; fi if [[ "$(echo $i | cut -d':' -f 11)" != "d" ]]; then SeekErrorsWarnx="$(echo $i | cut -d':' -f 11)"; fi if [[ "$(echo $i | cut -d':' -f 12)" != "d" ]]; then SeekErrorsCritx="$(echo $i | cut -d':' -f 12)"; fi if [[ "$(echo $i | cut -d':' -f 13)" != "d" ]]; then TestWarnAgex="$(echo $i | cut -d':' -f 13)"; fi testAgeOvrd="$(echo $i | cut -d':' -f 14)" if [[ "$(echo $i | cut -d':' -f 15)" != "d" ]]; then HeliumMinx="$(echo $i | cut -d':' -f 15)"; fi if [[ "$(echo $i | cut -d':' -f 16)" != "d" ]]; then wearLevelAdj="$(echo $i | cut -d':' -f 16)"; fi ### Remove this check in future version if [[ "$(echo $i | cut -d':' -f 16)" == "" ]] || [[ "$(echo $i | cut -d':' -f 16)" == "," ]]; then wearLevelAdj="d" echo "Illegal Custom Drive Configuration Data... Delete and recreate the Custom Drive configuration data." echo "Temporary patch applied for Wear Level Adjustment." else wearLevelAdj="$(echo $i | cut -d':' -f 16)" fi fi done ### Remove Leading Zeros from all variables # This is important because double square brackets interpret a leading zero as Octal number # This only works for positive numbers, not negative. Thankfully I should not have negative # numbers in this script. # Make onHours a base 10 number and remove any commas onHours=${onHours#0} # DO I STILL NEED THIS - REDUNDANT? onHours="${onHours//,}" ### Convert onHours to onTime if [[ $onHours -gt "1000000" ]]; then onHours=0; fi if [[ $lastTestHours != "" ]]; then let testAge=$(((($onHours - $lastTestHours) / 24))); fi let yrs=$((($onHours / 8760))) let mos=$(((($onHours % 8760) / 730))) let dys=$((((($onHours % 8760) % 730) / 24))) let hrs=$(((($onHours % 8760) % 730) % 24)) if [[ $PowerTimeFormat == "ymdh" ]]; then onTime="${yrs}y ${mos}m ${dys}d ${hrs}h"; elif [[ $PowerTimeFormat == "ymd" ]]; then onTime="${yrs}y ${mos}m ${dys}d"; elif [[ $PowerTimeFormat == "ym" ]]; then onTime="${yrs}y ${mos}m"; elif [[ $PowerTimeFormat == "y" ]]; then onTime="${yrs}y"; elif [[ $PowerTimeFormat == "h" ]]; then onTime=$onHours; else onTime=$onHours; fi ##### CALCULATE TOTAL DATA WRITTEN ########## # Display in TiB Written or GiB Written if [[ "$1" == "NVM" ]] && [[ $tdw != "" ]]; then tdwbytes=$((( $tdw * 512000 ))); else tdwbytes=$((( $ssdblocksize * ssdtdw ))); fi if [[ $ID241 != "" ]]; then tdwbytes=$((( $ID241 * 1074000000 ))); fi if [[ $tdw != "" ]] && [[ "$1" != "NVM" ]]; then # Assume SCSI Drive tdwbytes1=$( echo $tdw | cut -d'.' -f 1) tdwbytes2=$( echo $tdw | cut -d'.' -f 2) tdw=$tdwbytes1$tdwbytes2 tdwbytes=$((( $tdw * 1000000))) fi if [[ $tdwbytes != "" ]]; then if [[ $tdwbytes -ge 1000000000000000 ]]; then tdwbytes=$((( $tdwbytes / 10000000000000 ))) tdwbytes1="$((( $tdwbytes / 100 )))" tdwbytes2="$(echo $tdwbytes | rev | cut -c 1,2 | rev)" tdw=$tdwbytes1"."$tdwbytes2" PB" elif [[ $tdwbytes -ge 1000000000000 ]]; then tdwbytes=$((( $tdwbytes / 10000000000 ))) tdwbytes1="$((( $tdwbytes / 100 )))" tdwbytes2="$(echo $tdwbytes | rev | cut -c 1,2 | rev)" tdw=$tdwbytes1"."$tdwbytes2" TB" elif [[ $tdwbytes -ge 1000000000 ]]; then tdwbytes=$((( $tdwbytes / 10000000 ))) tdwbytes1="$((( $tdwbytes / 100 )))" tdwbytes2="$(echo $tdwbytes | rev | cut -c 1,2 | rev)" tdw=$tdwbytes1"."$tdwbytes2" GB" elif [[ $tdwbytes -ge 1000000 ]]; then tdwbytes=$((( $tdwbytes / 100000 ))) tdwbytes1="$((( $tdwbytes / 10 )))" tdwbytes2="$(echo $tdwbytes | rev | cut -c 1)" tdw=$tdwbytes1"."$tdwbytes2" MB" elif [[ $tdwbytes -ge 1000 ]]; then tdwbytes=$((( $tdwbytes / 100 ))) tdwbytes1="$((( $tdwbytes / 10 )))" tdwbytes2="$(echo $tdwbytes | rev | cut -c 1)" tdw=$tdwbytes1"."$tdwbytes2" KB" elif [[ $tdwbytes -ge 1 ]]; then tdw="<1 KB" else tdw=$Non_Exist_Value fi else tdw=$Non_Exist_Value fi ##### CUSTOM DRIVE HACK Section ##### # This will set the testAge value to 1 so it passes the math portion of the quality checks. if [[ $testAgeOvrd == "1" ]]; then testAge=1; fi ### WARRANTY DATE # Use Format: DriveWarranty="DriveSerialNumber YYYY-MM-DD," s="0" IFS=',' read -ra ADDR <<< "$Drive_Warranty_List" for i in "${ADDR[@]}"; do drivesn1="$(echo $i | cut -d':' -f 1)" drivedt1="$(echo $i | cut -d':' -f 2)" if [[ $drivesn1 == $serial ]]; then warrantyyear="$(echo $drivedt1 | cut -d '-' -f1)" warrantymonth="$(echo $drivedt1 | cut -d '-' -f2)" warrantyday="$(echo $drivedt1 | cut -d '-' -f3)" tempnow="$((`date +%s`))" if [[ $softver != "Linux" ]]; then warrantytemp="$((`date -j -v"$warrantyyear"y -v"$warrantymonth"m -v"$warrantyday"d +%s`))" else # Debian Date in seconds warrantytemp="$((`date -d "$drivedt1" +%s`))" fi warrantytemp="$((("$tempnow" - "$warrantytemp")/3600))" let waryrs=$((($warrantytemp / 8760))) let warmos=$(((($warrantytemp % 8760) / 730))) let wardys=$((((($warrantytemp % 8760) % 730) / 24))) let warhrs=$(((($warrantytemp % 8760) % 730) % 24)) let wardays=$((($warrantytemp / 24))) wartemp2=${wardays#-} wartemp3=${waryrs#-} if [[ $wartemp2 -gt 31 ]]; then if [[ $wartemp3 -gt 0 ]]; then wartext="${waryrs#-}y ${warmos#-}m ${wardys#-}d" else wartext="${warmos#-}m ${wardys#-}d" fi else wartext="${wardays#-}d" fi if [[ "$warrantytemp" > 0 ]]; then WarrantyClock=$wartext else WarrantyClock=${wartext#-} fi if [[ "$datestamp2" > "$drivedt1" ]]; then s="1" drivesn2=$drivesn1 drivedt2=$drivedt1 continue fi fi done if [[ "$WarrantyClock" == "" ]]; then WarrantyClock=$Non_Exist_Value fi if [[ $s != "0" ]]; then onTimeColor=$yellowColor if [[ $WarrantyBackgndColor != "none" ]]; then WarrantyBackgroundColor=$WarrantyBackgndColor; fi WarrantyBoxColor=$expiredWarrantyBoxColor logfile_warranty=$logfile_warranty"Drive "$drivesn2" Warranty Expired on "$drivedt2"
" fi ### SMART STATUS if [[ $smartStatus == "" || $smartStatus == "PASSED" || $smartStatus == "OK" ]]; then smartStatusColor=$okColor; else smartStatusColor=$critColor; fi if [[ $smartStatus == "" || $smartStatus == "PASSED" || $smartStatus == "OK" ]]; then a=1; else logfile_critical=$logfile_critical"$(printf "Drive "$device " - Check Smart Status
")"; fi if [[ $smartStatus == "" ]]; then smartStatus="$Non_Exist_Value"; fi ### BAD SECTORS s="0" IFS=',' read -ra ADDR <<< "$ReAllocated_Sector_List" for i in "${ADDR[@]}"; do badsectsn1="$(echo $i | cut -d':' -f 1)" badsectdt1="$(echo $i | cut -d':' -f 2)" if [[ $badsectsn1 == $serial ]]; then s="1" badsectsn2=$badsectsn1 badsectdt2=$badsectdt1 continue fi done if [[ $s != "0" ]]; then reAllocColor=$ovrdColor reAllocOrig=$reAlloc reAlloc=$(($reAlloc-$badsectdt2)) fi ### BAD SECTORS2 s="0" IFS=',' read -ra ADDR <<< "$ReAllocated_Sector_Events_List" for i in "${ADDR[@]}"; do badsectsn3="$(echo $i | cut -d':' -f 1)" badsectdt3="$(echo $i | cut -d':' -f 2)" if [[ $badsectsn3 == $serial ]]; then s="1" badsectsn4=$badsectsn3 badsectdt4=$badsectdt3 continue fi done if [[ $s != "0" ]]; then reAllocEventColor=$ovrdColor reAllocEventOrig=$reAllocEvent reAllocEvent=$(($reAllocEvent-$badsectdt4)) fi ########## TEMPERATURE SECTION ########## # LETS ZERO OUT BOGUS HIGH TEMPS and LOW TEMPS if [[ $temp != $Non_Exist_Value ]]; then if [[ $temp -gt 150 ]]; then temp="$Non_Exist_Value"; fi if [[ $temp -lt -60 ]]; then temp="$Non_Exist_Value"; fi fi ### TEMP for HDD if [[ $detail_level == "HDD" ]]; then if [[ $temp != "$Non_Exist_Value" ]]; then if [[ $temp -ge $HDDtempCritx ]]; then tempColor=$critColor; else if [[ $temp -ge $HDDtempWarnx ]]; then tempColor=$warnColor; fi; fi; fi if [[ $temp != "$Non_Exist_Value" ]]; then if [[ $temp -ge $HDDtempCritx ]]; then logfile_critical=$logfile_critical"$(printf "Drive "$serial" Critical Drive Temp "$temp" - Threshold = "$HDDtempCritx"
")"; else if [[ $temp -ge $HDDtempWarnx ]]; then logfile_warning=$logfile_warning"$(printf "Drive "$serial" High Drive Temp "$temp" - Threshold set at "$HDDtempWarnx"
")"; Temperature_Log=$Temperature_Log$"$(printf "Drive "$serial" High Temp "$temp"
")"; fi; fi; fi if [[ $HDD_Cur_Pwr_Max_Temp_Ovrd != "true" ]]; then if [[ $temp_max != "$Non_Exist_Value" ]]; then if [[ $temp_max -ge $HDDtempCritx ]]; then temp_maxColor=$critColor; else if [[ $temp_max -ge $HDDtempWarnx ]]; then temp_maxColor=$warnColor; fi; fi; fi if [[ $temp_max != "$Non_Exist_Value" ]]; then if [[ $temp_max -ge $HDDtempCritx ]]; then logfile_critical=$logfile_critical"$(printf "Drive "$serial" Critical Drive Temp "$temp_max" - Temp Max Threshold = "$HDDtempCritx"
")"; else if [[ $temp_max -ge $HDDtempWarnx ]]; then logfile_warning=$logfile_warning"$(printf "Drive "$serial" High Drive Temp "$temp_max" - Temp Max Threshold set at "$HDDtempWarnx"
")"; fi; fi; fi fi fi ### TEMP for SSD if [[ $detail_level == "SSD" ]]; then if [[ $temp != "$Non_Exist_Value" ]]; then if [[ $temp -ge $SSDtempCritx ]]; then tempColor=$critColor; else if [[ $temp -ge $SSDtempWarnx ]]; then tempColor=$warnColor; fi; fi; fi if [[ $temp != "$Non_Exist_Value" ]]; then if [[ $temp -ge $SSDtempCritx ]]; then logfile_critical=$logfile_critical"$(printf "Drive "$serial" Critical Drive Temp "$temp" - Threshold = "$SSDtempCritx"
")"; else if [[ $temp -ge $SSDtempWarnx ]]; then logfile_warning=$logfile_warning"$(printf "Drive "$serial" High Drive Temp "$temp" - Threshold set at "$SSDtempWarnx"
")"; Temperature_Log=$Temperature_Log"$(printf "Drive "$serial" High Temp "$temp"
")"; fi; fi; fi if [[ $SSD_Cur_Pwr_Max_Temp_Ovrd != "true" ]]; then if [[ $temp_max != "$Non_Exist_Value" ]]; then if [[ $temp_max -ge $SSDtempCritx ]]; then temp_maxColor=$critColor; else if [[ $temp_max -ge $SSDtempWarnx ]]; then temp_maxColor=$warnColor; fi; fi; fi if [[ $temp_max != "$Non_Exist_Value" ]]; then if [[ $temp_max -ge $SSDtempCritx ]]; then logfile_critical=$logfile_critical"$(printf "Drive "$serial" Critical Drive Temp "$temp_max" - Threshold = "$SSDtempCritx"
")"; else if [[ $temp_max -ge $SSDtempWarnx ]]; then logfile_warning=$logfile_warning"$(printf "Drive "$serial" High Drive Temp "$temp_max" - Temp Max Threshold set at "$SSDtempWarnx"
")"; fi; fi; fi fi fi ### TEMP for NVM if [[ $detail_level == "NVM" ]]; then if [[ $temp != "$Non_Exist_Value" ]]; then if [[ $temp -ge $NVMtempCritx ]]; then tempColor=$critColor; else if [[ $temp -ge $NVMtempWarnx ]]; then tempColor=$warnColor; fi; fi; fi if [[ $temp != "$Non_Exist_Value" ]]; then if [[ $temp -ge $NVMtempCritx ]]; then logfile_critical=$logfile_critical"$(printf "Drive "$serial" Critical Drive Temp "$temp" - Threshold = "$NVMtempCritx"
")"; else if [[ $temp -ge $NVMtempWarnx ]]; then logfile_warning=$logfile_warning"$(printf "Drive "$serial" High Drive Temp "$temp" - Threshold set at "$NVMtempWarnx"
")"; Temperature_Log=$Temperature_Log"$(printf "Drive "$serial" High Temp "$temp"
")"; fi; fi; fi ### TEMP_MAX for NVM if [[ $NVM_Cur_Pwr_Max_Temp_Ovrd != "true" ]]; then if [[ $temp_max != "$Non_Exist_Value" ]]; then if [[ $temp_max -ge $NVMtempCritx ]]; then temp_maxColor=$critColor; else if [[ $temp_max -ge $NVMtempWarnx ]]; then temp_maxColor=$warnColor; fi; fi; fi if [[ $temp_max != "$Non_Exist_Value" ]]; then if [[ $temp_max -ge $NVMtempCritx ]]; then logfile_critical=$logfile_critical"$(printf "Drive "$serial" Critical Drive Temp "$temp_max" - Threshold = "$NVMtempCritx"
")"; else if [[ $temp_max -ge $NVMtempWarnx ]]; then logfile_warning=$logfile_warning"$(printf "Drive "$serial" High Drive Temp "$temp_max" - Threshold set at "$NVMtempWarnx"
")"; fi; fi; fi fi fi # NVM CRITICAL WARNING if [[ $detail_level == "NVM" ]]; then NVMcriticalWarningColor="$okColor" if [[ $NVMcriticalWarning != "0" ]]; then NVMcriticalWarning="0"; fi if [[ $NVMcriticalWarning != "" ]]; then if [[ $NVMcriticalWarning != "0" ]]; then logfile_critical=$logfile_critical"$(printf "Drive "$serial" NVM Critical Warning "$NVMcriticalWarning"
")"; fi; fi if [[ $NVMcriticalWarning != "" ]]; then if [[ $NVMcriticalWarning != "0" ]]; then NVMcriticalWarningColor=$critColor; fi; fi if [[ $NVMcriticalWarning != "" ]]; then if [[ $NVMcriticalWarning != "0" ]]; then NVMcriticalWarning="CRITICAL FAILURE"; fi; fi if [[ $NVMcriticalWarning == "0" ]]; then NVMcriticalWarning="GOOD"; fi if [[ $NVMcriticalWarning == "" ]]; then NVMcriticalWarning="$Non_Exist_Value"; fi if [[ $mediaErrors != "" ]]; then if [[ $mediaErrors -ge $NVM_Media_Errors ]]; then mediaErrorsColor=$critColor logfile_critical=$logfile_critical"$(printf "Drive "$serial" - Media Errors
")" fi fi fi if [[ $detail_level == "HDD" ]]; then # Helium Critical Warning if [[ $Helium == "" ]]; then Helium="$Non_Exist_Value"; HeliumColor="$bgColor"; fi if [[ $Helium -ge $HeliumMinx ]] || [[ "$Helium" == "$Non_Exist_Value" ]]; then HeliumColor="$bgColor" else if [[ $HeliumAlarm == "true" ]]; then HeliumColor="$critColor" logfile_critical=$logfile_critical"$(printf "Drive "$serial" Helium Critical Warning - Value "$Helium"
")" fi fi > /dev/null 2>&1 fi if [[ $rotation == "" ]]; then rotation="$Non_Exist_Value"; fi if [[ $capacity == "" ]]; then capacity="$Non_Exist_Value"; fi ########## PROCESSING THAT AFFECTS EVERYTHING ########## ### SPINRETRY if [[ $spinRetry != "" ]]; then if [[ $spinRetry != "0" ]]; then spinRetryColor=$critColor; fi; fi if [[ $spinRetry != "" ]]; then if [[ $spinRetry != "0" ]]; then logfile_critical=$logfile_critical"$(printf "Drive "$serial" Spin Retry "$spinRetry" - Threshold = 0
")"; fi; fi if [[ $spinRetry == "" ]]; then spinRetry="$Non_Exist_Value"; fi ### REALLOC and REALLOCEVENT if [[ $reAlloc != "" ]]; then if [[ $(($reAlloc + 0)) -gt $SectorsCritx ]]; then reAllocColor=$critColor; else if [[ $(($reAlloc + 0)) -gt $SectorsWarnx ]]; then reAllocColor=$warnColor; fi; fi; fi if [[ $reAlloc != "" ]]; then if [[ $(($reAlloc + 0)) -gt $SectorsCritx ]]; then logfile_critical=$logfile_critical"$(printf "Drive "$serial" Critical Sectors "$reAlloc" - Threshold = "$SectorsCritx"
")"; else if [[ $(($reAlloc + 0)) -gt $SectorsWarnx ]]; then logfile_warning=$logfile_warning"$(printf "Drive "$serial" Warning Sectors "$reAlloc" - Threshold = "$SectorsWarnx"
")"; fi; fi; fi if [[ $reAlloc == "" ]]; then reAlloc="$Non_Exist_Value"; fi if [[ $reAllocEvent != "" ]]; then if [[ $(($reAllocEvent + 0)) -gt $SectorsCritx ]]; then reAllocEventColor=$critColor; else if [[ $(($reAllocEvent + 0)) -gt $SectorsWarnx ]]; then reAllocEventColor=$warnColor; fi; fi; fi if [[ $reAllocEvent != "" ]]; then if [[ $(($reAllocEvent + 0)) -gt $SectorsCritx ]]; then logfile_critical=$logfile_critical"$(printf "Drive "$serial" Critical Sectors "$reAllocEvent" - Threshold = "$SectorsCritx"
")"; else if [[ $(($reAllocEvent + 0)) -gt $SectorsWarnx ]]; then logfile_warning=$logfile_warning"$(printf "Drive "$serial" Warning Sectors "$reAllocEvent" - Threshold = "$SectorsWarnx"
")"; fi; fi; fi if [[ $reAllocEvent == "" ]]; then reAllocEvent="$Non_Exist_Value"; fi ### PENDING SECTORS if [[ $pending != "" ]]; then if [[ $(($pending + 0)) -gt $SectorsCritx ]]; then pendingColor=$critColor; else if [[ $(($pending + 0)) -gt $SectorsWarnx ]]; then pendingColor=$warnColor; fi; fi; fi if [[ $pending != "" ]]; then if [[ $(($pending + 0)) -gt $SectorsCritx ]]; then logfile_critical=$logfile_critical"$(printf "Drive "$serial" Sector Errors "$pending" - Threshold = "$SectorsCritx"
")"; else if [[ $(($pending + 0)) -gt $SectorsWarnx ]]; then logfile_warning=$logfile_warning"$(printf "Drive "$serial" Sector Errors "$pending" - Threshold = "$SectorsWarnx"
")"; fi; fi; fi if [[ $pending == "" ]]; then pending="$Non_Exist_Value"; fi ### OFFLINE UNCORRECTABLE SECTORS if [[ $offlineUnc != "" ]]; then if [[ $(($offlineUnc + 0)) > $SectorsCritx ]]; then offlineUncColor=$critColor; else if [[ $offlineUnc != 0 ]]; then offlineUncColor=$warnColor; fi; fi; fi if [[ $offlineUnc != "" ]]; then if [[ $(($offlineUnc + 0)) > $SectorsCritx ]]; then logfile_critical=$logfile_critical"$(printf "Drive "$serial" Uncorrectable Errors "$offlineUnc"
")"; else if [[ $(($offlineUnc + 0)) -gt $SectorsWarnx ]]; then logfile_warning=$logfile_warning"$(printf "Drive "$serial" Uncorrectable Errors "$offlineUnc" - Threshold = "$SectorsWarnx"
")";fi; fi; fi if [[ $offlineUnc == "" ]]; then offlineUnc="$Non_Exist_Value"; fi ### CRC ERRORS if [[ $crcErrors != "" ]]; then if [[ $crcErrors != "0" ]]; then crcErrorsColor=$critColor; fi; fi if [[ $crcErrors == "" ]]; then crcErrors="$Non_Exist_Value"; fi ### SMARTTESTING if [[ $smarttesting -gt 0 ]]; then lastTestType="$smarttesting% Remaining"; fi ### CHKREADFAILURE if [[ $lastTestStatusPass != "true" ]]; then lastTestType=$lastTestStatus; lastTestTypeColor=$critColor; logfile_critical=$logfile_critical"$(printf "Drive "$serial" Read Failure "$chkreadfailure"
")"; else lastTestTypeColor=$bgColor; fi # if [[ $chkreadfailure == "Completed:" ]]; then lastTestType="Read Failure"; lastTestTypeColor=$critColor; logfile_critical=$logfile_critical"$(printf "Drive "$serial" Read Failure "$chkreadfailure"
")"; else lastTestTypeColor=$bgColor; fi #if [[ $chkreadfailure == "Completed:" ]]; then logfile_critical=$logfile_critical"$(printf "Drive "$serial" Read Failure "$chkreadfailure"
")"; fi ### SEEK ERRORS # If seekErrorHealth RAW_VALUE is some crazy number, use the VALUE column data. # Seek Error Rate fix for Seagate Drives # We use seekErrorHealth for the Seagate Rate, and seekErrorHealth2 for the Normalized Rate if we must. if [[ $seekErrorHealth -gt 0 ]]; then # Lets see if this is a NORMAL drive, not reporting crazy ass numbers. if [[ $seekErrorHealth -lt $SeekErrorsWarnx ]] && [[ $seekErrorHealth -le 4294967295 ]]; then seek="done"; fi if [[ $seekErrorHealth -ge $SeekErrorsWarnx ]] && [[ $seekErrorHealth -lt $SeekErrorsCritx ]]; then seekErrorHealthColor=$warnColor seek="done" fi if [[ $seekErrorHealth -ge $SeekErrorsCritx ]] && [[ $seekErrorHealth -le 500 ]]; then seekErrorHealthColor=$critColor seek="done" fi # If the count is above the Seagate FFFFFFFF value, divide it out or if below FFFFFFFF then make value zero. if [[ $seekErrorHealth -lt 4294967295 ]] && [[ $seek != "done" ]]; then seekErrorHealth=0; fi if [[ $seekErrorHealth -ge 4294967295 ]]; then seekErrorHealth=$(($seekErrorHealth / 4294967295)); fi if [[ $IgnoreSeekError != "true" ]]; then if [[ $(($seekErrorHealth + 0)) -gt $SeekErrorsCritx ]]; then seekErrorHealthColor=$critColor logfile_critical=$logfile_critical"$(printf "Drive "$serial" Seek Errors "$seekErrorHealth" - Threshold = "$SeekErrorsCritx"
")" else if [[ $(($seekErrorHealth + 0)) -gt $SeekErrorsWarnx ]]; then seekErrorHealthColor=$warnColor logfile_warning=$logfile_warning"$(printf "Drive "$serial" Seek Errors "$seekErrorHealth" - Threshold = "$SeekErrorsWarnx"
")" fi fi fi fi seek="" ### Raw Read Error Rate # If seekErrorHealth RAW_VALUE is some crazy number, use the VALUE column data. # Raw Read Error Rate fix for Seagate Drives # We use rawReadErrorRate for the Seagate Rate, and rawReadErrorRate2 for the Normalized Rate if we must. if [[ $rawReadErrorRate -gt 0 ]] && [[ $rotation == "0" ]]; then rawReadErrorRate=""; fi if [[ $rawReadErrorRate -gt 0 ]]; then # Lets see if this is a NORMAL drive, not reporting crazy ass numbers. if [[ $rawReadErrorRate -lt $RawReadWarnx ]] && [[ $rawReadErrorRate -le 4294967295 ]]; then seek="done"; fi if [[ $rawReadErrorRate -ge $RawReadWarnx ]] && [[ $rawReadErrorRate -lt $RawReadCritx ]]; then rawReadErrorRateColor=$warnColor seek="done" fi if [[ $rawReadErrorRate -ge $RawReadCritx ]] && [[ $rawReadErrorRate -le 500 ]]; then rawReadErrorRateColor=$critColor seek="done" fi # If the count is above the Seagate FFFFFFFF value, divide it out or if below FFFFFFFF then make value zero if [[ $rawReadErrorRate -lt 4294967295 ]] && [[ $seek != "done" ]]; then rawReadErrorRate=0; fi if [[ $rawReadErrorRate -ge 4294967295 ]]; then rawReadErrorRate=$(($rawReadErrorRate / 4294967295)); fi if [[ $IgnoreReadError != "true" ]]; then if [[ $(($rawReadErrorRate + 0)) -ge $RawReadCritx ]]; then rawReadErrorRateColor=$critColor logfile_critical=$logfile_critical"$(printf "Drive "$serial" Raw Read Error Rate "$rawReadErrorRate" - Threshold = "$RawReadCritx"
")" else if [[ $(($rawReadErrorRate + 0)) -ge $RawReadWarnx ]]; then rawReadErrorRateColor=$warnColor logfile_warning=$logfile_warning"$(printf "Drive "$serial" Raw Read Error Rate "$rawReadErrorRate" - Threshold = "$RawReadWarnx"
")" fi fi fi fi seek="" wearLevelColor=$bgColor if [[ $multiZone == "" ]]; then multiZone="$Non_Exist_Value"; fi if [[ $wearLevel == "" || $wearLevel == "0" ]]; then wearLevel="$Non_Exist_Value"; else wearLevel=$(($wearLevel + 0)); fi if [[ $wearLevel != "$Non_Exist_Value" ]]; then if [[ $wearLevel -lt $WearLevelCrit ]]; then wearLevelColor=$warnColor; logfile_warning=$logfile_warning"$(printf "Drive: "$serial" - Wear Level = "$wearLevel"%%
")"; fi; fi if [[ $modelnumber == "" ]]; then modelnumber="$Non_Exist_Value"; fi if [[ $startStop == "" ]]; then startStop="$Non_Exist_Value"; fi if [[ $loadCycle == "" ]]; then loadCycle="$Non_Exist_Value"; fi if [[ $seekErrorHealth == "" ]]; then seekErrorHealth="$Non_Exist_Value"; fi if [[ $rawReadErrorRate == "" ]]; then rawReadErrorRate="$Non_Exist_Value"; fi if [[ $Helium == "" ]]; then Helium="$Non_Exist_Value"; fi if [[ $mediaErrors == "" ]]; then mediaErrors="$Non_Exist_Value"; fi ########## WRITE STATISTICAL DATA ########## # Save Statistical Data before we make any changes to it. if [[ $SDF_DataRecordEnable == "true" && $writing_data != "1" ]]; then writing_data=1 fi tempdrive=$drive # Replace Device ID in spreadsheet with TEST so we can identify test data and use the Purge routine. if [[ $testfilepath != "" ]]; then drive="TEST"; fi if [[ $SDF_DataRecordEnable == "true" ]] && [[ $Write_Statistics == "true" ]]; then printf $datestamp","$timestamp","$drive","$detail_level","$serial","$smartStatus","$temp","$onHours","$wearLevel","$startStop","$loadCycle","$spinRetry","$reAlloc","$reAllocEvent","$pending","$offlineUnc","$crcErrors","$seekErrorHealth","$multiZone","$rawReadErrorRate","$Helium",\n" >> "$statistical_data_file";fi drive="$tempdrive" ### Routine to zero out the UDMA CRC Error Count and Highlights it Yellow. if [[ $External_Config == "no" ]]; then CRC_Errors_List=$CRC_Errors_List; MultiZone_List=$MultiZone_List; ReAllocated_Sector_List=$ReAllocated_Sector_List; Drive_Warranty_List=$Drive_Warranty_List; fi s="0" IFS=',' read -ra ADDR <<< "$CRC_Errors_List" for i in "${ADDR[@]}"; do crc_errsn1="$(echo $i | cut -d':' -f 1)" crc_errst1="$(echo $i | cut -d':' -f 2)" if [[ $crc_errsn1 == $serial ]]; then s="1" crc_errsn2=$crc_errsn1 crc_errst2=$crc_errst1 continue fi done if [[ $s != "0" ]]; then crcErrorsColor=$ovrdColor crcErrorsOrig=$crcErrors crcErrors=$(($crcErrors-$crc_errst2)) fi s="0" IFS=',' read -ra ADDR <<< "$MultiZone_List" for i in "${ADDR[@]}"; do badsectsn1="$(echo $i | cut -d':' -f 1)" badsectdt1="$(echo $i | cut -d':' -f 2)" if [[ $badsectsn1 == $serial ]]; then s="1" badsectsn2=$badsectsn1 badsectdt2=$badsectdt1 continue fi done if [[ $s != "0" ]]; then multiZoneColor=$ovrdColor multiZoneOrig=$multiZone multiZone=$(($multiZone-$badsectdt2)) fi if [[ $IgnoreMultiZone != "true" ]]; then if [[ $multiZone != "$Non_Exist_Value" ]]; then if [[ $multiZone -gt $MultiZoneWarnx ]]; then multiZoneColor=$warnColor; logfile_warning=$logfile_warning"$(printf "Drive: "$serial" - MultiZone Errors = "$multiZone"
")"; fi; fi; fi if [[ $IgnoreMultiZone != "true" ]]; then if [[ $multiZone != "$Non_Exist_Value" ]]; then if [[ $multiZone -gt $MultiZoneCritx ]]; then multiZoneColor=$critColor; logfile_critical=$logfile_critical"$(printf "Drive: "$serial" - MultiZone Errors = "$multiZone"
")";fi ;fi ;fi if [[ $IgnoreUDMA != "true" ]]; then if [[ $crcErrors != "$Non_Exist_Value" ]]; then if [[ $crcErrors != "0" ]]; then logfile_critical=$logfile_critical"$(printf "Drive "$serial" CRC Errors "$crcErrors"
")";fi; fi; fi if [[ $testAge -ge $TestWarnAgex ]]; then testAgeColor=$warnColor; else testAgeColor=$bgColor; fi if [[ $testAge -ge $TestWarnAgex ]]; then logfile_warning=$logfile_warning"$(printf "Drive: "$serial" - Test Age = "$testAge" Days
")"; fi if [[ $lastTestHours == "0" || $lastTestHours == "" ]]; then testAge=$Non_Exist_Value; fi ########## DEVICE ID WARNING COLOR ########## if [[ $DeviceRedFlag == "true" ]]; then if [[ $deviceStatusColor != $critColor ]]; then if [[ $deviceStatusColor != $warnColor ]]; then deviceStatusColor=$HeliumColor; fi; fi; fi if [[ $DeviceRedFlag == "true" ]]; then if [[ $deviceStatusColor != $critColor ]]; then if [[ $deviceStatusColor != $warnColor ]]; then deviceStatusColor=$reAllocColor; fi; fi; fi if [[ $DeviceRedFlag == "true" ]]; then if [[ $deviceStatusColor != $critColor ]]; then if [[ $deviceStatusColor != $warnColor ]]; then deviceStatusColor=$reAllocEventColor; fi; fi; fi if [[ $DeviceRedFlag == "true" ]]; then if [[ $deviceStatusColor != $critColor ]]; then if [[ $deviceStatusColor != $warnColor ]]; then deviceStatusColor=$crcErrorsColor; fi; fi; fi if [[ $DeviceRedFlag == "true" ]]; then if [[ $deviceStatusColor != $critColor ]]; then if [[ $deviceStatusColor != $warnColor ]]; then deviceStatusColor=$pendingColor; fi; fi; fi if [[ $DeviceRedFlag == "true" ]]; then if [[ $deviceStatusColor != $critColor ]]; then if [[ $deviceStatusColor != $warnColor ]]; then deviceStatusColor=$offlineUncColor; fi; fi; fi if [[ $DeviceRedFlag == "true" ]]; then if [[ $deviceStatusColor != $critColor ]]; then if [[ $deviceStatusColor != $warnColor ]]; then deviceStatusColor=$seekErrorHealthColor; fi; fi; fi if [[ $DeviceRedFlag == "true" ]]; then if [[ $deviceStatusColor != $critColor ]]; then if [[ $deviceStatusColor != $warnColor ]]; then deviceStatusColor=$rawReadErrorRateColor; fi; fi; fi if [[ $DeviceRedFlag == "true" ]]; then if [[ $deviceStatusColor != $critColor ]]; then if [[ $deviceStatusColor != $warnColor ]]; then deviceStatusColor=$testAgeColor; fi; fi; fi if [[ $DeviceRedFlag == "true" ]]; then if [[ $deviceStatusColor != $critColor ]]; then if [[ $deviceStatusColor != $warnColor ]]; then deviceStatusColor=$multiZoneColor; fi; fi; fi if [[ $DeviceRedFlag == "true" ]]; then if [[ $deviceStatusColor != $critColor ]]; then if [[ $deviceStatusColor != $warnColor ]]; then deviceStatusColor=$wearLevelColor; fi; fi; fi if [[ $DeviceRedFlag == "true" ]]; then if [[ $deviceStatusColor != $critColor ]]; then if [[ $deviceStatusColor != $warnColor ]]; then deviceStatusColor=$spinRetryColor; fi; fi; fi if [[ $DeviceRedFlag == "true" ]]; then if [[ $deviceStatusColor != $critColor ]]; then if [[ $deviceStatusColor != $warnColor ]]; then deviceStatusColor=$temp_maxColor; fi; fi; fi if [[ $DeviceRedFlag == "true" ]]; then if [[ $deviceStatusColor != $critColor ]]; then if [[ $deviceStatusColor != $warnColor ]]; then deviceStatusColor=$tempColor; fi; fi; fi if [[ $smartStatusColor != $okColor ]]; then if [[ $DeviceRedFlag == "true" ]]; then if [[ $deviceStatusColor != $critColor ]]; then if [[ $deviceStatusColor != $warnColor ]]; then deviceStatusColor=$smartStatusColor; fi; fi; fi; fi if [[ $DeviceRedFlag == "true" ]]; then if [[ $deviceStatusColor != $critColor ]]; then if [[ $deviceStatusColor != $warnColor ]]; then deviceStatusColor=$mediaErrorsColor; fi; fi; fi # SCT Error Recovery Control Report scterc="$(smartctl -l scterc /dev/"$drive" | tail -3 | head -2)" if [[ $SCT_Warning_Level == "TLER" ]]; then # Warning Level TLER = Ignore Drives that do not report "seconds" or "Disable" # Warning Level TLER_No_Msg = same as above but will not report TLER disabled message until after trying to set TLER fails. if [[ $scterc =~ "Disabled" ]]; then logfile_warning=$logfile_warning"$(printf "Drive "$serial" TLER is Disabled
")"; fi fi if [[ $SCT_Warning_Level == "TLER_No_Msg" && $SCT_Enable == "true" ]]; then if [[ $scterc =~ "Disabled" ]]; then # Now we set the TLER ONLY for Disabled Drives because we do not know how it will affect other drives. smartctl -l scterc,"$SCT_Read_Timeout","$SCT_Write_Timeout" /dev/"$drive" > /dev/null 2>&1 scterc="$(smartctl -l scterc /dev/"$drive" | tail -3 | head -2)" if [[ $scterc =~ "seconds" ]]; then logfile_messages=$logfile_messages"$(printf "Drive "$serial" TLER is NOW ENABLED !
")"; fi if [[ $scterc =~ "Disabled" ]]; then logfile_warning=$logfile_warning"$(printf "Drive "$serial" TLER is Disabled and failed to set.
")"; fi fi fi if [[ $SCT_Warning_Level == "all" ]]; then if [[ $scterc =~ "Disabled" ]]; then logfile_warning=$logfile_warning"$(printf "Drive "$serial" TLER is Disabled
")" else if [[ ! $scterc =~ "seconds" ]]; then logfile_warning=$logfile_warning"$(printf "Drive "$serial" TLER is Unsupported
")"; fi fi fi if [[ $SCT_Enable == "true" ]]; then if [[ $scterc =~ "Disabled" ]]; then # Now we set the TLER ONLY for Disabled Drives because we do not know how it will affect other drives. smartctl -l scterc,"$SCT_Read_Timeout","$SCT_Write_Timeout" /dev/"$drive" > /dev/null 2>&1 scterc="$(smartctl -l scterc /dev/"$drive" | tail -3 | head -2)" if [[ $SCT_Warning_Level == "all" || $SCT_Warning_Level == "TLER" ]]; then logfile_warning=$logfile_warning"$(printf "Drive "$serial" TLER is NOW ENABLED !
")"; fi if [[ $scterc =~ "Disabled" ]]; then if [[ $SCT_Warning_Level == "all" || SCT_Warning_Level == "TLER" ]]; then logfile_warning=$logfile_warning"$(printf "Drive "$serial" TLER is Disabled and failed to set.
")"; fi fi fi fi # This section will change the testAge value to the Non_Exist_Value for proper display in the chart. Displaying a bogus "1" is misleading. if [[ $testAgeOvrd == "1" ]]; then testAge=$Non_Exist_Value; fi } ########## UPDATE CONFIG FILE ########## ### This will generate or update a configuration file with any new changes. ### The customizations will remain until the end of a major version number and then ### be removed in order to support the next major version number. ### Current updates incorporated to support version 2.1.x to 3.0.x update_config_file () { # Lets backup the current config file and send to the user. cp "$Config_File_Name" /tmp/Old_multi_report_config.txt ( # Lets convert any old variables into new variables if ! [[ $email == "" ]]; then Email=$email; fi if ! [[ $from == "" ]]; then From=$from; fi if ! [[ $MonitorEmail == "" ]]; then AlertEmail=$MonitorEmail; fi if ! [[ $urgentemail == "" ]]; then AlertEmail=$urgentemail; fi if ! [[ $usedWarn == "" ]]; then PoolUsedWarn=$usedWarn; fi if ! [[ $scrubAgeWarn == "" ]]; then ScrubAgeWarn=$scrubAgeWarn; fi if ! [[ $HDDmaxovrd == "" ]]; then HDD_Cur_Pwr_Max_Temp_Ovrd=$HDDmaxovrd; fi if ! [[ $SSDmaxovrd == "" ]]; then SSD_Cur_Pwr_Max_Temp_Ovrd=$SSDmaxovrd; fi if ! [[ $NVMmaxovrd == "" ]]; then NVM_Cur_Pwr_Max_Temp_Ovrd=$NVMmaxovrd; fi if ! [[ $wearLevelCrit == "" ]]; then WearLevelCrit=$wearLevelCrit; fi if ! [[ $powerTimeFormat == "" ]]; then PowerTimeFormat="$powerTimeFormat"; fi if ! [[ $tempdisplay == "" ]]; then TempDisplay=$tempdisplay; fi if ! [[ $Non_Exist_Value == "" ]]; then Non_Exist_Value=$Non_Exist_Value; fi if ! [[ $pool_capacity == "" ]]; then Pool_Capacity=$pool_capacity; fi if ! [[ $ignoreUDMA == "" ]]; then IgnoreUDMA=$ignoreUDMA; fi if ! [[ $ignoreSeekError == "" ]]; then IgnoreSeekError=$ignoreSeekError; fi if ! [[ $ignoreReadError == "" ]]; then IgnoreReadError=$ignoreReadError; fi if ! [[ $ignoreMultiZone == "" ]]; then IgnoreMultiZone=$ignoreMultiZone; fi if ! [[ $disableWarranty == "" ]]; then DisableWarranty=$disableWarranty; fi if ! [[ $ata_auto_enable == "" ]]; then ATA_Auto_Enable=$ata_auto_enable; fi if ! [[ $includeSSD == "" ]]; then IncludeSSD=$includeSSD; fi if ! [[ $includeNVM == "" ]]; then IncludeSSD=$includeNVM; fi if ! [[ $reportnonSMART == "" ]]; then ReportNonSMART=$reportnonSMART; fi if ! [[ $disableRAWdata == "" ]]; then DisableRAWdata=$disableRAWdata; fi if ! [[ $sectorsWarn == "" ]]; then SectorsWarn=$sectorsWarn; fi if ! [[ $sectorsCrit == "" ]]; then SectorsCrit=$sectorsCrit; fi if ! [[ $reAllocWarn == "" ]]; then ReAllocWarn=$reAllocWarn; fi if ! [[ $multiZoneWarn == "" ]]; then MultiZoneWarn=$multiZoneWarn; fi if ! [[ $multiZoneCrit == "" ]]; then MultiZoneCrit=$multiZoneCrit; fi if ! [[ $deviceRedFlag == "" ]]; then DeviceRedFlag=$deviceRedFlag; fi if ! [[ $heliumAlarm == "" ]]; then HeliumAlarm=$heliumAlarm;fi if ! [[ $heliumMin == "" ]]; then HeliumMin=$heliumMin;fi if ! [[ $rawReadWarn == "" ]]; then RawReadWarn=$rawReadWarn; fi if ! [[ $rawReadCrit == "" ]]; then RawReadCrit=$rawReadCrit; fi if ! [[ $seekErrorsWarn == "" ]]; then SeekErrorsWarn=$seekErrorsWarn; fi if ! [[ $seekErrorsCrit == "" ]]; then SeekErrorsCrit=$seekErrorsCrit; fi if ! [[ $SCT_Drive_Enable == "" ]]; then SCT_Enable=$SCT_Drive_Enable; fi if ! [[ $SCT_Warning == "" ]]; then SCT_Warning_Level=$SCT_Warning; fi if ! [[ $testAgeWarn == "" ]]; then TestWarnAge=$testAgeWarn; fi if ! [[ $TestAgeWarn == "" ]]; then TestWarnAge=$TestAgeWarn; fi if ! [[ $zpoolFragWarn == "" ]]; then ZpoolFragWarn=$zpoolFragWarn; fi if ! [[ $enable_text == "" ]]; then Enable_Text_Section=$enable_text; fi if ! [[ $expDataEnable == "" ]]; then SDF_DataRecordEnable=$expDataEnable; fi if ! [[ $expDataPurge == "" ]]; then SDF_DataPurgeDays=$expDataPurge; fi if ! [[ $expDataEmail == "" ]]; then SDF_DataEmail=$expDataEmail; fi if ! [[ $expDataEmailSend == "" ]]; then SDF_DataEmailDay=$expDataEmailSend; fi if ! [[ $configBackup == "" ]]; then TrueNASConfigEmailEnable=$configBackup; fi if ! [[ $configSendDay == "" ]]; then TrueNASConfigEmailDay=$configSendDay; fi if ! [[ $saveBackup == "" ]]; then TrueNASConfigBackupSave=$saveBackup; fi if ! [[ $backupLocation == "" ]]; then TrueNASConfigBackupLocation=$backupLocation; fi if ! [[ $Config_Email_Enable == "" ]]; then MRConfigEmailEnable=$Config_Email_Enable; fi if ! [[ $Config_Changed_Email == "" ]]; then MRChangedEmailSend=$Config_Changed_Email; fi if ! [[ $Config_Backup_Day == "" ]]; then MRConfigEmailDay=$Config_Backup_Day; fi if ! [[ $CRC_ERRORS == "" ]]; then CRC_Errors_List=$CRC_ERRORS; fi if ! [[ $CRC_Errors == "" ]]; then CRC_Errors_List=$CRC_Errors; fi if ! [[ $Ignore_Drives == "" ]]; then Ignore_Drives_List=$Ignore_Drives; fi if [[ $Ignore_Drives_List == "VMWare,1JUMLBD,21HNSAFC21410E" ]]; then Ignore_Drives_List="none"; fi if ! [[ $MULTI_Zone == "" ]]; then MultiZone_List=$MULTI_Zone; fi if ! [[ $Multi_Zone == "" ]]; then MultiZone_List=$Multi_Zone; fi if ! [[ $BAD_SECTORS == "" ]]; then ReAllocated_Sector_List=$BAD_SECTORS; fi if ! [[ $Bad_Sectors == "" ]]; then ReAllocated_Sector_List=$Bad_Sectors; fi if ! [[ $Bad_Sectors2 == "" ]]; then ReAllocated_Sector_Events_List=$Bad_Sectors2; fi if ! [[ $ata_errors == "" ]]; then ATA_Errors_List=$ata_errors; fi if ! [[ $Custom_Drives == "" ]]; then Custom_Drives_List=$Custom_Drives; fi if ! [[ $DRIVE_WARRANTY == "" ]]; then Drive_Warranty_List=$DRIVE_WARRANTY; fi # Remove any early entries not on Joe's system. if ! [[ $Joes_System == "true" ]] && [[ $Drive_Warranty_List == "K1JUMLBD:2020-09-30,K1JRSWLD:2020-09-30,K1JUMW4D:2020-09-30,K1GVD84B:2020-10-12" ]]; then Drive_Warranty_List=""; fi if ! [[ $Drive_Warranty == "" ]]; then Drive_Warranty_List=$Drive_Warranty; fi if [[ $Drive_Warranty_List == "none" ]]; then Drive_Warranty_List=""; fi if [[ $SSD_Capacity_Title == "HDD Capacity" ]]; then SSD_Capacity_Title="SSD Capacity"; fi if [[ $NVM_Capacity_Title == "HDD Capacity" ]]; then NVM_Capacity_Title="NVMe Capacity"; fi # Lets write the configuration file. echo "#" $programver echo "#" echo "# This file is used exclusively to configure the multi_report version 2.1 or later." echo "#" echo "# The configuration file will be created in the same directory as the script." echo "#" echo "# The configuration file will override the default values coded into the script." echo " " echo "###### Email Address ######" echo "# Enter your Email address to send the report to. The from address does not need to be changed unless you experience" echo "# an error sending the email. Some email servers only use the email address associated with the email server." echo " " echo 'Email="'$Email'" # Send normal emails to this address' echo 'From="'$From'" # From Address' echo " " echo "### Alert Email Configuration ###" echo 'AlertEmail="'$AlertEmail'" # Alert email address used with the '-m' switch.' echo 'AlertOnWarningTemp="'$AlertOnWarningTemp'" # Send alert on Warning Temp. Default="true"' echo 'AlertOnCriticalError="'$AlertOnCriticalError'" # Send alert on Critical Error. Default="true"' echo " " echo "###### Spencer Integration" echo "# Warning Levels are: None, Warning, Critical -- This only affects the Email Subject Line, if any errors are present, an attachment will occur." echo 'spencer_new_warning_level="'$spencer_new_warning_level'" # What to do if a "new" error occurs.' echo 'spencer_existing_warning_level="'$spencer_existing_warning_level'" # What to do for an existing error.' echo 'spencer_enable="'$spencer_enable'" # To call the Spencer.py script if "true" or "false" to not run the Spencer.py script.' echo 'spencer_script_name="'$spencer_script_name'" # The default is "spencer.py" located in the default script directory.' echo " " echo "########## Script Update ##########" echo "# Ensure you understand these options." echo 'CheckForUpdates="'$CheckForUpdates'" # Will check GitHub for updates and include message in next email.' echo 'AutomaticUpdate="'$AutomaticUpdate'" # WARNING !!! This option will automatically update the script if a newer version exists on GitHub.' echo " " echo "###### Zpool Status Summary Table Settings" echo 'PoolUsedWarn='$PoolUsedWarn' # Pool used percentage for CRITICAL color to be used.' echo 'ScrubAgeWarn='$ScrubAgeWarn' # Maximum age (in days) of last pool scrub before CRITICAL error (30 + 7 days for day of week). Default=37.' echo " " echo "###### Temperature Settings" echo "HDDtempWarn=$HDDtempWarn # HDD Drive temp (in C) when WARNING message will be used." echo "HDDtempCrit=$HDDtempCrit # HDD Drive temp (in C) when CRITICAL message will be used." echo 'HDD_Cur_Pwr_Max_Temp_Ovrd="'$HDD_Cur_Pwr_Max_Temp_Ovrd'" # HDD Max Drive Temp Override. This value when "true" will not alarm on any Current Power Cycle Max Temperature Limit.' echo "SSDtempWarn=$SSDtempWarn # SSD Drive temp (in C) when WARNING message will be used." echo "SSDtempCrit=$SSDtempCrit # SSD Drive temp (in C) when CRITICAL message will be used." echo 'SSD_Cur_Pwr_Max_Temp_Ovrd="'$SSD_Cur_Pwr_Max_Temp_Ovrd'" # SSD Max Drive Temp Override. This value when "true" will not alarm on any Current Power Cycle Max Temperature Limit.' echo "NVMtempWarn=$NVMtempWarn # NVM Drive temp (in C) when WARNING message will be used." echo "NVMtempCrit=$NVMtempCrit # NVM Drive temp (in C) when CRITICAL message will be used." echo 'NVM_Cur_Pwr_Max_Temp_Ovrd="'$NVM_Cur_Pwr_Max_Temp_Ovrd'" # NVM Max Drive Temp Override. This value when "true" will not alarm on any Current Power Cycle Max Temperature Limit.' echo " # --- NOTE: NVMe drives currently do not report Min/Max temperatures so this is a future feature." echo " " echo "###### SSD/NVMe Specific Settings" echo " " echo "WearLevelCrit=$WearLevelCrit # Wear Level Alarm Setpoint for WARNING message, 9% is the default." echo " " echo "###### General Settings" echo "# Output Formats" echo 'PowerTimeFormat="'$PowerTimeFormat'" # Format for power-on hours string, valid options are "ymdh", "ymd", "ym", "y", or "h" (year month day hour).' echo 'TempDisplay="'$TempDisplay'" # The format you desire the temperature to be displayed in. Common formats are: "*C", "^C", or "^c". Choose your own.' echo 'Non_Exist_Value="'$Non_Exist_Value'" # How do you desire non-existent data to be displayed. The Default is "---", popular options are "N/A" or " ".' echo 'Pool_Capacity="'$Pool_Capacity'" # Select "zfs" or "zpool" for Zpool Status Report - Pool Size and Free Space capacities. "zfs" is default.' echo " " echo "# Ignore Alarms" echo 'IgnoreUDMA="'$IgnoreUDMA'" # Set to "true" to ignore all UltraDMA CRC Errors for the summary alarm (Email Header) only, errors will still appear in the graphical chart.' echo 'IgnoreSeekError="'$IgnoreSeekError'" # Set to "true" to ignore all Seek Error Rate/Health errors. Default is true.' echo 'IgnoreReadError="'$IgnoreReadError'" # Set to "true" to ignore all Raw Read Error Rate/Health errors. Default is true.' echo 'IgnoreMultiZone="'$IgnoreMultiZone'" # Set to "true" to ignore all MultiZone Errors. Default is false.' echo 'DisableWarranty="'$DisableWarranty'" # Set to "true to disable Email Subject line alerts for any expired warranty alert. The email body will still report the alert.' echo 'ATA_Auto_Enable="'$ATA_Auto_Enable'" # Set to "true" to automatically update Log Error count to only display a log error when a new one occurs.' echo " " echo "# Disable or Activate Input/Output File Settings" echo 'IncludeSSD="'$IncludeSSD'" # Set to "true" will engage SSD Automatic Detection and Reporting, false = Disable SSD Automatic Detection and Reporting.' echo 'IncludeNVM="'$IncludeNVM'" # Set to "true" will engage NVM Automatic Detection and Reporting, false = Disable NVM Automatic Detection and Reporting.' echo 'ReportNonSMART="'$ReportNonSMART'" # Will force even non-SMART devices to be reported, "true" = normal operation to report non-SMART devices.' echo 'DisableRAWdata="'$DisableRAWdata'" # Set to "true" to remove the 'smartctl -a' data and non-smart data appended to the normal report. Default is false.' echo " " echo "# Media Alarms" echo "SectorsWarn=$SectorsWarn # Number of sectors per drive to allow with errors before WARNING color/message will be used, this value should be less than SectorsCrit." echo "SectorsCrit=$SectorsCrit # Number of sectors per drive with errors before CRITICAL color/message will be used." echo "ReAllocWarn=$ReAllocWarn # Number of Reallocated sector events allowed. Over this amount is an alarm condition." echo "MultiZoneWarn=$MultiZoneWarn # Number of MultiZone Errors to allow before a Warning color/message will be used. Default is 0." echo "MultiZoneCrit=$MultiZoneCrit # Number of MultiZone Errors to allow before a Warning color/message will be used. Default is 5." echo 'DeviceRedFlag="'$DeviceRedFlag'" # Set to "true" to have the Device Column indicate RED for ANY alarm condition. Default is true.' echo 'HeliumAlarm="'$HeliumAlarm'" # Set to "true" to set for a critical alarm any He value below "HeliumMin" value. Default is true.' echo "HeliumMin=$HeliumMin # Set to 100 for a zero leak helium result. An alert will occur below this value." echo "RawReadWarn=$RawReadWarn # Number of read errors to allow before WARNING color/message will be used, this value should be less than RawReadCrit." echo "RawReadCrit=$RawReadCrit # Number of read errors to allow before CRITICAL color/message will be used." echo "SeekErrorsWarn=$SeekErrorsWarn # Number of seek errors to allow before WARNING color/message will be used, this value should be less than SeekErrorsCrit." echo "SeekErrorsCrit=$SeekErrorsCrit # Number of seek errors to allow before CRITICAL color/message will be used." echo "NVM_Media_Errors=$NVM_Media_Errors # Number of NVM Media Errors will cause a CRITICAL alarm. Default is 1." echo " " echo "# Time-Limited Error Recovery (TLER)" echo 'SCT_Enable="'$SCT_Enable'" # Set to "true" to send a command to enable SCT on your drives for user defined timeout if the TLER state is Disabled.' echo 'SCT_Warning_Level="'$SCT_Warning_Level'" # Set to "all" will generate a Warning Message for all devices not reporting SCT enabled. "TLER" reports only drive which support TLER.' echo ' # "TLER_No_Msg" will only report for TLER drives and not report a Warning Message if the drive can set TLER on.' echo "SCT_Read_Timeout=$SCT_Read_Timeout # Set to the read threshold. Default = 70 = 7.0 seconds." echo "SCT_Write_Timeout=$SCT_Write_Timeout # Set to the write threshold. Default = 70 = 7.0 seconds." echo " " echo "# SMART Testing Alarm" echo "TestWarnAge=$TestWarnAge # Maximum age (in days) of last SMART test before CRITICAL color/message will be used." echo " " echo "# Zpool Fragmentation Alarm" echo "ZpoolFragWarn=$ZpoolFragWarn # Percent of fragmentation before a Warning message occurs." echo " " echo '######## Enable-Disable Text Portion ########' echo 'Enable_Text_Section="'$Enable_Text_Section'" # This will display the Text Section when = "true" or remove it when not "true". Default="true"' echo " " echo "###### Statistical Data File (SDF)" echo 'statistical_data_file="'$statistical_data_file'" # Default location is where the script is located.' echo 'SDF_DataRecordEnable="'$SDF_DataRecordEnable'" # Set to "true" will save all drive data into a CSV file defined by "statistical_data_file" below.' echo "SDF_DataPurgeDays=$SDF_DataPurgeDays # Set to the number of day you wish to keep in the data. Older data will be purged. Default is 730 days (2 years). 0=Disable." echo 'SDF_DataEmail="'$SDF_DataEmail'" # Set to "true" to have an attachment of the file emailed to you. Default is true.' echo 'SDF_DataEmailDay="'$SDF_DataEmailDay'" # Set to the day of the week the statistical report is emailed. (All, Mon, Tue, Wed, Thu, Fri, Sat, Sun, Month)' echo " " echo "###### FreeNAS config backup settings" echo 'TrueNASConfigEmailEnable="'$TrueNASConfigEmailEnable'" # Set to "true" to save config backup (which renders next two options operational); "false" to keep disable config backups.' echo 'TrueNASConfigEmailDay="'$TrueNASConfigEmailDay'" # Set to the day of the week the config is emailed. (All, Mon, Tue, Wed, Thu, Fri, Sat, Sun, Month)' echo 'TrueNASConfigBackupSave="'$TrueNASConfigBackupSave'" # Set to "false" to delete FreeNAS config backup after mail is sent; "true" to keep it in dir below.' echo 'TrueNASConfigBackupLocation="'$TrueNASConfigBackupLocation'" # Directory in which to store the backup FreeNAS config files.' echo " " echo "###### Attach multi_report_config.txt to Email ######" echo 'MRConfigEmailEnable="'$MRConfigEmailEnable'" # Set to "true" to enable periodic email (which renders next two options operational).' echo 'MRChangedEmailSend="'$MRChangedEmailSend'" # If "true" it will attach the updated/changed file to the email.' echo 'MRConfigEmailDay="'$MRConfigEmailDay'" # Set to the day of the week the multi_report_config.txt is emailed. (All, Mon, Tue, Wed, Thu, Fri, Sat, Sun, Month, Never)' echo " " echo "########## REPORT CHART CONFIGURATION ##############" echo " " echo "###### REPORT HEADER TITLE ######" echo 'HDDreportTitle="'$HDDreportTitle'" # This is the title of the HDD report, change as you desire.' echo 'SSDreportTitle="'$SSDreportTitle'" # This is the title of the SSD report, change as you desire.' echo 'NVMreportTitle="'$NVMreportTitle'" # This is the title of the NVMe report, change as you desire.' echo " " echo "### CUSTOM REPORT CONFIGURATION ###" echo "# By default most items are selected. Change the item to "false" to have it not displayed in the graph, "true" to have it displayed." echo "# NOTE: Alarm setpoints are not affected by these settings, this is only what columns of data are to be displayed on the graph." echo "# I would recommend that you remove columns of data that you don't really care about to make the graph less busy." echo " " echo "# For Zpool Status Summary" echo 'Zpool_Pool_Name_Title="'$Zpool_Pool_Name_Title'"' echo 'Zpool_Status_Title="'$Zpool_Status_Title'"' echo 'Zpool_Pool_Size_Title="'$Zpool_Pool_Size_Title'"' echo 'Zpool_Free_Space_Title="'$Zpool_Free_Space_Title'"' echo 'Zpool_Used_Space_Title="'$Zpool_Used_Space_Title'"' echo 'Zfs_Pool_Size_Title="'$Zfs_Pool_Size_Title'"' echo 'Zfs_Free_Space_Title="'$Zfs_Free_Space_Title'"' echo 'Zfs_Used_Space_Title="'$Zfs_Used_Space_Title'"' echo 'Zpool_Read_Errors_Title="'$Zpool_Read_Errors_Title'"' echo 'Zpool_Write_Errors_Title="'$Zpool_Write_Errors_Title'"' echo 'Zpool_Checksum_Errors_Title="'$Zpool_Checksum_Errors_Title'"' echo 'Zpool_Scrub_Repaired_Title="'$Zpool_Scrub_Repaired_Title'"' echo 'Zpool_Scrub_Errors_Title="'$Zpool_Scrub_Errors_Title'"' echo 'Zpool_Scrub_Age_Title="'$Zpool_Scrub_Age_Title'"' echo 'Zpool_Scrub_Duration_Title="'$Zpool_Scrub_Duration_Title'"' echo " " echo "# For Hard Drive Section" echo 'HDD_Device_ID="'$HDD_Device_ID'"' echo 'HDD_Device_ID_Title="'$HDD_Device_ID_Title'"' echo 'HDD_Serial_Number="'$HDD_Serial_Number'"' echo 'HDD_Serial_Number_Title="'$HDD_Serial_Number_Title'"' echo 'HDD_Model_Number="'$HDD_Model_Number'"' echo 'HDD_Model_Number_Title="'$HDD_Model_Number_Title'"' echo 'HDD_Capacity="'$HDD_Capacity'"' echo 'HDD_Capacity_Title="'$HDD_Capacity_Title'"' echo 'HDD_Rotational_Rate="'$HDD_Rotational_Rate'"' echo 'HDD_Rotational_Rate_Title="'$HDD_Rotational_Rate_Title'"' echo 'HDD_SMART_Status="'$HDD_SMART_Status'"' echo 'HDD_SMART_Status_Title="'$HDD_SMART_Status_Title'"' echo 'HDD_Warranty="'$HDD_Warranty'"' echo 'HDD_Warranty_Title="'$HDD_Warranty_Title'"' echo 'HDD_Raw_Read_Error_Rate="'$HDD_Raw_Read_Error_Rate'"' echo 'HDD_Raw_Read_Error_Rate_Title="'$HDD_Raw_Read_Error_Rate_Title'"' echo 'HDD_Drive_Temp="'$HDD_Drive_Temp'"' echo 'HDD_Drive_Temp_Title="'$HDD_Drive_Temp_Title'"' echo 'HDD_Drive_Temp_Min="'$HDD_Drive_Temp_Min'"' echo 'HDD_Drive_Temp_Min_Title="'$HDD_Drive_Temp_Min_Title'"' echo 'HDD_Drive_Temp_Max="'$HDD_Drive_Temp_Max'"' echo 'HDD_Drive_Temp_Max_Title="'$HDD_Drive_Temp_Max_Title'"' echo 'HDD_Power_On_Hours="'$HDD_Power_On_Hours'"' echo 'HDD_Power_On_Hours_Title="'$HDD_Power_On_Hours_Title'"' echo 'HDD_Start_Stop_Count="'$HDD_Start_Stop_Count'"' echo 'HDD_Start_Stop_Count_Title="'$HDD_Start_Stop_Count_Title'"' echo 'HDD_Load_Cycle="'$HDD_Load_Cycle'"' echo 'HDD_Load_Cycle_Title="'$HDD_Load_Cycle_Title'"' echo 'HDD_Spin_Retry="'$HDD_Spin_Retry'"' echo 'HDD_Spin_Retry_Title="'$HDD_Spin_Retry_Title'"' echo 'HDD_Reallocated_Sectors="'$HDD_Reallocated_Sectors'"' echo 'HDD_Reallocated_Sectors_Title="'$HDD_Reallocated_Sectors_Title'"' echo 'HDD_Reallocated_Events="'$HDD_Reallocated_Events'"' echo 'HDD_Reallocated_Events_Title="'$HDD_Reallocated_Events_Title'"' echo 'HDD_Pending_Sectors="'$HDD_Pending_Sectors'"' echo 'HDD_Pending_Sectors_Title="'$HDD_Pending_Sectors_Title'"' echo 'HDD_Offline_Uncorrectable="'$HDD_Offline_Uncorrectable'"' echo 'HDD_Offline_Uncorrectable_Title="'$HDD_Offline_Uncorrectable_Title'"' echo 'HDD_UDMA_CRC_Errors_List="'$HDD_UDMA_CRC_Errors_List'"' echo 'HDD_UDMA_CRC_Errors_List_Title="'$HDD_UDMA_CRC_Errors_List_Title'"' echo 'HDD_Seek_Error_Rate="'$HDD_Seek_Error_Rate'"' echo 'HDD_Seek_Error_Rate_Title="'$HDD_Seek_Error_Rate_Title'"' echo 'HDD_MultiZone_Errors="'$HDD_MultiZone_Errors'"' echo 'HDD_MultiZone_Errors_Title="'$HDD_MultiZone_Errors_Title'"' echo 'HDD_Helium_Level="'$HDD_Helium_Level'"' echo 'HDD_Helium_Level_Title="'$HDD_Helium_Level_Title'"' echo 'HDD_Last_Test_Age="'$HDD_Last_Test_Age'"' echo 'HDD_Last_Test_Age_Title="'$HDD_Last_Test_Age_Title'"' echo 'HDD_Last_Test_Type="'$HDD_Last_Test_Type'"' echo 'HDD_Last_Test_Type_Title="'$HDD_Last_Test_Type_Title'"' echo 'HDD_Total_Data_Written="'$HDD_Total_Data_Written'"' echo 'HDD_Total_Data_Written_Title="'$HDD_Total_Data_Written_Title'"' echo " " echo "# For Solid State Drive Section" echo 'SSD_Device_ID="'$SSD_Device_ID'"' echo 'SSD_Device_ID_Title="'$SSD_Device_ID_Title'"' echo 'SSD_Serial_Number="'$SSD_Serial_Number'"' echo 'SSD_Serial_Number_Title="'$SSD_Serial_Number_Title'"' echo 'SSD_Model_Number="'$SSD_Model_Number'"' echo 'SSD_Model_Number_Title="'$SSD_Model_Number_Title'"' echo 'SSD_Capacity="'$SSD_Capacity'"' echo 'SSD_Capacity_Title="'$SSD_Capacity_Title'"' echo 'SSD_SMART_Status="'$SSD_SMART_Status'"' echo 'SSD_SMART_Status_Title="'$SSD_SMART_Status_Title'"' echo 'SSD_Warranty="'$SSD_Warranty'"' echo 'SSD_Warranty_Title="'$SSD_Warranty_Title'"' echo 'SSD_Drive_Temp="'$SSD_Drive_Temp'"' echo 'SSD_Drive_Temp_Title="'$SSD_Drive_Temp_Title'"' echo 'SSD_Drive_Temp_Min="'$SSD_Drive_Temp_Min'"' echo 'SSD_Drive_Temp_Min_Title="'$SSD_Drive_Temp_Min_Title'"' echo 'SSD_Drive_Temp_Max="'$SSD_Drive_Temp_Max'"' echo 'SSD_Drive_Temp_Max_Title="'$SSD_Drive_Temp_Max_Title'"' echo 'SSD_Power_On_Hours="'$SSD_Power_On_Hours'"' echo 'SSD_Power_On_Hours_Title="'$SSD_Power_On_Hours_Title'"' echo 'SSD_Wear_Level="'$SSD_Wear_Level'"' echo 'SSD_Wear_Level_Title="'$SSD_Wear_Level_Title'"' echo 'SSD_Reallocated_Sectors="'$SSD_Reallocated_Sectors'"' echo 'SSD_Reallocated_Sectors_Title="'$SSD_Reallocated_Sectors_Title'"' echo 'SSD_Reallocated_Events="'$SSD_Reallocated_Events'"' echo 'SSD_Reallocated_Events_Title="'$SSD_Reallocated_Events_Title'"' echo 'SSD_Pending_Sectors="'$SSD_Pending_Sectors'"' echo 'SSD_Pending_Sectors_Title="'$SSD_Pending_Sectors_Title'"' echo 'SSD_Offline_Uncorrectable="'$SSD_Offline_Uncorrectable'"' echo 'SSD_Offline_Uncorrectable_Title="'$SSD_Offline_Uncorrectable_Title'"' echo 'SSD_UDMA_CRC_Errors_List="'$SSD_UDMA_CRC_Errors_List'"' echo 'SSD_UDMA_CRC_Errors_List_Title="'$SSD_UDMA_CRC_Errors_List_Title'"' echo 'SSD_Last_Test_Age="'$SSD_Last_Test_Age'"' echo 'SSD_Last_Test_Age_Title="'$SSD_Last_Test_Age_Title'"' echo 'SSD_Last_Test_Type="'$SSD_Last_Test_Type'"' echo 'SSD_Last_Test_Type_Title="'$SSD_Last_Test_Type_Title'"' echo 'SSD_Total_Data_Written="'$SSD_Total_Data_Written'"' echo 'SSD_Total_Data_Written_Title="'$SSD_Total_Data_Written_Title'"' echo " " echo "# For NVMe Drive Section" echo 'NVM_Device_ID="'$NVM_Device_ID'"' echo 'NVM_Device_ID_Title="'$NVM_Device_ID_Title'"' echo 'NVM_Serial_Number="'$NVM_Serial_Number'"' echo 'NVM_Serial_Number_Title="'$NVM_Serial_Number_Title'"' echo 'NVM_Model_Number="'$NVM_Model_Number'"' echo 'NVM_Model_Number_Title="'$NVM_Model_Number_Title'"' echo 'NVM_Capacity="'$NVM_Capacity'"' echo 'NVM_Capacity_Title="'$NVM_Capacity_Title'"' echo 'NVM_SMART_Status="'$NVM_SMART_Status'"' echo 'NVM_SMART_Status_Title="'$NVM_SMART_Status_Title'"' echo 'NVM_Warranty="'$NVM_Warranty'"' echo 'NVM_Warranty_Title="'$NVM_Warranty_Title'"' echo 'NVM_Critical_Warning="'$NVM_Critical_Warning'"' echo 'NVM_Critical_Warning_Title="'$NVM_Critical_Warning_Title'"' echo 'NVM_Drive_Temp="'$NVM_Drive_Temp'"' echo 'NVM_Drive_Temp_Title="'$NVM_Drive_Temp_Title'"' echo 'NVM_Drive_Temp_Min="'$NVM_Drive_Temp_Min'" # I have not found this on an NVMe drive yet, so set to false' echo 'NVM_Drive_Temp_Min_Title="'$NVM_Drive_Temp_Min_Title'"' echo 'NVM_Drive_Temp_Max="'$NVM_Drive_Temp_Max'" # I have not found this on an NVMe drive yet, so set to false' echo 'NVM_Drive_Temp_Max_Title="'$NVM_Drive_Temp_Max_Title'"' echo 'NVM_Power_On_Hours="'$NVM_Power_On_Hours'"' echo 'NVM_Power_On_Hours_Title="'$NVM_Power_On_Hours_Title'"' echo 'NVM_Wear_Level="'$NVM_Wear_Level'"' echo 'NVM_Wear_Level_Title="'$NVM_Wear_Level_Title'"' echo 'NVM_Media_Error="'$NVM_Media_Error'"' echo 'NVM_Media_Error_Title="'$NVM_Media_Error_Title'"' echo 'NVM_Total_Data_Written="'$NVM_Total_Data_Written'"' echo 'NVM_Total_Data_Written_Title="'$NVM_Total_Data_Written_Title'"' echo " " echo " " echo "###### Mouseover" echo "# This will display the original value of an overridden value (one in yellow)" echo "# This is a tri-state value as explained below." echo '# "true" will displaying the actual value via a mouseover html link. Still working to make this a true Mouseover.' echo '# "false" will not generate any special chart and will run as previous versions.' echo "# "alt" will place the actual value within parentheses. An option if your email client can't display mouseover." echo " " echo 'Mouseover="'$Mouseover'"' echo " " echo " " echo "###### Drive Ignore List" echo "# What does it do:" echo "# Use this to list any drives to ignore and remove from the report. This is very useful for ignoring USB Flash Drives" echo '# or other drives for which good data is not able to be collected (non-standard).' echo "#" echo "# How to use it:" echo "# We are using a comma delimited file to identify the drive serial numbers. You MUST use the exact and full serial" echo "# number smartctl reports, if there is no identical match then it will not match. Additionally you may list drives" echo "# from other systems and they will not have any effect on a system where the drive does not exist. This is great" echo "# to have one configuration file that can be used on several systems." echo "#" echo '# Example: "VMWare,1JUMLBD,21HNSAFC21410E"' echo " " echo 'Ignore_Drives_List="'$Ignore_Drives_List'"' echo " " echo "###### Drive UDMA_CRC_Error_Count List" echo "# What does it do:" echo '# If you have a drive which has an UDMA count other than 0 (zero), this setting will offset the' echo "# value back to zero for the concerns of monitoring future increases of this specific error. Any match will" echo '# subtract the given value to report a 0 (zero) value and highlight it in yellow to denote it was overridden.' echo "# The Warning Title will not be flagged if this is zero'd out in this manner." echo "# NOTE: UDMA_CRC_Errors_List are typically permanently stored in the drive and cannot be reset to zero even though" echo "# they are frequently caused by a data cable communications error." echo "#" echo "# How to use it:" echo "# List each drive by serial number and include the current UDMA_CRC_Error_Count value." echo "# The format is very specific and will not work if you "wing it", use the Live EXAMPLE." echo "#" echo "# Set the FLAG in the FLAGS Section IgnoreUDMA to false." echo "#" echo "# If the error count exceeds the limit minus the offset then a warning message will be generated." echo "# On the Status Report the UDMA CRC Errors block will be YELLOW with a value of "0" for an overridden value." echo "# -- NOTE: We are using the colon : as the separator between the drive serial number and the value to change." echo "#" echo "# Format: variable="Drive_Serial_Number:Current_UDMA_Error_Count" and add a comma if you have more than one drive." echo "#" echo "# The below example shows drive WD-WMC4N2578099 has 1 UDMA_CRC_Error, drive S2X1J90CA48799 has 2 errors." echo "#" echo '# Live Example: "WD-WMC4N2578099:1,S2X1J90CA48799:2,P02618119268:1"' echo " " # Below line retained to be able to update from version 1.6c echo 'CRC_Errors_List="'$CRC_Errors_List'"' echo " " echo "###### MultiZone_List_Errors List" echo "# What does it do:" echo "# This identifies drives with MultiZone_List_Errors which may be irritating people." echo "# MultiZone_List_Errors "for some drives, not all drives" are pretty much meaningless." echo "#" echo "# How to use it:" echo '# Use same format as CRC_Errors_List.' echo " " # Below line retained to be able to update from version 1.6c echo 'MultiZone_List="'$MultiZone_List'"' echo " " echo "####### Reallocated Sectors Exceptions" echo "# What does it do:" echo "# This will offset any Reallocated Sectors count by the value provided." echo "#" echo "# I do not recommend using this feature as I'm a believer in if you have over 5 bad sectors, odds are the drive will get worse." echo "# I'd recommend replacing the drive before complete failure. But that is your decision." echo "#" echo "# Why is it even an option?" echo "# I use it for testing purposes only but you may want to use it." echo "#" echo "# How to use it:" echo '# Use same format as CRC_Errors_List.' echo " " # Below line retained to be able to update from version 1.6c echo 'ReAllocated_Sector_List="'$ReAllocated_Sector_List'"' echo 'ReAllocated_Sector_Events_List="'$ReAllocated_Sector_Events_List'"' echo " " echo "######## ATA Error Log Silencing ##################" echo "# What does it do:" echo "# This will ignore error log messages equal to or less than the threshold." echo "# How to use:" echo "# Same as the CRC_Errors_List, [drive serial number:error count]" echo " " echo 'ATA_Errors_List="'$ATA_Errors_List'"' echo " " echo "####### Custom Drive Configuration (Experimental)" echo "# Used to define specific alarm values for specific drives by serial number." echo "# This should only be used for drives where the default alarm settings" echo "# are not proper. Up to 24 unique drive values may be stored." echo "#" echo "# Use -config to set these values." echo " " # Need to adjust for Wear Level field if required. # -- Check to see if the value exists and if it does, append 'd' to the end of each entry. echo 'Custom_Drives_List="'$Custom_Drives_List'"' echo " " echo "####### Warranty Expiration Date" echo "# What does it do:" echo "# This section is used to add warranty expirations for designated drives and to create an alert when they expire." echo "# The date format is YYYY-MM-DD." echo "#" echo "# Below is an example for the format using my own drives, which yes, are expired." echo "# As previously stated above, drive serial numbers must be an exact match to what smartctl reports to function." echo "#" echo "# If the drive does not exist, for example my drives are not on your system, then nothing will happen." echo "#" echo "# How to use it:" echo '# Use the format ="Drive_Serial_Number:YYYY-MM-DD" and add a comma if you have more than one drive.' echo '# Example: $Drive_Warranty_List="K1JUMLBD:2020-09-30,K1JRSWLD:2020-09-30,K1JUMW4D:2020-09-30,K1GVD84B:2020-10-12"' echo " " # Below line retained to be able to update from version 1.6c echo 'Drive_Warranty_List="'$Drive_Warranty_List'"' echo " " echo '######## Expired Drive Warranty Setup' echo 'expiredWarrantyBoxColor="'$expiredWarrantyBoxColor'" # "black" = normal box perimeter color.' echo 'WarrantyBoxPixels="'$WarrantyBoxPixels'" # Box line thickness. 1 = normal, 2 = thick, 3 = Very Thick, used for expired drives only.' echo 'WarrantyBackgndColor="'$WarrantyBackgndColor'" # Background color for expired drives. "none" = normal background.' echo " " echo "###### Global table of colors" echo "# The colors selected you can change but you will need to look up the proper HEX code for a color." echo " " # Reset the colors option in '-config' if [[ $Update_Colors != "1" ]]; then echo 'okColor="'$okColor'" # Hex code for color to use in SMART Status column if drives pass (default is darker light green, #b5fcb9).' echo 'warnColor="'$warnColor'" # Hex code for WARN color (default is orange, #F38B16).' echo 'critColor="'$critColor'" # Hex code for CRITICAL color (default is red, #f44336).' echo 'altColor="'$altColor'" # Table background alternates row colors between white and this color (default is light gray, #f4f4f4).' echo 'whtColor="'$whtColor'" # Hex for White background.' echo 'ovrdColor="'$ovrdColor'" # Hex code for Override Yellow.' echo 'blueColor="'$blueColor'" # Hex code for Sky Blue, used for the SCRUB/SMART Test In Progress/background.' echo 'yellowColor="'$yellowColor'" # Hex code for pale yellow.' else echo 'okColor="#b5fcb9" # Hex code for color to use in SMART Status column if drives pass (default is darker light green, #b5fcb9).' echo 'warnColor="#F38B16" # Hex code for WARN color (default is orange, #F38B16).' echo 'critColor="#f44336" # Hex code for CRITICAL color (default is red, #f44336).' echo 'altColor="#f4f4f4" # Table background alternates row colors between white and this color (default is light gray, #f4f4f4).' echo 'whtColor="#ffffff" # Hex for White background.' echo 'ovrdColor="#ffffe4" # Hex code for Override Yellow.' echo 'blueColor="#87ceeb" # Hex code for Sky Blue, used for the SCRUB In Progress background.' echo 'yellowColor="#f1ffad" # Hex code for pale yellow.' fi ) > "$Config_File_Name" if [[ $MRChangedEmailSend == "true" ]]; then MR_Attach_Config="1"; fi } ########## GENERATE CONFIG FILE ########## generate_config_file () { SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) Config_File_Name="$SCRIPT_DIR/multi_report_config.txt" for (( z=1; z<=50; z++ )); do clear echo $programver echo " " echo " Configuration File Management" echo " " if test -e "$Config_File_Name"; then echo " *** WARNING - A CONFIGURATION CURRENTLY FILE EXISTS ***"; fi echo " " echo " N)ew configuration file (creates a new clean external configuration file)" echo " U)pdate configuration file (updates select static variables to default)" echo " A)dvanced configuration (must have a configuration file already present)" echo " S)pencer Integration (configure Spencer add-on)" echo " H)ow to use this configuration tool (general instructions)" echo " X) Exit" echo " " echo "NOTE: In using this configuration script when the value is:" echo " Number or Text: The current value will be displayed. You have the option to" echo " just press Enter/Return to accept the current value or you may enter a" echo " different value." echo " " echo " True or False: The current value will be displayed. You have the option to" echo " press Enter/Return to accept the current value or you may press 't' for true" echo " or 'f' for false." echo " " echo " " echo -n " Make your selection: " read -s -n 1 Keyboard_var shopt -s nocasematch case $Keyboard_var in # First Level Start A) clear echo " " echo " Advanced Configuration Settings" echo " " echo " Loading Configuration File Data..." echo " " if [[ ! -f "$Config_File_Name" ]]; then echo "You do not have an external configuration file yet." echo "Please create an external configuration file." echo " " exit 1 fi load_config echo "This is not a complete configuration setup, it is just the most common settings" echo "that a user would typically require for a normal setup. You may directly edit" echo "the config text file with any text editor to take full advantage of the options." echo " " echo "The config text file is located here: "$Config_File_Name echo " " for (( x=1; x<=50; x++ )); do clear echo " Advanced Configuration Settings" echo " " echo " A) Alarm Setpoints (Temp, Zpool, Media, Activate In/Out, Ignore)" echo " B) Config-Backup (Edit Config-Backup & Multi-Report_Config Settings)" echo " C) Email Address (Edit Email address and Encryption)" echo " D) HDD Column Selection (Select columns to display/hide)" echo " E) SSD Column Selection (Select columns to display/hide)" echo " F) NVMe Column Selection (Select columns to display/hide)" echo " G) Output Formats (Hours, Temp, Non-Existent, Pool Capacity)" echo " H) Report Header Titles (Edit Header Titles, Add/Remove Text Section)" echo " I) Statistical Data File Setup" echo " J) TLER / SCT (Setup if TLER is active)" echo " K) Drive Errors and Custom Builds (Ignore Drives, UDMA CRC, MultiZone," echo " Reallocated Sectors, ATA Errors, Warranty Expiration)" echo " S) Custom Drive Configuration" echo " U) Update Script (Automatic or Manual operation)" echo " W) Write Configuration File (Save your changes)" echo " X) Exit - Will not automatically save changes" echo " " echo -n " Make your selection: " read -s -n 1 Keyboard_var2 echo " " shopt -s nocasematch case $Keyboard_var2 in # Advanced Configuration Level Start A) for (( y=1; y<=50; y++ )); do clear echo " Alarm Configuration Settings" echo " " echo "These setting affect all drives unless they are overridden" echo " using the Custom Drive Configuration option." echo " " echo " A) Temperature Settings (Various Temperature Settings)" echo " B) Zpool Settings (Scrub Age, Pool % Avail, and Frag % Alarms)" echo " C) Media Alarm Settings (Sectors and CRC Type Alarms)" echo " D) Activate Input/Output Settings (Enable SSD/NVMe/Non-SMART)" echo " E) Ignore Alarms (Ignore CRC/MultiZone/Seek Type Errors)" echo " F) Monitor Email Settings" echo " X) Exit - Return to previous menu" echo " " echo -n " Make your selection: " read -s -n 1 Keyboard_var3 echo " " shopt -s nocasematch case $Keyboard_var3 in A) clear echo "Temperature Settings" echo " " echo "Current value is displayed. Enter a new value or Return to keep." echo " " echo -n "HDD Warning Temperature ("$HDDtempWarn") " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then HDDtempWarn=$Keyboard_yn; fi echo "Set Value: ("$HDDtempWarn")" echo " " echo -n "HDD Critical Temperature ("$HDDtempCrit") " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then HDDtempCrit=$Keyboard_yn; fi echo "Set Value: ("$HDDtempCrit")" echo " " echo "HDD Max Temperature Override for Power Cycle Enabled ("$HDD_Cur_Pwr_Max_Temp_Ovrd") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HDD_Cur_Pwr_Max_Temp_Ovrd="true"; else HDD_Cur_Pwr_Max_Temp_Ovrd="false"; fi fi echo "Set Value: ("$HDD_Cur_Pwr_Max_Temp_Ovrd")" echo " " echo -n "SSD Warning Temperature ("$SSDtempWarn") " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then SSDtempWarn=$Keyboard_yn; fi echo "Set Value: ("$SSDtempWarn")" echo " " echo -n "SSD Critical Temperature ("$SSDtempCrit") " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then SSDtempCrit=$Keyboard_yn; fi echo "Set Value: ("$SSDtempCrit")" echo " " echo "SSD Max Temperature Override for Power Cycle Enabled ("$SSD_Cur_Pwr_Max_Temp_Ovrd") " echo 'When "true" will not alarm on any Current Power Cycle Max Temperature Limit.' read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then SSD_Cur_Pwr_Max_Temp_Ovrd="true"; else SSD_Cur_Pwr_Max_Temp_Ovrd="false"; fi fi echo "Set Value: ("$SSD_Cur_Pwr_Max_Temp_Ovrd")" echo " " echo -n "NVMe Warning Temperature ("$NVMtempWarn") " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then NVMtempWarn=$Keyboard_yn; fi echo "Set Value: ("$NVMtempWarn")" echo " " echo -n "NVMe Critical Temperature ("$NVMtempCrit") " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then NVMtempCrit=$Keyboard_yn; fi echo "Set Value: ("$NVMtempCrit")" echo " " echo "returning..." sleep 2 ;; B) clear echo "Zpool Settings" echo " " echo "Maximum age (in days) since last pool scrub before CRITICAL color will be used." echo -n "Scrub maximum days since last completion ("$ScrubAgeWarn") " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then ScrubAgeWarn=$Keyboard_yn; fi echo "Set Value: ("$ScrubAgeWarn")" echo " " echo "Pool used percentage for CRITICAL color to be used." echo -n "Pool Space Used Alert ("$PoolUsedWarn") " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then PoolUsedWarn=$Keyboard_yn; fi echo "Set Value: ("$PoolUsedWarn")" echo " " echo "Pool Fragmentation percentage for WARNING color to be used." echo -n "Pool Frag Alert ("$ZpoolFragWarn") " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then ZpoolFragWarn=$Keyboard_yn; fi echo "Set Value: ("$ZpoolFragWarn")" echo "returning..." sleep 2 ;; C) clear echo "Media Alarm Settings" echo " " echo -n "SSD/NVMe Wear Level lower limit ("$WearLevelCrit") " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then WearLevelCrit=$Keyboard_yn; fi echo "Set Value: ("$WearLevelCrit")" echo " " echo -n "Sector Errors Warning ("$SectorsWarn") " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then SectorsWarn=$Keyboard_yn; fi echo "Set Value: ("$SectorsWarn")" echo " " echo -n "Sector Errors Critical ("$SectorsCrit") " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then SectorsCrit=$Keyboard_yn; fi echo "Set Value: ("$SectorsCrit")" echo " " echo -n "Reallocated Sectors Warning ("$ReAllocWarn") " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then ReAllocWarn=$Keyboard_yn; fi echo "Set Value: ("$ReAllocWarn")" echo " " echo -n "Raw Read Errors Warning ("$RawReadWarn") " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then RawReadWarn=$Keyboard_yn; fi echo "Set Value: ("$RawReadWarn")" echo " " echo -n "Raw Read Errors Critical ("$RawReadCrit") " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then RawReadCrit=$Keyboard_yn; fi echo "Set Value: ("$RawReadCrit")" echo " " echo -n "Seek Errors Warning ("$SeekErrorsWarn") " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then SeekErrorsWarn=$Keyboard_yn; fi echo "Set Value: ("$SeekErrorsWarn")" echo " " echo -n "Seek Errors Critical ("$SeekErrorsCrit") " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then SeekErrorsCrit=$Keyboard_yn; fi echo "Set Value: ("$SeekErrorsCrit")" echo " " echo -n "MultiZone Errors Warning ("$MultiZoneWarn") " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then MultiZoneWarn=$Keyboard_yn; fi echo "Set Value: ("$MultiZoneWarn")" echo " " echo -n "MultiZone Errors Critical ("$MultiZoneCrit") " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then MultiZoneCrit=$Keyboard_yn; fi echo "Set Value: ("$MultiZoneCrit")" echo " " echo -n "Helium Minimum Level ("$HeliumMin") " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then HeliumMin=$Keyboard_yn; fi echo "Set Value: ("$HeliumMin")" echo " " echo "Helium Critical Alert Message ("$HeliumAlarm") " echo 'A "true" value will generate an email subject line alert for a error.' read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HeliumAlarm="true"; else HeliumAlarm="false"; fi fi echo "Set Value: ("$HeliumAlarm")" echo " " echo -n "S.M.A.R.T. Test Age Warning ("$TestWarnAge") " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then TestWarnAge=$Keyboard_yn; fi echo "Set Value: ("$TestWarnAge")" echo " " echo -n "NVMe Media Errors ("$NVM_Media_Errors") " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then NVM_Media_Errors=$Keyboard_yn; fi echo "Set Value: ("$NVM_Media_Errors")" echo " " echo -n "Flag Device ID RED on Error ("$DeviceRedFlag") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then DeviceRedFlag="true"; else DeviceRedFlag="false"; fi fi echo "Set Value: ("$DeviceRedFlag")" echo " " echo "returning..." sleep 2 ;; D) clear echo "Activate/Disable Input/Output Settings" echo " " echo 'Set to "true" will engage SSD Automatic Detection and Reporting' echo "Include SSD's in report ("$IncludeSSD") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then IncludeSSD="true"; else IncludeSSD="false"; fi fi echo "Set Value: ("$IncludeSSD")" echo " " echo "Set to "true" will engage NVM Automatic Detection and Reporting" echo "Include NVMe's in report ("$IncludeNVM") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then IncludeNVM="true"; else IncludeNVM="false"; fi fi echo "Set Value: ("$IncludeNVM")" echo " " echo "Will force even non-SMART devices to be reported" echo "Report Non-SMART Devices ("$ReportNonSMART") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then ReportNonSMART="true"; else ReportNonSMART="false"; fi fi echo "Set Value: ("$ReportNonSMART")" echo " " echo 'Set to "true" to remove the smartctl -a data and non-smart data appended to the normal report.' echo "Remove Non-SMART Data from report ("$DisableRAWdata") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then DisableRAWdata="true"; else DisableRAWdata="false"; fi fi echo "Set Value: ("$DisableRAWdata")" echo " " echo "returning..." sleep 2 ;; E) clear echo "Ignore Alarm Settings" echo " " echo "Ignore UDMA CRC Errors ("$IgnoreUDMA") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then IgnoreUDMA="true"; else IgnoreUDMA="false"; fi fi echo "Set Value: ("$IgnoreUDMA")" echo " " echo "Ignore Raw Read Errors ("$IgnoreReadError") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then IgnoreReadError="true"; else IgnoreReadError="false"; fi fi echo "Set Value: ("$IgnoreReadError")" echo " " echo "Ignore Seek Errors ("$IgnoreSeekError") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then IgnoreSeekError="true"; else IgnoreSeekError="false"; fi fi echo "Set Value: ("$IgnoreSeekError")" echo " " echo "Ignore MultiZone Errors ("$IgnoreMultiZone") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then IgnoreMultiZone="true"; else IgnoreMultiZone="false"; fi fi echo "Set Value: ("$IgnoreMultiZone")" echo " " echo "Disable Warranty Email Header Warning ("$DisableWarranty") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then DisableWarranty="true"; else DisableWarranty="false"; fi fi echo "Set Value: ("$DisableWarranty")" echo " " echo "ATA Auto Enable ("$ATA_Auto_Enable") " echo 'Set to "true" to automatically update Log Error count to ONLY display a log' echo "error when a new one occurs." read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then ATA_Auto_Enable="true"; else ATA_Auto_Enable="false"; fi fi echo "Set Value: ("$ATA_Auto_Enable")" echo " " echo "returning..." sleep 2 ;; F) clear echo "Monitor Email Settings" echo " " echo "AlertOnWarningTemp ("$AlertOnWarningTemp") " echo 'Set to "true" will send Temperature Warnings to the Monitor Email address' echo 'when using the "-m" switch. (t/f)' read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then AlertOnWarningTemp="true"; else AlertOnWarningTemp="false"; fi fi echo "Set Value: ("$AlertOnWarningTemp")" echo " " echo "AlertOnCriticalError ("$AlertOnCriticalError") " echo 'Set to "true" will send Critical Alarms to the Monitor Email address' echo 'when using the "-m" switch. (t/f)' read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then AlertOnCriticalError="true"; else AlertOnCriticalError="false"; fi fi echo "Set Value: ("$AlertOnCriticalError")" echo " " ;; X) clear echo "Returning to the previous menu..." sleep 2 y=100 ;; *) echo "Invalid Option" sleep 2 ;; esac done ;; B) clear echo "TrueNAS Configuration Backup Setup" echo " " echo "Configuration Backup Enabled ("$TrueNASConfigEmailEnable") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then TrueNASConfigEmailEnable="true"; else TrueNASConfigEmailEnable="false"; fi fi echo "Set Value: ("$TrueNASConfigEmailEnable")" echo " " if [[ $TrueNASConfigEmailEnable == "true" ]]; then echo "Save a local copy of the config-backup file (t/f) ("$TrueNASConfigBackupSave") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then TrueNASConfigBackupSave="true"; else TrueNASConfigBackupSave="false"; fi fi echo "Set Value: ("$TrueNASConfigBackupSave")" if [[ $TrueNASConfigBackupSave == "true" ]]; then echo " " echo "TrueNAS Backup Configuration file location ("$TrueNASConfigBackupLocation")" echo "Enter new location or press Enter/Return to accept current value" echo "NOTE: Do not use any spaces." echo -n ": " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then TrueNASConfigBackupLocation=$Keyboard_yn; fi echo "Set Value: ("$TrueNASConfigBackupLocation")" fi echo " " echo "What day of the week would you like the file attached?" echo "Current Value: "$TrueNASConfigEmailDay echo "(All, Mon, Tue, Wed, Thu, Fri, Sat, Sun, Month)" echo -n ": " read Keyboard_HDD if [[ ! $Keyboard_HDD == "" ]]; then TrueNASConfigEmailDay=$Keyboard_HDD; fi echo "Set Value: ("$TrueNASConfigEmailDay")" fi echo " " echo '"multi_report_config.txt" Backup Setup' echo " " echo "Enable sending multi_report_config.txt file" echo "(will enable next two options if true) (t/f) ("$MRConfigEmailEnable") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then MRConfigEmailEnable="true"; else MRConfigEmailEnable="false"; fi fi echo "Set Value: ("$MRConfigEmailEnable")" if [[ $MRConfigEmailEnable == "true" ]]; then echo " " echo "What day of the week would you like the file attached?" echo "Current Value: "$MRConfigEmailDay echo "(All, Mon, Tue, Wed, Thu, Fri, Sat, Sun, Month, Never)" echo -n ": " read Keyboard_HDD if [[ ! $Keyboard_HDD == "" ]]; then MRConfigEmailDay=$Keyboard_HDD; fi echo "Set Value: ("$MRConfigEmailDay")" echo " " echo " " echo "Send email of multi_report_config.txt file for any change (t/f) ("$MRChangedEmailSend") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then MRChangedEmailSend="true"; else MRChangedEmailSend="false"; fi fi echo "Set Value: ("$MRChangedEmailSend")" fi echo " " echo "returning..." sleep 2 ;; C) clear echo "Email Settings" echo " " echo "Current Email address(s): "$Email" " echo " " echo "Separate multiple email addresses with a comma " echo 'Enter nothing to accept the default or change it to something new.' echo -n ": " read Keyboard_Email if [[ ! $Keyboard_Email == "" ]]; then Email=$Keyboard_Email; fi echo "Set Value: "$Email echo " " echo "Current Email address(s): "$AlertEmail" " echo " " echo "Enter your Drive temperature monitoring Email address. This is the email" echo "or series of emails (comma separated) address(s) to send an alert to." echo "press Return to use your normal email address" echo -n ": " read Keyboard_Email if [[ $Keyboard_Email == "" ]]; then echo "Using normal Email address..." AlertEmail=$Email fi if [[ ! $Keyboard_Email == "" ]]; then AlertEmail=$Keyboard_Email; fi echo "Set Value: "$AlertEmail echo " " echo "Current from address: "$From" " echo 'While most people are able to use the default "from" address,' echo 'Some email servers will not work unless you use the email address' echo 'the email address the server is associated with.' echo 'Enter nothing to accept the default or change it.' echo -n ": " read Keyboard_Email if [[ ! $Keyboard_Email == "" ]]; then From=$Keyboard_Email; fi echo "Set Value: "$From echo " " echo "Current TrueNAS Configuraiton File passphrase: "$TrueNASConfigEmailEncryption echo " " echo 'This will encrypt just the TrueNAS Configuraiton' echo 'Backup file. Use 7-zip, PKZIP, WinZip or similar' echo 'to decrypt it.' echo 'Press Enter/Return to accept the current passphrase,' echo 'Enter "disable" to disable, or type a new passphrase.' echo -n ": " read Keyboard_Email if [[ ! $Keyboard_Email == "" ]]; then TrueNASConfigEmailEncryption=$Keyboard_Email; fi if [[ $Keyboard_Email == "disable" ]]; then TrueNASConfigEmailEncryption=""; fi echo "Set Value: "$TrueNASConfigEmailEncryption echo " " if [[ $Keyboard_Email == "" ]]; then echo "No change" else echo "Writing Passphrase into Script Now..." sed '5s/.*/TrueNASConfigEmailEncryption="'$TrueNASConfigEmailEncryption'" # Set this to "" for no encryption or enter some text as your passphrase./' "$mefull" > /tmp/multi_report_update.txt cp "/tmp/multi_report_update.txt" "$mefull" rm "/tmp/multi_report_update.txt" fi echo "You still need to write any other changes made such as an email address." echo "returning..." sleep 2 ;; D) clear echo "HDD Column Selection" echo " " echo "This does not disable/enable any alarm settings." echo " " echo -n "Device ID ("$HDD_Device_ID") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HDD_Device_ID="true"; else HDD_Device_ID="false"; fi fi echo "Set Value: ("$HDD_Device_ID")" echo " " echo -n "Serial Number ("$HDD_Serial_Number") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HDD_Serial_Number="true"; else HDD_Serial_Number="false"; fi fi echo "Set Value: ("$HDD_Serial_Number")" echo " " echo -n "Model Number ("$HDD_Model_Number") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HDD_Model_Number="true"; else HDD_Model_Number="false"; fi fi echo "Set Value: ("$HDD_Model_Number")" echo " " echo -n "Capacity ("$HDD_Capacity") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HDD_Capacity="true"; else HDD_Capacity="false"; fi fi echo "Set Value: ("$HDD_Capacity")" echo " " echo -n "Rotational Rate ("$HDD_Rotational_Rate") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HDD_Rotational_Rate="true"; else HDD_Rotational_Rate="false"; fi fi echo "Set Value: ("$HDD_Rotational_Rate")" echo " " echo -n "SMART Status ("$HDD_SMART_Status") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HDD_SMART_Status="true"; else HDD_SMART_Status="false"; fi fi echo "Set Value: ("$HDD_SMART_Status")" echo " " echo -n "Warranty ("$HDD_Warranty") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HDD_Warranty="true"; else HDD_Warranty="false"; fi fi echo "Set Value: ("$HDD_Warranty")" echo " " echo -n "Drive Temp ("$HDD_Drive_Temp") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HDD_Drive_Temp="true"; else HDD_Drive_Temp="false"; fi fi echo "Set Value: ("$HDD_Drive_Temp")" echo " " echo -n "Drive Temp Minimum for power cycle ("$HDD_Drive_Temp_Min") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HDD_Drive_Temp_Min="true"; else HDD_Drive_Temp_Min="false"; fi fi echo "Set Value: ("$HDD_Drive_Temp_Min")" echo " " echo -n "Drive Temp Maximum for power cycle ("$HDD_Drive_Temp_Max") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HDD_Drive_Temp_Max="true"; else HDD_Drive_Temp_Max="false"; fi fi echo "Set Value: ("$HDD_Drive_Temp_Max")" echo " " echo -n "Power On Hours ("$HDD_Power_On_Hours") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HDD_Power_On_Hours="true"; else HDD_Power_On_Hours="false"; fi fi echo "Set Value: ("$HDD_Power_On_Hours")" echo " " echo -n "Start / Stop Count ("$HDD_Start_Stop_Count") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HDD_Start_Stop_Count="true"; else HDD_Start_Stop_Count="false"; fi fi echo "Set Value: ("$HDD_Start_Stop_Count")" echo " " echo -n "Load Cycle Count ("$HDD_Load_Cycle") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HDD_Load_Cycle="true"; else HDD_Load_Cycle="false"; fi fi echo "Set Value: ("$HDD_Load_Cycle")" echo " " echo -n "Spin Retry Count ("$HDD_Spin_Retry") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HDD_Spin_Retry="true"; else HDD_Spin_Retry="false"; fi fi echo "Set Value: ("$HDD_Spin_Retry")" echo " " echo -n "Reallocated Sectors ("$HDD_Reallocated_Sectors") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HDD_Reallocated_Sectors="true"; else HDD_Reallocated_Sectors="false"; fi fi echo "Set Value: ("$HDD_Reallocated_Sectors")" echo " " echo -n "Reallocated Events ("$HDD_Reallocated_Events") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HDD_Reallocated_Events="true"; else HDD_Reallocated_Events="false"; fi fi echo "Set Value: ("$HDD_Reallocated_Events")" echo " " echo -n "Pending Sectors ("$HDD_Pending_Sectors") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HDD_Pending_Sectors="true"; else HDD_Pending_Sectors="false"; fi fi echo "Set Value: ("$HDD_Pending_Sectors")" echo " " echo -n "Offline Uncorrectable Errors ("$HDD_Offline_Uncorrectable") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HDD_Offline_Uncorrectable="true"; else HDD_Offline_Uncorrectable="false"; fi fi echo "Set Value: ("$HDD_Offline_Uncorrectable")" echo " " echo -n "UDMA CRC Errors ("$HDD_UDMA_CRC_Errors_List") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HDD_UDMA_CRC_Errors_List="true"; else HDD_UDMA_CRC_Errors_List="false"; fi fi echo "Set Value: ("$HDD_UDMA_CRC_Errors_List")" echo " " echo -n "Raw Read Error Rate ("$HDD_Raw_Read_Error_Rate") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HDD_Raw_Read_Error_Rate="true"; else HDD_Raw_Read_Error_Rate="false"; fi fi echo "Set Value: ("$HDD_Raw_Read_Error_Rate")" echo " " echo -n "Seek Error Rate ("$HDD_Seek_Error_Rate") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HDD_Seek_Error_Rate="true"; else HDD_Seek_Error_Rate="false"; fi fi echo "Set Value: ("$HDD_Seek_Error_Rate")" echo " " echo -n "MultiZone Errors ("$HDD_MultiZone_Errors") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HDD_MultiZone_Errors="true"; else HDD_MultiZone_Errors="false"; fi fi echo "Set Value: ("$HDD_MultiZone_Errors")" echo " " echo -n "Helium Level ("$HDD_Helium_Level") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HDD_Helium_Level="true"; else HDD_Helium_Level="false"; fi fi echo "Set Value: ("$HDD_Helium_Level")" echo " " echo -n "Last Test Age ("$HDD_Last_Test_Age") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HDD_Last_Test_Age="true"; else HDD_Last_Test_Age="false"; fi fi echo "Set Value: ("$HDD_Last_Test_Age")" echo " " echo -n "Last Test Type ("$HDD_Last_Test_Type") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HDD_Last_Test_Type="true"; else HDD_Last_Test_Type="false"; fi fi echo "Set Value: ("$HDD_Last_Test_Type")" echo " " echo -n "Total Data Written ("$HDD_Total_Data_Written") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then HDD_Total_Data_Written="true"; else HDD_Total_Data_Written="false"; fi fi echo "Set Value: ("$HDD_Total_Data_Written")" echo " " echo " " echo "returning..." sleep 2 ;; E) clear echo "SSD Column Selection" echo " " echo "This does not disable/enable any alarm settings." echo " " echo -n "Device ID ("$SSD_Device_ID") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then SSD_Device_ID="true"; else SSD_Device_ID="false"; fi fi echo "Set Value: ("$SSD_Device_ID")" echo " " echo -n "Serial Number ("$SSD_Serial_Number") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then SSD_Serial_Number="true"; else SSD_Serial_Number="false"; fi fi echo "Set Value: ("$SSD_Serial_Number")" echo " " echo -n "Model Number ("$SSD_Model_Number") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then SSD_Model_Number="true"; else SSD_Model_Number="false"; fi fi echo "Set Value: ("$SSD_Model_Number")" echo " " echo -n "Capacity ("$SSD_Capacity") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then SSD_Capacity="true"; else SSD_Capacity="false"; fi fi echo "Set Value: ("$SSD_Capacity")" echo " " echo -n "SMART Status ("$SSD_SMART_Status") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then SSD_SMART_Status="true"; else SSD_SMART_Status="false"; fi fi echo "Set Value: ("$SSD_SMART_Status")" echo " " echo -n "Drive Temp ("$SSD_Drive_Temp") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then SSD_Drive_Temp="true"; else SSD_Drive_Temp="false"; fi fi echo "Set Value: ("$SSD_Drive_Temp")" echo " " echo -n "Warranty ("$SSD_Warranty") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then SSD_Warranty="true"; else SSD_Warranty="false"; fi fi echo "Set Value: ("$SSD_Warranty")" echo " " echo -n "Drive Temp Minimum for power cycle ("$SSD_Drive_Temp_Min") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then SSD_Drive_Temp_Min="true"; else SSD_Drive_Temp_Min="false"; fi fi echo "Set Value: ("$SSD_Drive_Temp_Min")" echo " " echo -n "Drive Temp Maximum for power cycle ("$SSD_Drive_Temp_Max") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then SSD_Drive_Temp_Max="true"; else SSD_Drive_Temp_Max="false"; fi fi echo "Set Value: ("$SSD_Drive_Temp_Max")" echo " " echo -n "Power On Hours ("$SSD_Power_On_Hours") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then SSD_Power_On_Hours="true"; else SSD_Power_On_Hours="false"; fi fi echo "Set Value: ("$SSD_Power_On_Hours")" echo " " echo -n "Wear Level ("$SSD_Wear_Level") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then SSD_Wear_Level="true"; else SSD_Wear_Level="false"; fi fi echo "Set Value: ("$SSD_Wear_Level")" echo " " echo -n "Reallocated Sectors ("$SSD_Reallocated_Sectors") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then SSD_Reallocated_Sectors="true"; else SSD_Reallocated_Sectors="false"; fi fi echo "Set Value: ("$SSD_Reallocated_Sectors")" echo " " echo -n "Reallocated Events ("$SSD_Reallocated_Events") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then SSD_Reallocated_Events="true"; else SSD_Reallocated_Events="false"; fi fi echo "Set Value: ("$SSD_Reallocated_Events")" echo " " echo -n "Pending Sectors ("$SSD_Pending_Sectors") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then SSD_Pending_Sectors="true"; else SSD_Pending_Sectors="false"; fi fi echo "Set Value: ("$SSD_Pending_Sectors")" echo " " echo -n "Offline Uncorrectable Errors ("$SSD_Offline_Uncorrectable") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then SSD_Offline_Uncorrectable="true"; else SSD_Offline_Uncorrectable="false"; fi fi echo "Set Value: ("$SSD_Offline_Uncorrectable")" echo " " echo -n "UDMA CRC Errors ("$SSD_UDMA_CRC_Errors_List") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then SSD_UDMA_CRC_Errors_List="true"; else SSD_UDMA_CRC_Errors_List="false"; fi fi echo "Set Value: ("$SSD_UDMA_CRC_Errors_List")" echo " " echo -n "Last Test Age ("$SSD_Last_Test_Age") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then SSD_Last_Test_Age="true"; else SSD_Last_Test_Age="false"; fi fi echo "Set Value: ("$SSD_Last_Test_Age")" echo " " echo -n "Last Test Type ("$SSD_Last_Test_Type") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then SSD_Last_Test_Type="true"; else SSD_Last_Test_Type="false"; fi fi echo "Set Value: ("$SSD_Last_Test_Type")" echo " " echo -n "Total Data Written ("$SSD_Total_Data_Written") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then SSD_Total_Data_Written="true"; else SSD_Total_Data_Written="false"; fi fi echo "Set Value: ("$SSD_Total_Data_Written")" echo " " echo " " echo "returning..." sleep 2 ;; F) clear echo "NVMe Column Selection" echo " " echo "This does not disable/enable any alarm settings." echo " " echo -n "Device ID ("$NVM_Device_ID") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then NVM_Device_ID="true"; else NVM_Device_ID="false"; fi fi echo "Set Value: ("$NVM_Device_ID")" echo " " echo -n "Serial Number ("$NVM_Serial_Number") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then NVM_Serial_Number="true"; else NVM_Serial_Number="false"; fi fi echo "Set Value: ("$NVM_Serial_Number")" echo " " echo -n "Model Number ("$NVM_Model_Number") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then NVM_Model_Number="true"; else NVM_Model_Number="false"; fi fi echo "Set Value: ("$NVM_Model_Number")" echo " " echo -n "Capacity ("$NVM_Capacity") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then NVM_Capacity="true"; else NVM_Capacity="false"; fi fi echo "Set Value: ("$NVM_Capacity")" echo " " echo -n "SMART Status ("$NVM_SMART_Status") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then NVM_SMART_Status="true"; else NVM_SMART_Status="false"; fi fi echo "Set Value: ("$NVM_SMART_Status")" echo " " echo -n "Warranty ("$NVM_Warranty") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then NVM_Warranty="true"; else NVM_Warranty="false"; fi fi echo "Set Value: ("$NVM_Warranty")" echo " " echo -n "Critical Warning Status ("$NVM_Critical_Warning") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then NVM_Critical_Warning="true"; else NVM_Critical_Warning="false"; fi fi echo "Set Value: ("$NVM_Critical_Warning")" echo " " echo -n "Drive Temp ("$NVM_Drive_Temp") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then NVM_Drive_Temp="true"; else NVM_Drive_Temp="false"; fi fi echo "Set Value: ("$NVM_Drive_Temp")" echo " " echo -n "Drive Temp Minimum for power cycle ("$NVM_Drive_Temp_Min") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then NVM_Drive_Temp_Min="true"; else NVM_Drive_Temp_Min="false"; fi fi echo "Set Value: ("$NVM_Drive_Temp_Min")" echo " " echo -n "Drive Temp Maximum for power cycle ("$NVM_Drive_Temp_Max") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then NVM_Drive_Temp_Max="true"; else NVM_Drive_Temp_Max="false"; fi fi echo "Set Value: ("$NVM_Drive_Temp_Max")" echo " " echo -n "Power On Hours ("$NVM_Power_On_Hours") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then NVM_Power_On_Hours="true"; else NVM_Power_On_Hours="false"; fi fi echo "Set Value: ("$NVM_Power_On_Hours")" echo " " echo -n "Wear Level ("$NVM_Wear_Level") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then NVM_Wear_Level="true"; else NVM_Wear_Level="false"; fi fi echo "Set Value: ("$NVM_Wear_Level")" echo " " echo -n "Media Errors ("$NVM_Media_Error") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then NVM_Media_Error="true"; else NVM_Media_Error="false"; fi fi echo "Set Value: ("$NVM_Media_Error")" echo " " echo -n "Total Data Written ("$NVM_Total_Data_Written") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then NVM_Total_Data_Written="true"; else NVM_Total_Data_Written="false"; fi fi echo "Set Value: ("$NVM_Total_Data_Written")" echo " " echo " " echo "returning..." sleep 2 ;; G) clear echo "Output Formats" echo " " echo "Power On Hours Time Format ("$PowerTimeFormat") " echo -n "valid options are "ymdh", "ymd", "ym", "y", or "h" (year month day hour): " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then PowerTimeFormat=$Keyboard_yn; fi echo "Set Value: "$PowerTimeFormat echo " " echo "Temperature Display ("$TempDisplay") " echo -n "You may use anything you desire. Common formats are: *C, ^C, or ^c: " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then TempDisplay=$Keyboard_yn; fi echo "Set Value: "$TempDisplay echo " " echo "Non-existent Value ("$Non_Exist_Value") " echo "You may use anything you desire. Common formats are: ---, N/A, or a" echo -n "space character: " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then Non_Exist_Value=$Keyboard_yn; fi echo "Set Value: "$Non_Exist_Value echo " " echo "Pool Size and Free Space" echo "ZFS is the most accurate and conforms to the GUI values." echo "Current Value: ("$Pool_Capacity_Type") " echo -n "Enter 'zfs' or 'zpool' or Enter/Return for unchanged: " read Keyboard_yn if [[ $Keyboard_yn != "" ]] && [[ $Keyboard_yn != "zfs" ]] && [[ $Keyboard_yn != "zpool" ]]; then echo "INCORRECT VALUE!: "$Keyboard_yn echo "Setting default value: 'zfs'" Keyboard_yn="zfs" fi if [[ ! $Keyboard_yn == "" ]]; then Pool_Capacity_Type=$Keyboard_yn; fi echo "Set Value: "$Pool_Capacity_Type echo " " echo "Mouseover" echo "This will allow any 'normalized' value to display the actual value" echo "using one of three options:" echo "'t'=true - Mouseover will popup the actual number over the normalized number." echo "'f'=false - Only display the normalized value. (default)" echo "'alt'=alternate - Will display the actual number in parentheses." echo " " echo "Current value: ("$Mouseover")" echo "Enter 't' (true), 'f' (false), or 'a' (alt) :" read -s -n 1 Keyboard_yn if [[ $Keyboard_yn != "" ]] && [[ $Keyboard_yn != "t" ]] && [[ $Keyboard_yn != "f" ]] && [[ $Keyboard_yn != "a" ]]; then echo "INCORRECT VALUE!: "$Keyboard_yn echo "Setting default value" Keyboard_yn="f" fi if [[ $Keyboard_yn == "t" ]]; then Keyboard_yn="true"; fi if [[ $Keyboard_yn == "f" ]]; then Keyboard_yn="false"; fi if [[ $Keyboard_yn == "a" ]]; then Keyboard_yn="alt"; fi if [[ ! $Keyboard_yn == "" ]]; then Mouseover=$Keyboard_yn; fi echo "Set Value: ("$Mouseover")" echo " " echo "returning..." sleep 2 ;; H) clear echo "Report Header Titles" echo " " echo 'Current HDD Report Header: "'$HDDreportTitle'" ' echo -n 'Enter new value or Return to accept current value: ' read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then HDDreportTitle=$Keyboard_yn; fi echo 'Set Value: "'$HDDreportTitle'"' echo " " echo 'Current SSD Report Header: "'$SSDreportTitle'" ' echo -n 'Enter new value or Return to accept current value: ' read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then SSDreportTitle=$Keyboard_yn; fi echo 'Set Value: "'$SSDreportTitle'"' echo " " echo 'Current NVM Report Header: "'$NVMreportTitle'" ' echo -n 'Enter new value or Return to accept current value: ' read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then NVMreportTitle=$Keyboard_yn; fi echo 'Set Value: "'$NVMreportTitle'"' echo " " echo "Enable/Disable Text Section" echo "This will display (true) or remove (false) the Text Section of the email report." echo 'Current value: "'$Enable_Text_Section'" ' echo 'Enter new value or Return to accept current value: ' read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then Enable_Text_Section="true"; else Enable_Text_Section="false"; fi fi echo 'Set Value: "'$Enable_Text_Section'"' echo " " echo "returning..." sleep 2 ;; I) clear echo "Statistical Data Setup" echo " " echo " " echo -n "Statistical Data Recording Enabled ("$SDF_DataRecordEnable") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then SDF_DataRecordEnable="true"; else SDF_DataRecordEnable="false"; fi fi echo "Set Value: ("$SDF_DataRecordEnable")" if [[ $SDF_DataRecordEnable == "true" ]]; then echo " " echo -n "Statistical Data Email Enabled ("$SDF_DataEmail") " read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then SDF_DataEmail="true"; else SDF_DataEmail="false"; fi fi echo "Set Value: ("$SDF_DataEmail")" echo " " echo "Statistical file location and name:" echo "("$statistical_data_file")" echo "Enter new location and file name or press Enter/Return to accept current value:" read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then statistical_data_file=$Keyboard_yn; fi echo "Set Value: ("$statistical_data_file")" echo " " echo -n "Statistical Data Purge Days ("$SDF_DataPurgeDays") " read Keyboard_HDD if [[ ! $Keyboard_HDD == "" ]]; then SDF_DataPurgeDays=$Keyboard_HDD; fi echo "Set Value: ("$SDF_DataPurgeDays")" echo " " echo "What day of the week would you like the file attached?" echo "Current Value: "$SDF_DataEmailDay echo "(All, Mon, Tue, Wed, Thu, Fri, Sat, Sun, Month)" echo -n "Enter: " read Keyboard_SDF_DataEmailDay if [[ ! $Keyboard_SDF_DataEmailDay == "" ]]; then SDF_DataEmailDay=$Keyboard_SDF_DataEmailDay; fi echo "Set Value: ("$SDF_DataEmailDay")" fi echo " " echo "returning..." sleep 2 ;; J) clear echo "Activate TLER" echo " " echo " " echo "Activate TLER ("$SCT_Enable") " echo "true = This will attempt to turn on TLER if the drive is reporting it is off." read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then SCT_Enable="true"; else SCT_Enable="false"; fi fi echo "Set Value: ("$SCT_Enable")" if [[ $SCT_Enable == "true" ]]; then echo " " echo "TLER Warning Level: ("$SCT_Warning_Level") " echo " 1) TLER_No_Msg = Only generate an error message if TLER cannot be turned on for" echo " a supported drive." echo " 2) TLER = Report error messages in WARNING Section and email header." echo " 3) all = Report drive which also do not support TLER." echo " " echo "Note: The default 'TLER_No_Msg' is recommended." echo -n "Enter: " read Keyboard_SCT_Warning_Level if [[ $Keyboard_SCT_Warning_Level == "1" ]]; then SCT_Warning_Level="TLER_No_Msg"; fi if [[ $Keyboard_SCT_Warning_Level == "2" ]]; then SCT_Warning_Level="TLER"; fi if [[ $Keyboard_SCT_Warning_Level == "3" ]]; then SCT_Warning_Level="all"; fi echo "Set Value: ("$SCT_Warning_Level")" echo " " echo -n "SCT Read Timeout Setting ("$SCT_Read_Timeout") " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then SCT_Read_Timeout=$Keyboard_yn; fi echo "Set Value: ("$SCT_Read_Timeout")" echo " " echo -n "SCT Write Timeout Setting ("$SCT_Write_Timeout") " read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then SCT_Write_Timeout=$Keyboard_yn; fi echo "Set Value: ("$SCT_Write_Timeout")" fi echo " " echo "returning..." sleep 2 ;; K) clear echo "Drive Errors and Custom Builds" echo " " echo "Collecting data, Please wait..." # Lets go ahead and grab all the drive data we will need for the entire K section. get_smartHDD_listings get_smartSSD_listings get_smartNVM_listings smartdrivesall="$smartdrives $smartdrivesSSD $smartdrivesNVM" echo " " echo "NOTE: Entering 'd' will delete the data and move to the next section." echo " " echo "Ignore Drives - Enter drive serial numbers, multiple drives are separated" echo "by a comma." echo "Current: "$Ignore_Drives_List echo " " echo "Enter/Return to accept the current value(s) or press 'e' to Edit," echo "or 'd' to delete the data." read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then Ignore_Drives_List=$Keyboard_yn; fi if [[ $Keyboard_yn == "d" ]]; then Ignore_Drives_List="none"; fi if [[ $Keyboard_yn == "e" ]]; then # Let's list each drive and ask to keep or reject for drive in $smartdrivesall; do clear_variables get_drive_data echo " " echo "Do you want to ignore this drive (y/n): Drive ID: "$drive" Serial Number: "$serial read -s -n 1 Keyboard_yn if [[ $Keyboard_yn == "y" ]]; then ignoredriveslist=$ignoredriveslist$serial","; fi echo "Updated Value="$ignoredriveslist done if [[ ! $ignoredriveslist == "" ]]; then Ignore_Drives_List="$(echo "$ignoredriveslist" | sed 's/.$//')"; else Ignore_Drives_List="none"; fi fi echo "Set Value: "$Ignore_Drives_List echo " " echo "Press any key to continue" read -s -n 1 Keyboard_yn clear echo "Drive Errors and Custom Builds" echo " " echo "AUTOMATIC DRIVE COMPENSATION - UDMA_CRC, MultiZone, and Reallocated Sectors" echo " " echo "You have the option to automatically setup offset values for UDMA_CRC," echo "MultiZone, and Bad Sectors that are permanently recorded on the drives." echo " " echo "This will create an offset to be displayed in the charts resulting in a" echo "zero value and the value will be colored in light yellow by default." echo "You will be able to see any future changes/failures which occur." echo " " echo "If you select No, you will be allowed to manually enter each offset." echo " " echo "Enter 'y' for yes or 'n' for no, or Return to skip this section." read -s -n 1 Keyboard_yn if [[ $Keyboard_yn == "y" ]]; then autoselect=1 echo "Automatic Configuration selected..." echo " " for drive in $smartdrivesall; do clear_variables get_drive_data if [[ ! $crcErrors == "0" ]] && [[ ! $crcErrors == "" ]]; then listofdrivescrc="$listofdrivescrc$serial":"$crcErrors,"; fi if [[ ! $multiZone == "0" ]] && [[ ! $multiZone == "" ]]; then listofdrivesmulti="$listofdrivesmulti$serial":"$multiZone,"; fi if [[ ! $reAlloc == "0" ]] && [[ ! $reAlloc == "" ]]; then listofdrivesbad="$listofdrivesbad$serial":"$reAlloc,"; fi if [[ ! $reAllocEvent == "0" ]] && [[ ! $reAllocEvent == "" ]]; then listofdrivesbad2="$listofdrivesbad2$serial":"$reAllocEvent,"; fi done echo "Scanning Results:" if [[ ! $listofdrivescrc == "" ]]; then CRC_Errors_List="$(echo "$listofdrivescrc" | sed 's/.$//')"; echo "UDMA_CRC Errors detected"; else CRC_Errors_List=""; echo "No UDMA_CRC Errors"; fi if [[ ! $listofdrivesmulti == "" ]]; then MultiZone_List="$(echo "$listofdrivesmulti" | sed 's/.$//')"; echo "MultiZone Errors Detected"; else MultiZone_List=""; echo "No MultiZone Errors"; fi if [[ ! $listofdrivesbad == "" ]]; then ReAllocated_Sector_List="$(echo "$listofdrivesbad" | sed 's/.$//')"; echo "Bad Sectors Detected"; else ReAllocated_Sector_List=""; echo "No Reallocated Sectors"; fi if [[ ! $listofdrivesbad2 == "" ]]; then ReAllocated_Sector_Events_List="$(echo "$listofdrivesbad2" | sed 's/.$//')"; echo "Bad Sectors Detected"; else ReAllocated_Sector_Events_List=""; echo "No Reallocated Sectors"; fi echo " " echo "Values Set:" echo "CRC_Errors_List: "$CRC_Errors_List echo "MultiZone_List_Errors: "$MultiZone_List echo "Reallocated_Sectors: "$ReAllocated_Sector_List echo "Reallocated_Sectors_Events: "$ReAllocated_Sector_Events_List echo " " fi if [[ ! $autoselect == "1" ]] && [[ $Keyboard_yn == "n" ]]; then clear echo "Drive Errors and Custom Builds" echo " " echo "Offset UDMA CRC Errors" echo "Press 'd' to delete, 'e' to edit, or Enter/Return to accept." echo "Current List: "$CRC_Errors_List read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then CRC_Errors_List=$Keyboard_yn; fi if [[ $Keyboard_yn == "d" ]]; then CRC_Errors_List=""; Keyboard_yn=""; fi if [[ $Keyboard_yn == "e" ]]; then # Let's list each drive and ask to keep or reject drive_select="" for drive in $smartdrivesall; do clear_variables get_drive_data echo " " echo "Do you want to add this drive (y/n): Drive ID: "$drive" Serial Number: "$serial read -s -n 1 Keyboard_yn if [[ $Keyboard_yn == "y" ]]; then drive_select=$drive_select$serial":" echo "Enter the sector count offset you desire: " read Keyboard_yn drive_select=$drive_select$Keyboard_yn"," echo "drive_select="$drive_select fi done if [[ ! $drive_select == "" ]]; then CRC_Errors_List="$(echo "$drive_select" | sed 's/.$//')"; else CRC_Errors_List="none"; fi fi echo "Set Value: "$CRC_Errors_List echo " " echo "Press any key to continue" read -s -n 1 Keyboard_yn clear echo "Drive Errors and Custom Builds" echo " " echo "Offset MultiZone Errors" echo "Press 'd' to delete, 'e' to edit, or Enter/Return to accept." echo "Current: "$MultiZone_List read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then MultiZone_List=$Keyboard_yn; fi if [[ $Keyboard_yn == "d" ]]; then MultiZone_List=""; fi if [[ $Keyboard_yn == "e" ]]; then # Let's list each drive and ask to keep or reject drive_select="" for drive in $smartdrivesall; do clear_variables get_drive_data echo " " echo "Do you want to add this drive (y/n): Drive ID: "$drive" Serial Number: "$serial read -s -n 1 Keyboard_yn if [[ $Keyboard_yn == "y" ]]; then drive_select=$drive_select$serial":" echo "Enter the MultiZone_List count offset you desire: " read Keyboard_yn drive_select=$drive_select$Keyboard_yn"," echo "drive_select="$drive_select fi done if [[ ! $drive_select == "" ]]; then MultiZone_List="$(echo "$drive_select" | sed 's/.$//')"; else MultiZone_List="none"; fi fi echo "Set Value: "$MultiZone_List echo " " echo "Press any key to continue" read -s -n 1 Keyboard_yn clear echo "Drive Errors and Custom Builds" echo " " echo "Offset Bad Sector Errors" echo "Press 'd' to delete, 'e' to edit, or Enter/Return to accept." echo "Current: "$ReAllocated_Sector_List read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then ReAllocated_Sector_List=$Keyboard_yn; fi if [[ $Keyboard_yn == "d" ]]; then ReAllocated_Sector_List=""; fi if [[ $Keyboard_yn == "e" ]]; then # Let's list each drive and ask to keep or reject drive_select="" for drive in $smartdrivesall; do clear_variables get_drive_data echo " " echo "Do you want to add this drive (y/n): Drive ID: "$drive" Serial Number: "$serial read -s -n 1 Keyboard_yn if [[ $Keyboard_yn == "y" ]]; then drive_select=$drive_select$serial":" echo "Enter the Bad Sector count offset you desire: " read Keyboard_yn drive_select=$drive_select$Keyboard_yn"," echo "drive_select="$drive_select fi done if [[ ! $drive_select == "" ]]; then ReAllocated_Sector_List="$(echo "$drive_select" | sed 's/.$//')"; else ReAllocated_Sector_List="none"; fi fi echo "Set Value: "$ReAllocated_Sector_List echo " " echo "Press any key to continue" read -s -n 1 Keyboard_yn clear echo "Drive Errors and Custom Builds" echo " " echo "Offset Bad Sector Event Errors" echo "Press 'd' to delete, 'e' to edit, or Enter/Return to accept." echo "Current: "$ReAllocated_Sector_Events_List read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then ReAllocated_Sector_Events_List=$Keyboard_yn; fi if [[ $Keyboard_yn == "d" ]]; then ReAllocated_Sector_Events_List=""; fi if [[ $Keyboard_yn == "e" ]]; then # Let's list each drive and ask to keep or reject drive_select="" for drive in $smartdrivesall; do clear_variables get_drive_data echo " " echo "Do you want to add this drive (y/n): Drive ID: "$drive" Serial Number: "$serial read -s -n 1 Keyboard_yn if [[ $Keyboard_yn == "y" ]]; then drive_select=$drive_select$serial":" echo "Enter the Bad Sector count offset you desire: " read Keyboard_yn drive_select=$drive_select$Keyboard_yn"," echo "drive_select="$drive_select fi done if [[ ! $drive_select == "" ]]; then ReAllocated_Sector_Events_List="$(echo "$drive_select" | sed 's/.$//')"; else ReAllocated_Sector_Events_List="none"; fi fi echo "Set Value: "$ReAllocated_Sector_Events_List fi echo " " echo "Press any key to continue" read -s -n 1 Keyboard_yn clear echo "Drive Errors and Custom Builds" echo " " echo "Automatic ATA Error Count Updates - This will automatically have the script" echo "update the multi_report_config.txt file with the current Error Log count." echo "This might be desirable if you have a drive that keeps throwing minor errors." echo "Enter/Return to keep current value, 't' (enable), or 'f' (disable) this feature." echo "Current: "$ATA_Auto_Enable read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then if [[ $Keyboard_yn == "t" ]]; then ATA_Auto_Enable="true"; else ATA_Auto_Enable="false"; fi; fi echo "Set Value: "$ATA_Auto_Enable echo " " echo "Press any key to continue" read -s -n 1 Keyboard_yn clear echo "Drive Errors and Custom Builds" echo " " echo "ATA Error Count - This will ignore any drive with an error count less than" echo "the number provided. When the drive errors exceed this value then the" echo "Error Log will be present again." echo "Enter 'd' to delete, 'e' to edit, or Enter/Return for no change." echo "Current: "$ATA_Errors read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then ATA_Errors=$Keyboard_yn; fi if [[ $Keyboard_yn == "d" ]]; then ATA_Errors=""; fi if [[ $Keyboard_yn == "e" ]]; then ATA_Errors="" for drive in $smartdrivesall; do clear_variables get_drive_data echo " " echo "Do you want to add this drive (y/n): Drive ID: "$drive" Serial Number: "$serial read -s -n 1 Keyboard_yn if [[ $Keyboard_yn == "y" ]]; then echo "Enter the Error Log threshold: " read Keyboard_yn ATA_Errors=$ATA_Errors$serial":"$Keyboard_yn"," fi echo "ATA_Errors="$ATA_Errors done if [[ ! $ATA_Errors == "" ]]; then ATA_Errors="$(echo "$ATA_Errors" | sed 's/.$//')"; else ATA_Errors="none"; fi fi echo "Set Value: "$ATA_Errors echo " " echo "Press any key to continue" read -s -n 1 Keyboard_yn clear echo "Drive Errors and Custom Builds" echo " " echo "Drive Warranty Expiration Date Warning - This will provide a yellow background" echo "and a text message when the warranty date occurs." echo "The format is: drive_serial_number:yyyy-mm-dd and separated by a comma for" echo "multiple drives. Enter 'd' to delete, 'e' to edit, 'a' to add, Enter/Return for no change." echo "Current: "$Drive_Warranty_List read -s -n 1 Keyboard_yn if [[ $Keyboard_yn == "d" ]]; then Drive_Warranty_List="" echo "Working..." sleep 3 fi if [[ $Keyboard_yn == "e" ]]; then echo "Working..." for drive in $smartdrivesall; do clear_variables get_drive_data echo " " # Lets compare the $serial to the Drive_Warranty_List IFS=',' read -ra ADDR <<< "$Drive_Warranty_List" for i in "${ADDR[@]}"; do drivesn1="$(echo $i | cut -d':' -f 1)" drivedt1="$(echo $i | cut -d':' -f 2)" if [[ $drivesn1 == $serial ]]; then warrantyyear="$(echo $drivedt1 | cut -d '-' -f1)" warrantymonth="$(echo $drivedt1 | cut -d '-' -f2)" warrantyday="$(echo $drivedt1 | cut -d '-' -f3)" # Display the results and ask to edit, keep, or delete. echo "Drive S/N: "$serial" Warranty Date is "$warrantyyear"-"$warrantymonth"-"$warrantyday echo "Do you want to (k)eep, (e)dit, or (d)elete this entry? " read -s -n 1 Keyboard_yn if [[ $Keyboard_yn == "e" ]]; then # Edit the drive string echo "Enter new warranty date in format yyyy-mm-dd" # Need to ask for the date to be entered again read Keyboard_yn # Save serial and date to $warrantydrivelist if [[ $warrantydrivelist == "" ]]; then warrantydrivelist=$serial":"$Keyboard_yn else warrantydrivelist=$warrantydrivelist","$serial":"$Keyboard_yn fi elif [[ $Keyboard_yn == "d" ]]; then # Delete the entry echo "Deleting "$serial" from the warranty list" else # Leave the drive warranty data unaltered. echo "Leaving drive "$serial" untouched." if [[ $warrantydrivelist == "" ]]; then warrantydrivelist=$serial":"$warrantyyear"-"$warrantymonth"-"$warrantyday else warrantydrivelist=$warrantydrivelist","$serial":"$warrantyyear"-"$warrantymonth"-"$warrantyday fi fi echo "Edited Value is: "$warrantydrivelist else addserial="1" fi done done if [[ ! $warrantydrivelist == "" ]]; then Drive_Warranty_List="$(echo "$warrantydrivelist" | sed 's/.$//')"; else Drive_Warranty_List=""; fi fi if [[ $Keyboard_yn == "a" ]]; then echo "Working..." echo " " echo "Searching for drives not in the Warranty List..." echo " " for drive in $smartdrivesall; do clear_variables addserial="" get_drive_data if [[ $Drive_Warranty_List == "" ]] || [[ $Drive_Warranty_List == "none" ]]; then addserial=$serial; fi # Lets compare the $serial to the Drive_Warranty_List IFS=',' read -ra ADDR <<< "$Drive_Warranty_List" for i in "${ADDR[@]}"; do drivesn1="$(echo $i | cut -d':' -f 1)" drivedt1="$(echo $i | cut -d':' -f 2)" if [[ $drivesn1 == $serial ]]; then addserial="" break else addserial=$serial fi done if [[ $addserial != "" ]]; then # Display the results and ask to edit, keep, or delete. newdrivesfound="1" echo "Do you want to add this drive "$serial" (y/n)?" read -s -n 1 Keyboard_yn if [[ $Keyboard_yn == "y" ]]; then # Keep the drive from the string echo " " echo "Enter the date the drive expires in the following format: yyyy-mm-dd" read Keyboard_yn if [[ $Drive_Warranty_List == "" ]] || [[ $Drive_Warranty_List == "none" ]]; then Drive_Warranty_List=$serial":"$Keyboard_yn else Drive_Warranty_List=$Drive_Warranty_List","$serial":"$Keyboard_yn fi else echo "Not adding the drive to the Drive Warranty List" fi fi done if [[ $newdrivesfound != "1" ]]; then echo "No new drives were found."; fi fi echo " " echo "Set Value: "$Drive_Warranty_List echo " " echo "Press any key to continue" read -s -n 1 Keyboard_yn clear echo "Drive Errors and Custom Builds" echo " " echo "Drive Warranty Expiration Chart Box Pixel Thickness" echo "Sometimes a thicker boarder around a chart box pulls the eye." echo "This is good if you use the default background color (next two options)." echo "Enter/Return = no change, or enter 1, 2, or 3" echo "Current: "$WarrantyBoxPixels read -s -n 1 Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then WarrantyBoxPixels=$Keyboard_yn; fi echo "Set Value: "$WarrantyBoxPixels echo " " echo "Press any key to continue" read -s -n 1 Keyboard_yn clear echo "Drive Errors and Custom Builds" echo " " echo "Drive Warranty Expiration Chart Box Pixel Color" echo "Enter/Return = no change, or enter Hex Color Code (Google it)" echo "Examples: black=#000000, red=#FF0000, lightblue=#add8e6" echo "Current: "$expiredWarrantyBoxColor read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then expiredWarrantyBoxColor=$Keyboard_yn; fi echo "Set Value: "$expiredWarrantyBoxColor echo " " echo "Press any key to continue" read -s -n 1 Keyboard_yn clear echo "Drive Errors and Custom Builds" echo " " echo "Drive Warranty Expiration Chart Box Background Color" echo "Enter/Return = no change, or enter Hex Color Code (Google it)" echo "Examples: black=#000000, red=#FF0000, lightblue=#add8e6" echo 'You may also enter "none" to use the default background.' echo "Current: "$WarrantyBackgndColor read Keyboard_yn if [[ ! $Keyboard_yn == "" ]]; then WarrantyBackgndColor=$Keyboard_yn; fi echo "Set Value: "$WarrantyBackgndColor echo " " echo "returning..." sleep 2 ;; S) clear echo "Custom Drive Configuration Mode" echo " " echo "This series of questions will allow you to customize each alarm setting" echo "for up to 24 drives on your system. It is suggested that only drives" echo "which need customization be included here." echo " " echo "If you choose to customize a drive you will be presented with the" echo 'Drive ID, Drive Serial Number, and the "system default" alarm setting.' echo " " echo 'Press Return to accept the "system default" value. This means that if' echo "the system default value changes then this value will mirror that value." echo " or" echo "Enter a numeric value. This value will be hardcoded for this one drive." echo " " echo 'One additional setpoint is to disable "Last Test Age". This is useful for' echo "some older drives which may generate an alarm." echo " " echo "One additional setpoint is to Reverse the Wear Level value. Unfortunately" echo "sometimes the value is 0 and increasing to indicate wearing has occurred" echo "as opposed to a normal value of 100 and descending as the wearing occurs." echo "To fix this you can chose to reverse the value for a specific drive." echo " " echo " " echo "Follow the prompts." echo " " echo "Press any key to continue" read -s -n 1 key clear # Now lets list each drive, one by one and make some changes. echo "Collecting data, Please wait..." # Lets go ahead and grab all the drive data we will need for the entire section. get_smartHDD_listings get_smartSSD_listings get_smartNVM_listings smartdrivesall="$smartdrives $smartdrivesSSD $smartdrivesNVM" # So we have all the drives listed now. # We will step through each drive and then compare the S/N's to Custom_Drives_List, if there # is a match then we display the values. If no match then display default values. if [[ $Custom_Drives_List != "" ]]; then echo "Would you like to delete all Custom Configuration Data (y/n)?" read -s -n 1 Keyboard_yn if [[ $Keyboard_yn == "y" ]]; then echo " " Custom_Drives_List="" echo "Data Deleted" echo " " fi else echo "No Custom Drive Configuration Exists." fi echo "Would you like to exit (y/n)?" read -s -n 1 Keyboard_exit echo " " if [[ $Keyboard_exit == "y" ]]; then clear echo "If you deleted the custom configuration data," echo "Make sure you write your changes." echo " " echo "Press any key to continue" read -s -n 1 key continue fi sleep .5 clear echo "Collecting Data..." echo " " for drive in $smartdrivesall; do clear_variables get_drive_data echo "Drive ID: "$drive echo "Drive Serial Number: "$serial echo " " # Check to see if the drive is listed in the Custom_Drives_List file, if yes then list the alarm setpoints. # If the drive is not listed then ask to add it to the list. Next list the set/default setpoint values. if [[ "$(echo $Custom_Drives_List | grep $serial)" ]]; then echo "The drive serial number "$serial" is already listed as Custom." echo " " echo "Options are: Delete the entry for this drive, then you may" echo "re-add and edit it immediately." IFS=',' read -ra ADDR <<< "$Custom_Drives_List" for i in "${ADDR[@]}"; do cdrivesn1="$(echo $i | cut -d':' -f 1)" if [[ $cdrivesn1 == $serial ]]; then tempWarnx="$(echo $i | cut -d':' -f 2)" tempCritx="$(echo $i | cut -d':' -f 3)" SectorsWarnx="$(echo $i | cut -d':' -f 4)" SectorsCritx="$(echo $i | cut -d':' -f 5)" ReAllocWarnx="$(echo $i | cut -d':' -f 6)" MultiZoneWarnx="$(echo $i | cut -d':' -f 7)" MultiZoneCritx="$(echo $i | cut -d':' -f 8)" RawReadWarnx="$(echo $i | cut -d':' -f 9)" RawReadCritx="$(echo $i | cut -d':' -f 10)" SeekErrorsWarnx="$(echo $i | cut -d':' -f 11)" SeekErrorsCritx="$(echo $i | cut -d':' -f 12)" TestWarnAgex="$(echo $i | cut -d':' -f 13)" testAgeOvrd="$(echo $i | cut -d':' -f 14)" HeliumMinx="$(echo $i | cut -d':' -f 15)" wearLevelAdj="$(echo $i | cut -d":" -f 16)" fi #echo "wearLevelAdj2="$wearLevelAdj done echo " " echo "The current alarm setpoints are ('d' = system default):" echo "Temperature Warning=("$tempWarnx") Temperature Critical=("$tempCritx")" echo "Sectors Warning=("$SectorsWarnx") Sectors Critical=("$SectorsCritx") ReAllocated Sectors Warning=("$ReAllocWarnx")" echo "MultiZone Warning=("$MultiZoneWarnx") MultiZone Critical=("$MultiZoneCritx")" echo "Raw Read Error Rate Warning=("$RawReadWarnx") Raw Read Error Rate Critical=("$RawReadCritx")" echo "Seek Error Rate Warning=("$SeekErrorsWarnx") Seek Error Rate Critical=("$SeekErrorsCritx")" echo "Test Age=("$TestWarnAgex") Ignore Test Age=("$testAgeOvrd")" echo "Helium Minimum Level=("$HeliumMinx")" echo "Wear Level Adjustment=("$wearLevelAdj")" echo " " echo "Do you want to delete this drive from the custom configuration (y/n)?" read -s -n 1 Keyboard_yn if [[ $Keyboard_yn == "y" ]]; then echo "Custom Drive Configuration for "$serial" -- Deleting." echo " " echo " " ### Roll through the Custom_Drives_List data until a match for $serial occurs, then copy all but that data back. tempstring="" for (( i=1; i<=32; i++ )); do tempvar="$(echo $Custom_Drives_List | cut -d',' -f $i)" if [[ ! $tempvar == "" ]]; then tempsn="$(echo $tempvar | cut -d":" -f 1)" if [[ $tempsn == $serial ]]; then bogusvalue=1 else if [[ $tempstring == "" ]]; then tempstring=$tempvar else tempstring=$tempstring","$tempvar fi fi fi done Custom_Drives_List=$tempstring sleep 1 clear fi fi # Lets assign the local variables with the default values. They will be changed # later if the drive is in the Custom_Drives_List variable. sectorswarn=$SectorsWarn sectorscrit=$SectorsCrit reallocwarn=$ReAllocWarn multizonewarn=$MultiZoneWarn multizonecrit=$MultiZoneCrit rawreadwarn=$RawReadWarn rawreadcrit=$RawReadCrit seekerrorswarn=$SeekErrorsWarn seekerrorscrit=$SeekErrorsCrit testage=$TestWarnAge testAgeOvrd="0" heliummin=$HeliumMin wearleveladj="d" if [[ ! "$(echo $Custom_Drives_List | grep $serial)" ]]; then echo "The drive "$serial" is not in the Custom Drive Config database." echo " " echo "Displaying Default Values" if [[ $Custom_Drives_ListDrive == "HDD" ]]; then tempwarn=$HDDtempWarn; tempcrit=$HDDtempCrit fi if [[ $Custom_Drives_ListDrive == "SSD" ]]; then tempwarn=$SSDtempWarn; tempcrit=$SSDtempCrit fi if [[ $Custom_Drives_ListDrive == "NVM" ]]; then tempwarn=$NVMtempWarn; tempcrit=$NVMtempCrit fi echo " " echo "The current alarm setpoints are:" echo "Temperature Warning=("$tempwarn") Temperature Critical=("$tempcrit")" echo "Sectors Warning=("$sectorswarn") Sectors Critical=("$sectorscrit") ReAllocated Sectors Warning=("$reallocwarn")" echo "MultiZone Warning=("$multizonewarn") MultiZone Critical=("$multizonecrit")" echo "Raw Read Error Rate Warning=("$rawreadwarn") Raw Read Error Rate Critical=("$rawreadcrit")" echo "Seek Error Rate Warning=("$seekerrorswarn") Seek Error Rate Critical=("$seekerrorscrit")" echo "Test Age=("$testage") Ignore Test Age=("$testAgeOvrd")" echo "Helium Minimum Level=("$heliummin")" echo "Wear Level Adjustment=("$wearleveladj")" echo " " echo "Would you like to customize an Alarm Setpoint for this drive?" echo "Return accepts current setting, 'y' to modify. " read -s -n 1 Keyboard_yn if [[ $Keyboard_yn == "y" ]]; then echo " " echo "Let's modify some values..." echo " " echo "You have the following two options..." echo ' Return to accept the "system default" value, or' echo " Enter a numeric value to override the system default value." echo " " echo "Temperature Warning=("$tempwarn") " read Keyboard_yn if [[ $Keyboard_yn == "" ]]; then tempwarn="d" else tempwarn=$Keyboard_yn fi echo "Temperature Critical=("$tempcrit") " read Keyboard_yn if [[ $Keyboard_yn == "" ]]; then tempcrit="d" else tempcrit=$Keyboard_yn fi echo "Sectors Warning=("$sectorswarn") " read Keyboard_yn if [[ $Keyboard_yn == "" ]]; then sectorswarn="d" else sectorswarn=$Keyboard_yn fi echo "Sectors Critical=("$sectorscrit") " read Keyboard_yn if [[ $Keyboard_yn == "" ]]; then sectorscrit="d" else sectorscrit=$Keyboard_yn fi echo "Reallocated Sectors Warning=("$reallocwarn") " read Keyboard_yn if [[ $Keyboard_yn == "" ]]; then reallocwarn="d" else reallocwarn=$Keyboard_yn fi echo "MultiZone Warning=("$multizonewarn") " read Keyboard_yn if [[ $Keyboard_yn == "" ]]; then multizonewarn="d" else multizonewarn=$Keyboard_yn fi echo "MultiZone Critical=("$multizonecrit") " read Keyboard_yn if [[ $Keyboard_yn == "" ]]; then multizonecrit="d" else multizonecrit=$Keyboard_yn fi echo "Raw Read Rate Warning=("$rawreadwarn") " read Keyboard_yn if [[ $Keyboard_yn == "" ]]; then rawreadwarn="d" else rawreadwarn=$Keyboard_yn fi echo "Raw Read Rate Critical=("$rawreadcrit") " read Keyboard_yn if [[ $Keyboard_yn == "" ]]; then rawreadcrit="d" else rawreadcrit=$Keyboard_yn fi echo "Seek Errors Warning=("$seekerrorswarn") " read Keyboard_yn if [[ $Keyboard_yn == "" ]]; then seekerrorswarn="d" else seekerrorswarn=$Keyboard_yn fi echo "Seek Errors Critical=("$seekerrorscrit") " read Keyboard_yn if [[ $Keyboard_yn == "" ]]; then seekerrorscrit="d" else seekerrorscrit=$Keyboard_yn fi echo "Test Age=("$testage") " read Keyboard_yn if [[ $Keyboard_yn == "" ]]; then testage="d" else testage=$Keyboard_yn fi echo "Ignore Test Age=("$testAgeOvrd") (0=No, 1=Yes)" read Keyboard_yn if [[ $Keyboard_yn != $testAgeOvrd && $Keyboard_yn != "" ]]; then testAgeOvrd=$Keyboard_yn fi echo "Helium Warning Level=("$heliummin") " echo "NOTE: Helium may not be used by this drive so enter the default." read Keyboard_yn if [[ $Keyboard_yn == "" ]]; then heliummin="d" else heliummin=$Keyboard_yn fi echo "Wear Level Adjustment=("$wearleveladj") " echo "NOTE: Wear Level Adjustment is set to 'd' for default or 'r' for Reverse value." read Keyboard_yn if [[ $Keyboard_yn == "" ]]; then wearleveladj="d" else wearleveladj=$Keyboard_yn fi if [[ $wearleveladj == "d" ]] || [[ $wearleveladj == "r" ]] || [[ $wearleveladj == "" ]]; then if [[ $Keyboard_yn == "" ]]; then wearleveladj="d" else wearleveladj=$Keyboard_yn fi else echo "ERROR, Incorrect Value! Must be 'Enter/Return', 'd', or 'r'" echo -n "Enter new value :" read Keyboard_yn if [[ $Keyboard_yn == "" ]]; then wearleveladj="d" else wearleveladj=$Keyboard_yn fi fi echo " " echo "The current alarm setpoints are ('d' = system default):" echo "Temperature Warning=("$tempwarn") Temperature Critical=("$tempcrit")" echo "Sectors Warning=("$sectorswarn") Sectors Critical=("$sectorscrit") ReAllocated Sectors Warning=("$reallocwarn")" echo "MultiZone Warning=("$multizonewarn") MultiZone Critical=("$multizonecrit")" echo "Raw Read Error Rate Warning=("$rawreadwarn") Raw Read Error Rate Critical=("$rawreadcrit")" echo "Seek Error Rate Warning=("$seekerrorswarn") Seek Error Rate Critical=("$seekerrorscrit")" echo "Test Age=("$testage") Ignore Test Age=("$testAgeOvrd")" echo "Helium Minimum Level=("$heliummin")" echo "Wear Level Adjustment=("$wearleveladj")" echo " " echo "Adding "$drive" Serial Number: "$serial" to the custom configuration" echo "variable." echo " " # Add all the current values for this $serial to the Custom_Drives_List variable. if [[ $Custom_Drives_List == "" ]]; then Custom_Drives_List=$serial":"$tempwarn":"$tempcrit":"$sectorswarn":"$sectorscrit":"$reallocwarn":"$multizonewarn":"$multizonecrit":"$rawreadwarn":"$rawreadcrit":"$seekerrorswarn":"$seekerrorscrit":"$testage":"$testAgeOvrd":"$heliummin":"$wearleveladj else Custom_Drives_List=$Custom_Drives_List","$serial":"$tempwarn":"$tempcrit":"$sectorswarn":"$sectorscrit":"$reallocwarn":"$multizonewarn":"$multizonecrit":"$rawreadwarn":"$rawreadcrit":"$seekerrorswarn":"$seekerrorscrit":"$testage":"$testAgeOvrd":"$heliummin":"$wearleveladj fi echo " " else echo "Drive skipped." fi fi echo " " echo "Press any key to continue" read -s -n 1 key clear done echo "Make sure you write your changes." echo "Press any key to continue" read -s -n 1 key sleep .5 ;; U) clear echo "Update Script (Automatic or Manual operation)" echo " " echo " " echo "Automatic Script Update" echo " " echo " " echo "This section will allow you select normal Manual Updates" echo "or Fuly Automatic Updates." echo " " echo "Manual Updates requires the user to run the script using the" echo "'-update' switch and answering a question to update or not." echo " " echo "Automatic Updates is a fully automatic update and requires" echo "no user interaction. This happens completely automatically." echo " " if [[ $AutomaticUpdate != "true" ]]; then echo "You are currently using the default Manual Updates" else echo "You are curently using Fully Automatic Updates" fi echo " " echo "Press 'a' for Automatic, or 'm' for Manual Updates," echo "or any other key to keep the current setting." read -s -n 1 Keyboard_yn if [[ $Keyboard_yn == "" ]]; then echo "Keeping the current setting" elif [[ $Keyboard_yn == "a" ]]; then echo "You selected Fully Automatic Updates." AutomaticUpdate="true" elif [[ $Keyboard_yn == "m" ]]; then echo "You selected the default Manual Updates." AutomaticUpdate="false" fi echo " " echo "AutomaticUpdate ("$AutomaticUpdate")" echo " " echo "Make sure you write your changes." echo "Press any key to continue" read -s -n 1 key sleep .5 ;; W) echo " " echo "Writing Configuration File" echo " " echo " " sleep 1 update_config_file echo "File updated." echo " " x=100 sleep 1 ;; X) echo "Exiting, Not Saving" sleep 1 x=100 ;; *) echo "Invalid Option" sleep 2 ;; esac done ;; # Advanced Configuration Level End U) clear echo "Update Configuration File" echo " " echo "This update utility will update the variables listed in the" echo "configuration file to the current version default values." echo " " echo "This option is now available because the script will not automatically" echo "update variables which have already been set, making the assumption" echo "that the user desires those settings to remain unchanged. This option" echo "will force the select variables to be updated." echo " " echo "Note: The list below may be small but if you have a suggestion" echo "then forward it to Joe Schmuck for consideration. Of course all" echo "settings can be reset to default by creating a New configuration file." echo " " echo "1) Reset Config File" echo " " echo "a) Abort" echo " " echo -n "Enter your option: " read -s -n 1 Keyboard_yn if [[ $Keyboard_yn == "1" ]]; then Update_Colors="1" echo "Global Table of Colors" sleep .2 echo " " echo "Loading Configuration File Data..." sleep .2 echo " " if [[ ! -f "$Config_File_Name" ]]; then echo "You do not have an external configuration file yet." echo "Please create an external configuration file." echo " " exit 1 fi load_config update_config_file echo "File updated" else Update_Colors="0" fi if [[ $Keyboard_yn == "a" ]]; then echo "Aborting" fi echo " " echo -n "Press any key to continue " read -s -n 1 key ;; H) clear echo "HOW TO USE THIS CONFIGURATION TOOL" echo " " echo "This tool has many options and you should be able to perform a complete" echo "configuration using this tool." echo " " echo "In order to use the advanced options you will need to have created an external" echo "configuration file then the tool will be able to read and write to this file." echo " " echo "Throughout this process you will be asked questions that require three different" echo "responses:" echo " " echo " 1) String content: Where you will either enter a new string followed by the" echo " Enter/Return key, or just press Enter/Return to accept the current value." echo " " echo " 2) Numeric content: Where you will either enter a new number followed by the" echo " Enter/Return key, or just press Enter/Return to accept the current value." echo " " echo " 3) True/False content: Where you will either enter 't' or 'f' followed by the" echo " Enter/Return key, or just press Enter/Return to accept the current value." echo " " echo " 4) Some options will give you a choice of 'd' to delete the value and" echo " continue, or 'e' to Edit." echo " " echo -n "Press any key to continue" read -s -n 1 key echo " " echo " " echo "Just to re-iterate: Press the Enter/Return key to accept the current value." echo "Press 't' or 'f' to change to 'true' or 'false'. Enter a number or string" echo "followed by the Enter/Return key to change a value." echo " " echo "For more detailed Help information, run the program with the '-h' parameter." echo " " echo "Lastly this configuration process will not, I repeat, will not alter the script" echo "file. It will only alter the configuration file which by default will be" echo "located in the same directory as the script is located." echo " " echo -n "Press any key to continue" read -s -n 1 key echo " " sleep 1 ;; N) clear echo "Creating a new configuration file. This will overwrite an existing file." echo " " echo "Enter your email address to send the report to, or press Return to abort" echo -n ": " read Keyboard_Email if [[ $Keyboard_Email == "" ]]; then echo "Aborting" sleep 2 continue fi if [[ ! $Keyboard_Email == "" ]]; then Email=$Keyboard_Email; fi echo "Set Value: "$Email echo " " echo "Enter your Drive temperature monitoring email address. This is the email" echo "or series of emails (comma separated) address(es) to send an alert to." echo "press Return to use your normal email address" echo -n ": " read Keyboard_Email if [[ $Keyboard_Email == "" ]]; then echo "Using normal Email address..." AlertEmail=$Email fi if [[ ! $Keyboard_Email == "" ]]; then AlertEmail=$Keyboard_Email; fi echo "Set Value: "$AlertEmail echo " " echo "Current from address: "$From" " echo 'While most people are able to use the default "From" address, some email' echo 'servers will not work unless you use the email address that the email' echo 'server is associated with.' echo -n "Enter your from Email address: " read Keyboard_Email if [[ ! $Keyboard_Email == "" ]]; then From=$Keyboard_Email; fi echo "Set Value: "$From echo " " echo "Enter path and name of statistics file or just hit Enter to use the" echo "default (recommended): " echo 'Default is '$SCRIPT_DIR'/statistical_data_file.csv' read Keyboard_statistics if [[ $Keyboard_statistics == "" ]]; then echo "Default Selected"; Keyboard_statistics="$SCRIPT_DIR/statisticalsmartdata.csv"; fi echo "Set Value: "$Keyboard_statistics echo " " echo "Automatic Drive Offsets will effectively zero out the following values" echo "if they have any current errors - UDMA CRC Errors, MultiZone, and" echo "Reallocated Sectors. This is useful for tracking additional errors." echo " " echo "Would you like to scan the drives and setup these offsets (y/n): " read -s -n 1 Keyboard_yn if [[ $Keyboard_yn == "y" ]]; then echo " " echo "Collecting data, Please wait..." get_smartHDD_listings get_smartSSD_listings get_smartNVM_listings smartdrivesall="$smartdrives $smartdrivesSSD $smartdrivesNVM" echo " " echo "AUTOMATIC DRIVE COMPENSATION - UDMA_CRC, MultiZone, and Reallocated Sectors" echo " " for drive in $smartdrivesall; do clear_variables get_drive_data if [[ ! $crcErrors == "0" ]] && [[ ! $crcErrors == "" ]]; then listofdrivescrc="$listofdrivescrc$serial":"$crcErrors,"; fi if [[ ! $multiZone == "0" ]] && [[ ! $multiZone == "" ]]; then listofdrivesmulti="$listofdrivesmulti$serial":"$multiZone,"; fi if [[ ! $reAlloc == "0" ]] && [[ ! $reAlloc == "" ]]; then listofdrivesbad="$listofdrivesbad$serial":"$reAlloc,"; fi if [[ ! $reAllocEvent == "0" ]] && [[ ! $reAllocEvent == "" ]]; then listofdrivesbad2="$listofdrivesbad2$serial":"$reAllocEvent,"; fi done echo "Scanning Results:" if [[ ! $listofdrivescrc == "" ]]; then CRC_Errors_List="$(echo "$listofdrivescrc" | sed 's/.$//')"; echo "UDMA_CRC Errors detected"; else CRC_Errors_List=""; echo "No UDMA_CRC Errors"; fi if [[ ! $listofdrivesmulti == "" ]]; then MultiZone_List="$(echo "$listofdrivesmulti" | sed 's/.$//')"; echo "MultiZone Errors Detected"; else MultiZone_List=""; echo "No MultiZone Errors"; fi if [[ ! $listofdrivesbad == "" ]]; then ReAllocated_Sector_List="$(echo "$listofdrivesbad" | sed 's/.$//')"; echo "Bad Sectors Detected"; else ReAllocated_Sector_List=""; echo "No Reallocated Sectors"; fi if [[ ! $listofdrivesbad2 == "" ]]; then ReAllocated_Sector_Events_List="$(echo "$listofdrivesbad2" | sed 's/.$//')"; echo "Bad Sectors Detected"; else ReAllocated_Sector_Events_List=""; echo "No Reallocated Sectors"; fi echo " " echo "Values Set:" echo "CRC_Errors_List: "$CRC_Errors_List echo "MultiZone_List_Errors: "$MultiZone_List echo "Reallocated_Sectors: "$ReAllocated_Sector_List echo "Reallocated_Sectors_Events: "$ReAllocated_Sector_Events_List echo " " fi #The default values at the beginning of the script are written. echo "Creating the new file..." update_config_file echo " " echo "Humm, kind of slow..." sleep 1 echo " " echo "Do you have enough RAM? Looks like about 8KB. Troubleshooting..." echo " " sleep 1 echo "I found the problem, the system was identified as a Tandy TRS-80 Model 1" echo "Wow! That was a fantastic consumer computer, in it's day." echo "Adjusting for the 1.774 MHz clock rate..." sleep 1 echo "Success!" echo " " echo "New clean configuration complete." echo " " echo "Path and Name if the configuration file: "$Config_File_Name echo " " echo " " echo "If you desire more customization, rerun the -config and select Advanced options." echo " " echo "And for those who don't know what TRS-80 is, Google it." exit 0 ;; S) clear load_config echo "Spencer Add-On Script by NickF" echo " " echo "Spencer Enabled is currently: "$spencer_enable echo "Do you want to change this (y/n or Enter/Return to remain the same)" read -s -n 1 Keyboard_yn if [[ $Keyboard_yn == "y" ]]; then if [[ $spencer_enable == "true" ]]; then spencer_enable="false"; else spencer_enable="true"; fi fi echo "New Value: "$spencer_enable echo " " echo "It is recommended that spencer.py be located in the same directory as the multi_report.sh script." echo "You may change the path and file name if desired." echo " " echo "Current path and name: "$spencer_script_name echo "Enter a new path and name, Enter/Return to keep current value, or 'd' to reset to the default." read Keyboard_yn if [[ $Keyboard_yn != "" ]]; then if [[ $Keyboard_yn == "d" ]]; then spencer_script_name="$SCRIPT_DIR/spencer.py" else spencer_script_name=$Keyboard_yn fi echo "New Spencer script full path is: "$spencer_script_name else echo "Location not changed" fi if [ ! -e $spencer_script_name ]; then echo " " echo "Spencer is not found!" echo "Please ensure you install Spencer in the path you provided or reset to default." sleep 2 fi echo " " echo "Set Warning Levels" echo 'New "Not previously reported" Errors Warning Level' echo " " echo "Valid Error Levels" echo "1) None = No Email Subject Error Message" echo "2) Warning = New Errors are reported as a Warning Message (default)" echo "3) Critical = New Errors are reported as a Critical Message" echo " " echo "Current Value: "$spencer_new_warning_level echo "Enter new level or Enter/Return to leave unchanged (1/2/3)" read -n 1 Keyboard_yn if [[ $Keyboard_yn == "1" ]]; then spencer_new_warning_level="None" elif [[ $Keyboard_yn == "2" ]]; then spencer_new_warning_level="Warning" elif [[ $Keyboard_yn == "3" ]]; then spencer_new_warning_level="Critical" elif [[ $Keyboard_yn == "" ]]; then a=$a else echo "Invalid Entry" sleep 3 fi echo "New Value: "$spencer_new_warning_level echo " " echo 'Existing "Previously reported" Errors Warning Level' echo " " echo "Valid Error Levels" echo "1) None = No Email Subject Error Message (default)" echo "2) Warning = Existing Errors are reported as a Warning Message" echo "3) Critical = Existing Errors are reported as a Critical Message" echo " " echo "Current Value: "$spencer_existing_warning_level echo "Enter new level or Enter/Return to leave unchanged (1/2/3)" read -n 1 Keyboard_yn if [[ $Keyboard_yn == "1" ]]; then spencer_existing_warning_level="None" elif [[ $Keyboard_yn == "2" ]]; then spencer_existing_warning_level="Warning" elif [[ $Keyboard_yn == "3" ]]; then spencer_existing_warning_level="Critical" elif [[ $Keyboard_yn == "" ]]; then a=$a else echo "Invalid Entry" sleep 3 fi echo "New Value: "$spencer_existing_warning_level echo " " echo "Write the changes for Spencer (y/n)?" read -s -n 1 Keyboard_yn if [[ $Keyboard_yn == "y" ]]; then echo "Writing File" update_config_file else echo "Aborting" fi sleep 3 ;; X) echo " " echo " " echo "Exiting..." echo " " echo " " z=50 exit 1 ;; *) echo " " echo " " echo "DO YOU WANT TO PLAY A GAME?" echo " " echo " " sleep 2 ;; # End First Level esac done shopt -u nocasematch } ########## HELP INSTRUCTIONS ########## display_help () { clear echo "NAME" echo " Multi Report - System status reporting for TrueNAS Core and Scale" echo " " echo "SYNOPSIS" echo " multi_report.sh [options]" echo " " echo "COPYRIGHT AND LICENSE" echo " Multi Report is Copyright (C) by its authors and licensed under the" echo " GNU General Public License v3.0" echo " " echo "DESCRIPTION" echo " Multi Report generates an email containing a summary chart of your" echo " media and their health. Directly after the chart is a Text Section which" echo " may immediately contain failure information followed by Zpool data," echo " key SMART data for each drive, and raw data for drives that do not report" echo " SMART. In addition if configured, statistical data is collected for" echo " long-term monitoring. This script currently runs on both Core (FreeBSD)" echo " and Scale (Debian Linux) versions." echo " " echo "OPTIONS" echo " -help This message." echo " " echo " -h List the most common options." echo " " echo " -s [-m] Record drive statistics only, do not generate a" echo ' corresponding email, unless used with "-m"' echo " " echo " -config Generate or edit a configuration file in the directory the" echo " script is run from." echo " -delete Deletes the statistical data file if the file exists." echo " " echo " -dump [all] Generates an email with attachments of all drive data and" echo " [email] the multi_report_config.txt additionally it also suppress" echo " the config_backup file and statistics file from being" echo " attached to the email unless you use the [all] option, then" echo " the config_backup and statistics files will be appended." echo " The [email] option runs the normal -dump command but also" echo " will send the email to joeschmuck2023@hotmail.com for" echo " further analysis or just to provide drive data information" echo " to Joe to help make the product better. If you use the" echo " [-dump email] option, you will be asked to confirm" echo " sending of the email. So you cannot run this parameter" echo " from a script, it must be CLI. You will also be asked" echo " to enter a comment to aid Joe in your problem." echo " " echo " -m [-s] Monitor Drive Temperature Alarms. This will generate" echo ' an email to "AlertEmail" that contains a' echo " notification when a drive reaches the Warning Temp Limit." echo " Great for Smartphone Text Messages. By default it will not" echo ' write to the Statistical Data File, appending "-s"' echo " will update the statistics as well." echo " " echo " The options listed below are intended for developer use only." echo " " echo " HDD | SSD | NVM input_file(s) Use the selected drive data report(s)" echo " created from the -dump option. This assists in developer recognition" echo " of drives not properly reporting data." echo " " echo " -purge This will purge all test data from the statistical data file." echo " " echo " -t [path] [-dump] Use strickly for test files (.json format)" echo " This is strickly for development." echo " " echo " -u7zip This will remove 7-Zip from Scale if it was installed." echo " " echo " -update Manually check GitHub for an update." echo " " echo " Running the script without any switches will collect statistical data" echo " and generate an email report." echo " " echo "CONFIGURATION" echo " The script has become quite complex over time and with added features" echo " ultimately required an external configuration file with version 1.6c" echo " to simplify upgrades to the end user." echo " " echo " If the external configuration file does not exist, the script will use" echo " the values hard code into the script (just like versions 1.6b and" echo " earlier), however there is now an email address check to ensure you have" echo " changed the email address within the script." echo " " echo " In order to generate an external configuration file you must use the" echo " [-config] parameter when running the script which is the preferred" echo " method to configure your script. Five options will be available:" echo " " echo " N)ew configuration file" echo " U)pdate configuration file" echo " A)dvanced configuration" echo " H)ow to use this configuration tool" echo " X) Exit" echo " " echo " N)ew configuration file will create/overwrite a new configuration file." echo " This is the minimal setup before running the script without parameters." echo " The configuration file will be created in the directory the script is" echo " located in." echo " " echo " " echo " Default settings should be sufficient to test this script." echo " " echo "STATISTICAL DATA" echo " Besides the emailed chart the script can also email you attachments for" echo " your FreeNAS/TrueNAS configuration file and a Statistical Data file." echo " " echo " The statistical data file is a comma delimited collection of drive data" echo " that can be opened in any spreadsheet program such as Microsoft Excel." echo " This data could prove to be useful in diagnosing and troubleshooting" echo " drive or system problems." echo " " echo "EMAIL CONTENT" echo " Normal operation of this script will produce an output email and it may" echo " or may not have attachments per your configuration. The email will" echo " contain the following information:" echo " " echo " - Subject with Summary Result of 'All is Good', '*WARNING*', or" echo " '*CRITICAL ERROR*'" echo " The summary result is based on your settings of the warning and critical" echo " settings, where:" echo " All is Good = No Alarm indications." echo " *WARNING* = A warning threshold has been crossed and it means you should" echo " investigate and take action." echo " *CRITICAL* = Something significant has occurred and your data could be" echo " at risk, take immediate action." echo " " echo " - The version of the script and the version of TrueNAS you are running." echo " - The date and time the script was run." echo " - Zpool Status Report Summary: (Pool Name/Status/Size/Errors/Scrub Info)" echo " - HDD Summary Report Chart: (Drive ID/Serial Number/other data)" echo " - SSD Summary Report Chart: (Same as the HDD report)" echo " - NVMe Summary Report Chart: (Same as the SSD report)" echo " - Text Section: The Text section contains the text version of most of" echo " the previously displayed data. It will tell you" echo " -- if using an external configuration file" echo " -- if saving statistical information" echo " -- if a drive warranty has expired" echo " -- Zpool native report - in which the gptid's are listed '*followed by a" echo " listing of the drives that make up the pool and the drives are listed" echo " in the order the gptid numbers are listed.*'" echo " -- Drive relevant SMART data followed by if TLER/SCT is enabled/disabled" echo " or available." echo " -- NON-SMART data will be listed for drives that do not support SMART." echo " " echo " While all this data is nice to have, there is no substitute for having" echo " due diligence in examining your hardware and ensuring it is working" echo " correctly. This means you may need to examine your SMART data closer." echo " " echo "USAGE" echo " This script was designed to be run from the CRON service, generally once" echo " a day in order to produce an email output to notify the user of any" echo " problems or trends. To identify trends the script also collects" echo " statistical data for analysis." echo " " echo " A good starting point is to set up a CRON job to run this script once a" echo " day at e.g. 2:00AM using no switches. This will produce an email snapshot" echo " once a day." echo " " echo " In addition if you are trying to troubleshoot drive problems where SMART" echo " data would benefit you, I would recommend you setup an additional CRON" echo " job using the [-s] switch for collecting statistical data only (ie. no" echo " email report). This statistics cron job should run more frequently." echo " The corresponding cron job should be scheduled to not overlap with the" echo " daily report email." echo " --If you sleep your drives then this option may not be desirable.--" echo " " echo "CUSTOMIZATIONS AND FEATURES" echo " There are quite a few built in features in the external configuration file" echo " and these are a few:" echo " " echo " Custom Chart Titles: Change the name of any or all headers and columns." echo " " echo " Selectable Columns: Display or remove as many or few columns of data as" echo " desired. This is great for removing a column with no relevant data." echo " " echo " Alarm Setpoints: Practically everything has an alarm setpoint, from pool" echo " capacity to Scrub Age, to temperature Warnings and Critical Warnings," echo " and a plethora of options." echo " " echo " Custom Drive Configuration: Allows unique customizing of alarms where" echo " the general settings would not work properly for a drive," echo " including inverting the Wear Level value for SSD/NVM drives." echo " " echo " TLER: You can monitor and even have TLER automatically set if required," echo " for drives which support it. The default is to not automatically set" echo " TLER on, I believe the user should make that decision." echo " " echo " Statistical Data: Modify the location of your data, if you want it emailed" echo " and when to email, and we include an automatic purge to ensure the" echo " data file doesn't get too large (default 2 years)." echo " " echo " UDMA CRC Error Corrections: Have you ever had a hard drive UDMA_CRC_Error?" echo " Well they often are caused by a poor/loose data cable but the error" echo " will be recorded forever in the drive electronics. This option lets" echo " you zero out the value in the script. This feature is usable for" echo " Bad Sectors and Multi Zone Errors as well." echo " " echo " Ignore Drive: Every wished you could just ignore a USB Flash Drive or any" echo " drive just giving you problems? With this feature the drive will be" echo " completely ignored in the script." echo " " echo " Warranty Expiring Warning: You can configure the configuration file to" echo " provide a warning message for a drive on a certain date. This a great" echo " tool to keep track on when it might be time to consider buying some" echo " replacement drives." echo " " echo " Custom Colors: Use the HEX color codes to change the color scheme of the" echo " background and alerting colors on the charts. This is ONLY" echo " changeable manually, you have to edit the config file." echo " " echo " It is very important that if you edit the configuration file or the script" echo " that you need to maintain the proper formatting of the text or you will" echo " throw a wrench into things." echo " " echo "HOW TO HANDLE ERRORS" echo " If you run across errors running the script, odd are it is because a drive" echo " was not recognized properly. I recommend you post your error to the forum" echo " to as for assistance. It is possible you will be asked for a [-dump]" echo " of your data in order to let the developers assist you and correct the" echo " problem." echo " " echo " Please note that a [-dump] file is not the same as a cut/paste of a" echo " terminal/SSH window. Critical formatting data is lost that is required." echo " " echo "Recommendation: When troubleshooting a problem you may be asked to provide" echo "dump data to assist troubleshooting. Use the [-dump email] to include all" echo "relevant data (drive data and configuration file) which will send an email" echo "to joeschmuck2023@hotmail.com and your email address will not be shared!." echo "If you prefer you can use [-dump] and then open up a dialog with Joe Schmuck" echo "and attach the files in a message on the TrueNAS forum." echo " " } ########## HELP COMMANDS ########## display_help_commands () { clear echo "NAME" echo " Multi Report - System status reporting for TrueNAS Core and Scale" echo " " echo "SYNOPSIS" echo " multi_report.sh [options]" echo " " echo "COPYRIGHT AND LICENSE" echo " Multi Report is Copyright (C) by its authors and licensed under the" echo " GNU General Public License v3.0" echo " " echo "COMMON OPTIONS" echo " -h This message." echo " " echo " -help Full Help message." echo " " echo " -s [-m] Record drive statistics only, do not generate a" echo ' corresponding email, unless used with "-m".' echo " " echo " -m [-s] Monitor Drive Temperature Alarms, generate special email" echo " notification when a drive reaches the Warning Temp Limit." echo " " echo " -config Generate or edit a configuration file in the directory the" echo " script is run from." echo " " echo " -dump [all] Generates an email with attachments of all drive data and" echo " [email] the multi_report_config.txt additionally it also suppress" echo " the config_backup file and statistics file from being" echo " attached to the email unless you use the [all] option, then" echo " the config_backup and statistics files will be appended." echo " The [email] option runs the normal -dump command but also" echo " will send the email to joeschmuck2023@hotmail.com for" echo " further analysis or just to provide drive data information" echo " to Joe to help make the product better. If you use the" echo " [-dump email] option, you will be asked to confirm" echo " sending of the email. So you cannot run this parameter" echo " from a script, it must be CLI. You will also be asked" echo " to enter a comment to aid Joe in your problem." echo " " echo " -update Manually check GitHub for an update." echo " " echo "Recommendation: When troubleshooting a problem you may be asked to provide" echo "dump data to assist troubleshooting. Use the [-dump email] to include all" echo "relevant data (drive data and configuration file) which will send an email" echo "to joeschmuck2023@hotmail.com and your email address will not be shared!." echo "If you prefer you can use [-dump] and then open up a dialog with Joe Schmuck" echo "and attach the files in a message on the TrueNAS forum." echo " " } ### DEFINE FUNCTIONS END ### ####################### ####################### ### ### ### PROGRAM START ### ### ### ####################### ####################### # The order in which these processed occur is unfortunately dependent. # The -s switch will just collect statistical data. # The -t switch will allow a raw text file for smartctl -a, -x, and .json, followed by the input path. SECONDS=0 echo $programver smartdata="" # Don't think we need this here. if ! [[ "$1" == "-config" || "$1" == "-help" || "$1" == "-delete" || "$1" == "-s" || "$1" == "" || "$1" == "-dump" || "$1" == "-purge" || "$1" == "-h" || "$1" == "-t" || "$1" == "-m" || "$1" == "-u7zip" || $1 == "-update" ]]; then echo '"'$1'" is not a valid option.' echo "Use -h for help and look under OPTIONS." echo " " exit 1 fi #check_symlink # Set the default Write_Statistics="true" # If -m is used, turn off Statistics by default. if [[ "$1" == "-m" || "$2" == "-m" ]]; then Monitor="true"; Write_Statistics="false"; echo "Monitoring for Critical & Temperature Alarms"; else Monitor="false"; fi # If -s is in either position, turn on Statistics if we turned it off above. if [[ "$1" == "-s" || "$2" == "-s" ]]; then Write_Statistics="true"; fi if [[ "$1" == "-config" ]]; then generate_config_file exit 0 fi if [[ "$1" == "-help" ]]; then display_help exit 0 fi if [[ "$1" == "-h" ]]; then display_help_commands exit 0 fi # if -dump then interactive user selected dumping, if "all" then automatic dumping of everything. # Use dump_all=1 during the running routine to gather all the drive data and dump to drive ID files. # if -dump all is used, then include config and statistical attachments (dump_all=2). # if -dump email is used, send "-dump" data to Joe Schmuck for analysis (dump_all=3). # Dump the files into /tmp/ and then email them. if [[ "$1" == "-dump" || "$3" == "-dump" ]]; then Attach_Files1="true" zpool list > "/tmp/zpoollist.pool" zpool status > "/tmp/zpoolstatus.pool" if [[ "$2" == "all" ]]; then dump_all="2"; echo "Attaching Drive Data, Multi-Report Configuration, Statistics, and TrueNAS Configuration files."; fi if [[ "$2" == "" || "$3" == "-dump" ]]; then dump_all="1"; echo "Attaching Drive Data and Multi-Report Configuration files."; fi if [[ "$2" == "email" ]]; then echo "emailing to Joe Schmuck & Attaching Drive Data and Multi-Report Configuration." dump_all="3" echo " " echo "Processing..." echo "Collecting the data and sending to Joe Schmuck for analysis." echo " " echo " " echo "Please enter a comment to Joe or just press Return to continue." echo " " echo 'Example: "Hey Joe, ada2 last test age should be zero value. Could you' echo 'please take a look at it and let me know if it is okay? Thanks, Jimmy"' echo " " echo -n "Enter your message: " read Keyboard_Message if [[ $Keyboard_Message == "" ]]; then echo "No Message Included"; Keyboard_Message="No Message Included"; else Keyboard_Message='"'$Keyboard_Message'"'; fi echo " " echo "Working..." fi else dump_all="0" fi if [[ "$1" == "-delete" ]]; then echo "Preparing to Delete Statistical Data File" echo " " read -p "Press Enter/Return to Continue or CTRL-C to Abort Script" rm "$statistical_data_file" echo " " echo "File Obliterated !!!" echo " " exit 0 fi if [[ "$1" == "-purge" ]]; then purge_testdata exit 0 fi if [[ "$1" == "-s" || "$2" == "-s" ]]; then echo "Commencing Statistical Data Collection"; fi # Remove 7-Zip and Exit if [[ "$1" == "-u7zip" ]]; then if test -e "/usr/local/bin/7zzs"; then rm "/usr/local/bin/7zzs" # Remove 7zzs Standalone Program rm "/usr/local/bin/7z" # Remove Symbolic Link fi echo " " echo "7-Zip has been removed." echo " " exit 0 fi if [[ "$1" == "-update" ]]; then update_script echo "The script has been updated. Exiting." sleep 1 exit 0 fi load_config #if [[ $TrueNASConfigEmailEncryption != "" ]]; then check_7zip; fi check_7zip # Cleanup previous run files if anything is left (there shoudn't be). cleanup_files # Let's check for a script update. if [[ $CheckForUpdates == "true" ]] && [[ "$1" != "-t" && "$1" != "-m" && "$1" != "-dump" ]]; then checkforupdate fi # Let's Update Automatically if allowed. if [[ $UpdateAvailable == "true" ]] && [[ $AutomaticUpdate == "true" ]]; then # We are updating automatically update_script if [[ $AutomaticUpdate == "true" ]]; then echo "Running script normally." # How do we run the script when the script is the same name? Temporarily copy the new script to a new name and run that name. ( cd $SCRIPT_DIR"/" cp $SCRIPT_DIR"/"$runfilename $SCRIPT_DIR"/"temprunfile.sh ; chmod 755 $SCRIPT_DIR"/"temprunfile.sh ./temprunfile.sh $1 sleep 3 rm $SCRIPT_DIR"/"temprunfile.sh ) exit 0 else echo "Script Not Updated" echo "Running script normally..." fi fi # 1 = -dump : Normal Dump, No TrueNAS configuration and Statistical Data File. # 2 = -dump all : Attach Everything. # 3 = -dump email : Add Joe's email address and SAME data as #1. if [[ "$dump_all" == "1" ]]; then TrueNASConfigEmailEnable="false"; SDF_DataEmail="false"; fi if [[ "$dump_all" == "2" ]]; then TrueNASConfigEmailEnable="true"; SDF_DataEmail="true"; TrueNASConfigEmailDay="All"; SDF_DataEmailDay="All"; fi if [[ "$dump_all" == "3" ]]; then Email=$Email",joeschmuck2023@hotmail.com"; TrueNASConfigEmailEnable="false"; SDF_DataEmail="false"; fi testfilepath="" datestamp2=$(date +%Y-%m-%d) datestamp=$(date +%Y/%m/%d) # FreeBSD gets second resolution if [[ $softver != "Linux" ]]; then timestamp=$(date +%T) else # Linux gets second resolution as well timestamp=$(date +"%T") fi #### Test All Files In a Directory # Command Format: -t /path/to/files [-dump|-config|-m] if [[ "$1" == "-t" ]]; then # $2 is the path for the test files, it must be present. if [[ "$2" == "" ]]; then echo "Invalid Test File Path" exit 1 fi # Set testfilepath testfilepath=$2 # How many test files are there? testfilecount="$(ls $2/*.json | wc -l)" echo "testfilecount="$testfilecount # Collect all the test file names. testfilenames=( "$testfilepath"/*.json ) # Separate into HDD, SSD, NVM for (( i=0; i<=$testfilecount; i++ )); do if echo "${testfilenames[i]}" | grep -q "HDD"; then testfilenamesHDD+=("${testfilenames[$i]}") ########## TEST DRIVES CORE OR SCALE NAMING CONVENTION if [[ $softver != "Linux" ]]; then smartdrives+=("ada${i}") else numbertoletters=$i number_to_letters smartdrives+=("sd${numbertoletters}") fi fi if echo "${testfilenames[i]}" | grep -q "SSD"; then testfilenamesSSD+=("${testfilenames[$i]}") if [[ $softver != "Linux" ]]; then smartdrivesSSD+=("ada${i}") else numbertoletters=$i number_to_letters smartdrivesSSD+=("sd${numbertoletters}") fi fi if echo "${testfilenames[i]}" | grep -q "NVM"; then testfilenamesNVM+=("${testfilenames[$i]}") if [[ $softver != "Linux" ]]; then smartdrivesNVM+=("ada${i}") else numbertoletters=$i number_to_letters smartdrivesNVM+=("sd${numbertoletters}") fi fi done fi # Generate a config file using test json files. if [[ "$3" == "-config" ]] && [[ "$1" == "-t" ]]; then generate_config_file exit 0 fi # Do not collect real drive data if we are using test data. if [[ "$testfilepath" == "" ]]; then get_smartHDD_listings fi # Do not collect real drive data if we are using test data. if [[ "$testfilepath" == "" ]]; then get_smartSSD_listings fi # Do not collect real drive data if we are using test data. if [[ "$testfilepath" == "" ]]; then get_smartNVM_listings fi #testfile="" #testfile2="" get_smartOther_listings zpool_report if [[ $SDF_DataRecordEnable == "true" ]]; then if test -e "$statistical_data_file"; then # Purge items over SDF_DataPurgeDays days statistical_data_file_created=0 else # The file does not exist, create it. printf "Date,Time,Device ID,Drive Type,Serial Number,SMART Status,Temp,Power On Hours,Wear Level,Start Stop Count,Load Cycle,Spin Retry,Reallocated Sectors,\ Reallocated Sector Events,Pending Sectors,Offline Uncorrectable,UDMA CRC Errors,Seek Error Rate,Multi Zone Errors,Read Error Rate,Helium Level\n" > "$statistical_data_file" # And set flag the file was created. statistical_data_file_created=1 fi fi # Lets start processing the drives baby! if [[ $Develop == "true" ]]; then echo " " echo " " echo "smartdrives="${smartdrives[*]} echo " " fi # Generate SMART HDD Report SER1="" if [[ "$smartdrives" != "" ]]; then generate_table "HDD" for drive in ${smartdrives[@]}; do clear_variables get_drive_data "HDD" crunch_numbers "HDD" write_table "HDD" done end_table "HDD" fi if [[ $Develop == "true" ]]; then echo "End of HDD Section" duration=$SECONDS echo "$(($duration / 60)) minutes and $(($duration % 60)) seconds elapsed." echo " " echo " " echo "smartdrivesSSD="${smartdrivesSSD[*]} echo " " fi # Generate SSD Report SER1="" if [[ $IncludeSSD == "true" ]]; then # Test if any SSD's are available, if not then no report, if yes then generate SSD/NVM Report. if [[ $smartdrivesSSD != "" ]]; then generate_table "SSD" for drive in ${smartdrivesSSD[@]}; do clear_variables get_drive_data "SSD" crunch_numbers "SSD" write_table "SSD" done end_table "SSD" fi fi if [[ $Develop == "true" ]]; then echo "End of SSD Section" duration=$SECONDS echo "$(($duration / 60)) minutes and $(($duration % 60)) seconds elapsed." echo " " echo " " echo "smartdrivesNVM="${smartdrivesNVM[*]} echo " " fi # Generate NVMe Report SER1="" if [[ $IncludeNVM == "true" ]]; then if [[ $smartdrivesNVM != "" ]]; then generate_table "NVM" for drive in ${smartdrivesNVM[@]}; do clear_variables get_drive_data "NVM" crunch_numbers "NVM" write_table "NVM" done end_table "NVM" fi fi if [[ $Develop == "true" ]]; then echo "End of NVMe Section"; fi if [[ "$testfilepath" == "" ]]; then for drive in $nonsmartdrives; do clear_variables # routine does not have a "NON" option. Fix this! get_drive_data "NON" done fi if [[ $Develop == "true" ]]; then echo "$(echo "${json_error_log}")"; fi #echo "Done processing smart drives" # This purge happens here directly after data collection. # A zero (0) value = Disable Purging (Keep all data) if [[ $SDF_DataPurgeDays != 0 ]]; then purge_exportdata fi write_ATA_Errors="0" if [[ "$Enable_Text_Section" == "true" ]]; then if [[ $spencer_enable == "true" ]]; then if test -e $spencer_script_name; then python3 $spencer_script_name multi_report; fi spencer fi detailed_report $2 if [[ $ReportNonSMART == "true" ]]; then if [[ $DisableRAWdata != "true" ]]; then non_smart_report fi fi fi if [[ $Monitor == "false" ]]; then echo "End of data section" >> "$logfile" fi # Update multi_report_config.txt file if required. if [[ $write_ATA_Errors == "1" ]]; then ATA_Errors="$(echo "$temp_ATA_Errors" | sed 's/.$//')" update_config_file fi if [[ "$1" == "-s" || "$2" == "-s" ]] && [[ "$Monitor" == "false" ]]; then echo "Statistical Data Collection Complete" else Email_datafile remove_junk_report config_backup attach_files fi if [[ "$1" != "-s" && "$2" != "-s" ]] && [[ $Monitor == "false" ]]; then if [[ $Develop == "true" ]]; then echo "Create Normal Email"; fi create_Email elif [[ $Monitor_Send_Email == "true" ]]; then if [[ $Develop == "true" ]]; then echo "Create Monitor Email"; fi create_Email fi cleanup_files duration=$SECONDS if [[ $Develop == "true" ]]; then echo "$(($duration / 60)) minutes and $(($duration % 60)) seconds elapsed."; fi # All reporting files are left in the /tmp/ directory for troubleshooting and cleaned up when the script is initial run.