NAS rakentelut

  • Keskustelun aloittaja Keskustelun aloittaja dun
  • Aloitettu Aloitettu
Mitenkäs työ ootte kiinnitelly noita adaptereita koteloon, kun ei taida yhenkään adapterin reijät sattua koteloissa olevien standoffien kohalle?
Pitää koittaa nyt parempaa onnea CEACENT CNS41CX16I adapterilla. Tosin tuostakin on olemassa kahden SlimSAS liittimen malli (CNS42CX16R) ja noihin myös myydään erillistä PCIe korttia.
Tuo tosin on raportoitu toimivaksi niin on ainakin jotain takeita saada toimimaan.
 
Jaa, se näköjään vaatii boottamisen käyttöjärjestelmään (pelkkä BIOS ei riitä), että tohon SlimSAS PCIe adapterin tulee eloa ja siihen kytketty näytönohjain antaa kuvaa pihalle ja HBA/RAID ohjaimen konfigurointi menuun tolla ei ilmeisesti pääse mitenkään.

Olin jo valmis haukkumaan myös tuon Ceacentin adapterin, että eihän sekään toimi. Pitää vielä toi toinenkin adapteri testata, että onko se sittenkään siinä vika vai onko vika pelkästään käyttäjässä.
 
Uh oh, Toshiban N300 levyt käy kuumana nykysessä kopassa ja laukesi yksi aika nopeasti vrt. Seagaten ironwolffit. Tulisikos jollain mieleen joku parempi kotelo.

