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

"""
Common utils for burnin tests

"""

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
39 40
import re
import shutil
41
import unittest
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
42
import datetime
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
43
import tempfile
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
44 45
import traceback

46
from kamaki.clients.cyclades import CycladesClient, CycladesNetworkClient
47
from kamaki.clients.astakos import AstakosClient, parse_endpoints
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
48
from kamaki.clients.compute import ComputeClient
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
49
from kamaki.clients.pithos import PithosClient
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
50
from kamaki.clients.image import ImageClient
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
51

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
52
from synnefo_tools.burnin.logger import Log
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
53 54 55 56


# --------------------------------------------------------------------
# Global variables
57 58
logger = None   # pylint: disable=invalid-name
success = None  # pylint: disable=invalid-name
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
59
SNF_TEST_PREFIX = "snf-test-"
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
60
CONNECTION_RETRY_LIMIT = 2
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
61
SYSTEM_USERS = ["images@okeanos.grnet.gr", "images@demo.synnefo.org"]
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
62 63 64
KB = 2**10
MB = 2**20
GB = 2**30
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79


# --------------------------------------------------------------------
# BurninTestResult class
class BurninTestResult(unittest.TestResult):
    """Modify the TextTestResult class"""
    def __init__(self):
        super(BurninTestResult, self).__init__()

        # Test parameters
        self.failfast = True

    def startTest(self, test):  # noqa
        """Called when the test case test is about to be run"""
        super(BurninTestResult, self).startTest(test)
80
        logger.log(test.__class__.__name__, test.shortDescription())
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
81

82
    # pylint: disable=no-self-use
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
83 84
    def _test_failed(self, test, err):
        """Test failed"""
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
85 86 87 88 89
        # Get class name
        if test.__class__.__name__ == "_ErrorHolder":
            class_name = test.id().split('.')[-1].rstrip(')')
        else:
            class_name = test.__class__.__name__
90
        err_msg = str(test) + "... failed (%s)."
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
91 92
        timestamp = datetime.datetime.strftime(
            datetime.datetime.now(), "%a %b %d %Y %H:%M:%S")
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
93
        logger.error(class_name, err_msg, timestamp)
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
94 95
        (err_type, err_value, err_trace) = err
        trcback = traceback.format_exception(err_type, err_value, err_trace)
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
96
        logger.info(class_name, trcback)
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
97 98 99 100 101 102 103 104 105 106 107 108 109

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

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


# --------------------------------------------------------------------
110
# Helper Classes
111 112
# pylint: disable=too-few-public-methods
# pylint: disable=too-many-instance-attributes
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
113 114 115 116
class Clients(object):
    """Our kamaki clients"""
    auth_url = None
    token = None
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
117
    # Astakos
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
118 119
    astakos = None
    retry = CONNECTION_RETRY_LIMIT
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
120
    # Compute
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
121 122
    compute = None
    compute_url = None
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
123 124
    # Cyclades
    cyclades = None
125 126 127
    # Network
    network = None
    network_url = None
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
128 129 130
    # Pithos
    pithos = None
    pithos_url = None
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
131 132 133
    # Image
    image = None
    image_url = None
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
134

135 136
    def initialize_clients(self):
        """Initialize all the Kamaki Clients"""
137
        self.astakos = AstakosClient(self.auth_url, self.token)
138 139
        self.astakos.CONNECTION_RETRY_LIMIT = self.retry

140 141 142
        endpoints = self.astakos.authenticate()

        self.compute_url = _get_endpoint_url(endpoints, "compute")
143 144 145 146 147 148
        self.compute = ComputeClient(self.compute_url, self.token)
        self.compute.CONNECTION_RETRY_LIMIT = self.retry

        self.cyclades = CycladesClient(self.compute_url, self.token)
        self.cyclades.CONNECTION_RETRY_LIMIT = self.retry

149
        self.network_url = _get_endpoint_url(endpoints, "network")
150 151 152
        self.network = CycladesNetworkClient(self.network_url, self.token)
        self.network.CONNECTION_RETRY_LIMIT = self.retry

153
        self.pithos_url = _get_endpoint_url(endpoints, "object-store")
154 155 156
        self.pithos = PithosClient(self.pithos_url, self.token)
        self.pithos.CONNECTION_RETRY_LIMIT = self.retry

157
        self.image_url = _get_endpoint_url(endpoints, "image")
158 159 160 161
        self.image = ImageClient(self.image_url, self.token)
        self.image.CONNECTION_RETRY_LIMIT = self.retry


