diff --git a/defaults/main.yaml b/defaults/main.yaml index 3294ad9..1d8790d 100644 --- a/defaults/main.yaml +++ b/defaults/main.yaml @@ -8,6 +8,8 @@ ssd_health_packages: - python3-venv - sqlite3 - jq + - hdparm + - redis-tools # autologin vars autologin_password: "kingduy" @@ -31,9 +33,9 @@ extra_options: | # kiosk service vars kiosk_service_name: "drive_check" -kiosk_service_exe: "{{ service_folder }}/drive_check.sh" +kiosk_service_exe: "{{ service_folder }}/drive_check_service.sh" kiosk_service_templates: - - chrome_website: "http://0.0.0.0:{{ container_http_port }}" + - chrome_website: "http://0.0.0.0:80" service_name: ssd_dashboard service_description: "SSD Health Web Dashboard" user_data_dir: "" diff --git a/files/dashboard/styles.css b/files/dashboard/styles.css index e35261a..cb27f5f 100644 --- a/files/dashboard/styles.css +++ b/files/dashboard/styles.css @@ -23,6 +23,28 @@ body { cursor: pointer; } +a, +a:link, +a:visited { + color: #3498db; /* Default link color (blue) */ + text-decoration: none; + transition: color 0.2s ease; +} + +/* Visited links (distinct yet in the same palette) */ +a:visited { + color: #9b59b6; /* Soft purple */ +} + +/* Hover state – bright, engaging, and contrasting */ +a:hover { + color: #1abc9c; /* Turquoise‑green */ +} + +/* Active state – immediate feedback */ +a:active { + color: #e74c3c; /* Warm red */ +} table, th, td { border: 1px solid black; diff --git a/files/scripts/app.py b/files/scripts/app.py index 7994b89..a7fec7f 100644 --- a/files/scripts/app.py +++ b/files/scripts/app.py @@ -65,7 +65,7 @@ def get_host_stats(as_json=False): cpu_temp = run_command(cpu_temp_command, zero_only=True) cpu_temp_stripped = re.sub(r'\u00b0C', '', cpu_temp) cpu_temp_fixed = f"{cpu_temp_stripped} C" - ip_address_command = "ip -o -4 ad | grep -e eth -e tun | awk '{print $2\": \" $4}'" + ip_address_command = "ip -o -4 ad | grep -v -e docker -e 127.0.0.1 | awk '{print $2\": \" $4}'" ip_addresses = run_command(ip_address_command, zero_only=True) time_now_command = "date +%r" time_now = run_command(time_now_command, zero_only=True) @@ -79,6 +79,19 @@ def get_host_stats(as_json=False): "ip_addresses": ip_addresses, "time": time_now }] + if check_for_battery(): + battery_level_command = "acpi | grep Battery | awk {print'$3 \" \" $4'}" + battery_level = run_command(battery_level_command, zero_only=True) + stats = [{ + "memory_total": total_memory, + "memory_used": used_memory, + "memory_free": free_memory, + "cpu_load": cpu_load, + "cpu_temp": cpu_temp_fixed, + "ip_addresses": ip_addresses, + "battery_level": battery_level, + "time": time_now + }] if debug_output: print("=== Current Host Stats ===") print(json.dumps(stats, indent=2)) @@ -192,7 +205,10 @@ def run_command(cmd, zero_only=False): # Split the output into lines and store it in an array output_lines = [line for line in output.split('\n') if line] # Return result - return output_lines[0] if zero_only else output_lines + try: + return output_lines[0] if zero_only else output_lines + except: + return output_lines # Function to return all drive records in database def get_all_drive_records(as_json=True): @@ -234,6 +250,14 @@ def check_serial_exists(serial): print(serial_check) return bool(query_db(serial_check)) +def check_for_battery(): + battery_check_command = "acpi | grep Battery | awk {print'$1'}" + battery_check = run_command(battery_check_command, zero_only=True) + if battery_check == 'Battery': + return True + else: + return False + #################################################### ### Flask Routes #################################################### diff --git a/tasks/main.yaml b/tasks/main.yaml index 8bfc8e7..c06fea9 100644 --- a/tasks/main.yaml +++ b/tasks/main.yaml @@ -18,8 +18,7 @@ include_tasks: autologin.yaml # configure service-mode -- name: Drive health - configure service-mode, disable autologin - when: install_kiosk | bool or service_only | bool +- name: Drive health - configure service-mode include_tasks: service_mode.yaml # Install chrome kiosk diff --git a/tasks/service_mode.yaml b/tasks/service_mode.yaml index c34a56c..c9cb938 100644 --- a/tasks/service_mode.yaml +++ b/tasks/service_mode.yaml @@ -1,42 +1,43 @@ --- -# This will run the drive_check.sh script as a service instead of the autologin +# This will run the drive_check_service.sh script as a service - name: Service Mode - set service mode vars set_fact: sleep_time: ".5" service_mode: true -- name: "Service Mode - {{ kiosk_service_name }}.service - stop service if running" +- name: "Service Mode - drive_check.service - stop service if running" ignore_errors: yes systemd: - name: "{{ kiosk_service_name }}.service" + name: "drive_check.service" state: stopped -- name: "Service Mode - template drive_check.sh again" +- name: "Service Mode - template drive_check_service.sh " template: - src: drive_check.sh - dest: "{{ service_folder }}/drive_check.sh" + src: drive_check_service.sh + dest: "{{ service_folder }}/drive_check_service.sh" mode: 0755 owner: "{{ autologin_user }}" group: "{{ autologin_user }}" -- name: "Service Mode - template {{ kiosk_service_name }}.service" +- name: "Service Mode - template drive_check_service.service" vars: - service_name: "{{ kiosk_service_name }}" + service_name: "drive_check" service_working_folder: "{{ service_folder }}" - service_exe: "{{ kiosk_service_exe }}" + service_exe: "{{ service_folder }}/drive_check_service.sh" template: src: "service_template.service" - dest: "/etc/systemd/system/{{ kiosk_service_name }}.service" + dest: "/etc/systemd/system/drive_check.service" mode: 0644 -- name: "Service Mode - {{ kiosk_service_name }} - enable and start service api and daemon reload" +- name: "Service Mode - drive_check_service - enable and start service api and daemon reload" systemd: daemon_reload: yes - name: "{{ kiosk_service_name }}.service" + name: "drive_check.service" state: started enabled: yes - name: Service Mode - remove autologin + when: install_kiosk | bool or service_only | bool include_tasks: no_autologin.yaml ... \ No newline at end of file diff --git a/templates/drive_check.sh b/templates/drive_check.sh index 9d4750a..1d1867a 100644 --- a/templates/drive_check.sh +++ b/templates/drive_check.sh @@ -2,27 +2,17 @@ # this is a big loop # it shows SSD health data -SERVICE_MODE = {{ service_mode }} - exec 2> /dev/null while true; do clear # Show IP Info - - if ! $SERVICE_MODE; then - echo ===Visit the IP in your browser to view history====== - fi + echo ===Visit the IP in your browser to view history====== ip -o -4 ad | grep -v -e docker -e 127.0.0.1 -e br- | awk '{print $2 "," $4}' | column -s , -t # get all disks DISK_LIST=$(ls -lo /dev/sd? | awk '{print $9}') # process each disk IFS=$'\n' read -rd '' -a DISK_ARRAY <<< "$DISK_LIST" for DISK in "${DISK_ARRAY[@]}"; do - # update active drives more frequently - if ! $SERVICE_MODE; then - echo "Issuing request to update drive database" - fi - curl -s "http://172.17.0.1:5000/refresh_active_drives" # store smartctl data once SMART_DATA=$(smartctl -x $DISK) NVME_CHECK=$(echo "$SMART_DATA" | grep "NVMe Version") @@ -41,35 +31,23 @@ while true; do MODEL=$(echo "$SMART_DATA" | grep "Device Model" | cut -d ":" -f 2 | xargs) SMART=$(echo "$SMART_DATA" | grep "self-assessment test result" | cut -d ":" -f 2 | xargs) FLAVOR="SATA SSD" - DRIVE_EXISTS=$(curl -s "http://172.17.0.1:5000/check?serial_lookup=$SERIAL" | jq .serial_number_exists) - if ! $SERVICE_MODE; then - # Display drive data - echo "============ $DISK Disk Info - SSD: ============" - #echo "DRIVE_EXISTS: $DRIVE_EXISTS" - echo "Serial Number: $SERIAL" - echo "Model String: $MODEL" - echo "SMART Check: $SMART" - echo "Disk capacity: $CAPACITY" - echo "TB Written: $TBW TB" - if [ -z "$PLR" ] ; then - echo "Percent Lifetime Remaining data not available" - else - echo "$DISK has $PLR% lifetime remaining" - fi - echo - fi if [ -x "$TBW"] ; then TBW="unknown" fi - # database handler - if [ "$DRIVE_EXISTS" == "false" ] ; then - H_MODEL=$(echo $MODEL | sed 's/ /%20/g') - H_FLAVOR=$(echo $FLAVOR | sed 's/ /%20/g') - H_CAPACITY=$(echo $CAPACITY | sed 's/ /%20/g') - curl -s "http://172.17.0.1:5000/add_drive?serial=$SERIAL&model=$H_MODEL&flavor=$H_FLAVOR&capacity=$H_CAPACITY&TBW=$TBW&smart=$SMART" + # Display drive data + echo "============ $DISK Disk Info - SSD: ============" + #echo "DRIVE_EXISTS: $DRIVE_EXISTS" + echo "Serial Number: $SERIAL" + echo "Model String: $MODEL" + echo "SMART Check: $SMART" + echo "Disk capacity: $CAPACITY" + echo "TB Written: $TBW TB" + if [ -z "$PLR" ] ; then + echo "Percent Lifetime Remaining data not available" else - curl -s "http://172.17.0.1:5000/update_drive?serial=$SERIAL&TBW=$TBW&smart=$SMART" - fi + echo "$DISK has $PLR% lifetime remaining" + fi + echo # NVMe Logic elif [ -n "$NVME_CHECK" ] ; then # Set Variables @@ -81,41 +59,25 @@ while true; do CAPACITY=$(echo "$SMART_DATA" | grep "amespace 1 Size" | cut -d '[' -f 2 | sed 's/]//g') SMART=$(echo "$SMART_DATA" | grep "self-assessment test result" | cut -d ":" -f 2 | xargs) FLAVOR="NVMe" - DRIVE_EXISTS=$(curl -s "http://172.17.0.1:5000/check?serial_lookup=$SERIAL" | jq .serial_number_exists) - if ! $SERVICE_MODE; then - # Display Disk Info - echo "============ $DISK Disk Info - NVMe: ============" - #echo "DRIVE_EXISTS: $DRIVE_EXISTS" - echo "Serial Number: $SERIAL" - echo "Model String: $MODEL" - echo "SMART Check: $SMART" - echo "Disk capacity: $CAPACITY" - echo "TB Written: $TBW TB" - echo "NAND spare blocks: $AVAIL_SPARE" - echo - if [ -x "$TBW"] ; then - TBW="unknown" - fi + if [ -x "$TBW"] ; then + TBW="unknown" fi - # database handler - if [ "$DRIVE_EXISTS" == "false" ] ; then - H_MODEL=$(echo $MODEL | sed 's/ /%20/g') - H_FLAVOR=$(echo $FLAVOR | sed 's/ /%20/g') - H_CAPACITY=$(echo $CAPACITY | sed 's/ /%20/g') - curl -s "http://172.17.0.1:5000/add_drive?serial=$SERIAL&model=$H_MODEL&flavor=$H_FLAVOR&capacity=$H_CAPACITY&TBW=$TBW&smart=$SMART" - else - curl -s "http://172.17.0.1:5000/update_drive?serial=$SERIAL&TBW=$TBW&smart=$SMART" - fi + # Display Disk Info + echo "============ $DISK Disk Info - NVMe: ============" + #echo "DRIVE_EXISTS: $DRIVE_EXISTS" + echo "Serial Number: $SERIAL" + echo "Model String: $MODEL" + echo "SMART Check: $SMART" + echo "Disk capacity: $CAPACITY" + echo "TB Written: $TBW TB" + echo "NAND spare blocks: $AVAIL_SPARE" + echo fi else - if ! $SERVICE_MODE; then - echo "Skipping $DISK, not SATA SSD or NVMe" - fi + echo "Skipping $DISK, not SATA SSD or NVMe" fi done # wait {{ sleep_time }} seconds, loop again - if ! $SERVICE_MODE; then - echo "Sleeping {{ sleep_time }} seconds" - fi + echo "Sleeping {{ sleep_time }} seconds" sleep {{ sleep_time }} done diff --git a/templates/drive_check_service.sh b/templates/drive_check_service.sh new file mode 100644 index 0000000..8f3814a --- /dev/null +++ b/templates/drive_check_service.sh @@ -0,0 +1,74 @@ +#!/bin/bash +# this is a big loop to handle the database + +exec 2> /dev/null +while true; do + # update active drives more frequently + curl -s "http://172.17.0.1:5000/refresh_active_drives" + clear + # get all disks + DISK_LIST=$(ls -lo /dev/sd? | awk '{print $9}') + # process each disk + IFS=$'\n' read -rd '' -a DISK_ARRAY <<< "$DISK_LIST" + for DISK in "${DISK_ARRAY[@]}"; do + # store smartctl data once + SMART_DATA=$(smartctl -x $DISK) + NVME_CHECK=$(echo "$SMART_DATA" | grep "NVMe Version") + SSD_CHECK=$(echo "$SMART_DATA" | grep "Rotation Rate" | grep "Solid State") + # if either SATA SSD or NVMe + if [ -n "$NVME_CHECK" ] || [ -n "$SSD_CHECK" ]; then + BLOCK_SIZE=$(fdisk -l $DISK | grep 'Sector size' | awk '{print $4}' ) + # SATA Logic + if [ -n "$SSD_CHECK" ] ; then + # Set Variables + TBW=$(echo "$SMART_DATA" | grep "Logical Sectors Written" | \ + awk -v BLOCK_SIZE="$BLOCK_SIZE" '{print $4 * BLOCK_SIZE / (2 ^ 40)}') + PLR=$(echo "$SMART_DATA" | grep Percent_Lifetime_Remain | awk '{print $4}') + CAPACITY=$(echo "$SMART_DATA" | grep "User Capacity" | cut -d '[' -f 2 | sed 's/]//g') + SERIAL=$(echo "$SMART_DATA" | grep "Serial Number" | cut -d ":" -f 2 | xargs) + MODEL=$(echo "$SMART_DATA" | grep "Device Model" | cut -d ":" -f 2 | xargs) + SMART=$(echo "$SMART_DATA" | grep "self-assessment test result" | cut -d ":" -f 2 | xargs) + FLAVOR="SATA SSD" + DRIVE_EXISTS=$(curl -s "http://172.17.0.1:5000/check?serial_lookup=$SERIAL" | jq .serial_number_exists) + if [ -x "$TBW"] ; then + TBW="unknown" + fi + # database handler + if [ "$DRIVE_EXISTS" == "false" ] ; then + H_MODEL=$(echo $MODEL | sed 's/ /%20/g') + H_FLAVOR=$(echo $FLAVOR | sed 's/ /%20/g') + H_CAPACITY=$(echo $CAPACITY | sed 's/ /%20/g') + curl -s "http://172.17.0.1:5000/add_drive?serial=$SERIAL&model=$H_MODEL&flavor=$H_FLAVOR&capacity=$H_CAPACITY&TBW=$TBW&smart=$SMART" + else + curl -s "http://172.17.0.1:5000/update_drive?serial=$SERIAL&TBW=$TBW&smart=$SMART" + fi + # NVMe Logic + elif [ -n "$NVME_CHECK" ] ; then + # Set Variables + MODEL=$(echo "$SMART_DATA" | grep "Model Number" | cut -d ":" -f 2 | xargs) + SERIAL=$(echo "$SMART_DATA" | grep "Serial Number" | cut -d ":" -f 2 | xargs) + TBW=$(echo "$SMART_DATA" | grep "Data Units Written" | sed 's/,//g' | \ + awk -v BLOCK_SIZE="$BLOCK_SIZE" '{print $4 * BLOCK_SIZE / (2 ^ 30)}') + AVAIL_SPARE=$(echo "$SMART_DATA" | grep "Available Spare:" | cut -d ':' -f 2 | xargs) + CAPACITY=$(echo "$SMART_DATA" | grep "amespace 1 Size" | cut -d '[' -f 2 | sed 's/]//g') + SMART=$(echo "$SMART_DATA" | grep "self-assessment test result" | cut -d ":" -f 2 | xargs) + FLAVOR="NVMe" + DRIVE_EXISTS=$(curl -s "http://172.17.0.1:5000/check?serial_lookup=$SERIAL" | jq .serial_number_exists) + if [ -x "$TBW"] ; then + TBW="unknown" + fi + # database handler + if [ "$DRIVE_EXISTS" == "false" ] ; then + H_MODEL=$(echo $MODEL | sed 's/ /%20/g') + H_FLAVOR=$(echo $FLAVOR | sed 's/ /%20/g') + H_CAPACITY=$(echo $CAPACITY | sed 's/ /%20/g') + curl -s "http://172.17.0.1:5000/add_drive?serial=$SERIAL&model=$H_MODEL&flavor=$H_FLAVOR&capacity=$H_CAPACITY&TBW=$TBW&smart=$SMART" + else + curl -s "http://172.17.0.1:5000/update_drive?serial=$SERIAL&TBW=$TBW&smart=$SMART" + fi + fi + fi + done + # sleep and loop again + sleep 0.5 +done