Nyt on Silverstone DS380 (https://www.silverstonetek.com/en/product/info/server-nas/DS380/) koppa ja mietin että jos vaihtaisi CS381:een. Vielä on hyllyssä pari kappaletta HPe Microserver 10:stä jääneitä Toshiban levyjä, mutta nyt kun katsoin uusia levyjä, niin aikaisemmin n. 100€ arvoiset 4tb Seagate Ironwolffit alkaa olla 215€ hintaisia halvimmillaan. Yksi levy näyttää scriptin mukaan vetelmään viimeisiä, niin pitää varmana vaihtaa se kun sanoo itsensä irti jossain vaiheessa. Vielä ei tarvii vaihtaa kun ZFS ilmoittaa että kaikki kunnossa.

Tein tämmöisen scriptin joka kertoo tärkeät tiedot.

Koodi:
KERNEL   MODEL                      SERIAL               SIZE     HEALTH     POH        TEMP     AGE          LIFE    REALLOC  UNCORR
------   -----                      ------               ----     ------     ---        ----     ---          ----    -------  ------
sdj      Samsung SSD 840 EVO 1TB    -                    931,5G   PASSED     105159     33C      12y 0m 1d    94%     -        -
sdi      Samsung SSD 850 PRO 256GB  -                    238,5G   PASSED     79239      34C      9y 0m 16d    94%     -        -
sdh      TOSHIBA HDWG480            -                    7,3T     PASSED     16021      51C      1y 10m 2d    -       0        0
sdf      ST4000VN008-2DR166         -                    3,6T     PASSED     74879      46C      8y 6m 19d    -       0        0
sdg      ST4000VN008-2DR166         -                    3,6T     PASSED     74895      45C      8y 6m 20d    -       0        0
sde      ST4000VN008-2DR166         -                    3,6T     PASSED     74872      43C      8y 6m 19d    -       0        0
sdd      ST4000VN008-2DR166         -                    3,6T     PASSED     56387      36C      6y 5m 9d     -       0        0
sdc      ST4000VN008-2DR166         -                    3,6T     PASSED     56389      39C      6y 5m 9d     -       184      328
sdb      ST4000VN008-2DR166         -                    3,6T     PASSED     46681      41C      5y 4m 0d     -       0        0
sda      WDC WD40EFRX-68N32N0       -                    3,6T     PASSED     39996      41C      4y 6m 26d    -       0        0

Edit: Jos jotain muuten kiinnostaa n. 6kk käytössä ollut HPe Microserver 10, niin olisi yksi ylimääräinen jolle ei ole tällä hetkellä mitään käyttöä.Ei ole vieläkään käyttöä, mutta ei ole mitään järkeä näköjään myöskään myydä pois kun tolle voi tulla jossain vaiheessa käyttöä ja uutena joutuu nyt 5 vuotta vanhasta raudata maksamaan 760€. Eli enpä myykään.
 
Viimeksi muokattu:
Uh oh, Toshiban N300 levyt käy kuumana nykysessä kopassa ja laukesi yksi aika nopeasti vrt. Seagaten ironwolffit. Tulisikos jollain mieleen joku parempi kotelo.

Nyt on Silverstone DS380 (https://www.silverstonetek.com/en/product/info/server-nas/DS380/) koppa ja mietin että jos vaihtaisi CS381:een. Vielä on hyllyssä pari kappaletta HPe Microserver 10:stä jääneitä Toshiban levyjä, mutta nyt kun katsoin uusia levyjä, niin aikaisemmin n. 100€ arvoiset 4tb Seagate Ironwolffit alkaa olla 215€ hintaisia halvimmillaan. Yksi levy näyttää scriptin mukaan vetelmään viimeisiä, niin pitää varmana vaihtaa se kun sanoo itsensä irti jossain vaiheessa. Vielä ei tarvii vaihtaa kun ZFS ilmoittaa että kaikki kunnossa.

Tein tämmöisen scriptin joka kertoo tärkeät tiedot.

Koodi:
KERNEL   MODEL                      SERIAL               SIZE     HEALTH     POH        TEMP     AGE          LIFE    REALLOC  UNCORR
------   -----                      ------               ----     ------     ---        ----     ---          ----    -------  ------
sdj      Samsung SSD 840 EVO 1TB    -                    931,5G   PASSED     105159     33C      12y 0m 1d    94%     -        -
sdi      Samsung SSD 850 PRO 256GB  -                    238,5G   PASSED     79239      34C      9y 0m 16d    94%     -        -
sdh      TOSHIBA HDWG480            -                    7,3T     PASSED     16021      51C      1y 10m 2d    -       0        0
sdf      ST4000VN008-2DR166         -                    3,6T     PASSED     74879      46C      8y 6m 19d    -       0        0
sdg      ST4000VN008-2DR166         -                    3,6T     PASSED     74895      45C      8y 6m 20d    -       0        0
sde      ST4000VN008-2DR166         -                    3,6T     PASSED     74872      43C      8y 6m 19d    -       0        0
sdd      ST4000VN008-2DR166         -                    3,6T     PASSED     56387      36C      6y 5m 9d     -       0        0
sdc      ST4000VN008-2DR166         -                    3,6T     PASSED     56389      39C      6y 5m 9d     -       184      328
sdb      ST4000VN008-2DR166         -                    3,6T     PASSED     46681      41C      5y 4m 0d     -       0        0
sda      WDC WD40EFRX-68N32N0       -                    3,6T     PASSED     39996      41C      4y 6m 26d    -       0        0

Edit: Jos jotain muuten kiinnostaa n. 6kk käytössä ollut HPe Microserver 10, niin olisi yksi ylimääräinen jolle ei ole tällä hetkellä mitään käyttöä.

Kehtaisitko jakaa tuon skriptin ketjuun? Itsekin miettinyt että pitäisi levyjen tarkkailuun kehitellä joku vastaava työkalu mistä voisi yhdellä silmäyksellä seurata lämpötilaa ym. elonmerkkejä, mutta en ole vielä saanut aikaiseksi.

Omassa nassissa tällä hetkellä levyt idlaa vielä melkein siedettävissä lämpötiloissa, mutta aina kun zfs aloittaa scrubin, lämmöt meinaa äkkiä karata 50 väärälle puolelle. Jotain aktiivijäähdytystä tuokin vaatisi.
 
Kehtaisitko jakaa tuon skriptin ketjuun? Itsekin miettinyt että pitäisi levyjen tarkkailuun kehitellä joku vastaava työkalu mistä voisi yhdellä silmäyksellä seurata lämpötilaa ym. elonmerkkejä, mutta en ole vielä saanut aikaiseksi.

Omassa nassissa tällä hetkellä levyt idlaa vielä melkein siedettävissä lämpötiloissa, mutta aina kun zfs aloittaa scrubin, lämmöt meinaa äkkiä karata 50 väärälle puolelle. Jotain aktiivijäähdytystä tuokin vaatisi.
Ollos hyvä, laitoin MIT lisenssin alle, eli antaa mennä vaan ja muokkailkaa miten lystää.

disk_inventory.sh
Koodi:
#!/usr/bin/env bash
# Copyright 2026 Dun
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

set -euo pipefail

# disk_inventory.sh
#
# Prints one row per physical disk using /dev/disk/by-id and smartctl.
#
# Features:
# - skips partition aliases: *-part1, *-part2, *-part9, etc.
# - deduplicates multiple aliases for the same disk
# - prefers stable /dev/disk/by-id paths for disk discovery
# - prints kernel device, model, serial, size, SMART health,
#   power-on hours, temperature and estimated age
# - prints remaining SSD life when available
# - prints reallocated and uncorrectable sectors for HDDs
#
# AGE format:
# 1y 2m 10d
#
# AGE is calculated from Power_On_Hours.
# Example:
# 1y 2m 10d = approximately 1 year, 2 months, 10 days powered on
#
# TEMP format:
# 32C
#
# LIFE format:
# 95%
#
# LIFE is only shown for SSDs when a supported SMART lifetime
# indicator is available.
#
# REALLOC:
# Reallocated_Sector_Ct for HDDs.
#
# UNCORR:
# Offline_Uncorrectable for HDDs.
#
# Requirements:
# sudo apt install smartmontools util-linux
#
# Usage:
# chmod +x disk_inventory.sh
# ./disk_inventory.sh
#
# Optional:
# SUDO="" ./disk_inventory.sh
# BY_ID_GLOB="/dev/disk/by-id/scsi-SATA_*" ./disk_inventory.sh

SUDO="${SUDO:-sudo}"
BY_ID_GLOB="${BY_ID_GLOB:-/dev/disk/by-id/scsi-*}"

command -v smartctl >/dev/null 2>&1 || {
    echo "ERROR: smartctl not found. Install with: sudo apt install smartmontools" >&2
    exit 1
}

command -v lsblk >/dev/null 2>&1 || {
    echo "ERROR: lsblk not found." >&2
    exit 1
}

hours_to_age() {
    local hours="$1"

    if [[ ! "$hours" =~ ^[0-9]+$ ]]; then
        echo "-"
        return
    fi

    local total_days=$((hours / 24))
    local years=$((total_days / 365))
    local remaining_days=$((total_days % 365))
    local months=$((remaining_days / 30))
    local days=$((remaining_days % 30))

    printf "%dy %dm %dd" "$years" "$months" "$days"
}

get_temperature() {
    local smart_data="$1"
    local temp=""

    # ATA/SATA drives:
    #
    # Example:
    # 194 Temperature_Celsius ... 33
    #
    # Some drives instead expose attribute 190:
    # 190 Airflow_Temperature_Cel ... 31
    temp="$(
        awk '
        /Temperature_Celsius/ {
            print $10
            exit
        }
        /Airflow_Temperature_Cel/ {
            print $10
            exit
        }
        ' <<< "$smart_data"
    )"

    # SAS/SCSI drives:
    #
    # Example:
    # Current Drive Temperature:     34 C
    if [[ -z "$temp" ]]; then
        temp="$(
            awk '
            /Current Drive Temperature:/ {
                for (i = 1; i <= NF; i++) {
                    if ($i ~ /^[0-9]+$/) {
                        print $i
                        exit
                    }
                }
            }
            ' <<< "$smart_data"
        )"
    fi

    # NVMe and some newer smartctl output:
    #
    # Example:
    # Temperature:                        35 Celsius
    if [[ -z "$temp" ]]; then
        temp="$(
            awk '
            /^[[:space:]]*Temperature:/ {
                for (i = 1; i <= NF; i++) {
                    if ($i ~ /^[0-9]+$/) {
                        print $i
                        exit
                    }
                }
            }
            ' <<< "$smart_data"
        )"
    fi

    if [[ "$temp" =~ ^-?[0-9]+$ ]]; then
        printf "%sC" "$temp"
    else
        printf "-"
    fi
}

