util.py 34.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright 2014 Budapest University of Technology and Economics (BME IK)
#
# This file is part of CIRCLE Cloud.
#
# CIRCLE is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# CIRCLE is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with CIRCLE.  If not, see <http://www.gnu.org/licenses/>.
from datetime import datetime
20
import inspect
21 22 23 24 25 26
import logging
import random
import re
import time
import urlparse

27
from selenium.common.exceptions import (
28 29
    NoSuchElementException, StaleElementReferenceException,
    TimeoutException)
30 31 32 33 34
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.select import Select
from selenium.webdriver.support.ui import WebDriverWait

35 36 37
from .config import SeleniumConfig

logger = logging.getLogger(SeleniumConfig.logger_name)
38 39


40
class SeleniumMixin(object):
41 42 43 44 45 46
    def create_screenshot(self):
        name = 'ss_from_%(caller_name)s.png' % {
            'caller_name': inspect.stack()[1][3]}
        logger.warning('Creating screenshot "%s"' % name)
        self.driver.save_screenshot(name)

47
    def get_url(self, fragment_needed=False, fragment=None):
Csók Tamás committed
48 49 50 51
        url_base = urlparse.urlparse(self.driver.current_url)
        url_save = ("%(host)s%(url)s" % {
            'host': self.conf.host,
            'url': urlparse.urljoin(url_base.path, url_base.query)})
52 53 54 55 56
        if fragment is None:
            fragment = url_base.fragment
        else:
            fragment_needed = True
        if fragment_needed and fragment:
Csók Tamás committed
57 58
            url_save = ("%(url)s#%(fragment)s" % {
                'url': url_save,
59
                'fragment': fragment})
Csók Tamás committed
60 61
        return url_save

62 63 64 65 66 67 68 69 70 71 72 73
    def list_options(self, select):
        try:
            option_dic = {}
            select = Select(select)
            for option in select.options:
                key = option.get_attribute('value')
                if key is not None and key:
                    option_dic[key] = [option.text]
            return option_dic
        except:
            logger.exception("Selenium cannot list the"
                             " select possibilities")
74
            self.create_screenshot()
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
            raise Exception(
                'Cannot list the select possibilities')

    def select_option(self, select, what=None):
        """
        From an HTML select imput type try to choose the specified one.
        Select is a selenium web element type. What represent both the
        text of the option and it's ID.
        """
        try:
            my_choice = None
            options = self.list_options(select)
            select = Select(select)
            if what is not None:
                for key, value in options.iteritems():
                    if what in key:
                        my_choice = key
                    else:
                        if isinstance(value, list):
                            for single_value in value:
                                if what in single_value:
                                    my_choice = key
                        else:
                            if what in value:
                                my_choice = key
            if my_choice is None:
                my_choose_list = options.keys()
                my_choice = my_choose_list[random.randint(
                    0, len(my_choose_list) - 1)]
            select.select_by_value(my_choice)
        except:
            logger.exception("Selenium cannot select the chosen one")
107
            self.create_screenshot()
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
            raise Exception(
                'Cannot select the chosen one')

    def get_link_by_href(self, target_href, attributes=None):
        try:
            links = self.driver.find_elements_by_tag_name('a')
            for link in links:
                href = link.get_attribute('href')
                if href is not None and href:
                    if target_href in href:
                        perfect_fit = True
                        if isinstance(attributes, dict):
                            for key, target_value in attributes.iteritems():
                                attr_check = link.get_attribute(key)
                                if attr_check is not None and attr_check:
                                    if target_value not in attr_check:
                                        perfect_fit = False
                        if perfect_fit:
                            return link
        except:
            logger.exception(
                "Selenium cannot find the href=%s link" % target_href)
130
            self.create_screenshot()
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
            raise Exception('Cannot find the requested href')

    def click_on_link(self, link):
        """
        There are situations when selenium built in click() function
        doesn't work as intended, that's when this function is used.
        Fires a click event via javascript injection.
        """
        try:
            # Javascript function to simulate a click on a link
            javascript = """
                var link = arguments[0];
                var cancelled = false;
                if(document.createEvent) {
                   var event = document.createEvent("MouseEvents");
                   event.initMouseEvent(
                       "click", true, true, window, 0, 0, 0, 0, 0,
                       false,false,false,false,0,null);
                   cancelled = !link.dispatchEvent(event);
                } else if(link.fireEvent) {
                   cancelled = !link.fireEvent("onclick");
                } if (!cancelled) {
                   window.location = link.href;
                }"""
            self.driver.execute_script(javascript, link)
        except:
            logger.exception("Selenium cannot inject javascript to the page")
