Обновить

Комментарии 26

НЛО прилетело и опубликовало эту надпись здесь

smartd шлёт на почту отчеты, хорошо бы научить его слать ещё в телеграм. Хотя тут можно на уровне почты передавать письма скрипту и делать дальше с ними что угодно.

НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь

На почту много кто умеет слать, например, виндовый CrystalDiskInfo. Кстати, он опенсорсный, так что технически можно и его научить отправлять в TG.

чтобы

"Reallocated > 0" и разве этого недостаточно? Надо поглумиться ради хелловина что-ли? Положите его в плов и выкиньте вместе с луком...

Смотря для чего, для помойки нормально. Для критических применений в утиль от греха подальше. По идее важна вообще динамика появился 1 пендинг>1 релокейт и 5 лет так висит - ну ладно. Ну и в статье мешанина из дисков и твердотельных, которые немножко по разному работают и оцениваются. И восстановить что то викторией итп. никогда ничего было нельзя - можно только до упора писать в не читаемый сектор с надежной что контроллер сам его переназначит.

"Reallocated > 0" и разве этого недостаточно?

А есть разница между собственно Reallocated и попыток Reallocated?

А негде посмотреть попытки переназначения, это нутрянка диска и доступа к ней у простых смертных нет. Есть только уже релокейты/переназначенные сектора и пендинги/кандидаты. Кандидаты - это не обязательно битые сектора, может кривая запись и не может прочитаться и контроллер пометил сектор, а если перезаписать сектор все будет нормально. Это собственно "ремонт" Викторией в режиме Эрейз, Ремап тоже самое только ПО делает очень много попыток записи в сектор.

Это собственно "ремонт" Викторией в режиме Эрейз, Ремап тоже самое

т.е. по факту это никакое не лечение? И получается что диск с одним таким сектором может как и долго-долго жить нормально с постоянными перезаписями , так и внезапно окончательно отказать?

Инфа из смарта не даёт никакой информации по одиночному дефекту в конкретный момент времени. Те дает, но только в 1 варианте: критичная инфа - списываем. Не критичный - одиночный дефект игнорируем и смотрим что будет дальше.

Более подробной инфы, не у производителя, уже не будет - некому собрать статистику - в бытовухе диски массово уже не используются, в датацентрах их(надеюсь) сразу списывают... Есть те, кто занимаются датарекавери, но к ним диск попадает уже на другом этапе...

Если вы за reallocated раз в неделю следите, то у меня для вас плохи новости. Ибо если диск сыпется всерьёз, то за неделю он рассыпется окончательно.

Так что следить за смартом надо в реальном времени. Ну и диски с reallocated переводить в разряд дискеток или мусорных помоек. А за такими можно и не следить.

А самое забавное, smart ничего не гарантирует. Попалась мне на одном из серверов парочка дисков с идеальным смартом, при этом на них живого места не было, это уже даже не зомби, а скелеты были (куча секторов со временем чтения 0.3-0.5 секунд, то есть попытки с сотой). Правда, они оба сидели в RAID1 и поэтому OS с них таки грузилась, да, за полчаса, но ведь грузилась :)

Поддерживаю. недавно было - диски в RAID1, внезапно в журнале системы пошли ошибки по записи на этот рейд. При этом контроллер говорит что с дисками все в порядке, смарт показывает что ошибок нет. А данные пришлось из ночного бэкапа вытаскивать, так как в базе сплошные ошибки.

Хороший смарт не гарантирует. Но вот плохой смарт обычно всё же гарантирует проблемы.

... столько воды ради неопределенных целей! ... толи хдд, толи ссд м.2 ... а как настраивал новый на первый запуск ни слова ... не туда ты копаешь, что очевидно не всем ... 🤦🏾‍♂️🥱

Для того чтобы что-то мониторить - неплохо понимать как все работает.

Если вы делали скан диска, после этого провели стирание и состояние ухудшилось, то диск ваш либо уроненый либо головке кирдык.

Мониторинг смарта в этом случае ничего не покажет, сектора читаются с задержкой, но читаются.

Вырастет raw read error rate, но там обычно и так все непросто.

Никто не писал бы mhdd и её наследников если бы достаточно было смарта для диагностики.