is_ssd() {
    local device="$1"
    local rotational=""

    rotational="$(
        lsblk -dn -o ROTA "$device" 2>/dev/null |
            awk '{$1=$1; print}'
    )"

    [[ "$rotational" == "0" ]]
}

get_ssd_life() {
    local smart_data="$1"
    local life=""
    local used=""
    local value=""

    # NVMe:
    #
    # Percentage Used: 12%
    #
    # Convert endurance used into estimated life remaining.
    used="$(
        awk '
        /^[[:space:]]*Percentage Used:/ {
            for (i = 1; i <= NF; i++) {
                if ($i ~ /^[0-9]+%$/) {
                    gsub(/%/, "", $i)
                    print $i
                    exit
                }
            }
        }
        ' <<< "$smart_data"
    )"

    if [[ "$used" =~ ^[0-9]+$ ]]; then
        value=$((10#$used))

        if (( value >= 100 )); then
            printf "0%%"
        else
            printf "%d%%" "$((100 - value))"
        fi

        return
    fi

    # SATA SSDs that directly report percentage life remaining.
    life="$(
        awk '
        /Percent_Lifetime_Remain/ ||
        /Percent_Lifetime_Remaining/ ||
        /SSD_Life_Left/ {
            if ($4 ~ /^[0-9]+$/) {
                print $4
                exit
            }
        }
        ' <<< "$smart_data"
    )"

    if [[ "$life" =~ ^[0-9]+$ ]]; then
        value=$((10#$life))

        if (( value >= 0 && value <= 100 )); then
            printf "%d%%" "$value"
            return
        fi
    fi

    # Samsung SATA SSDs commonly expose SMART attribute 177:
    #
    # Wear_Leveling_Count
    #
    # The normalized VALUE field normally starts at 100 and decreases
    # as NAND endurance is consumed.
    life="$(
        awk '
        $1 == "177" && $2 == "Wear_Leveling_Count" {
            if ($4 ~ /^[0-9]+$/) {
                print $4
                exit
            }
        }
        ' <<< "$smart_data"
    )"

    if [[ "$life" =~ ^[0-9]+$ ]]; then
        value=$((10#$life))

        if (( value >= 0 && value <= 100 )); then
            printf "%d%%" "$value"
            return
        fi
    fi

    printf "-"
}

get_reallocated_sectors() {
    local smart_data="$1"
    local value=""

    value="$(
        awk '
        $2 == "Reallocated_Sector_Ct" {
            print $10
            exit
        }
        ' <<< "$smart_data"
    )"

    if [[ "$value" =~ ^[0-9]+$ ]]; then
        printf "%s" "$value"
    else
        printf "-"
    fi
}

get_uncorrectable_sectors() {
    local smart_data="$1"
    local value=""

    value="$(
        awk '
        $2 == "Offline_Uncorrectable" {
            print $10
            exit
        }
        ' <<< "$smart_data"
    )"

    if [[ "$value" =~ ^[0-9]+$ ]]; then
        printf "%s" "$value"
    else
        printf "-"
    fi
}

printf "%-8s %-26s %-20s %-8s %-10s %-10s %-8s %-12s %-7s %-8s %-8s\n" \
    "KERNEL" "MODEL" "SERIAL" "SIZE" "HEALTH" "POH" "TEMP" "AGE" "LIFE" "REALLOC" "UNCORR"

printf "%-8s %-26s %-20s %-8s %-10s %-10s %-8s %-12s %-7s %-8s %-8s\n" \
    "------" "-----" "------" "----" "------" "---" "----" "---" "----" "-------" "------"

declare -A seen

shopt -s nullglob

for byid in $BY_ID_GLOB; do
    [[ -e "$byid" ]] || continue

    [[ "$byid" =~ -part[0-9]+$ ]] && continue

    realdev="$(readlink -f "$byid" 2>/dev/null || true)"
    [[ -n "$realdev" ]] || continue
    [[ -b "$realdev" ]] || continue

    kernel="$(basename "$realdev")"

    info="$($SUDO smartctl -i "$byid" 2>/dev/null || true)"
    all="$($SUDO smartctl -a "$byid" 2>/dev/null || true)"
    extra="$($SUDO smartctl -x "$byid" 2>/dev/null || true)"

    model="$(
        awk -F: '
        /Device Model:/ {
            gsub(/^[ \t]+/, "", $2)
            print $2
            exit
        }
        /Product:/ {
            gsub(/^[ \t]+/, "", $2)
            print $2
            exit
        }
        /Model Number:/ {
            gsub(/^[ \t]+/, "", $2)
            print $2
            exit
        }
        ' <<< "$info"
    )"

    serial="$(
        awk -F: '
        /Serial Number:/ {
            gsub(/^[ \t]+/, "", $2)
            print $2
            exit
        }
        ' <<< "$info"
    )"

    [[ -n "$model" ]] || model="-"
    [[ -n "$serial" ]] || serial="-"

    if [[ "$serial" != "-" ]]; then
        key="serial:$serial"
    else
        key="kernel:$kernel"
    fi

    [[ -n "${seen[$key]:-}" ]] && continue
    seen[$key]=1

    size="$(
        lsblk -dn -o SIZE "$realdev" 2>/dev/null |
            awk '{$1=$1; print}' || true
    )"
    [[ -n "$size" ]] || size="-"

    health="$(
        awk -F: '
        /SMART overall-health self-assessment test result:/ {
            gsub(/^[ \t]+/, "", $2)
            print $2
            exit
        }
        /SMART Health Status:/ {
            gsub(/^[ \t]+/, "", $2)
            print $2
            exit
        }
        ' <<< "$all"
    )"
    [[ -n "$health" ]] || health="-"

    poh="$(
        awk '
        /Power_On_Hours/ {
            print $10
            exit
        }
        /Power-on Hours/ {
            print $NF
            exit
        }
        /Accumulated power on time/ {
            print $0
            exit
        }
        ' <<< "$all"
    )"
    [[ -n "$poh" ]] || poh="-"

    temp="$(get_temperature "$all")"

    if [[ "$temp" == "-" ]]; then
        temp="$(get_temperature "$extra")"
    fi

    age="$(hours_to_age "$poh")"

    if is_ssd "$realdev"; then
        life="$(get_ssd_life "$all")"

        if [[ "$life" == "-" ]]; then
            life="$(get_ssd_life "$extra")"
        fi

        realloc="-"
        uncorr="-"
    else
        life="-"
        realloc="$(get_reallocated_sectors "$all")"
        uncorr="$(get_uncorrectable_sectors "$all")"
    fi

    printf "%-8s %-26.26s %-20.20s %-8s %-10.10s %-10.10s %-8.8s %-12.12s %-7.7s %-8.8s %-8.8s\n" \
        "$kernel" \
        "$model" \
        "$serial" \
        "$size" \
        "$health" \
        "$poh" \
        "$temp" \
        "$age" \
        "$life" \
        "$realloc" \
        "$uncorr"