158
            self.create_screenshot()
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
            raise Exception(
                'Cannot inject javascript to the page')

    def get_text(self, node, tag):
        """
        There are some cases where selenium default WebElement text()
        method returns less then it actually could contain. Solving that
        here is a simple regular expression. Give the closest html element
        then specify the html tag of the enclosed text.
        """
        text = ""
        try:
            text_whole = re.search(
                r'<%(tag)s[^>]*>([^<]+)</%(tag)s>' % {
                    'tag': tag},
                node.get_attribute("outerHTML")).group()
            text_parts = text_whole.splitlines()
            for part in text_parts:
                if '<' not in part and '>' not in part:
                    text += part
            text = text.replace(" ", "")
        except:
            return node.text
        if len(node.text) >= len(text):
            text = node.text
        else:
            logger.warning("Better text found which is '%s'" % text)
        return text.strip()


class CircleSeleniumMixin(SeleniumMixin):
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
    def login(self, location=None):
        driver = self.driver
        if location is None:
            location = '/dashboard/'
        driver.get('%s%s' % (self.conf.host, location))
        #  Only if we aren't logged in already
        if location not in urlparse.urlparse(self.driver.current_url).path:
            try:
                name_input = driver.find_element_by_id("id_username")
                password_input = driver.find_element_by_id("id_password")
                submit_input = driver.find_element_by_id("submit-id-submit")
            except:
                inputs = driver.find_elements_by_tag_name("input")
                for current_input in inputs:
                    input_type = current_input.get_attribute("type")
                    if input_type == "text":
                        name_input = current_input
                    if input_type == "password":
                        password_input = current_input
                    if input_type == "submit":
                        submit_input = current_input
            try:
                name_input.clear()
                name_input.send_keys(self.conf.client_name)
                password_input.clear()
                password_input.send_keys(self.conf.random_pass)
                submit_input.click()
                try:
                    # If selenium runs only in a small (virtual) screen
                    driver.find_element_by_class_name('navbar-toggle').click()
                    WebDriverWait(self.driver, self.conf.wait_max_sec).until(
                        ec.element_to_be_clickable((
                            By.CSS_SELECTOR,
                            "a[href*='/dashboard/profile/']")))
                except:
                    time.sleep(0.5)
            except:
227
                logger.exception("Selenium cannot find the form controls")
228
                self.create_screenshot()
229
                raise Exception('Cannot find the form controls')
230

231 232 233 234 235 236 237
    def fallback(self, fallback_url, fallback_function):
        logger.warning(
            "However error was anticipated falling back to %(url)s" % {
                'url': fallback_url})
        self.driver.get(fallback_url)
        return fallback_function()

238 239
    def wait_and_accept_operation(self, argument=None, try_wait=None,
                                  fallback_url=None):
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
        """
        Accepts the operation confirmation pop up window.
        Fills out the text inputs before accepting if argument is given.
        """
        try:
            accept = WebDriverWait(self.driver, self.conf.wait_max_sec).until(
                ec.element_to_be_clickable((
                    By.CLASS_NAME, "modal-accept")))
            if argument is not None:
                possible = self.driver.find_elements_by_css_selector(
                    "div.controls > input[type='text']")
                if isinstance(argument, list):
                    for x in range(0, len(possible)):
                        possible[x].clear()
                        possible[x].send_keys(argument[x % len(argument)])
                else:
                    for form in possible:
                        form.clear()
                        form.send_keys(argument)
            accept.click()
260
            if try_wait is not None:
261 262 263
                WebDriverWait(self.driver, self.conf.wait_max_sec).until(
                    ec.visibility_of_element_located((
                        By.CSS_SELECTOR, try_wait)))
264 265 266 267 268 269 270
        except TimeoutException:
            logger.exception("Selenium cannot accept the"
                             " operation confirmation")
            if fallback_url is not None:
                self.fallback(
                    fallback_url,
                    lambda: self.wait_and_accept_operation(argument))
271 272 273 274
            else:
                self.create_screenshot()
                raise Exception(
                    'Cannot accept the operation confirmation')
275
        except:
276 277
            logger.exception("Selenium cannot accept the"
                             " operation confirmation")
278
            if fallback_url is not None:
279 280 281
                self.fallback(
                    fallback_url,
                    lambda: self.wait_and_accept_operation(argument, try_wait))
282
            else:
283
                self.create_screenshot()
284 285
                raise Exception(
                    'Cannot accept the operation confirmation')
286 287 288

    def save_template_from_vm(self, name):
        try:
289
            url_save = self.get_url()
290 291 292 293 294
            WebDriverWait(self.driver, self.conf.wait_max_sec).until(
                ec.element_to_be_clickable((
                    By.CSS_SELECTOR,
                    "a[href$='/op/deploy/']")))
            self.click_on_link(self.get_link_by_href("/op/deploy/"))
295 296 297
            fallback_url = "%sop/deploy/" % url_save
            self.wait_and_accept_operation(
                try_wait="a[href$='/op/shut_off/']", fallback_url=fallback_url)
298
            recent_deploy = self.recently(self.get_timeline_elements(
299
                "vm.Instance.deploy", url_save))
300 301
            if not self.check_operation_result(
                    recent_deploy, "a[href*='#activity']"):
302 303 304
                logger.warning("Selenium cannot deploy the "
                               "chosen template virtual machine")
                raise Exception('Cannot deploy the virtual machine')
305 306 307 308 309
            self.click_on_link(WebDriverWait(
                self.driver, self.conf.wait_max_sec).until(
                    ec.element_to_be_clickable((
                        By.CSS_SELECTOR,
                        "a[href$='/op/shut_off/']"))))
310 311 312
            fallback_url = "%sop/shut_off/" % url_save
            self.wait_and_accept_operation(
                try_wait="a[href$='/op/deploy/']", fallback_url=fallback_url)
313
            recent_shut_off = self.recently(self.get_timeline_elements(
314
                "vm.Instance.shut_off", url_save))
315 316
            if not self.check_operation_result(
                    recent_shut_off, "a[href*='#activity']"):
317 318 319
                logger.warning("Selenium cannot shut off the "
                               "chosen template virtual machine")
                raise Exception('Cannot shut off the virtual machine')
320 321 322 323 324
            self.click_on_link(WebDriverWait(
                self.driver, self.conf.wait_max_sec).until(
                    ec.element_to_be_clickable((
                        By.CSS_SELECTOR,
                        "a[href$='/op/save_as_template/']"))))
325 326 327 328 329 330 331 332 333 334 335
            fallback_url = "%sop/save_as_template/" % url_save
            self.wait_and_accept_operation(
                argument=name, fallback_url=fallback_url)
            recent_save_template = self.recently(self.get_timeline_elements(
                "vm.Instance.save_as_template", url_save))
            if not self.check_operation_result(
                    recent_save_template, "a[href*='#activity']"):
                logger.warning("Selenium cannot save the "
                               "chosen virtual machine as a template")
                raise Exception(
                    'Cannot save the virtual machine as a template')
336 337
            logger.warning("Selenium created %(name)s template" % {
                'name': name})
338 339
            return name
        except:
340
            logger.exception("Selenium cannot save a vm as a template")
341
            self.create_screenshot()
342 343
            raise Exception(
                'Cannot save a vm as a template')
344 345 346 347 348

    def create_base_template(self, name=None, architecture="x86-64",
                             method=None, op_system=None, lease=None,
                             network="vm"):
        if name is None:
349
            name = "new_%s" % self.conf.client_name
350 351 352 353 354 355 356 357 358 359 360 361 362 363
        if op_system is None:
            op_system = "!os %s" % self.conf.client_name
        try:
            self.driver.get('%s/dashboard/template/choose/' % self.conf.host)
            self.driver.find_element_by_css_selector(
                "input[type='radio'][value='base_vm']").click()
            self.driver.find_element_by_id(
                "template-choose-next-button").click()
            template_name = WebDriverWait(
                self.driver, self.conf.wait_max_sec).until(
                    ec.visibility_of_element_located((
                        By.ID, 'id_name')))
            template_name.clear()
            template_name.send_keys(name)