Это не мониторинг и алертинг к сожалению, а просто периодические отчёты в тг. Реализовать exporter для Prometheus на go иди python не так сложно, тем более что логика уже вся описана в скриптах. Хотя, если погуглить, то всё уже давно есть. А про этот экспортёр даже статья на хабре имеется.

Первые 3 абзаца - просто "чаааавооооо"?

Кого изменилось? Когда изменилось? В рот мне ногу, руку и бэдсектора. Из какого криогенного сна автора достали? Времен перфокарт? Или анга-банга-телечанга в пещере?

Никогда, никогда, никогда в жизни нельзя было ЗАСТАВИТЬ сектор добавиться в G-лист. Только если прошивка диска сочла это нужным. Про P-лист в целом промолчу. Ни MHDD, ни Виктория никогда ничего не добавляла сама.

Максимум этого можно добиться с PC3000, WDmarvel и подобными тулзами. И то в основном лишь отчистить G-лист.

И эта логика уже на протяжении лет 20 еще со времен IDE дисков сколько я себя помню.

Размер дефект листа НИКОГДА не был особо публичным. И для каждого диска он всегда был разным.

Тот же P-лист можно было пересоздать только бурн комплектом, которых в паблике обычно днем с огнем не сыщешь.

И ни G, ни P лист не спасал никогда от ситуаций, когда головка размазывает мусор и царапины дальше по поляне. Максимум что можно было - отключить одну голову.

Заберите домохозяек с хабра. Пусть не изобретают велик, curl-ом вызовут GET запрос вебхука в телегу из любого уже существующего софта и будут довольны.

Как человек, у которого диски спокойно доживают до 80-100к часов аптайма и работают без каких либо проблем, если их не греть и не играть в футбол (статистический брак все еще 3-5%), отключить угребищную парковку принудительную (во всякой синей, зеленой серии есть. выставить на уровне софта неактивность 15-30 минут, если так хочется), могу сказать, что все выше действие ради действия.

Коллеги, а может кто подскажет, как можно мониторить под Windows smart ssd дискa или дисков на контроллере perc h710p отданных под cachecade?

Для домашнего сервера небольшой самописный скрипт раз в сутки отправляет в тг информацию по всем основным параметрам, включая smart дисков и состояние raid. Если что не так - светит красным.

Можно скриптик в студию, пожалуйста

import os
import subprocess
import datetime
import psutil
import requests
import shutil
from reportlab.pdfgen import canvas
from reportlab.lib import colors
from reportlab.lib.pagesizes import letter  # будем задавать ширину вручную
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont

# Регистрируем шрифты (убедись, что файлы шрифтов существуют!)
pdfmetrics.registerFont(TTFont('DejaVuSans', '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf'))
pdfmetrics.registerFont(TTFont('DejaVuSans-Bold', '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf'))
pdfmetrics.registerFont(TTFont('DejaVuSansMono', '/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf'))

# Параметры для Telegram 
BOT_TOKEN = 'token'
CHAT_ID = 'ID_chat'

# Пути для хранения отчётов
SCRIPT_DIR = '/home/user/script/report'
PDF_DIR = os.path.join(SCRIPT_DIR, 'pdf')
os.makedirs(PDF_DIR, exist_ok=True)

# Параметры отрисовки
LINE_HEIGHT = 14
TOP_MARGIN = 50
BOTTOM_MARGIN = 50
PAGE_WIDTH = 595  # фиксированная ширина (A4)

def get_uptime():
    try:
        return subprocess.check_output(['uptime', '-p'], universal_newlines=True).strip()
    except Exception as e:
        return f"Ошибка получения аптайма: {e}"

def get_temperatures():
    try:
        out = subprocess.check_output(['sensors'], universal_newlines=True)
        temps = [line.strip() for line in out.splitlines() if '°C' in line]
        return "\n".join(temps) if temps else "Нет данных о температуре"
    except Exception as e:
        return f"Ошибка: {e}"

def get_critical_errors():
    try:
        cmd = ['journalctl', '-p', 'crit', '--since', '24 hours ago', '--no-pager']
        return subprocess.check_output(cmd, universal_newlines=True)
    except Exception as e:
        return f"Ошибка получения критических ошибок: {e}"