done

Edit: Bitbucket linkki: Bitbucket
 
Viimeksi muokattu:
Ollos hyvä, laitoin MIT lisenssin alle, eli antaa mennä vaan ja muokkailkaa miten lystää.

disk_inventory.sh
Koodi:
#!/usr/bin/env bash
# Copyright 2026 Dun
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

set -euo pipefail

# disk_inventory.sh
#
# Prints one row per physical disk using /dev/disk/by-id and smartctl.
#
# Features:
# - skips partition aliases: *-part1, *-part2, *-part9, etc.
# - deduplicates multiple aliases for the same disk
# - prefers stable /dev/disk/by-id paths for disk discovery
# - prints kernel device, model, serial, size, SMART health,
#   power-on hours, temperature and estimated age
# - prints remaining SSD life when available
# - prints reallocated and uncorrectable sectors for HDDs
#
# AGE format:
# 1y 2m 10d
#
# AGE is calculated from Power_On_Hours.
# Example:
# 1y 2m 10d = approximately 1 year, 2 months, 10 days powered on
#
# TEMP format:
# 32C
#
# LIFE format:
# 95%
#
# LIFE is only shown for SSDs when a supported SMART lifetime
# indicator is available.
#
# REALLOC:
# Reallocated_Sector_Ct for HDDs.
#
# UNCORR:
# Offline_Uncorrectable for HDDs.
#
# Requirements:
# sudo apt install smartmontools util-linux
#
# Usage:
# chmod +x disk_inventory.sh
# ./disk_inventory.sh
#
# Optional:
# SUDO="" ./disk_inventory.sh
# BY_ID_GLOB="/dev/disk/by-id/scsi-SATA_*" ./disk_inventory.sh

