Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NAS-130745 / 25.04 / Update smartctl usage to use JSON #14479

Merged
merged 32 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
df69897
Updated smartctl usage
aiden3c Aug 29, 2024
cc78135
Fixed syntax
aiden3c Aug 29, 2024
d36f54f
Update tests
aiden3c Sep 3, 2024
016d61d
Merge remote-tracking branch 'origin/master' into NAS-130745
aiden3c Sep 3, 2024
92af136
Merge remote-tracking branch 'origin/master' into NAS-130745
aiden3c Sep 10, 2024
dec3cb1
Merge remote-tracking branch 'origin/master' into NAS-130745
aiden3c Sep 10, 2024
b8fadfe
Unit test fixes
aiden3c Sep 10, 2024
392a162
Load the JSON
aiden3c Sep 10, 2024
6b18210
Quote confusion
aiden3c Sep 10, 2024
bced6a7
Self test fix
aiden3c Sep 10, 2024
0c9b2fc
Fixed remaining
aiden3c Sep 10, 2024
e9bcbbe
Actually the test is wrong
aiden3c Sep 10, 2024
ca58c73
Unit test fix, smart key update
aiden3c Sep 10, 2024
dc5091e
Fix for sct/code
aiden3c Sep 10, 2024
c1ade49
More fixes
aiden3c Sep 10, 2024
343c96d
Two out of three?
aiden3c Sep 10, 2024
d1f38e8
Key fixes
aiden3c Sep 11, 2024
fa3011d
Final fix
aiden3c Sep 11, 2024
21e1bb2
Suggested change: remove unnecessary json loads
aiden3c Sep 13, 2024
da4bce6
Oops
aiden3c Sep 13, 2024
a80b5a6
Report smart attributes for all relevant drive types
aiden3c Sep 13, 2024
2f1ef08
Schema test
aiden3c Sep 16, 2024
9ad689d
Refactor schema and tests
aiden3c Sep 17, 2024
36eefe3
Weird import?
aiden3c Sep 17, 2024
0647da7
Fixed scheme implementation
aiden3c Sep 17, 2024
40fa526
Updating implementation
aiden3c Sep 17, 2024
48f8597
Final fix
aiden3c Sep 17, 2024
0412117
Float fix
aiden3c Sep 17, 2024
bf0de90
Fixed disk smart reporting
aiden3c Sep 17, 2024
fa237ad
Unused regex
aiden3c Sep 17, 2024
4f790ef
Merge remote-tracking branch 'origin/master' into NAS-130745
aiden3c Sep 17, 2024
2ffd485
Suggested changes
aiden3c Sep 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/middlewared/middlewared/api/v25_04_0/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
from .privilege import * # noqa
from .user import * # noqa
from .vendor import * # noqa
from .smartctl import * # noqa
38 changes: 38 additions & 0 deletions src/middlewared/middlewared/api/v25_04_0/smartctl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from typing import Any

from middlewared.api.base import BaseModel

__all__ = ["AtaSelfTest", "NvmeSelfTest", "ScsiSelfTest"]


class AtaSelfTest(BaseModel):
aiden3c marked this conversation as resolved.
Show resolved Hide resolved
num: int
description: str
status: str
status_verbose: str
remaining: float
lifetime: int
lba_of_first_error: int | None = None
aiden3c marked this conversation as resolved.
Show resolved Hide resolved


class NvmeSelfTest(BaseModel):
num: int
description: str
status: str
status_verbose: str
power_on_hours: int
failing_lba: int | None = None
nsid: int | None = None
seg: int | None = None
sct: int | None = 0x0
themylogin marked this conversation as resolved.
Show resolved Hide resolved
code: int | None = 0x0


class ScsiSelfTest(BaseModel):
num: int
description: str
status: str
status_verbose: str
segment_number: int | None = None
lifetime: int | None = None
lba_of_first_error: int | None = None
10 changes: 6 additions & 4 deletions src/middlewared/middlewared/etc_files/smartd.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import re
import shlex
import subprocess
import json

from middlewared.common.smart.smartctl import get_smartctl_args, smartctl, SMARTCTX
from middlewared.plugins.smart_.schedule import SMARTD_SCHEDULE_PIECES, smartd_schedule_piece
Expand All @@ -23,15 +24,16 @@ async def ensure_smart_enabled(args):
if any(arg.startswith("/dev/nvme") for arg in args):
return True