def get_services_status(services):
    statuses = {}
    for svc in services:
        try:
            stat = subprocess.check_output(['systemctl', 'is-active', svc], universal_newlines=True).strip()
        except subprocess.CalledProcessError:
            stat = "inactive"
        statuses[svc] = stat
    return statuses

def get_disk_usage():
    return psutil.disk_usage('/')

def get_smart_status(disk):
    if not shutil.which("smartctl"):
        return "smartctl не установлен"
    try:
        return subprocess.check_output(['smartctl', '-a', disk], universal_newlines=True)
    except Exception as e:
        return f"Ошибка: {e}"

def get_smart_overall(disk):
    """Возвращаем общий статус SMART (например, PASSED) для диска."""
    if not shutil.which("smartctl"):
        return "smartctl не установлен"
    try:
        overall_output = subprocess.check_output(['smartctl', '-H', disk], universal_newlines=True)
        for line in overall_output.splitlines():
            if "SMART overall-health self-assessment test result:" in line:
                return line.split(":", 1)[1].strip()
        return overall_output.strip()
    except Exception as e:
        return f"Ошибка: {e}"

def parse_smart_full(disk):
    full_output = get_smart_status(disk)
    lines = full_output.splitlines()
    start_idx = None
    end_idx = None
    for i, line in enumerate(lines):
        if "SMART Attributes Data Structure" in line:
            start_idx = i
            break
    if start_idx is None:
        return full_output
    for j in range(start_idx, len(lines)):
        if lines[j].startswith("SMART Error Log"):
            end_idx = j
            break
    block = lines[start_idx:end_idx] if end_idx else lines[start_idx:]
    return "\n".join(block)

def get_raid_status():
    try:
        with open('/proc/mdstat', 'r') as f:
            return f.read()
    except Exception as e:
        return f"Ошибка получения RAID статуса: {e}"

def get_fail2ban_stats():
    jails = ['sshd', 'postgresql', 'vsftpd']
    stats = {}
    for jail in jails:
        try:
            out = subprocess.check_output(['fail2ban-client', 'status', jail], universal_newlines=True)
            banned = "0"
            for line in out.splitlines():
                if "Currently banned" in line:
                    banned = line.split(":")[1].strip()
                    break
            stats[jail] = banned
        except Exception as e:
            stats[jail] = f"Ошибка: {e}"
    return stats

def build_report_lines(report_data):
    lines = []
    def add_line(text, font="DejaVuSans", size=10, color=colors.black):
        lines.append((text, font, size, color))
    def add_separator():
        add_line("____________________", font="DejaVuSans", size=10, color=colors.black)
    
    # Заголовок
    timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    add_line(f"Ежедневный отчёт о состоянии системы - {timestamp}", font="DejaVuSans-Bold", size=14)
    add_separator()
    
    # 1. Аптайм
    add_line("Аптайм системы:", font="DejaVuSans-Bold", size=12)
    for line in report_data['uptime'].splitlines():
        add_line(line)
    add_separator()
    
    # 2. Температуры
    add_line("Температуры (по всем датчикам):", font="DejaVuSans-Bold", size=12)
    for line in report_data['temperatures'].splitlines():
        add_line(line)
    add_separator()
    
    # 3. Критические ошибки
    add_line("Критические ошибки за последние 24 часа:", font="DejaVuSans-Bold", size=12, color=colors.red)
    crit = report_data['critical_errors'].strip()
    if crit:
        for line in crit.splitlines():
            add_line(line, color=colors.red)
    else:
        add_line("Нет критических ошибок", color=colors.green)
    add_separator()
    
    # 4. Состояние критических служб
    add_line("Состояние критических служб:", font="DejaVuSans-Bold", size=12)
    for svc, stat in report_data['services_status'].items():
        col = colors.green if stat == "active" else colors.red
        add_line(f"{svc}: {stat}", color=col)
    add_separator()
    
    # 5. SMART статус дисков
    add_line("SMART статус дисков:", font="DejaVuSans-Bold", size=12)
    for disk in report_data['smart']:
        overall = get_smart_overall(disk)
        header_color = colors.green if "PASSED" in overall.upper() else colors.red
        add_line(f"{disk} - {overall}", font="DejaVuSans-Bold", size=10, color=header_color)
        smart_text = parse_smart_full(disk)
        for line in smart_text.splitlines():
            # Выводим SMART-атрибуты обычным (чёрным) шрифтом
            add_line(line, font="DejaVuSansMono", size=7, color=colors.black)
        add_separator()
    
    # 6. RAID статус
    add_line("RAID статус:", font="DejaVuSans-Bold", size=12)
    raid_lines = report_data['raid_status'].splitlines()
    raid_color = colors.green if ("degraded" not in report_data['raid_status'].lower() and "inactive" not in report_data['raid_status'].lower()) else colors.red
    for line in raid_lines:
        add_line(line, color=raid_color)
    add_separator()
    
    # 7. Использование дискового пространства
    add_line("Использование дискового пространства:", font="DejaVuSans-Bold", size=12)
    du = report_data['disk_usage']
    add_line(f"Общий объём: {du.total / (1024**3):.2f} GB")
    add_line(f"Использовано: {du.used / (1024**3):.2f} GB")
    add_line(f"Свободно: {du.free / (1024**3):.2f} GB")
    add_separator()
    
    # 8. Статистика fail2ban
    add_line("Статистика fail2ban (количество забаненных адресов):", font="DejaVuSans-Bold", size=12)
    for jail, banned in report_data['fail2ban'].items():
        col = colors.green if banned == "0" else colors.red
        add_line(f"{jail}: {banned}", color=col)
    add_separator()
    
    # 9. Дополнительная информация
    add_line("Дополнительная информация:", font="DejaVuSans-Bold", size=12)
    load = os.getloadavg()
    add_line(f"Load average (1, 5, 15 мин): {load}")
    mem = psutil.virtual_memory()
    add_line(f"Использование памяти: {mem.percent}%")
    add_separator()
    
    return lines

