Statistics
| Branch: | Tag: | Revision:

root / snf-tools / synnefo_tools / burnin / common.py @ fe15cd00

History | View | Annotate | Download (9.2 kB)

1
# Copyright 2013 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
"""
35
Common utils for burnin tests
36

37
"""
38

    
39
import sys
40
import datetime
41
import traceback
42
# Use backported unittest functionality if Python < 2.7
43
try:
44
    import unittest2 as unittest
45
except ImportError:
46
    if sys.version_info < (2, 7):
47
        raise Exception("The unittest2 package is required for Python < 2.7")
48
    import unittest
49

    
50
from kamaki.clients.astakos import AstakosClient
51
from kamaki.clients.compute import ComputeClient
52

    
53
from synnefo_tools.burnin.logger import Log
54

    
55

    
56
# --------------------------------------------------------------------
57
# Global variables
58
logger = None  # Invalid constant name. pylint: disable-msg=C0103
59
SNF_TEST_PREFIX = "snf-test-"
60
CONNECTION_RETRY_LIMIT = 2
61

    
62

    
63
# --------------------------------------------------------------------
64
# BurninTestResult class
65
class BurninTestResult(unittest.TestResult):
66
    """Modify the TextTestResult class"""
67
    def __init__(self):
68
        super(BurninTestResult, self).__init__()
69

    
70
        # Test parameters
71
        self.failfast = True
72

    
73
    def startTest(self, test):  # noqa
74
        """Called when the test case test is about to be run"""
75
        super(BurninTestResult, self).startTest(test)
76
        logger.log(test.__class__.__name__, test.shortDescription())
77

    
78
    # Method could be a function. pylint: disable-msg=R0201
79
    def _test_failed(self, test, err):
80
        """Test failed"""
81
        err_msg = str(test) + "... failed (%s)."
82
        timestamp = datetime.datetime.strftime(
83
            datetime.datetime.now(), "%a %b %d %Y %H:%M:%S")
84
        logger.error(test.__class__.__name__, err_msg, timestamp)
85
        (err_type, err_value, err_trace) = err
86
        trcback = traceback.format_exception(err_type, err_value, err_trace)
87
        logger.info(test.__class__.__name__, trcback)
88

    
89
    def addError(self, test, err):  # noqa
90
        """Called when the test case test raises an unexpected exception"""
91
        super(BurninTestResult, self).addError(test, err)
92
        self._test_failed(test, err)
93

    
94
    def addFailure(self, test, err):  # noqa
95
        """Called when the test case test signals a failure"""
96
        super(BurninTestResult, self).addFailure(test, err)
97
        self._test_failed(test, err)
98

    
99

    
100
# --------------------------------------------------------------------
101
# BurninTests class
102
# Too few public methods (0/2). pylint: disable-msg=R0903
103
class Clients(object):
104
    """Our kamaki clients"""
105
    auth_url = None
106
    token = None
107

    
108
    astakos = None
109
    retry = CONNECTION_RETRY_LIMIT
110

    
111
    compute = None
112
    compute_url = None
113

    
114

    
115
# Too many public methods (45/20). pylint: disable-msg=R0904
116
class BurninTests(unittest.TestCase):
117
    """Common class that all burnin tests should implement"""
118
    clients = Clients()
119
    opts = None
120
    run_id = None
121

    
122
    @classmethod
123
    def setUpClass(cls):  # noqa
124
        """Initialize BurninTests"""
125
        cls.suite_name = cls.__name__
126
        logger.testsuite_start(cls.suite_name)
127

    
128
        # Set test parameters
129
        cls.longMessage = True
130

    
131
    def _setattr(self, attr, value):
132
        """Used by tests to set an attribute to TestCase
133

134
        Since each instance of the TestCase will only be used to run a single
135
        test method (a new fixture is created for each test) the attributes can
136
        not be saved in the class instance. Instead the class itself should be
137
        used.
138

139
        """
140
        setattr(self.__class__, attr, value)
141

    
142
    def test_000_clients_setup(self):
143
        """Initializing astakos/cyclades/pithos clients"""
144
        # Update class attributes
145
        self.info("Astakos auth url is %s", self.clients.auth_url)
146
        self.clients.astakos = AstakosClient(
147
            self.clients.auth_url, self.clients.token)
148
        self.clients.astakos.CONNECTION_RETRY_LIMIT = self.clients.retry
149

    
150
        self.clients.compute_url = \