162 163 164 165 166 167 168
def _get_endpoint_url(endpoints, endpoint_type):
    """Get the publicURL for the specified endpoint"""

    service_catalog = parse_endpoints(endpoints, ep_type=endpoint_type)
    return service_catalog[0]['endpoints'][0]['publicURL']


169 170 171 172 173 174
class Proper(object):
    """A descriptor used by tests implementing the TestCase class

    Since each instance of the TestCase will only be used to run a single
    test method (a new fixture is created for each test) the attributes can
    not be saved in the class instances. Instead we use descriptors.
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
175

176 177 178 179 180 181 182 183 184 185 186 187 188
    """
    def __init__(self, value=None):
        self.val = value

    def __get__(self, obj, objtype=None):
        return self.val

    def __set__(self, obj, value):
        self.val = value


# --------------------------------------------------------------------
# BurninTests class
189
# pylint: disable=too-many-public-methods
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
190 191
class BurninTests(unittest.TestCase):
    """Common class that all burnin tests should implement"""
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
192
    clients = Clients()
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
193
    run_id = None
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
194 195 196 197
    use_ipv6 = None
    action_timeout = None
    action_warning = None
    query_interval = None
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
198
    system_user = None
199 200
    images = None
    flavors = None
201
    delete_stale = False
202
    temp_directory = None
203
    failfast = None
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
204

205 206
    quotas = Proper(value=None)

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
207 208 209 210 211 212 213 214 215
    @classmethod
    def setUpClass(cls):  # noqa
        """Initialize BurninTests"""
        cls.suite_name = cls.__name__
        logger.testsuite_start(cls.suite_name)

        # Set test parameters
        cls.longMessage = True

216
    def test_000_clients_setup(self):
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
217 218
        """Initializing astakos/cyclades/pithos clients"""
        # Update class attributes
219
        self.clients.initialize_clients()
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
220 221
        self.info("Astakos auth url is %s", self.clients.auth_url)
        self.info("Cyclades url is %s", self.clients.compute_url)
222
        self.info("Network url is %s", self.clients.network_url)
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
223
        self.info("Pithos url is %s", self.clients.pithos_url)
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
224
        self.info("Image url is %s", self.clients.image_url)
225

226
        user_uuid = self._get_uuid()
227
        self.quotas = self._get_quotas()
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
        for puuid, quotas in self.quotas.items():
            project_name = self._get_project_name(puuid, user_uuid)
            self.info("  Project %s:", project_name)
            self.info("    Disk usage is         %s bytes",
                      quotas['cyclades.disk']['usage'])
            self.info("    VM usage is           %s",
                      quotas['cyclades.vm']['usage'])
            self.info("    DiskSpace usage is    %s bytes",
                      quotas['pithos.diskspace']['usage'])
            self.info("    Ram usage is          %s bytes",
                      quotas['cyclades.ram']['usage'])
            self.info("    Floating IPs usage is %s",
                      quotas['cyclades.floating_ip']['usage'])
            self.info("    CPU usage is          %s",
                      quotas['cyclades.cpu']['usage'])
            self.info("    Network usage is      %s",
                      quotas['cyclades.network.private']['usage'])
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
245

246 247
    def _run_tests(self, tcases):
        """Run some generated testcases"""
248
        global success  # pylint: disable=invalid-name, global-statement
249 250 251 252 253 254 255

        for tcase in tcases:
            self.info("Running testsuite %s", tcase.__name__)
            success = run_test(tcase) and success
            if self.failfast and not success:
                break

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
256 257
    # ----------------------------------
    # Loggers helper functions
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
    def log(self, msg, *args):
        """Pass the section value to logger"""
        logger.log(self.suite_name, msg, *args)

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

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

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

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

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
    # ----------------------------------
    # Helper functions that every testsuite may need
    def _get_uuid(self):
        """Get our uuid"""
        authenticate = self.clients.astakos.authenticate()
        uuid = authenticate['access']['user']['id']
        self.info("User's uuid is %s", uuid)
        return uuid

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

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
294
    def _create_tmp_directory(self):
295 296
        """Create a tmp directory"""
        temp_dir = tempfile.mkdtemp(dir=self.temp_directory)
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
        self.info("Temp directory %s created", temp_dir)
        return temp_dir

    def _remove_tmp_directory(self, tmp_dir):
        """Remove a tmp directory"""
        try:
            shutil.rmtree(tmp_dir)
            self.info("Temp directory %s deleted", tmp_dir)
        except OSError:
            pass

    def _get_uuid_of_system_user(self):
        """Get the uuid of the system user

        This is the user that upload the 'official' images.

        """
        self.info("Getting the uuid of the system user")
        system_users = None
        if self.system_user is not None:
317 318
            try:
                su_type, su_value = parse_typed_option(self.system_user)
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
319 320 321 322 323 324 325 326
                if su_type == "name":
                    system_users = [su_value]
                elif su_type == "id":
                    self.info("System user's uuid is %s", su_value)
                    return su_value
                else:
                    self.error("Unrecognized system-user type %s", su_type)
                    self.fail("Unrecognized system-user type")
327 328 329
            except ValueError:
                msg = "Invalid system-user format: %s. Must be [id|name]:.+"
                self.warning(msg, self.system_user)
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
330 331 332 333

        if system_users is None:
            system_users = SYSTEM_USERS

334
        uuids = self.clients.astakos.get_uuids(system_users)
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
335 336 337 338 339 340 341 342 343
        for su_name in system_users:
            self.info("Trying username %s", su_name)
            if su_name in uuids:
                self.info("System user's uuid is %s", uuids[su_name])
                return uuids[su_name]

        self.warning("No system user found")
        return None

344 345 346 347 348 349
    def _skip_if(self, condition, msg):
        """Skip tests"""
        if condition:
            self.info("Test skipped: %s" % msg)
            self.skipTest(msg)

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
350 351
    # ----------------------------------
    # Flavors
352 353 354 355 356 357 358 359 360
    def _get_list_of_flavors(self, detail=False):
        """Get (detailed) list of flavors"""
        if detail:
            self.info("Getting detailed list of flavors")
        else:
            self.info("Getting simple list of flavors")
        flavors = self.clients.compute.list_flavors(detail=detail)
        return flavors

361 362 363 364 365 366 367 368 369 370 371 372
    def _find_flavors(self, patterns, flavors=None):
        """Find a list of suitable flavors to use

        The patterns is a list of `typed_options'. A list of all flavors
        matching this patterns will be returned.

        """
        if flavors is None:
            flavors = self._get_list_of_flavors(detail=True)

        ret_flavors = []
        for ptrn in patterns:
373 374 375
            try:
                flv_type, flv_value = parse_typed_option(ptrn)
            except ValueError:
376 377 378
                msg = "Invalid flavor format: %s. Must be [id|name]:.+"
                self.warning(msg, ptrn)
                continue
379

380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
            if flv_type == "name":
                # Filter flavor by name
                msg = "Trying to find a flavor with name %s"
                self.info(msg, flv_value)
                filtered_flvs = \
                    [f for f in flavors if
                     re.search(flv_value, f['name'], flags=re.I) is not None]
            elif flv_type == "id":
                # Filter flavors by id
                msg = "Trying to find a flavor with id %s"
                self.info(msg, flv_value)
                filtered_flvs = \
                    [f for f in flavors if str(f['id']) == flv_value]
            else:
                self.error("Unrecognized flavor type %s", flv_type)
                self.fail("Unrecognized flavor type")

            # Append and continue
            ret_flavors.extend(filtered_flvs)

        self.assertGreater(len(ret_flavors), 0,
                           "No matching flavors found")
        return ret_flavors

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
    # ----------------------------------
    # Images
    def _get_list_of_images(self, detail=False):
        """Get (detailed) list of images"""
        if detail:
            self.info("Getting detailed list of images")
        else:
            self.info("Getting simple list of images")
        images = self.clients.image.list_public(detail=detail)
        # Remove images registered by burnin
        images = [img for img in images
                  if not img['name'].startswith(SNF_TEST_PREFIX)]
        return images

    def _get_list_of_sys_images(self, images=None):
        """Get (detailed) list of images registered by system user or by me"""
        self.info("Getting list of images registered by system user or by me")
        if images is None:
            images = self._get_list_of_images(detail=True)

        su_uuid = self._get_uuid_of_system_user()
        my_uuid = self._get_uuid()
        ret_images = [i for i in images
                      if i['owner'] == su_uuid or i['owner'] == my_uuid]

        return ret_images

431 432
    def _find_images(self, patterns, images=None):
        """Find a list of suitable images to use
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
433

434 435
        The patterns is a list of `typed_options'. A list of all images
        matching this patterns will be returned.
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
436 437 438 439 440

        """
        if images is None:
            images = self._get_list_of_sys_images()

441
        ret_images = []
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
442
        for ptrn in patterns:
443 444 445
            try:
                img_type, img_value = parse_typed_option(ptrn)
            except ValueError:
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
446 447 448
                msg = "Invalid image format: %s. Must be [id|name]:.+"
                self.warning(msg, ptrn)
                continue
449

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
            if img_type == "name":
                # Filter image by name
                msg = "Trying to find an image with name %s"
                self.info(msg, img_value)
                filtered_imgs = \
                    [i for i in images if
                     re.search(img_value, i['name'], flags=re.I) is not None]
            elif img_type == "id":
                # Filter images by id
                msg = "Trying to find an image with id %s"
                self.info(msg, img_value)
                filtered_imgs = \
                    [i for i in images if
                     i['id'].lower() == img_value.lower()]
            else:
                self.error("Unrecognized image type %s", img_type)
                self.fail("Unrecognized image type")

468 469
            # Append and continue
            ret_images.extend(filtered_imgs)
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
470

471 472 473
        self.assertGreater(len(ret_images), 0,
                           "No matching images found")
        return ret_images
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
474 475 476

    # ----------------------------------
    # Pithos
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
477
    def _set_pithos_account(self, account):
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
478
        """Set the Pithos account"""
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
479 480
        assert account, "No pithos account was given"

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
481
        self.info("Setting Pithos account to %s", account)
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
482 483
        self.clients.pithos.account = account

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
484 485 486 487 488 489 490
    def _set_pithos_container(self, container):
        """Set the Pithos container"""
        assert container, "No pithos container was given"

        self.info("Setting Pithos container to %s", container)
        self.clients.pithos.container = container

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
    def _get_list_of_containers(self, account=None):
        """Get list of containers"""
        if account is not None:
            self._set_pithos_account(account)
        self.info("Getting list of containers")
        return self.clients.pithos.list_containers()

    def _create_pithos_container(self, container):
        """Create a pithos container

        If the container exists, nothing will happen

        """
        assert container, "No pithos container was given"

        self.info("Creating pithos container %s", container)
        self.clients.pithos.container = container
        self.clients.pithos.container_put()

510 511 512 513
    # ----------------------------------
    # Quotas
    def _get_quotas(self):
        """Get quotas"""
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
514
        self.info("Getting quotas")
515
        return dict(self.clients.astakos.get_quotas())
516

517 518
    # pylint: disable=invalid-name
    # pylint: disable=too-many-arguments
519
    def _check_quotas(self, puuid=None, disk=None, vm=None, diskspace=None,
520
                      ram=None, ip=None, cpu=None, network=None):
521 522 523 524 525 526
        """Check that quotas' changes are consistent

        @param puuid: The uuid of the project, quotas are assigned to

        """

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
527
        assert any(v is None for v in
528
                   [disk, vm, diskspace, ram, ip, cpu, network]), \
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
529 530 531 532 533 534 535
            "_check_quotas require arguments"

        self.info("Check that quotas' changes are consistent")
        old_quotas = self.quotas
        new_quotas = self._get_quotas()
        self.quotas = new_quotas

536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574
        user_uuid = self._get_uuid()
        if puuid is None:
            puuid = user_uuid

        self.assertListEqual(sorted(old_quotas.keys()),
                             sorted(new_quotas.keys()))
        for project in old_quotas.keys():
            # Check Disk usage
            project_name = self._get_project_name(project, user_uuid)
            self._check_quotas_aux(old_quotas[project], new_quotas[project],
                                   project_name, "cyclades.disk",
                                   disk, project == puuid)
            # Check VM usage
            self._check_quotas_aux(old_quotas[project], new_quotas[project],
                                   project_name, "cyclades.vm",
                                   vm, project == puuid)
            # Check DiskSpace usage
            self._check_quotas_aux(old_quotas[project], new_quotas[project],
                                   project_name, "pithos.diskspace",
                                   diskspace, project == puuid)
            # Check Ram usage
            self._check_quotas_aux(old_quotas[project], new_quotas[project],
                                   project_name, "cyclades.ram",
                                   ram, project == puuid)
            # Check Floating IPs usage
            self._check_quotas_aux(old_quotas[project], new_quotas[project],
                                   project_name, "cyclades.floating_ip",
                                   ip, project == puuid)
            # Check CPU usage
            self._check_quotas_aux(old_quotas[project], new_quotas[project],
                                   project_name, "cyclades.cpu",
                                   cpu, project == puuid)
            # Check Network usage
            self._check_quotas_aux(old_quotas[project], new_quotas[project],
                                   project_name, "cyclades.network.private",
                                   network, project == puuid)

    def _check_quotas_aux(self, old_quotas, new_quotas,
                          project_name, resource, value, check):
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
575
        """Auxiliary function for _check_quotas"""