p = await smartctl(args + ["-i"], stderr=subprocess.STDOUT, check=False, encoding="utf8", errors="ignore")
if not re.search("SMART.*abled", p.stdout):
p = await smartctl(args + ["-i", "--json=c"], check=False, stderr=subprocess.STDOUT, encoding="utf8", errors="ignore")
pjson = json.loads(p.stdout)
if not pjson["smart_support"]["available"]:
logger.debug("SMART is not supported on %r", args)
return False

if re.search("SMART.*Enabled", p.stdout):
if pjson["smart_support"]["enabled"]:
return True

p = await smartctl(args + ["-s", "on"], stderr=subprocess.STDOUT, check=False)
p = await smartctl(args + ["-s", "on"], check=False, stderr=subprocess.STDOUT)
if p.returncode == 0:
return True
else:
Expand Down
8 changes: 3 additions & 5 deletions src/middlewared/middlewared/plugins/disk.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

RE_SED_RDLOCK_EN = re.compile(r'(RLKEna = Y|ReadLockEnabled:\s*1)', re.M)
RE_SED_WRLOCK_EN = re.compile(r'(WLKEna = Y|WriteLockEnabled:\s*1)', re.M)
RE_SMART_AVAILABLE = re.compile(r'SMART support is:\s+Available')


class DiskModel(sa.Model):
Expand Down Expand Up @@ -140,12 +139,11 @@ async def disk_extend(self, disk, context):

disk['supports_smart'] = None
if context['supports_smart']:
if await self.middleware.call('truenas.is_ix_hardware'):
if await self.middleware.call('truenas.is_ix_hardware') or disk['name'].startswith('nvme'):
disk['supports_smart'] = True
else:
disk['supports_smart'] = disk['name'].startswith('nvme') or bool(RE_SMART_AVAILABLE.search(
await self.middleware.call('disk.smartctl', disk['name'], ['-a'], {'silent': True}) or ''
))
disk_query = await self.middleware.call('disk.smartctl', disk['name'], ['-a', '--json=c'], {'silent': True})
aiden3c marked this conversation as resolved.
Show resolved Hide resolved
disk['supports_smart'] = disk_query.get('smart_support', {}).get('available', False)

if disk['name'] in context['boot_pool_disks']:
disk['pool'] = context['boot_pool_name']
Expand Down
8 changes: 6 additions & 2 deletions src/middlewared/middlewared/plugins/disk_/smart_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,16 @@ async def smart_attributes(self, name):
"""
Returns S.M.A.R.T. attributes values for specified disk name.
"""
output = json.loads(await self.middleware.call('disk.smartctl', name, ['-A', '-j']))
output = json.loads(await self.middleware.call('disk.smartctl', name, ['-a', '--json=c']))

if 'ata_smart_attributes' in output:
aiden3c marked this conversation as resolved.
Show resolved Hide resolved
return output['ata_smart_attributes']['table']
if 'nvme_smart_health_information_log' in output:
return output['nvme_smart_health_information_log']
if 'scsi_error_counter_log' in output and 'scsi_grown_defect_list' in output:
return {'scsi_error_counter_log': output['scsi_error_counter_log'], 'scsi_grown_defect_list': output['scsi_grown_defect_list']}

raise CallError('Only ATA device support S.M.A.R.T. attributes')
raise CallError('Only ATA/SCSI/NVMe devices support S.M.A.R.T. attributes')

@private
async def sata_dom_lifetime_left(self, name):
Expand Down
6 changes: 3 additions & 3 deletions src/middlewared/middlewared/plugins/disk_/temperature.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import asyncio
import datetime
import time
import json

import async_timeout

Expand Down Expand Up @@ -61,9 +62,8 @@ async def temperature(self, name, options):

@private
async def temperature_uncached(self, name, powermode):
output = await self.middleware.call('disk.smartctl', name, ['-a', '-n', powermode.lower()], {'silent': True})
if output is not None:
return parse_smartctl_for_temperature_output(output)
if output := await self.middleware.call('disk.smartctl', name, ['-a', '-n', powermode.lower(), '--json=c'], {'silent': True}):
return parse_smartctl_for_temperature_output(json.loads(output))

@private
async def reset_temperature_cache(self):
Expand Down
Loading
Loading