interpret stderr and raise custom exceptions

1.  Detects failed authentication message from Windows systems
    and raises SatoriSMBAuthenticationException
2.  Detects error message stating that the user has been locked
    out of a Windows system and raises SatoriSMBLockoutException
3.  Detects error message stating shared access flags are
    incompatible on a Windows system and raises
    SatoriSMBFileSharingException

Consolidates (DRY) the logic which listens to command output in
smb.py so that the custom Exceptions mentioned above can be raised
if any of the known error output is encountered.

Unchanged:  A non-zero exit status with an unknown error message
            raises a SubprocessError including that error message.

Previously, all error messages corresponding to non-zero exit codes
were "unknown".

Closes-Bug: #1389034
Change-Id: I157adfe9034177f7975473ff96f29fc17c6ad12b
This commit is contained in:
Samuel Stavinoha 2014-11-04 20:18:53 +00:00
parent 5f7272abf7
commit dea382bae1
2 changed files with 52 additions and 18 deletions

View File

@ -46,6 +46,26 @@ class SatoriShellException(SatoriException):
"""Invalid shell parameters."""
class SatoriAuthenticationException(SatoriException):
"""Invalid login credentials."""
class SatoriSMBAuthenticationException(SatoriAuthenticationException):
"""Invalid login credentials for use over SMB to server."""
class SatoriSMBLockoutException(SatoriSMBAuthenticationException):
"""Too many invalid logon attempts, the user has been locked out."""
class SatoriSMBFileSharingException(SatoriException):
"""Incompatible shared access flags for a file on the Windows system."""
class GetPTYRetryFailure(SatoriException):
"""Tried to re-run command with get_pty to no avail."""

View File

@ -29,6 +29,7 @@ import shlex
import subprocess
import tempfile
from satori import errors
from satori import ssh
from satori import tunnel
@ -277,6 +278,33 @@ class SMBClient(object): # pylint: disable=R0902
return self.remote_execute(command, powershell=powershell,
retry=retry - 1)
def _handle_output(self, output):
"""Check for process termination, exit code, or error messages.
If the exit code is available and is zero, return True. This rountine
will raise an exception in the case of a non-zero exit code.
"""
if self._process.poll() is not None:
if self._process.returncode == 0:
return True
if "The attempted logon is invalid" in output:
msg = [k for k in output.splitlines() if k][-1].strip()
raise errors.SatoriSMBAuthenticationException(msg)
elif "The user account has been automatically locked" in output:
msg = [k for k in output.splitlines() if k][-1].strip()
raise errors.SatoriSMBLockoutException(msg)
elif "cannot be opened because the share access flags" in output:
# A file cannot be opened because the share
# access flags are incompatible
msg = [k for k in output.splitlines() if k][-1].strip()
raise errors.SatoriSMBFileSharingException(msg)
else:
raise SubprocessError("subprocess with pid: %s has "
"terminated unexpectedly with "
"return code: %s | %s"
% (self._process.pid,
self._process.poll(), output))
def _get_output(self, prompt_expected=True, wait=500):
"""Retrieve output from _process.
@ -293,15 +321,8 @@ class SMBClient(object): # pylint: disable=R0902
tmp_out += self._file_read.read()
# leave loop if underlying process has a return code
# obviously meaning that it has terminated
if self._process.poll() is not None:
if self._process.returncode == 0:
break
error = tmp_out
raise SubprocessError("subprocess with pid: %s has terminated "
"unexpectedly with return code: %s | %s"
% (self._process.pid,
self._process.poll(), error))
time.sleep(0)
if self._handle_output(tmp_out):
break
time.sleep(float(wait) / 1000)
else:
LOG.debug("Loop 1 - stdout read: %s", tmp_out)
@ -315,15 +336,8 @@ class SMBClient(object): # pylint: disable=R0902
stdout += tmp_out
# leave loop if underlying process has a return code
# obviously meaning that it has terminated
if self._process.poll() is not None:
if self._process.returncode == 0:
break
error = tmp_out
raise SubprocessError("subprocess with pid: %s has terminated "
"unexpectedly with return code: %s | %s"
% (self._process.pid,
self._process.poll(), error))
time.sleep(0)
if self._handle_output(tmp_out):
break
time.sleep(float(wait) / 1000)
else:
LOG.debug("Loop 2 - stdout read: %s", tmp_out)