364

365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
            self.select_option(self.driver.find_element_by_id(
                "id_arch"), architecture)
            self.select_option(self.driver.find_element_by_id(
                "id_access_method"), method)
            system_name = self.driver.find_element_by_id("id_system")
            system_name.clear()
            system_name.send_keys(op_system)
            self.select_option(self.driver.find_element_by_id(
                "id_lease"), lease)
            self.select_option(self.driver.find_element_by_id(
                "id_networks"), network)
            self.driver.find_element_by_css_selector(
                "input.btn[type='submit']").click()
            return self.save_template_from_vm(name)
        except:
380 381
            logger.exception("Selenium cannot create a base"
                             " template virtual machine")
382
            self.create_screenshot()
383 384
            raise Exception(
                'Cannot create a base template virtual machine')
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406

    def get_template_id(self, name=None, from_all=False):
        """
        In default settings find all templates ID in the template list.
        If name is specified searches that specific template's ID
        from_all sets whether to use owned templates or all of them
        Returns list of the templates ID
        """
        try:
            self.driver.get('%s/dashboard/template/list/' % self.conf.host)
            css_selector_of_a_template = ("a[data-original-title]"
                                          "[href*='/dashboard/template/']")
            if from_all:
                self.select_option(self.driver.find_element_by_id(
                    'id_stype'), "all")
                self.driver.find_element_by_css_selector(
                    "button[type='submit']").click()
                try:
                    WebDriverWait(self.driver, self.conf.wait_max_sec).until(
                        ec.presence_of_element_located((
                            By.CSS_SELECTOR, css_selector_of_a_template)))
                except:
407 408
                    logger.warning("Selenium could not locate any templates")
                    raise Exception("Could not locate any templates")
409 410 411 412 413
            template_table = self.driver.find_element_by_css_selector(
                "table[class*='template-list-table']")
            templates = template_table.find_elements_by_css_selector("td.name")
            found_template_ids = []
            for template in templates:
414 415 416
                # Little magic to outsmart accented naming errors
                template_name = self.get_text(template, "a")
                if name is None or name in template_name:
417 418 419 420 421 422 423
                    try:
                        template_link = template.find_element_by_css_selector(
                            css_selector_of_a_template)
                        template_id = re.search(
                            r'\d+',
                            template_link.get_attribute("outerHTML")).group()
                        found_template_ids.append(template_id)
424 425
                        logger.warning("Found '%(name)s' "
                                       "template's ID as %(id)s" % {
426
                                           'name': template_name,
427
                                           'id': template_id})
428 429 430 431
                    except NoSuchElementException:
                        pass
                    except:
                        raise
432 433 434 435 436 437 438 439 440
                else:
                    logger.warning(
                        "Searching for %(searched)s so"
                        " %(name)s is dismissed" % {
                            'searched': name,
                            'name': template_name})
                    logger.warning(
                        "Dismissed template html code: %(code)s" % {
                            'code': template.get_attribute("outerHTML")})
441
            if not found_template_ids and name is not None:
442 443 444 445
                logger.warning("Selenium could not find the specified "
                               "%(name)s template in the list" % {
                                   'name': name})
                raise Exception("Could not find the specified template")
446 447
            return found_template_ids
        except:
448
            logger.exception('Selenium cannot find the template\'s id')
449
            self.create_screenshot()
450 451
            raise Exception(
                'Cannot find the template\'s id')
452

453 454
    def check_operation_result(self, operation_id, restore_selector=None,
                               restore=True):
455 456 457 458 459
        """
        Returns wheter the operation_id result is success (returns: boolean)
        """
        try:
            if restore:
460
                url_save = self.get_url(True)
461 462 463 464 465 466
            self.driver.get('%(host)s/dashboard/vm/activity/%(id)s/' % {
                'host': self.conf.host,
                'id': operation_id})
            result = WebDriverWait(self.driver, self.conf.wait_max_sec).until(
                ec.visibility_of_element_located((
                    By.ID, "activity_status")))
467
            logger.warning("%(id)s's result is '%(result)s'" % {
468 469 470 471 472 473
                'id': operation_id,
                'result': result.text})
            if (result.text == "success"):
                out = True
            elif (result.text == "wait"):
                time.sleep(2)