def draw_report(lines, pdf_file_path, page_width, page_height):
    c = canvas.Canvas(pdf_file_path, pagesize=(page_width, page_height))
    y = page_height - TOP_MARGIN
    for text, font, size, color in lines:
        c.setFont(font, size)
        c.setFillColor(color)
        c.drawString(50, y, text)
        y -= LINE_HEIGHT
    c.save()

def main():
    # Собираем данные отчёта
    report_data = {}
    report_data['uptime'] = get_uptime()
    report_data['temperatures'] = get_temperatures()
    report_data['critical_errors'] = get_critical_errors()
    report_data['services_status'] = get_services_status(['fail2ban', 'ssh', 'postgresql'])
    report_data['disk_usage'] = get_disk_usage()
    # Задаём список дисков для SMART 
    report_data['smart'] = ['/dev/sda', '/dev/sdb']
    report_data['raid_status'] = get_raid_status()
    report_data['fail2ban'] = get_fail2ban_stats()

    # Собираем список строк отчёта
    lines = build_report_lines(report_data)
    total_lines = len(lines)
    required_height = TOP_MARGIN + (total_lines * LINE_HEIGHT) + BOTTOM_MARGIN
    print(f"Всего строк: {total_lines}, требуемая высота: {required_height}")

    now = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
    pdf_file = os.path.join(PDF_DIR, f'report_{now}.pdf')
    draw_report(lines, pdf_file, PAGE_WIDTH, required_height)
    print(f"PDF отчёт сохранён: {pdf_file}")

    # Отправляем PDF через Telegram
    url = f"https://api.telegram.org/bot{BOT_TOKEN}/sendDocument"
    with open(pdf_file, 'rb') as f:
        files = {'document': f}
        data = {'chat_id': CHAT_ID, 'caption': 'Ежедневный отчёт сервера'}
        response = requests.post(url, files=files, data=data)
    print("Ответ Telegram:", response.text)

if __name__ == '__main__':
    main()

Пожалуйста. Бот собирает и отправляет в чат один раз в сутки (через crontab) pdf с параметрами. Здесь у меня из параметров:
- аптайм
- температура
- критические ошибки
- состояние критических служб
- smart дисков
- статус RAID
- использование дискового пространства
- статистика fail2ban
- средняя нагрузка
- использование памяти

Если в виктории пошли красные сектора, ты уже насилуешь труп.

У этого диска явно кончилась область с резервными секторами и это никак не связано умный он или нет, а кончилась она потому что в диск попала пыль, мусор, который в процессе работы задирает поверхность, вот тебе и красные сектора, ты борешься со следствием, а не причиной.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации