hdd.sh 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. #!/bin/bash
  2. #########################################################################
  3. # Script for check HDD state #
  4. # Created by Pavol Macko - DoIT #
  5. # ver 0.4 #
  6. #########################################################################
  7. #------------------------------ VARIABLES ------------------------------#
  8. LOG="/tmp/hdd.log"
  9. HTML_DIR="/var/www/hdd"
  10. HTML_OUT="$HTML_DIR/hdd.html"
  11. SRV=$(hostname -s)
  12. EMAIL_TO="info@doit.sk"
  13. EMAIL_SUBJECT="[CRITICAL] Disk Issue Detected on $SRV"
  14. EMAIL_BODY=""
  15. HAS_ERRORS=0
  16. TEMP=55
  17. #------------------------------- REPORT --------------------------------#
  18. mkdir -p "$HTML_DIR"
  19. echo -e "- \e[1;32mExtracting\e[0m Local Storage info from [\e[1;33m$SRV\e[0m]"
  20. echo "DEV|MODEL|SERIAL|SIZE(GB)|TYPE|TEMP(C)|HOURS|HEALTH|REALLOC" > $LOG
  21. cat << EOF > "$HTML_OUT"
  22. <!DOCTYPE html>
  23. <html lang="en">
  24. <head>
  25. <meta charset="UTF-8">
  26. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  27. <title>$SRV Storage</title>
  28. <style>
  29. :root {
  30. --bg: #0f172a;
  31. --surface: #1e293b;
  32. --text: #f8fafc;
  33. --text-muted: #94a3b8;
  34. --border: #334155;
  35. --ok-bg: rgba(34, 197, 94, 0.15);
  36. --ok-text: #4ade80;
  37. --warn-bg: rgba(239, 68, 68, 0.15);
  38. --warn-text: #f87171;
  39. }
  40. body {
  41. background-color: var(--bg);
  42. color: var(--text);
  43. font-family: system-ui, sans-serif;
  44. padding: 2rem;
  45. margin: 0;
  46. }
  47. .container {
  48. max-width: 1200px;
  49. margin: 0 auto;
  50. }
  51. .header {
  52. display: flex;
  53. justify-content: space-between;
  54. align-items: flex-end;
  55. border-bottom: 1px solid var(--border);
  56. padding-bottom: 1rem;
  57. margin-bottom: 2rem;
  58. }
  59. .header h1 {
  60. margin: 0;
  61. font-size: 1.5rem;
  62. font-weight: 500;
  63. }
  64. table {
  65. width: 100%;
  66. border-collapse: collapse;
  67. background-color: var(--surface);
  68. border-radius: 8px;
  69. overflow: hidden;
  70. }
  71. th, td {
  72. padding: 1rem 1.25rem;
  73. text-align: left;
  74. border-bottom: 1px solid var(--border);
  75. }
  76. th {
  77. background-color: rgba(0, 0, 0, 0.2);
  78. color: var(--text-muted);
  79. font-weight: 600;
  80. font-size: 0.75rem;
  81. text-transform: uppercase;
  82. }
  83. tr:last-child td {
  84. border-bottom: none;
  85. }
  86. tr:hover {
  87. background-color: rgba(255, 255, 255, 0.02);
  88. }
  89. .serial {
  90. color: var(--text-muted);
  91. font-family: monospace;
  92. font-size: 0.85rem;
  93. }
  94. .badge {
  95. padding: 0.25rem 0.75rem;
  96. border-radius: 999px;
  97. font-size: 0.75rem;
  98. font-weight: 600;
  99. display: inline-block;
  100. }
  101. .badge.ok {
  102. background-color: var(--ok-bg);
  103. color: var(--ok-text);
  104. }
  105. .badge.warn {
  106. background-color: var(--warn-bg);
  107. color: var(--warn-text);
  108. }
  109. .timestamp {
  110. font-size: 0.85rem;
  111. color: var(--text-muted);
  112. margin-top: 1.5rem;
  113. text-align: center;
  114. }
  115. </style>
  116. </head>
  117. <body>
  118. <div class="container">
  119. <div class="header">
  120. <h1>Drive overview on <span style="color: var(--text-muted);">[$SRV]</span></h1>
  121. </div>
  122. <table>
  123. <thead>
  124. <tr>
  125. <th>Device</th>
  126. <th>Model</th>
  127. <th>Serial</th>
  128. <th>Size (GB)</th>
  129. <th>Type</th>
  130. <th>Temp (°C)</th>
  131. <th>Hours</th>
  132. <th>Health</th>
  133. <th>Realloc</th>
  134. </tr>
  135. </thead>
  136. <tbody>
  137. EOF
  138. #------------------------------- EXTRACT -------------------------------#
  139. lsblk -d -n -b -o NAME,TYPE,TRAN,SIZE | awk '$2=="disk" && $1 !~ /^(zd|loop|md|dm-|ram)/' | while read -r d type tran bytes;
  140. do
  141. [[ "$tran" == "usb" ]] && continue
  142. HCAP=$(awk -v b="$bytes" 'BEGIN { printf "%.2f", b / (1024^3) }')
  143. SMART=$(smartctl -a /dev/$d 2>/dev/null)
  144. [[ -z "$SMART" ]] && continue
  145. HMOD=$(echo "$SMART" | grep -E "^(Device Model|Model Number):" | awk -F: '{print $2}' | xargs)
  146. HSER=$(echo "$SMART" | grep -i "^Serial Number:" | awk -F: '{print $2}' | xargs)
  147. HROT=$(echo "$SMART" | grep -i "^Rotation Rate:" | awk -F: '{print $2}' | xargs)
  148. if [[ "$tran" == "nvme" ]] || [[ "$HROT" == *"Solid State"* ]] || [[ -z "$HROT" ]]; then
  149. HTYPE="SSD/NVMe"
  150. else
  151. HTYPE="HDD"
  152. fi
  153. HHEA=$(echo "$SMART" | grep -iE "overall-health|SMART overall-health" | awk '{print $NF}')
  154. HTEMP=$(echo "$SMART" | awk '/^194 Temperature_Celsius/ {print $10}; /^Temperature:/ {print $2}' | head -n 1)
  155. HHOURS=$(echo "$SMART" | awk '/^ 9 Power_On_Hours/ {print $10}; /^Power On Hours:/ {print $4}' | tr -d ',' | head -n 1)
  156. HREALLOC=$(echo "$SMART" | awk '/^ 5 Reallocated_Sector_Ct/ {print $10}' | head -n 1)
  157. HMOD=${HMOD:--}
  158. HSER=${HSER:--}
  159. HTEMP=${HTEMP:--}
  160. HHOURS=${HHOURS:--}
  161. HHEA=${HHEA:--}
  162. HREALLOC=${HREALLOC:--}
  163. echo "$d|${HMOD}|${HSER}|$HCAP|$HTYPE|${HTEMP}|${HHOURS}|${HHEA}|${HREALLOC}" >> $LOG
  164. CLASS_HEA="ok"
  165. if [[ "$HHEA" != "PASSED" && "$HHEA" != "OK" ]]; then
  166. CLASS_HEA="warn"
  167. HAS_ERRORS=1
  168. EMAIL_BODY="${EMAIL_BODY}\n- /dev/$d ($HMOD, SN: $HSER): Health status is '$HHEA'!"
  169. fi
  170. CLASS_REA="ok"
  171. if [[ "$HREALLOC" =~ ^[0-9]+$ ]] && [[ "$HREALLOC" -gt 0 ]]; then
  172. CLASS_REA="warn"
  173. HAS_ERRORS=1
  174. EMAIL_BODY="${EMAIL_BODY}\n- /dev/$d ($HMOD, SN: $HSER): Reallocated Sectors count is $HREALLOC!"
  175. fi
  176. if [[ "$HTEMP" =~ ^[0-9]+$ ]] && [[ "$HTEMP" -ge "$TEMP" ]]; then
  177. HAS_ERRORS=1
  178. EMAIL_BODY="${EMAIL_BODY}\n- /dev/$d ($HMOD, SN: $HSER): High Temperature ($HTEMP °C)!"
  179. fi
  180. cat << EOF >> "$HTML_OUT"
  181. <tr>
  182. <td style="font-weight: 500;">/dev/$d</td>
  183. <td>${HMOD}</td>
  184. <td class="serial">${HSER}</td>
  185. <td>$HCAP</td>
  186. <td>$HTYPE</td>
  187. <td>${HTEMP}</td>
  188. <td>${HHOURS}</td>
  189. <td><span class="badge $CLASS_HEA">${HHEA}</span></td>
  190. <td><span class="badge $CLASS_REA">${HREALLOC}</span></td>
  191. </tr>
  192. EOF
  193. done
  194. if [[ "$HAS_ERRORS" -eq 1 ]]; then
  195. FULL_MAIL_BODY="HDD/SSD alert triggered on server: $SRV\nDate: $(date +'%Y-%m-%d %H:%M:%S')\n\nDetected issues:$EMAIL_BODY\n\nCheck full dashboard at HTML report."
  196. echo -e "$FULL_MAIL_BODY" | mail -s "$EMAIL_SUBJECT" "$EMAIL_TO"
  197. fi
  198. CLI_OUT=$(column -t -s '|' $LOG)
  199. HEADER=$(echo "$CLI_OUT" | head -n 1)
  200. DASHES=$(echo "$HEADER" | sed 's/[^ ]/-/g')
  201. echo -e "\e[1;36m$DASHES\e[0m"
  202. echo -e "\e[1;36m$HEADER\e[0m"
  203. echo -e "\e[1;36m$DASHES\e[0m"
  204. echo "$CLI_OUT" | tail -n +2
  205. rm -f $LOG
  206. cat << EOF >> "$HTML_OUT"
  207. </tbody>
  208. </table>
  209. <div class="timestamp">Last updated: $(date +'%Y-%m-%d %H:%M:%S')</div>
  210. </div>
  211. </body>
  212. </html>
  213. EOF