diff --git a/src/rockstor/system/smart.py b/src/rockstor/system/smart.py index bff954ed5..637f123ea 100644 --- a/src/rockstor/system/smart.py +++ b/src/rockstor/system/smart.py @@ -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 @@ -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:', @@ -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', @@ -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)): @@ -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)