474 475
                out = self.check_operation_result(
                    operation_id=operation_id, restore=False)
476
            else:
477 478 479 480 481 482 483 484 485 486 487 488
                try:
                    result_text = WebDriverWait(
                        self.driver, self.conf.wait_max_sec).until(
                            ec.visibility_of_element_located((
                                By.ID, "activity_result_text")))
                    logger.warning(
                        "%(id)s's result text is: '%(result_text)s'" % {
                            'id': operation_id,
                            'result_text': result_text.text})
                except:
                    logger.warning("Cannot read %(id)s's result text" % {
                        'id': operation_id})
489 490
                out = False
            if restore:
491
                logger.warning("Restoring to %s url" % url_save)
492
                self.driver.get(url_save)
493 494 495 496
                if restore_selector is not None and restore_selector:
                    WebDriverWait(self.driver, self.conf.wait_max_sec).until(
                        ec.visibility_of_element_located((
                            By.CSS_SELECTOR, restore_selector)))
497 498
            return out
        except:
499 500
            logger.exception("Selenium cannot check the"
                             " result of an operation")
501
            self.create_screenshot()
502 503
            raise Exception(
                'Cannot check the result of an operation')
504 505 506 507 508 509

    def recently(self, timeline_dict, second=None):
        if second is None:
            second = self.conf.recently_sec
        try:
            if isinstance(timeline_dict, dict):
510
                recent = None
511
                for key, value in timeline_dict.iteritems():
512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530
                    if recent is None or int(key) > int(recent):
                        recent = key
                if len(timeline_dict) > 1:
                    logger.warning(
                        "Searching for most recent activity"
                        " from the received %(count)s pieces" % {
                            'count': len(timeline_dict)})
                    logger.warning("Found at %(id)s @ %(time)s" % {
                        'id': timeline_dict[recent],
                        'time': datetime.fromtimestamp(
                            int(recent)).strftime('%Y-%m-%d %H:%M:%S')})
                logger.warning(
                    "Checking wheter %(id)s started in the"
                    " recent %(second)s seconds" % {
                        'id': timeline_dict[recent],
                        'second': second})
                delta = datetime.now() - datetime.fromtimestamp(int(recent))
                if delta.total_seconds() <= second:
                    return timeline_dict[recent]
531
        except:
532 533
            logger.exception("Selenium cannot filter timeline "
                             "activities to find most recent")
534
            self.create_screenshot()
535
            raise Exception(
536
                'Cannot filter timeline activities to find most recent')
537

538
    def get_timeline_elements(self, code=None, fallback_url=None):
539 540 541
        try:
            if code is None:
                css_activity_selector = "div[data-activity-code]"
542
                code_text = "all activity"
543
            else:
544
                code_text = code
545 546
                css_activity_selector = ("div[data-activity-code="
                                         "'%(code)s']" % {
547
                                             'code': code})
548
            try:
549 550 551 552
                self.click_on_link(WebDriverWait(
                    self.driver, self.conf.wait_max_sec).until(
                        ec.element_to_be_clickable((
                            By.CSS_SELECTOR, "a[href*='#activity']"))))
553 554 555 556 557 558 559 560 561 562
                activity_dict = {}
                timeline = WebDriverWait(
                    self.driver, self.conf.wait_max_sec).until(
                        ec.visibility_of_element_located((
                            By.ID, "activity-timeline")))
                searched_activity = timeline.find_elements_by_css_selector(
                    css_activity_selector)
                logger.warning("Found activity list for %s:" % code_text)
                for activity in searched_activity:
                    activity_id = activity.get_attribute('data-activity-id')
Csók Tamás committed
563
                    key = activity.get_attribute('data-timestamp')
564 565 566 567 568 569 570
                    logger.warning("%(id)s @ %(activity)s" % {
                        'id': activity_id,
                        'activity': datetime.fromtimestamp(
                            int(key)).strftime('%Y-%m-%d %H:%M:%S')})
                    activity_dict[key] = activity_id
            except StaleElementReferenceException:
                logger.warning('Timeline changed while processing it')
571
                return self.get_timeline_elements(code, fallback_url)
