Skip to content

Commit

Permalink
Merge pull request #1108 from phillxnet/1107_enhance_smart_return_cod…
Browse files Browse the repository at this point in the history
…e_handling

enhance smart return code handling. Fixes #1107
  • Loading branch information
schakrava committed Jan 24, 2016
2 parents 4206820 + 67430e0 commit a7d03b7
Showing 1 changed file with 49 additions and 23 deletions.
72 changes: 49 additions & 23 deletions src/rockstor/system/smart.py
Expand Up @@ -46,8 +46,7 @@ def info(device, test_mode=TESTMODE):
o, e, rc = run_command([SMART, '-H', '--info', '/dev/%s' % device],
throw=False)
else: # we are testing so use a smartctl -H --info file dump instead
o, e, rc = run_command([CAT, '/root/smartdumps/smart-H--info.out'],
throw=False)
o, e, rc = run_command([CAT, '/root/smartdumps/smart-H--info.out'])
res = {}
# List of string matches to look for in smartctrl -H --info output.
# Note the "|" char allows for defining alternative matches ie A or B
Expand Down Expand Up @@ -87,8 +86,7 @@ def extended_info(device, test_mode=TESTMODE):
if not test_mode:
o, e, rc = run_command([SMART, '-a', '/dev/%s' % device], throw=False)
else: # we are testing so use a smartctl -a file dump instead
o, e, rc = run_command([CAT, '/root/smartdumps/smart-a.out'],
throw=False)
o, e, rc = run_command([CAT, '/root/smartdumps/smart-a.out'])
attributes = {}
for i in range(len(o)):
if (re.match('Vendor Specific SMART Attributes with Thresholds:',
Expand Down Expand Up @@ -159,28 +157,18 @@ def error_logs(device, test_mode=TESTMODE):
error number.
:return: log_l: A list containing each line in turn of the error log.
"""
smart_command = [SMART, '-l', 'error', '/dev/%s' % device]
if not test_mode:
o, e, rc = run_command([SMART, '-l', 'error', '/dev/%s' % device],
throw=False)
o, e, rc = run_command(smart_command, throw=False)
else:
o, e, rc = run_command([CAT, '/root/smartdumps/smart-l-error.out'],
throw=False)
o, e, rc = run_command([CAT, '/root/smartdumps/smart-l-error.out'])
# As we mute exceptions when calling the above command we should at least
# examine what we have as return code (rc); 64 has been seen when the error
# log contains errors but otherwise executes successfully so we catch this.
if rc == 64:
e_msg = 'Drive /dev/%s has logged S.M.A.R.T errors. Please view ' \
'the Error logs tab for this device.' % device
logger.error(e_msg)
email_root('S.M.A.R.T error', e_msg)
# In all other instances that are an error (non zero) we raise exception
# as normal.
elif rc != 0:
e_msg = ('non-zero code(%d) returned by command: %s -l error output: '
'%s error: %s' % (rc, SMART, o, e))
logger.error(e_msg)
raise CommandException(('%s -l error /dev/%s' % (SMART, device)), o, e,
rc)
overide_rc = 64
e_msg = 'Drive /dev/%s has logged S.M.A.R.T errors. Please view ' \
'the Error logs tab for this device.' % device
screen_return_codes(e_msg, overide_rc, o, e, rc, smart_command)
ecode_map = {
'ABRT' : 'Command ABoRTed',
'AMNF' : 'Address Mark Not Found',
Expand Down Expand Up @@ -238,12 +226,23 @@ def test_logs(device, test_mode=TESTMODE):
:param test_mode: Not True causes cat from file rather than smartctl command
:return: test_d as a dictionary of summarized test
"""
smart_command = [SMART, '-l', 'selftest', '-l', 'selective', '/dev/%s'
% device]
if not test_mode:
o, e, rc = run_command(
[SMART, '-l', 'selftest', '-l', 'selective', '/dev/%s' % device])
o, e, rc = run_command(smart_command, throw=False)
else:
o, e, rc = run_command(
[CAT, '/root/smartdumps/smart-l-selftest-l-selective.out'])
# A return code of 128 (non zero so run_command raises an exception) has
# been seen when executing this command. Strange as it means
# "Invalid argument to exit" anyway if we silence the throw of a generic
# non 0 exception we can catch the 128, akin to 64 catch in error_logs().
# N.B. no official list of rc = 128 in /usr/include/sysexits.h
overide_rc = 128
e_msg = 'run_command(%s) returned an error of %s. This has undetermined ' \
'meaning. Please view the Self-Test Logs tab for this device.' \
% (smart_command, overide_rc)
screen_return_codes(e_msg, overide_rc, o, e, rc, smart_command)
test_d = {}
log_l = []
for i in range(len(o)):
Expand Down Expand Up @@ -315,3 +314,30 @@ def update_config(config):
tfo.write('%s\n' % l)

return move(npath, SMARTD_CONFIG)


def screen_return_codes(msg_on_hit, return_code_target, o, e, rc, command):
"""
Provides a central mechanism to screen return codes from executing smart
commands. This is required as some non zero return codes would otherwise
trigger a generic exception clause in our general purpose run_command.
If the target return code is seen then email root with the message
provided, otherwise raise a generic exception with the command information.
N.B. May be done better by acting as a SMART run_command wrapper (Future).
:param msg_on_hit: message used to email root
:param return_code_target: return code to screen for
:param o: the output from the command when it was run
:param e: the error from the command when it was run
:param rc: the return code from running the command
"""
# if our return code is our target then log with our message and email root
# with the same.
if rc == return_code_target:
logger.error(msg_on_hit)
email_root('S.M.A.R.T error', msg_on_hit)
# In all other non zero (error) instances we raise an exception as normal.
elif rc != 0:
e_msg = ('non-zero code(%d) returned by command: %s output: '
'%s error: %s' % (rc, command, o, e))
logger.error(e_msg)
raise CommandException(('%s' % command), o, e, rc)

0 comments on commit a7d03b7

Please sign in to comment.