SUDO="${SUDO:-sudo}"
BY_ID_GLOB="${BY_ID_GLOB:-/dev/disk/by-id/scsi-*}"

command -v smartctl >/dev/null 2>&1 || {
    echo "ERROR: smartctl not found. Install with: sudo apt install smartmontools" >&2
    exit 1
}

command -v lsblk >/dev/null 2>&1 || {
    echo "ERROR: lsblk not found." >&2
    exit 1
}

hours_to_age() {
    local hours="$1"

    if [[ ! "$hours" =~ ^[0-9]+$ ]]; then
        echo "-"
        return
    fi

    local total_days=$((hours / 24))
    local years=$((total_days / 365))
    local remaining_days=$((total_days % 365))
    local months=$((remaining_days / 30))
    local days=$((remaining_days % 30))

    printf "%dy %dm %dd" "$years" "$months" "$days"
}

get_temperature() {
    local smart_data="$1"
    local temp=""

    # ATA/SATA drives:
    #
    # Example:
    # 194 Temperature_Celsius ... 33
    #
    # Some drives instead expose attribute 190:
    # 190 Airflow_Temperature_Cel ... 31
    temp="$(
        awk '
        /Temperature_Celsius/ {
            print $10
            exit
        }
        /Airflow_Temperature_Cel/ {
            print $10
            exit
        }
        ' <<< "$smart_data"
    )"

    # SAS/SCSI drives:
    #
    # Example:
    # Current Drive Temperature:     34 C
    if [[ -z "$temp" ]]; then
        temp="$(
            awk '
            /Current Drive Temperature:/ {
                for (i = 1; i <= NF; i++) {
                    if ($i ~ /^[0-9]+$/) {
                        print $i
                        exit
                    }
                }
            }
            ' <<< "$smart_data"
        )"
    fi

    # NVMe and some newer smartctl output:
    #
    # Example:
    # Temperature:                        35 Celsius
    if [[ -z "$temp" ]]; then
        temp="$(
            awk '
            /^[[:space:]]*Temperature:/ {
                for (i = 1; i <= NF; i++) {
                    if ($i ~ /^[0-9]+$/) {
                        print $i
                        exit
                    }
                }
            }
            ' <<< "$smart_data"
        )"
    fi

    if [[ "$temp" =~ ^-?[0-9]+$ ]]; then
        printf "%sC" "$temp"
    else
        printf "-"
    fi
}

is_ssd() {
    local device="$1"
    local rotational=""

    rotational="$(
        lsblk -dn -o ROTA "$device" 2>/dev/null |
            awk '{$1=$1; print}'
    )"

    [[ "$rotational" == "0" ]]
}