572 573
            except TimeoutException:
                logger.warning('Can not found timeline in the page')
574 575 576 577
                if fallback_url is not None:
                    return self.fallback(
                        fallback_url,
                        lambda: self.get_timeline_elements(code))
578
                else:
579
                    self.create_screenshot()
580
                    raise Exception('Selenium could not locate the timeline')
581 582
            except:
                logger.exception('Selenium cannot get timeline elemets')
583
                self.create_screenshot()
584 585 586
                raise Exception('Cannot get timeline elements')
            if len(activity_dict) == 0:
                logger.warning('Found activity list is empty')
587
                self.create_screenshot()
588
                raise Exception('Selenium did not found any activity')
589 590
            return activity_dict
        except:
591
            logger.exception('Selenium cannot find the searched activity')
592
            self.create_screenshot()
593
            raise Exception('Cannot find the searched activity')
594 595 596 597

    def create_template_from_base(self, delete_disk=True, name=None):
        try:
            if name is None:
598
                name = "from_%s" % self.conf.client_name
599 600 601 602 603 604 605 606 607 608 609 610
            self.driver.get('%s/dashboard/template/choose/' % self.conf.host)
            choice_list = []
            choices = self.driver.find_elements_by_css_selector(
                "input[type='radio']")
            choice_list = [item for item in choices if (
                'test' not in item.get_attribute('value')
                and item.get_attribute('value') != 'base_vm')]
            chosen = random.randint(0, len(choice_list) - 1)
            choice_list[chosen].click()
            self.driver.find_element_by_id(
                "template-choose-next-button").click()
            if delete_disk:
611
                url_save = self.get_url(fragment='activity')
612 613 614 615 616 617 618 619 620 621 622
                self.click_on_link(
                    self.get_link_by_href("#resources"))
                disks = WebDriverWait(
                    self.driver, self.conf.wait_max_sec).until(
                        ec.visibility_of_element_located((
                            By.ID, 'vm-details-resources-disk')))
                disk_list = disks.find_elements_by_css_selector(
                    "h4[class*='list-group-item-heading']")
                if len(disk_list) > 0:
                    self.click_on_link(
                        self.get_link_by_href("/op/remove_disk/"))
623 624
                    self.wait_and_accept_operation(
                        try_wait="a[href*='#activity']")
625 626
                    recent_remove_disk = self.recently(
                        self.get_timeline_elements(
627
                            "vm.Instance.remove_disk", url_save))
628
                    if not self.check_operation_result(recent_remove_disk):
629 630 631
                        logger.warning("Selenium cannot delete disk "
                                       "of the chosen template")
                        raise Exception('Cannot delete disk')
632 633
                return self.save_template_from_vm(name)
        except:
634 635
            logger.exception("Selenium cannot start a"
                             " template from a base one")
636
            self.create_screenshot()
637 638
            raise Exception(
                'Cannot start a template from a base one')
639 640 641 642 643

    def delete_template(self, template_id):
        try:
            self.driver.get(
                '%s/dashboard/template/%s/' % (self.conf.host, template_id))
644 645 646
            url_save = "%(host)s/dashboard/template/delete/%(pk)s/" % {
                'host': self.conf.host,
                'pk': template_id}
647 648 649
            self.click_on_link(
                self.get_link_by_href(
                    "/dashboard/template/delete/%s/" % template_id))
650
            self.wait_and_accept_operation(fallback_url=url_save)
651 652 653 654 655
            WebDriverWait(self.driver, self.conf.wait_max_sec).until(
                ec.visibility_of_element_located((
                    By.CLASS_NAME, 'alert-success')))
            url = urlparse.urlparse(self.driver.current_url)
            if "/template/list/" not in url.path:
656 657
                logger.warning('CIRCLE does not redirect to /template/list/')
                raise Exception(
658
                    'System does not redirect to template listing')
659 660
            logger.warning('Successfully deleted template: id - %(pk)s' % {
                'pk': template_id})
661
        except:
662
            logger.exception("Selenium cannot delete the desired template")
663
            self.create_screenshot()
664
            raise Exception('Cannot delete the desired template')
