Use exit codes enum
We have much more, than 2 possible exit codes ant it's difficult to remember all of it's, so switch to IntEnum (int compatible operations) Change-Id: I3f3732ba407f20d8397dc385e53519d868afb82f
This commit is contained in:
parent
0ef3f35079
commit
6ea7050191
|
@ -21,6 +21,7 @@ from yaml import safe_load
|
|||
|
||||
from devops.error import DevopsError
|
||||
from devops.error import DevopsNotImplementedError
|
||||
from devops.helpers.proc_enums import ExitCodes
|
||||
from devops import logger
|
||||
|
||||
|
||||
|
@ -42,20 +43,23 @@ class ExecResult(object):
|
|||
'__lock'
|
||||
]
|
||||
|
||||
def __init__(self, cmd, stdout=None, stderr=None, exit_code=-1):
|
||||
def __init__(self, cmd, stdout=None, stderr=None,
|
||||
exit_code=ExitCodes.EX_INVALID):
|
||||
"""Command execution result read from fifo
|
||||
|
||||
:type cmd: str
|
||||
:type stdout: list
|
||||
:type stderr: list
|
||||
:type exit_code: int
|
||||
:type exit_code: ExitCodes
|
||||
"""
|
||||
self.__lock = RLock()
|
||||
|
||||
self.__cmd = cmd
|
||||
self.__stdout = stdout if stdout is not None else []
|
||||
self.__stderr = stderr if stderr is not None else []
|
||||
self.__exit_code = exit_code
|
||||
|
||||
self.__exit_code = None
|
||||
self.exit_code = exit_code
|
||||
|
||||
# By default is none:
|
||||
self.__stdout_str = None
|
||||
|
@ -217,9 +221,12 @@ class ExecResult(object):
|
|||
|
||||
:type new_val: int
|
||||
"""
|
||||
if not isinstance(new_val, int):
|
||||
if not isinstance(new_val, (int, ExitCodes)):
|
||||
raise TypeError('Exit code is strictly int')
|
||||
with self.lock:
|
||||
if isinstance(new_val, int) and \
|
||||
new_val in ExitCodes.__members__.values():
|
||||
new_val = ExitCodes(new_val)
|
||||
self.__exit_code = new_val
|
||||
|
||||
def __deserialize(self, fmt):
|
||||
|
@ -313,7 +320,7 @@ class ExecResult(object):
|
|||
def __repr__(self):
|
||||
return (
|
||||
'{cls}(cmd={cmd}, stdout={stdout}, stderr={stderr}, '
|
||||
'exit_code={exit_code})'.format(
|
||||
'exit_code={exit_code!s})'.format(
|
||||
cls=self.__class__.__name__,
|
||||
cmd=self.cmd,
|
||||
stdout=self.stdout,
|
||||
|
@ -326,7 +333,7 @@ class ExecResult(object):
|
|||
"{cls}(\n\tcmd={cmd},"
|
||||
"\n\t stdout=\n'{stdout_brief}',"
|
||||
"\n\tstderr=\n'{stderr_brief}', "
|
||||
'\n\texit_code={exit_code}\n)'.format(
|
||||
'\n\texit_code={exit_code!s}\n)'.format(
|
||||
cls=self.__class__.__name__,
|
||||
cmd=self.cmd,
|
||||
stdout_brief=self.stdout_brief,
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
# Copyright 2016 Mirantis, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from enum import IntEnum
|
||||
from enum import unique
|
||||
|
||||
|
||||
@unique
|
||||
class SigNum(IntEnum):
|
||||
SIGHUP = 1 # Hangup (POSIX).
|
||||
SIGINT = 2 # Interrupt (ANSI).
|
||||
SIGQUIT = 3 # Quit (POSIX).
|
||||
SIGILL = 4 # Illegal instruction (ANSI).
|
||||
SIGTRAP = 5 # Trace trap (POSIX).
|
||||
SIGABRT = 6 # Abort (ANSI).
|
||||
SIGBUS = 7 # BUS error (4.2 BSD).
|
||||
SIGFPE = 8 # Floating-point exception (ANSI).
|
||||
SIGKILL = 9 # Kill, unblockable (POSIX).
|
||||
SIGUSR1 = 10 # User-defined signal 1 (POSIX).
|
||||
SIGSEGV = 11 # Segmentation violation (ANSI).
|
||||
SIGUSR2 = 12 # User-defined signal 2 (POSIX).
|
||||
SIGPIPE = 13 # Broken pipe (POSIX).
|
||||
SIGALRM = 14 # Alarm clock (POSIX).
|
||||
SIGTERM = 15 # Termination (ANSI).
|
||||
SIGSTKFLT = 16 # Stack fault.
|
||||
SIGCHLD = 17 # Child status has changed (POSIX).
|
||||
SIGCONT = 18 # Continue (POSIX).
|
||||
SIGSTOP = 19 # Stop, unblockable (POSIX).
|
||||
SIGTSTP = 20 # Keyboard stop (POSIX).
|
||||
SIGTTIN = 21 # Background read from tty (POSIX).
|
||||
SIGTTOU = 22 # Background write to tty (POSIX).
|
||||
SIGURG = 23 # Urgent condition on socket (4.2 BSD).
|
||||
SIGXCPU = 24 # CPU limit exceeded (4.2 BSD).
|
||||
SIGXFSZ = 25 # File size limit exceeded (4.2 BSD).
|
||||
SIGVTALRM = 26 # Virtual alarm clock (4.2 BSD).
|
||||
SIGPROF = 27 # Profiling alarm clock (4.2 BSD).
|
||||
SIGWINCH = 28 # Window size change (4.3 BSD, Sun).
|
||||
SIGPOLL = 29 # Pollable event occurred (System V)
|
||||
SIGPWR = 30 # Power failure restart (System V).
|
||||
SIGSYS = 31 # Bad system call.
|
||||
|
||||
def __str__(self):
|
||||
return "{name}<{value:d}(0x{value:02X})>".format(
|
||||
name=self.name,
|
||||
value=self.value
|
||||
)
|
||||
|
||||
|
||||
@unique
|
||||
class ExitCodes(IntEnum):
|
||||
EX_OK = 0 # successful termination
|
||||
|
||||
EX_INVALID = 0xDEADBEEF # uint32 debug value. Impossible for POSIX
|
||||
|
||||
EX_ERROR = 1 # general failure
|
||||
EX_BUILTIN = 2 # Misuse of shell builtins (according to Bash)
|
||||
|
||||
EX_USAGE = 64 # command line usage error
|
||||
EX_DATAERR = 65 # data format error
|
||||
EX_NOINPUT = 66 # cannot open input
|
||||
EX_NOUSER = 67 # addressee unknown
|
||||
EX_NOHOST = 68 # host name unknown
|
||||
EX_UNAVAILABLE = 69 # service unavailable
|
||||
EX_SOFTWARE = 70 # internal software error
|
||||
EX_OSERR = 71 # system error (e.g., can't fork)
|
||||
EX_OSFILE = 72 # critical OS file missing
|
||||
EX_CANTCREAT = 73 # can't create (user) output file
|
||||
EX_IOERR = 74 # input/output error
|
||||
EX_TEMPFAIL = 75 # temp failure; user is invited to retry
|
||||
EX_PROTOCOL = 76 # remote error in protocol
|
||||
EX_NOPERM = 77 # permission denied
|
||||
EX_CONFIG = 78 # configuration error
|
||||
|
||||
EX_NOEXEC = 126 # If a command is found but is not executable
|
||||
EX_NOCMD = 127 # If a command is not found
|
||||
|
||||
# Signal exits:
|
||||
EX_SIGHUP = 128 + SigNum.SIGHUP
|
||||
EX_SIGINT = 128 + SigNum.SIGINT
|
||||
EX_SIGQUIT = 128 + SigNum.SIGQUIT
|
||||
EX_SIGILL = 128 + SigNum.SIGILL
|
||||
EX_SIGTRAP = 128 + SigNum.SIGTRAP
|
||||
EX_SIGABRT = 128 + SigNum.SIGABRT
|
||||
EX_SIGBUS = 128 + SigNum.SIGBUS
|
||||
EX_SIGFPE = 128 + SigNum.SIGFPE
|
||||
EX_SIGKILL = 128 + SigNum.SIGKILL
|
||||
EX_SIGUSR1 = 128 + SigNum.SIGUSR1
|
||||
EX_SIGSEGV = 128 + SigNum.SIGSEGV
|
||||
EX_SIGUSR2 = 128 + SigNum.SIGUSR2
|
||||
EX_SIGPIPE = 128 + SigNum.SIGPIPE
|
||||
EX_SIGALRM = 128 + SigNum.SIGALRM
|
||||
EX_SIGTERM = 128 + SigNum.SIGTERM
|
||||
EX_SIGSTKFLT = 128 + SigNum.SIGSTKFLT
|
||||
EX_SIGCHLD = 128 + SigNum.SIGCHLD
|
||||
EX_SIGCONT = 128 + SigNum.SIGCONT
|
||||
EX_SIGSTOP = 128 + SigNum.SIGSTOP
|
||||
EX_SIGTSTP = 128 + SigNum.SIGTSTP
|
||||
EX_SIGTTIN = 128 + SigNum.SIGTTIN
|
||||
EX_SIGTTOU = 128 + SigNum.SIGTTOU
|
||||
EX_SIGURG = 128 + SigNum.SIGURG
|
||||
EX_SIGXCPU = 128 + SigNum.SIGXCPU
|
||||
EX_SIGXFSZ = 128 + SigNum.SIGXFSZ
|
||||
EX_SIGVTALRM = 128 + SigNum.SIGVTALRM
|
||||
EX_SIGPROF = 128 + SigNum.SIGPROF
|
||||
EX_SIGWINCH = 128 + SigNum.SIGWINCH
|
||||
EX_SIGPOLL = 128 + SigNum.SIGPOLL
|
||||
EX_SIGPWR = 128 + SigNum.SIGPWR
|
||||
EX_SIGSYS = 128 + SigNum.SIGSYS
|
||||
|
||||
def __str__(self):
|
||||
return "{name}<{value:d}(0x{value:02X})>".format(
|
||||
name=self.name,
|
||||
value=self.value
|
||||
)
|
|
@ -28,6 +28,7 @@ import six
|
|||
from devops.error import DevopsCalledProcessError
|
||||
from devops.error import TimeoutError
|
||||
from devops.helpers.exec_result import ExecResult
|
||||
from devops.helpers.proc_enums import ExitCodes
|
||||
from devops.helpers.retry import retry
|
||||
from devops import logger
|
||||
|
||||
|
@ -567,12 +568,21 @@ class SSHClient(six.with_metaclass(_MemorizedSSH, object)):
|
|||
:raises: DevopsCalledProcessError
|
||||
"""
|
||||
if expected is None:
|
||||
expected = [0]
|
||||
expected = [ExitCodes.EX_OK]
|
||||
else:
|
||||
expected = [
|
||||
ExitCodes(code)
|
||||
if (
|
||||
isinstance(code, int) and
|
||||
code in ExitCodes.__members__.values())
|
||||
else code
|
||||
for code in expected
|
||||
]
|
||||
ret = self.execute(command, verbose, timeout)
|
||||
if ret['exit_code'] not in expected:
|
||||
message = (
|
||||
"{append}Command '{cmd}' returned exit code {code} while "
|
||||
"expected {expected}\n"
|
||||
"{append}Command '{cmd}' returned exit code {code!s} while "
|
||||
"expected {expected!s}\n"
|
||||
"\tSTDOUT:\n"
|
||||
"{stdout}"
|
||||
"\n\tSTDERR:\n"
|
||||
|
@ -614,7 +624,7 @@ class SSHClient(six.with_metaclass(_MemorizedSSH, object)):
|
|||
if ret['stderr']:
|
||||
message = (
|
||||
"{append}Command '{cmd}' STDERR while not expected\n"
|
||||
"\texit code: {code}\n"
|
||||
"\texit code: {code!s}\n"
|
||||
"\tSTDOUT:\n"
|
||||
"{stdout}"
|
||||
"\n\tSTDERR:\n"
|
||||
|
@ -721,7 +731,7 @@ class SSHClient(six.with_metaclass(_MemorizedSSH, object)):
|
|||
if verbose:
|
||||
logger.info(
|
||||
'{cmd} execution results:\n'
|
||||
'Exit code: {code}\n'
|
||||
'Exit code: {code!s}\n'
|
||||
'STDOUT:\n'
|
||||
'{stdout}\n'
|
||||
'STDERR:\n'
|
||||
|
|
|
@ -27,6 +27,7 @@ from devops.error import TimeoutError
|
|||
from devops.helpers.decorators import threaded
|
||||
from devops.helpers.exec_result import ExecResult
|
||||
from devops.helpers.metaclasses import SingletonMeta
|
||||
from devops.helpers.proc_enums import ExitCodes
|
||||
from devops import logger
|
||||
|
||||
|
||||
|
@ -144,7 +145,7 @@ class Subprocess(with_metaclass(SingletonMeta, object)):
|
|||
if verbose:
|
||||
logger.info(
|
||||
'{cmd} execution results:\n'
|
||||
'Exit code: {code}\n'
|
||||
'Exit code: {code!s}\n'
|
||||
'STDOUT:\n'
|
||||
'{stdout}\n'
|
||||
'STDERR:\n'
|
||||
|
@ -185,12 +186,21 @@ class Subprocess(with_metaclass(SingletonMeta, object)):
|
|||
"""
|
||||
|
||||
if expected is None:
|
||||
expected = [0]
|
||||
expected = [ExitCodes.EX_OK]
|
||||
else:
|
||||
expected = [
|
||||
ExitCodes(code)
|
||||
if (
|
||||
isinstance(code, int) and
|
||||
code in ExitCodes.__members__.values())
|
||||
else code
|
||||
for code in expected
|
||||
]
|
||||
ret = cls.execute(command, verbose, timeout)
|
||||
if ret['exit_code'] not in expected:
|
||||
message = (
|
||||
"{append}Command '{cmd}' returned exit code {code} while "
|
||||
"expected {expected}\n"
|
||||
"{append}Command '{cmd}' returned exit code {code!s} while "
|
||||
"expected {expected!s}\n"
|
||||
"\tSTDOUT:\n"
|
||||
"{stdout}"
|
||||
"\n\tSTDERR:\n"
|
||||
|
@ -235,7 +245,7 @@ class Subprocess(with_metaclass(SingletonMeta, object)):
|
|||
if ret['stderr']:
|
||||
message = (
|
||||
"{append}Command '{cmd}' STDERR while not expected\n"
|
||||
"\texit code: {code}\n"
|
||||
"\texit code: {code!s}\n"
|
||||
"\tSTDOUT:\n"
|
||||
"{stdout}"
|
||||
"\n\tSTDERR:\n"
|
||||
|
|
|
@ -23,6 +23,7 @@ import mock
|
|||
from devops.helpers.exec_result import DevopsError
|
||||
from devops.helpers.exec_result import DevopsNotImplementedError
|
||||
from devops.helpers.exec_result import ExecResult
|
||||
from devops.helpers.proc_enums import ExitCodes
|
||||
|
||||
|
||||
cmd = 'ls -la'
|
||||
|
@ -48,17 +49,17 @@ class TestExecResult(TestCase):
|
|||
self.assertEqual(exec_result.stdout_brief, exec_result['stdout_brief'])
|
||||
self.assertEqual(exec_result.stderr_brief, '')
|
||||
self.assertEqual(exec_result.stderr_brief, exec_result['stderr_brief'])
|
||||
self.assertEqual(exec_result.exit_code, -1)
|
||||
self.assertEqual(exec_result.exit_code, ExitCodes.EX_INVALID)
|
||||
self.assertEqual(exec_result.exit_code, exec_result['exit_code'])
|
||||
self.assertEqual(
|
||||
repr(exec_result),
|
||||
'{cls}(cmd={cmd}, stdout={stdout}, stderr={stderr}, '
|
||||
'exit_code={exit_code})'.format(
|
||||
'exit_code={exit_code!s})'.format(
|
||||
cls=ExecResult.__name__,
|
||||
cmd=cmd,
|
||||
stdout=[],
|
||||
stderr=[],
|
||||
exit_code=-1
|
||||
exit_code=ExitCodes.EX_INVALID
|
||||
)
|
||||
)
|
||||
self.assertEqual(
|
||||
|
@ -66,12 +67,12 @@ class TestExecResult(TestCase):
|
|||
"{cls}(\n\tcmd={cmd},"
|
||||
"\n\t stdout=\n'{stdout_brief}',"
|
||||
"\n\tstderr=\n'{stderr_brief}', "
|
||||
'\n\texit_code={exit_code}\n)'.format(
|
||||
'\n\texit_code={exit_code!s}\n)'.format(
|
||||
cls=ExecResult.__name__,
|
||||
cmd=cmd,
|
||||
stdout_brief='',
|
||||
stderr_brief='',
|
||||
exit_code=-1
|
||||
exit_code=ExitCodes.EX_INVALID
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -91,7 +92,7 @@ class TestExecResult(TestCase):
|
|||
|
||||
self.assertEqual(
|
||||
hash(exec_result),
|
||||
hash((ExecResult, cmd, '', '', -1))
|
||||
hash((ExecResult, cmd, '', '', ExitCodes.EX_INVALID))
|
||||
)
|
||||
|
||||
self.assertEqual(exec_result.stdout_len, 0)
|
||||
|
@ -112,7 +113,7 @@ class TestExecResult(TestCase):
|
|||
|
||||
def test_setters(self):
|
||||
exec_result = ExecResult(cmd=cmd)
|
||||
self.assertEqual(exec_result.exit_code, -1)
|
||||
self.assertEqual(exec_result.exit_code, ExitCodes.EX_INVALID)
|
||||
exec_result.exit_code = 0
|
||||
self.assertEqual(exec_result.exit_code, 0)
|
||||
self.assertEqual(exec_result.exit_code, exec_result['exit_code'])
|
||||
|
|
|
@ -1120,7 +1120,7 @@ class TestExecute(TestCase):
|
|||
logger.assert_has_calls((
|
||||
mock.call.info(
|
||||
'{cmd} execution results:\n'
|
||||
'Exit code: {code}\n'
|
||||
'Exit code: {code!s}\n'
|
||||
'STDOUT:\n'
|
||||
'{stdout}\n'
|
||||
'STDERR:\n'
|
||||
|
@ -1173,7 +1173,7 @@ class TestExecute(TestCase):
|
|||
logger.assert_has_calls((
|
||||
mock.call.info(
|
||||
'{cmd} execution results:\n'
|
||||
'Exit code: {code}\n'
|
||||
'Exit code: {code!s}\n'
|
||||
'STDOUT:\n'
|
||||
'{stdout}\n'
|
||||
'STDERR:\n'
|
||||
|
|
|
@ -120,7 +120,7 @@ class TestSubprocessRunner(TestCase):
|
|||
call.debug("Executing command: '{}'".format(command.rstrip())),
|
||||
call.info(
|
||||
'{cmd} execution results:\n'
|
||||
'Exit code: {code}\n'
|
||||
'Exit code: {code!s}\n'
|
||||
'STDOUT:\n'
|
||||
'{stdout}\n'
|
||||
'STDERR:\n'
|
||||
|
|
4
setup.py
4
setup.py
|
@ -12,6 +12,8 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from sys import version_info
|
||||
|
||||
from setuptools import find_packages
|
||||
from setuptools import setup
|
||||
|
||||
|
@ -37,6 +39,7 @@ setup(
|
|||
'bin/dos_check_db.sh',
|
||||
],
|
||||
data_files=[('bin', ['bin/dos_functions.sh'])],
|
||||
# Use magic in install_requires due to risk of old setuptools
|
||||
install_requires=[
|
||||
'keystoneauth1>=2.1.0',
|
||||
'netaddr>=0.7.12,!=0.7.16',
|
||||
|
@ -48,6 +51,7 @@ setup(
|
|||
'tabulate',
|
||||
'six>=1.9.0',
|
||||
'python-dateutil>=2.4.2',
|
||||
'enum34' if version_info.major == 2 else ''
|
||||
],
|
||||
tests_require=[
|
||||
'pytest>=2.7.1',
|
||||
|
|
Loading…
Reference in New Issue