get_ssd_life() {
    local smart_data="$1"
    local life=""
    local used=""
    local value=""

    # NVMe:
    #
    # Percentage Used: 12%
    #
    # Convert endurance used into estimated life remaining.
    used="$(
        awk '
        /^[[:space:]]*Percentage Used:/ {
            for (i = 1; i <= NF; i++) {
                if ($i ~ /^[0-9]+%$/) {
                    gsub(/%/, "", $i)
                    print $i
                    exit
                }
            }
        }
        ' <<< "$smart_data"
    )"

    if [[ "$used" =~ ^[0-9]+$ ]]; then
        value=$((10#$used))

        if (( value >= 100 )); then
            printf "0%%"
        else
            printf "%d%%" "$((100 - value))"
        fi

        return
    fi

    # SATA SSDs that directly report percentage life remaining.
    life="$(
        awk '
        /Percent_Lifetime_Remain/ ||
        /Percent_Lifetime_Remaining/ ||
        /SSD_Life_Left/ {
            if ($4 ~ /^[0-9]+$/) {
                print $4
                exit
            }
        }
        ' <<< "$smart_data"
    )"

    if [[ "$life" =~ ^[0-9]+$ ]]; then
        value=$((10#$life))

        if (( value >= 0 && value <= 100 )); then
            printf "%d%%" "$value"
            return
        fi
    fi

    # Samsung SATA SSDs commonly expose SMART attribute 177:
    #
    # Wear_Leveling_Count
    #
    # The normalized VALUE field normally starts at 100 and decreases
    # as NAND endurance is consumed.
    life="$(
        awk '
        $1 == "177" && $2 == "Wear_Leveling_Count" {
            if ($4 ~ /^[0-9]+$/) {
                print $4
                exit
            }
        }
        ' <<< "$smart_data"
    )"

    if [[ "$life" =~ ^[0-9]+$ ]]; then
        value=$((10#$life))

        if (( value >= 0 && value <= 100 )); then
            printf "%d%%" "$value"
            return
        fi
    fi

    printf "-"
}

get_reallocated_sectors() {
    local smart_data="$1"
    local value=""

    value="$(
        awk '
        $2 == "Reallocated_Sector_Ct" {
            print $10
            exit
        }
        ' <<< "$smart_data"
    )"

    if [[ "$value" =~ ^[0-9]+$ ]]; then
        printf "%s" "$value"
    else
        printf "-"
    fi
}

get_uncorrectable_sectors() {
    local smart_data="$1"
    local value=""

    value="$(
        awk '
        $2 == "Offline_Uncorrectable" {
            print $10
            exit
        }
        ' <<< "$smart_data"
    )"

    if [[ "$value" =~ ^[0-9]+$ ]]; then
        printf "%s" "$value"
    else
        printf "-"
    fi
}

printf "%-8s %-26s %-20s %-8s %-10s %-10s %-8s %-12s %-7s %-8s %-8s\n" \
    "KERNEL" "MODEL" "SERIAL" "SIZE" "HEALTH" "POH" "TEMP" "AGE" "LIFE" "REALLOC" "UNCORR"

printf "%-8s %-26s %-20s %-8s %-10s %-10s %-8s %-12s %-7s %-8s %-8s\n" \
    "------" "-----" "------" "----" "------" "---" "----" "---" "----" "-------" "------"

declare -A seen

shopt -s nullglob

for byid in $BY_ID_GLOB; do
    [[ -e "$byid" ]] || continue

    [[ "$byid" =~ -part[0-9]+$ ]] && continue

    realdev="$(readlink -f "$byid" 2>/dev/null || true)"
    [[ -n "$realdev" ]] || continue
    [[ -b "$realdev" ]] || continue

    kernel="$(basename "$realdev")"

    info="$($SUDO smartctl -i "$byid" 2>/dev/null || true)"
    all="$($SUDO smartctl -a "$byid" 2>/dev/null || true)"
    extra="$($SUDO smartctl -x "$byid" 2>/dev/null || true)"

    model="$(
        awk -F: '
        /Device Model:/ {
            gsub(/^[ \t]+/, "", $2)
            print $2
            exit
        }
        /Product:/ {
            gsub(/^[ \t]+/, "", $2)
            print $2
            exit
        }
        /Model Number:/ {
            gsub(/^[ \t]+/, "", $2)
            print $2
            exit
        }
        ' <<< "$info"
    )"

    serial="$(
        awk -F: '
        /Serial Number:/ {
            gsub(/^[ \t]+/, "", $2)
            print $2
            exit
        }
        ' <<< "$info"
    )"

    [[ -n "$model" ]] || model="-"
    [[ -n "$serial" ]] || serial="-"

    if [[ "$serial" != "-" ]]; then
        key="serial:$serial"
    else
        key="kernel:$kernel"
    fi

    [[ -n "${seen[$key]:-}" ]] && continue
    seen[$key]=1

    size="$(
        lsblk -dn -o SIZE "$realdev" 2>/dev/null |
            awk '{$1=$1; print}' || true
    )"
    [[ -n "$size" ]] || size="-"

    health="$(
        awk -F: '
        /SMART overall-health self-assessment test result:/ {
            gsub(/^[ \t]+/, "", $2)
            print $2
            exit
        }
        /SMART Health Status:/ {
            gsub(/^[ \t]+/, "", $2)
            print $2
            exit
        }
        ' <<< "$all"
    )"
    [[ -n "$health" ]] || health="-"

    poh="$(
        awk '
        /Power_On_Hours/ {
            print $10
            exit
        }
        /Power-on Hours/ {
            print $NF
            exit
        }
        /Accumulated power on time/ {
            print $0
            exit
        }
        ' <<< "$all"
    )"
    [[ -n "$poh" ]] || poh="-"

    temp="$(get_temperature "$all")"

    if [[ "$temp" == "-" ]]; then
        temp="$(get_temperature "$extra")"
    fi

    age="$(hours_to_age "$poh")"

    if is_ssd "$realdev"; then
        life="$(get_ssd_life "$all")"

        if [[ "$life" == "-" ]]; then
            life="$(get_ssd_life "$extra")"
        fi

        realloc="-"
        uncorr="-"
    else
        life="-"
        realloc="$(get_reallocated_sectors "$all")"
        uncorr="$(get_uncorrectable_sectors "$all")"
    fi

    printf "%-8s %-26.26s %-20.20s %-8s %-10.10s %-10.10s %-8.8s %-12.12s %-7.7s %-8.8s %-8.8s\n" \
        "$kernel" \
        "$model" \
        "$serial" \
        "$size" \
        "$health" \
        "$poh" \
        "$temp" \
        "$age" \
        "$life" \
        "$realloc" \
        "$uncorr"
done
Kiitoksia.