665 666 667 668 669 670 671 672 673 674

    def create_random_vm(self):
        try:
            self.driver.get('%s/dashboard/vm/create/' % self.conf.host)
            vm_list = []
            pk = None
            vm_list = self.driver.find_elements_by_class_name(
                'vm-create-template-summary')
            choice = random.randint(0, len(vm_list) - 1)
            vm_list[choice].click()
675 676 677 678 679 680 681 682 683 684
            try:
                WebDriverWait(self.driver, self.conf.wait_max_sec).until(
                    ec.element_to_be_clickable((
                        By.CLASS_NAME, "vm-create-start"))).click()
            except TimeoutException:
                # Selenium can time out not findig it even though it is present
                self.driver.find_element_by_tag_name('form').submit()
            except:
                logger.exception("Selenium could not submit create vm form")
                raise Exception('Could not submit a form')
685 686 687 688 689 690 691
            WebDriverWait(self.driver, self.conf.wait_max_sec).until(
                ec.visibility_of_element_located((
                    By.CLASS_NAME, 'alert-success')))
            url = urlparse.urlparse(self.driver.current_url)
            pk = re.search(r'\d+', url.path).group()
            return pk
        except:
692
            logger.exception("Selenium cannot start a VM")
693
            self.create_screenshot()
694
            raise Exception('Cannot start a VM')
695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725

    def view_change(self, target_box):
        driver = self.driver
        driver.get('%s/dashboard/' % self.conf.host)
        list_view = driver.find_element_by_id('%s-list-view' % target_box)
        graph_view = driver.find_element_by_id('%s-graph-view' % target_box)
        js_script = 'return arguments[0].style.display;'
        required_attributes = {'data-index-box': target_box}
        graph_view_link = self.get_link_by_href(
            '#index-graph-view',
            required_attributes).find_element_by_tag_name('i')
        list_view_link = self.get_link_by_href(
            '#index-list-view',
            required_attributes).find_element_by_tag_name('i')
        self.click_on_link(list_view_link)
        states = [driver.execute_script(js_script, list_view),
                  driver.execute_script(js_script, graph_view)]
        self.click_on_link(graph_view_link)
        states.extend([driver.execute_script(js_script, list_view),
                       driver.execute_script(js_script, graph_view)])
        self.click_on_link(list_view_link)
        states.extend([driver.execute_script(js_script, list_view),
                       driver.execute_script(js_script, graph_view)])
        return states

    def delete_vm(self, pk):
        try:
            # For relability reasons instead of using the JS operatation
            self.driver.get("%(host)s/dashboard/vm/%(id)s/op/destroy/" % {
                'host': self.conf.host,
                'id': pk})
726
            self.wait_and_accept_operation(try_wait="a[href*='/op/recover/']")
727 728 729 730 731 732 733 734 735 736
            try:
                status_span = WebDriverWait(
                    self.driver, self.conf.wait_max_sec).until(
                        ec.visibility_of_element_located((
                            By.ID, 'vm-details-state')))
                WebDriverWait(status_span, self.conf.wait_max_sec).until(
                    ec.visibility_of_element_located((
                        By.CLASS_NAME, 'fa-trash-o')))
            except:
                # Selenium can time-out by not realising the JS refresh
737
                url_save = self.get_url(fragment='activity')
738
                recent_destroy_vm = self.recently(
739 740
                    self.get_timeline_elements(
                        "vm.Instance.destroy", url_save))
741
                if not self.check_operation_result(recent_destroy_vm):
742 743 744 745
                    logger.warning("Selenium cannot destroy "
                                   "the chosen %(id)s vm" % {
                                       'id': pk})
                    raise Exception('Cannot destroy the specified vm')
746 747 748 749 750 751
            self.driver.get('%s/dashboard/vm/%s/' % (self.conf.host, pk))
            try:
                WebDriverWait(self.driver, self.conf.wait_max_sec).until(
                    ec.visibility_of_element_located((
                        By.CSS_SELECTOR,
                        "span[data-status*='DESTROYED']")))
752 753 754
                logger.warning(
                    'Successfully deleted virtual machine: id - %(pk)s' % {
                        'pk': pk})
755 756 757 758
                return True
            except:
                return False
        except:
759
            logger.exception("Selenium can not destroy a VM")
760 761
            self.create_screenshot()
            raise Exception("Cannot destroy a VM")