151
            self.clients.astakos.get_service_endpoints('compute')['publicURL']
152
        self.info("Cyclades url is %s", self.clients.compute_url)
153
        self.clients.compute = ComputeClient(
154
            self.clients.compute_url, self.clients.token)
155
        self.clients.compute.CONNECTION_RETRY_LIMIT = self.clients.retry
156

    
157
    # ----------------------------------
158
    # Loggers helper functions
159
    def log(self, msg, *args):
160
        """Pass the section value to logger"""
161
        logger.log(self.suite_name, msg, *args)
162

    
163
    def info(self, msg, *args):
164
        """Pass the section value to logger"""
165
        logger.info(self.suite_name, msg, *args)
166

    
167
    def debug(self, msg, *args):
168
        """Pass the section value to logger"""
169
        logger.debug(self.suite_name, msg, *args)
170

    
171
    def warning(self, msg, *args):
172
        """Pass the section value to logger"""
173
        logger.warning(self.suite_name, msg, *args)
174

    
175
    def error(self, msg, *args):
176
        """Pass the section value to logger"""
177
        logger.error(self.suite_name, msg, *args)
178

    
179
    # ----------------------------------
180
    # Helper functions that every testsuite may need
181
    def _get_uuid(self):
182
        """Get our uuid"""
183
        authenticate = self.clients.astakos.authenticate()
184
        uuid = authenticate['access']['user']['id']
185
        self.info("User's uuid is %s", uuid)
186
        return uuid
187

    
188
    def _get_username(self):
189
        """Get our User Name"""
190
        authenticate = self.clients.astakos.authenticate()
191
        username = authenticate['access']['user']['name']
192
        self.info("User's name is %s", username)
193
        return username
194

    
195
    def _get_list_of_flavors(self, detail=False):
196
        """Get (detailed) list of flavors"""
197
        if detail:
198
            self.info("Getting detailed list of flavors")
199
        else:
200
            self.info("Getting simple list of flavors")
201
        flavors = self.clients.compute.list_flavors(detail=detail)
202
        return flavors
203

    
204

    
205
# --------------------------------------------------------------------
206
# Initialize Burnin
207
def initialize(opts, testsuites):
208
    """Initalize burnin
209

210
    Initialize our logger and burnin state
211

212
    """
213
    # Initialize logger
214
    global logger  # Using global statement. pylint: disable-msg=C0103,W0603
215
    logger = Log(opts.log_folder, verbose=opts.verbose,
216
                 use_colors=opts.use_colors, in_parallel=False,
217
                 quiet=opts.quiet)
218

    
219
    # Initialize clients
220
    Clients.auth_url = opts.auth_url
221
    Clients.token = opts.token
222

    
223
    # Pass the rest options to BurninTests
224
    BurninTests.opts = opts
225
    BurninTests.run_id = datetime.datetime.strftime(
226
        datetime.datetime.now(), "%Y%m%d%H%M%S")
227

    
228
    # Choose tests to run
229
    if opts.tests != "all":
230
        testsuites = opts.tests
231
    if opts.exclude_tests is not None:
232
        testsuites = [tsuite for tsuite in testsuites
233
                      if tsuite not in opts.exclude_tests]
234

    
235
    return testsuites
236

    
237

    
238
# --------------------------------------------------------------------
239
# Run Burnin
240
def run(testsuites, failfast=False, final_report=False):
241
    """Run burnin testsuites"""
242
    global logger  # Using global. pylint: disable-msg=C0103,W0603,W0602
243

    
244
    success = True
245
    for tcase in testsuites:
246
        tsuite = unittest.TestLoader().loadTestsFromTestCase(tcase)
247
        results = tsuite.run(BurninTestResult())
248

    
249
        was_success = was_successful(tcase.__name__, results.wasSuccessful())
250
        success = success and was_success
251
        if failfast and not success:
252
            break
253

    
254
    # Are we going to print final report?
255
    if final_report:
256
        logger.print_logfile_to_stdout()
257
    # Clean up our logger
258
    del(logger)
259

    
260
    # Return
261
    return 0 if success else 1
262

    
263

    
264
# --------------------------------------------------------------------
265
# Helper functions
266
def was_successful(tsuite, success):
267
    """Handle whether a testsuite was succesful or not"""
268
    if success:
269
        logger.testsuite_success(tsuite)
270
        return True
271
    else:
272
        logger.testsuite_failure(tsuite)
273
        return False