Sen verran pitää kysyä että oliko tuossa mitään muita (distrokohtaisia?) riippuvuuksia kuin smartmontools ja util-linux? Ihan suorilta en saanut tuota vielä toimimaan; pikaisesti 22.04 LTS Ubuntulla kokeiltuna printtaa ainoastaan sarakeotsikot ja niiden alle viivat muttei mitään levyjen tietoja.

Muuten kuitenkin ilmeisen huolella kirjoitettu skripti että tuskin on kovin isosta kiinni, luultavasti multa vaan puuttuu systeemistä joku pikkupaketti tai työkalu. Kunhan tässä on aikaa niin täytyy debugata tarkemmin.
 
Kiitoksia.

Sen verran pitää kysyä että oliko tuossa mitään muita (distrokohtaisia?) riippuvuuksia kuin smartmontools ja util-linux? Ihan suorilta en saanut tuota vielä toimimaan; pikaisesti 22.04 LTS Ubuntulla kokeiltuna printtaa ainoastaan sarakeotsikot ja niiden alle viivat muttei mitään levyjen tietoja.

Muuten kuitenkin ilmeisen huolella kirjoitettu skripti että tuskin on kovin isosta kiinni, luultavasti multa vaan puuttuu systeemistä joku pikkupaketti tai työkalu. Kunhan tässä on aikaa niin täytyy debugata tarkemmin.

Sinäänsä ne ei ole distrokohtaisia sillä ittellä tuo toimii fedoralla ihan suoraan. Sulta varmaan puuttuu awk se ei ole monesti asennettuna distroissa eli koitappas apt-get install awk taikka gawk jospa se sillä lähtisi. Nopeasti en muuta huomaa tuossa.
 
Kiitoksia.

Sen verran pitää kysyä että oliko tuossa mitään muita (distrokohtaisia?) riippuvuuksia kuin smartmontools ja util-linux? Ihan suorilta en saanut tuota vielä toimimaan; pikaisesti 22.04 LTS Ubuntulla kokeiltuna printtaa ainoastaan sarakeotsikot ja niiden alle viivat muttei mitään levyjen tietoja.

Muuten kuitenkin ilmeisen huolella kirjoitettu skripti että tuskin on kovin isosta kiinni, luultavasti multa vaan puuttuu systeemistä joku pikkupaketti tai työkalu. Kunhan tässä on aikaa niin täytyy debugata tarkemmin

Vähän muutoksia tein itselle niin listasi nvme asemat myös.

Koodi:
SUDO="${SUDO:-sudo}"
BY_ID_GLOB="${BY_ID_GLOB:-/dev/disk/by-path/*}"
 
Sinäänsä ne ei ole distrokohtaisia sillä ittellä tuo toimii fedoralla ihan suoraan. Sulta varmaan puuttuu awk se ei ole monesti asennettuna distroissa eli koitappas apt-get install awk taikka gawk jospa se sillä lähtisi. Nopeasti en muuta huomaa tuossa.
Ihan hyvä veikkaus, aluksi epäilin samaa kun ilmeisesti vielä 22.04 Desktop ja Server asentaa oletuksena eri version (mawk vs. gawk).

Ei ollut kuitenkaan siitä kiinni vaan näemmä /disk/by-id/ listaa satalevyt scsi-* sijaan ata-* -prefiksillä, ja tuon muuttamalla alkoi jo levyjä löytymään.

Vähän muutoksia tein itselle niin listasi nvme asemat myös.

Koodi:
SUDO="${SUDO:-sudo}"
BY_ID_GLOB="${BY_ID_GLOB:-/dev/disk/by-path/*}"

Tässä kuitenkin luultavasti vielä astetta tyylipuhtaampi ratkaisu, joten tällä mennään. :thumbsup:
 
Jatketaan vielä hiukan, omassa koneessa on SK hynix BC711 HFM256GD3JX nvme ja smartctl 7.5 2025-04-30 r5714.
Käyttöaika ilmoitetaan muodossa "Power On Hours: 2,918"

eli skriptiin pitää lisätä "Power On Hours":
Bash:
    poh="$(
        awk '
        /Power_On_Hours/ {
            print $10
            exit
        }
        /Power-on Hours/ {
            print $NF
            exit
        }
        /Power On Hours/ {
            print $NF
            exit
        }
        /Accumulated power on time/ {
            print $0
            exit
        }
        ' <<< "$all"
    )"

ja
Bash:
poh=$(echo ${poh} | tr -d ',')

ennen hours_to_age kutsua niin saa siitäkin oikean tuloksen.

Ei varmasti paras ratkaisu mutta saipahan nopeasti toimimaan
 
Jatketaan vielä hiukan, omassa koneessa on SK hynix BC711 HFM256GD3JX nvme ja smartctl 7.5 2025-04-30 r5714.
Käyttöaika ilmoitetaan muodossa "Power On Hours: 2,918"

eli skriptiin pitää lisätä "Power On Hours":
Bash:
    poh="$(
        awk '
        /Power_On_Hours/ {
            print $10
            exit
        }
        /Power-on Hours/ {
            print $NF
            exit
        }
        /Power On Hours/ {
            print $NF
            exit
        }
        /Accumulated power on time/ {
            print $0
            exit
        }
        ' <<< "$all"
    )"