576 577 578
        old_value = old_quotas[resource]['usage']
        new_value = new_quotas[resource]['usage']
        if check and value is not None:
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
579 580 581 582
            assert isinstance(value, int), \
                "%s value has to be integer" % resource
            old_value += value
        self.assertEqual(old_value, new_value,
583 584 585 586 587 588 589 590 591 592 593 594 595 596
                         "Project %s: %s quotas don't match" %
                         (project_name, resource))

    # ----------------------------------
    # Projects
    def _get_project_name(self, puuid, uuid=None):
        """Get the name of a project"""
        if uuid is None:
            uuid = self._get_uuid()
        if puuid == uuid:
            return "base"
        else:
            project_info = self.clients.astakos.get_project(puuid)
            return project_info['name']
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
597

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
598

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
599 600
# --------------------------------------------------------------------
# Initialize Burnin
601
def initialize(opts, testsuites, stale_testsuites):
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
602 603 604 605 606 607
    """Initalize burnin

    Initialize our logger and burnin state

    """
    # Initialize logger
608
    global logger  # pylint: disable=invalid-name, global-statement
609
    curr_time = datetime.datetime.now()
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
610
    logger = Log(opts.log_folder, verbose=opts.verbose,
611
                 use_colors=opts.use_colors, in_parallel=False,
612
                 log_level=opts.log_level, curr_time=curr_time)
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
613 614 615 616 617 618

    # Initialize clients
    Clients.auth_url = opts.auth_url
    Clients.token = opts.token

    # Pass the rest options to BurninTests
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
619 620 621 622
    BurninTests.use_ipv6 = opts.use_ipv6
    BurninTests.action_timeout = opts.action_timeout
    BurninTests.action_warning = opts.action_warning
    BurninTests.query_interval = opts.query_interval
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
623
    BurninTests.system_user = opts.system_user
624 625
    BurninTests.flavors = opts.flavors
    BurninTests.images = opts.images
626
    BurninTests.delete_stale = opts.delete_stale
627
    BurninTests.temp_directory = opts.temp_directory
628
    BurninTests.failfast = opts.failfast
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
629
    BurninTests.run_id = SNF_TEST_PREFIX + \
630
        datetime.datetime.strftime(curr_time, "%Y%m%d%H%M%S")
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
631 632

    # Choose tests to run
633 634
    if opts.show_stale:
        # We will run the stale_testsuites
635
        return (stale_testsuites, True)
636

Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
637 638 639 640 641 642
    if opts.tests != "all":
        testsuites = opts.tests
    if opts.exclude_tests is not None:
        testsuites = [tsuite for tsuite in testsuites
                      if tsuite not in opts.exclude_tests]

643
    return (testsuites, opts.failfast)
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
644 645 646 647


# --------------------------------------------------------------------
# Run Burnin
648
def run_burnin(testsuites, failfast=False):
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
649
    """Run burnin testsuites"""
650 651
    # pylint: disable=invalid-name,global-statement
    # pylint: disable=global-variable-not-assigned
652
    global logger, success
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
653 654

    success = True
655
    run_tests(testsuites, failfast=failfast)
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
656 657

    # Clean up our logger
658
    del logger
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
659 660 661 662 663

    # Return
    return 0 if success else 1


664 665
def run_tests(tcases, failfast=False):
    """Run some testcases"""
666 667 668
    # pylint: disable=invalid-name,global-statement
    # pylint: disable=global-variable-not-assigned
    global success
669 670 671 672 673 674 675 676

    for tcase in tcases:
        was_success = run_test(tcase)
        success = success and was_success
        if failfast and not success:
            break


677 678 679 680 681 682 683 684
def run_test(tcase):
    """Run a testcase"""
    tsuite = unittest.TestLoader().loadTestsFromTestCase(tcase)
    results = tsuite.run(BurninTestResult())

    return was_successful(tcase.__name__, results.wasSuccessful())


Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
685 686
# --------------------------------------------------------------------
# Helper functions
687
def was_successful(tsuite, successful):
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
688
    """Handle whether a testsuite was succesful or not"""
689
    if successful:
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
690
        logger.testsuite_success(tsuite)
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
691
        return True
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
692 693
    else:
        logger.testsuite_failure(tsuite)
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
694
        return False
Ilias Tsitsimpis's avatar
Ilias Tsitsimpis committed
695 696 697 698 699 700 701 702 703 704 705 706 707 708


def parse_typed_option(value):
    """Parse typed options (flavors and images)

    The options are in the form 'id:123-345' or 'name:^Debian Base$'

    """
    try:
        [type_, val] = value.strip().split(':')
        if type_ not in ["id", "name"]:
            raise ValueError
        return type_, val
    except ValueError:
709
        raise