GwCmdValidatorsPattern.__init__()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
c 1
b 0
f 0
dl 0
loc 4
rs 10
1
import sys
2
from re import finditer
3
4
from groundwork_validation.patterns import GwValidatorsPattern
5
6
# This uses a backport of subrprocess for python < 3
7
# This is needed to use the timeout functionality.
8
if sys.version_info[0] < 3:
9
    import subprocess32 as subprocess
10
else:
11
    import subprocess
12
13
14
class GwCmdValidatorsPattern(GwValidatorsPattern):
15
    """
16
    Allows the validation of output, return code and execution time of a given command.
17
18
    Usage::
19
20
        class My_Plugin(GwCmdValidatorsPattern):
21
            def __init__(self, app, **kwargs):
22
                self.name = "My_Plugin"
23
                super(My_Plugin, self).__init__(app, **kwargs)
24
25
            def activate(self):
26
                if self.validators.cmd.validate("dir", search="my_folder"):
27
                    print("Command 'dir' works a expected.")
28
                else:
29
                    print("Command 'dir' seems not to work correctly. We stop here")
30
                    sys.exit(1)
31
32
            def deactivate(self):
33
                pass
34
    """
35
36
    def __init__(self, app, **kwargs):
37
        super(GwCmdValidatorsPattern, self).__init__(app, **kwargs)
38
        self.app = app
39
        self.validators.cmd = CmdValidatorsPlugin(self)
40
41
42
class CmdValidatorsPlugin:
43
44
    def __init__(self, plugin):
45
        self.plugin = plugin
46
47
    def validate(self, command, search=None, regex=None, timeout=2, allowed_return_codes=None, decode="utf-8"):
48
        """
49
        Validates the output of a given command.
50
51
        The validation can be based on a simple string search or on a complex regular expression.
52
        Also the return_code can be validated. As well as the execution duration by setting a timeout.
53
54
        :param command: string, which is used as command for a new subprocess. E.g. 'git -v'.
55
        :param search: string, which shall be contained in the output of the command. Default is None
56
        :param regex:  regular expression, which is tested against the command output.
57
                       Default is None
58
        :param timeout: Time ins seconds, after which the execution is stopped and the validation fails.
59
                        Default is 2 seconds
60
        :param allowed_return_codes: List of allowed return values. Default is []
61
        :param decode: Format of the console encoding, which shall be used. Default is 'utf-8'
62
        :return: True, if validation succeeded. Else False.
63
        """
64
        if search is None and regex is None:
65
            raise ValueError("Parameter search or regex must be set.")
66
        if search is not None and regex is not None:
67
            raise ValueError("Only search OR regex is allowed to be used. Not both!")
68
69
        if allowed_return_codes is None:
70
            allowed_return_codes = []
71
        if isinstance(allowed_return_codes, int):
72
            allowed_return_codes = [allowed_return_codes]
73
        if not isinstance(allowed_return_codes, list):
74
            raise TypeError("allowed_return_code must be a list of integers")
75
76
        try:
77
            output = subprocess.check_output(command, stderr=subprocess.STDOUT, shell=True, timeout=timeout)
78
            return_code = 0
79
        except subprocess.CalledProcessError as e:
80
            output = e.output
81
            return_code = e.returncode
82
        except subprocess.TimeoutExpired as e:
83
            raise CommandTimeoutExpired(e)
84
85
        if len(allowed_return_codes) > 0 and return_code not in allowed_return_codes:
86
            raise NotAllowedReturnCode("For command %s got return code '%s', which is not in %s"
87
                                       % (command, return_code, allowed_return_codes))
88
89
        self.plugin.log.debug("Executed '%s' with return code: %s" % (command, return_code))
90
91
        output = output.decode(decode)
92
        found = False
93
        if search is not None:
94
            if search in output:
95
                found = True
96
        elif regex is not None:
97
            for m in finditer(regex, output):
98
                self.plugin.log.debug("Found cmd validation '%s' at %02d-%02d" % (m.group(0), m.start(), m.end()))
99
                found = True
100
        return found
101
102
103
class NotAllowedReturnCode(BaseException):
104
    pass
105
106
107
class CommandTimeoutExpired(BaseException):
108
    pass
109