ja
Bash:
poh=$(echo ${poh} | tr -d ',')

ennen hours_to_age kutsua niin saa siitäkin oikean tuloksen.

Ei varmasti paras ratkaisu mutta saipahan nopeasti toimimaan

Koodi:
        /Power On Hours:/ {
                gsub(",","")
                print $NF
                exit
        }

Tuon gsub kanssa saapi sen pilkun pois niin ei tartte erikseen enää tr:n kautta pyöräyttää. Itte oon muokkaamassa tuota alkuperäistä hiukan raskaammin koska siinä on muitakin ongelmia. Ittellä ei näkynyt kuin parista ssd levystä kulutus prosentit koska tuntuu aika mielivaltaisesti eri valmistajat esittävän sen. Onneksi smartctl osaa ulostaa eri formaateissa tuon ulosteensa niin siellä on json formaatissa huomattavasti yhtenäisempiä avaimia joilla hakea arvoja.
 
Koodi:
        /Power On Hours:/ {
                gsub(",","")
                print $NF
                exit
        }

Tuon gsub kanssa saapi sen pilkun pois niin ei tartte erikseen enää tr:n kautta pyöräyttää. Itte oon muokkaamassa tuota alkuperäistä hiukan raskaammin koska siinä on muitakin ongelmia. Ittellä ei näkynyt kuin parista ssd levystä kulutus prosentit koska tuntuu aika mielivaltaisesti eri valmistajat esittävän sen. Onneksi smartctl osaa ulostaa eri formaateissa tuon ulosteensa niin siellä on json formaatissa huomattavasti yhtenäisempiä avaimia joilla hakea arvoja.
Totta, on jäänyt awk sen verran vähäiselle käytölle ettei taas osannut ajatella... ja liian helposti menee helposti ekalla ajatuksella vaikka ei olekaan "oikein".
Eri levyt ja eri smartctl versiot tuottaa kivasti eri tuloksia, toivottavasti json auttaa
 
Juu, ton scriptin ei ollut tarkoituskaan olla mikään yleispätevä, mutta hyvä jos saatte itsellene toimimaaan. Olis ton voinut laittaa jonnekin bitbuckettiin tai githubiinkin, mutta ei jaksanut ja sit olis tullut ehkä vielä Pull requsti kyselyitä, niin helpommin meni näin.

Edit: Sain tossa kopassa lämmötkin kuriin Toshiban levyillä kun huitasin pika-kelkkojen eteen vaan kylmästi 80cm Noctuan tuulettimen ulkoisella virtalähteellä ja 5V jännitteellä. Hyvin tippu 50C => 36C.

Pitää miettiä/katsoa olisko joku tehnyt 3d tulostettavan tuuletinrivaston tolle Silverstone DS380 kopalle jonka voisi vaan tulostaa vai pitääkö pistää Freecad päälle ja suunnitella oma että saisi 2kpl nätisti ettei ruuveilla tarvisi roikkutella ja saisi sormisuojan samalla.

Edit2: Laitoin kuitenkin julkisen repon pystyyn: Bitbucket
 
Viimeksi muokattu:
Juu, ton scriptin ei ollut tarkoituskaan olla mikään yleispätevä, mutta hyvä jos saatte itsellene toimimaaan. Olis ton voinut laittaa jonnekin bitbuckettiin tai githubiinkin, mutta ei jaksanut ja sit olis tullut ehkä vielä Pull requsti kyselyitä, niin helpommin meni näin.

Sinäänsä scriptihän on ainakin omasta mielestäni tosi hyvin kirjoitettu, parempi se on mitä itte saan tuotettua mutta kaiken huomioon ottaminen on se mikä on hankalaa. Huomannu sen ihan omissä kötöstyksissä, toimii hyvin sillä raudalla jossa kötöstyksen teet mutta heti kun vaihtuu rauta niin toiminta onkin puutteellista. :)

Oikeastaan ainoa sellainen suurta ihmetysta hetättänyt seikka oli toi että teet kolmeen kertaan smartctl eli -i -a -x kun -x tarjoaa kaiken edellisen.

Toi toimii oikein hyvänä pohja koska ollut aikomus tuollainen kötöstys rakentaa itsekin mutta en ole laiskuuttani saanut sitä aikaiseksi.
 
No joo, työkseni aikoinaan paljon näitä kirjoittelin, niin silloin tuli eri raudalla esimerkkejä vastaan ja aika nopeasti eri varlmistajien erikoisuudet tuli hanskattua ja implementoitua. Omalla raudalla jos toimii kotona niin riittää :). Mutta laitoin nyt kuitenkin julkiseen repoon jakoon, niin jos tulee hyviä ideoita niin voi PR:n siitä kirjoitella nopsaan.
 
Viimeksi muokattu:

Statistiikka

Viestiketjuista
312 918
Viestejä
5 309 811
Jäsenet
84 390
Uusin jäsen
Lehtipihvi

Hinta.fi

Back
Ylös Bottom