示例#1
0
def exists(root, sub=None, and_is_not_greyed=False):
    """ Checks presence and usability of toolbar buttons.

    By default it checks whether the button is available, not caring whether it is greyed or not.
    You can optionally enable check for greyedness.

    Args:
        root: Button name.
        sub: Item name (optional)
        and_is_not_greyed: Check if the button is available to click.

    """
    sel.wait_for_ajax()
    if isinstance(root, dict):
        root = version.pick(root)
    if isinstance(sub, dict):
        sub = version.pick(sub)

    try:
        greyed = is_greyed(root, sub)
        if and_is_not_greyed:
            return not greyed
        else:
            return True
    except sel.NoSuchElementException:
        return False
示例#2
0
def test_vm_retire_extend(request, testing_vm, soft_assert, retire_extend_button):
    """ Tests extending a retirement using an AE method.

    Prerequisities:
        * A running VM on any provider.

    Steps:
        * It creates a button pointing to ``Request/vm_retire_extend`` instance. The button should
            live in the VM and Instance button group.
        * Then it sets a retirement date for the VM
        * Then it waits until the retirement date is set
        * Then it clicks the button that was created and it waits for the retirement date to extend.

    Metadata:
        test_flag: retire, provision
    """
    num_days = 5
    soft_assert(testing_vm.retirement_date == 'Never', "The retirement date is not 'Never'!")
    retirement_date = generate_retirement_date(delta=num_days)
    testing_vm.set_retirement_date(retirement_date)
    wait_for(lambda: testing_vm.retirement_date != 'Never', message="retirement date set")
    set_date = testing_vm.retirement_date
    if not BZ(1419150, forced_streams='5.6').blocks:
        soft_assert(set_date == retirement_date.strftime(pick(VM.RETIRE_DATE_FMT)),
                    "The retirement date '{}' did not match expected date '{}'"
                    .format(set_date, retirement_date.strftime(pick(VM.RETIRE_DATE_FMT))))
    # current_retirement_date = testing_vm.retirement_date

    # Now run the extend stuff
    retire_extend_button()
    def delete(self, cancel=False, wait_deleted=True, force=False):
        """Deletes the manager through UI

        Args:
            cancel (bool): Whether to cancel out of the deletion, when the alert pops up.
            wait_deleted (bool): Whether we want to wait for the manager to disappear from the UI.
                True will wait; False will only delete it and move on.
            force (bool): Whether to try to delete the manager even though it doesn't exist.
                True will try to delete it anyway; False will check for its existence and leave,
                if not present.
        """
        if not force and not self.exists:
            return
        navigate_to(self, 'All')
        sel.check(Quadicon(self.quad_name, None).checkbox())
        item_text = version.pick({
            '5.6': 'Remove selected items from the VMDB',
            '5.7': 'Remove selected items'
        })
        cfg_btn(item_text, invokes_alert=True)

        sel.handle_alert(cancel)
        if not cancel:
            flash_msg = version.pick({
                '5.6': 'Delete initiated for 1 provider',
                '5.7': 'Delete initiated for 1 Provider'
            })

            flash.assert_message_match(flash_msg)
            if wait_deleted:
                wait_for(func=lambda: self.exists,
                         fail_condition=True,
                         delay=15,
                         num_sec=60)
def verify_retirement_date(retire_vm, expected_date='Never'):
    """Verify the retirement date for a variety of situations

    Args:
        expected_date: a :py:class: `str` or :py:class: `parsetime` date
            or a dict of :py:class: `parsetime` dates with 'start' and 'end' keys.
    """
    if isinstance(expected_date, dict):
        # convert to a parsetime object for comparsion, function depends on version
        if 'UTC' in pick(VM.RETIRE_DATE_FMT):
            convert_func = parsetime.from_american_minutes_with_utc
        elif pick(VM.RETIRE_DATE_FMT).endswith('+0000'):
            convert_func = parsetime.from_saved_report_title_format
        else:
            convert_func = parsetime.from_american_date_only
        expected_date.update({'retire': convert_func(retire_vm.retirement_date)})
        logger.info('Asserting retire date "%s" is between "%s" and "%s"',  # noqa
                    expected_date['retire'],
                    expected_date['start'],
                    expected_date['end'])

        assert expected_date['start'] <= expected_date['retire'] <= expected_date['end']

    elif isinstance(expected_date, (parsetime, datetime, date)):
        assert retire_vm.retirement_date == expected_date.strftime(pick(VM.RETIRE_DATE_FMT))
    else:
        assert retire_vm.retirement_date == expected_date
示例#5
0
def exists(root, sub=None, and_is_not_greyed=False):
    """ Checks presence and usability of toolbar buttons.

    By default it checks whether the button is available, not caring whether it is greyed or not.
    You can optionally enable check for greyedness.

    Args:
        root: Button name.
        sub: Item name (optional)
        and_is_not_greyed: Check if the button is available to click.

    """
    sel.wait_for_ajax()
    if isinstance(root, dict):
        root = version.pick(root)
    if isinstance(sub, dict):
        sub = version.pick(sub)

    try:
        greyed = is_greyed(root, sub)
        if and_is_not_greyed:
            return not greyed
        else:
            return True
    except sel.NoSuchElementException:
        return False
示例#6
0
def verify_retirement_date(retire_vm, expected_date='Never'):
    """Verify the retirement date for a variety of situations

    Args:
        expected_date: a :py:class: `str` or :py:class: `parsetime` date
            or a dict of :py:class: `parsetime` dates with 'start' and 'end' keys.
    """
    if isinstance(expected_date, dict):
        # convert to a parsetime object for comparsion, function depends on version
        if 'UTC' in pick(VM.RETIRE_DATE_FMT):
            convert_func = parsetime.from_american_minutes_with_utc
        else:
            convert_func = parsetime.from_american_date_only
        expected_date.update(
            {'retire': convert_func(retire_vm.retirement_date)})
        logger.info(
            'Asserting retire date "%s" is between "%s" and "%s"',  # noqa
            expected_date['retire'],
            expected_date['start'],
            expected_date['end'])

        assert expected_date['start'] <= expected_date[
            'retire'] <= expected_date['end']

    elif isinstance(expected_date, (parsetime, datetime, date)):
        assert retire_vm.retirement_date == expected_date.strftime(
            pick(VM.RETIRE_DATE_FMT))
    else:
        assert retire_vm.retirement_date == expected_date
示例#7
0
    def view_value_mapping(self):

        out = {}

        out['hostname'] = version.pick({
            version.LOWEST: None,
            '5.9': self.hostname
        })
        out['api_port'] = version.pick({
            version.LOWEST: None,
            '5.9': self.api_port
        })
        out['sec_protocol'] = version.pick({
            version.LOWEST: None,
            '5.9': self.sec_protocol
        })

        if out['sec_protocol'] and self.sec_protocol.lower(
        ) == 'ssl trusting custom ca':
            out['trusted_ca_certificates'] = OpenshiftDefaultEndpoint.get_ca_cert(
                {
                    "username": self.ssh_creds.principal,
                    "password": self.ssh_creds.secret,
                    "hostname": self.master_hostname
                })

        return out
示例#8
0
 def view_value_mapping(self):
     tls_since_version = '5.8.0.8'
     return {'hostname': self.hostname,
             'api_port': getattr(self, 'api_port', None),
             'verify_tls': version.pick({
                 version.LOWEST: None,
                 tls_since_version: getattr(self, 'verify_tls', None)}),
             'ca_certs': version.pick({
                 version.LOWEST: None,
                 tls_since_version: getattr(self, 'ca_certs', None)})
             }
示例#9
0
 def view_value_mapping(self):
     tls_since_version = '5.8.0.8'
     return {'hostname': self.hostname,
             'api_port': getattr(self, 'api_port', None),
             'verify_tls': version.pick({
                 version.LOWEST: None,
                 tls_since_version: getattr(self, 'verify_tls', None)}),
             'ca_certs': version.pick({
                 version.LOWEST: None,
                 tls_since_version: getattr(self, 'ca_certs', None)})
             }
示例#10
0
def pf_select(root, sub=None, invokes_alert=False):
    """ Clicks on a button by calling the click event with the jquery trigger.

    Args:
        root: The root button's name as a string.
        sub: The sub button's name as a string. (optional)
        invokes_alert: If ``True``, then the behaviour is little bit different. After the last
            click, no ajax wait and no move away is done to be able to operate the alert that
            appears after click afterwards. Defaults to ``False``.
    Returns: ``True`` if everything went smoothly
    Raises: :py:class:`cfme.exceptions.ToolbarOptionGreyedOrUnavailable`
    """

    sel.wait_for_ajax()
    if isinstance(root, dict):
        root = version.pick(root)
    if isinstance(sub, dict):
        sub = version.pick(sub)

    if sub:
        q_sub = xpath_quote(sub).replace("'", "\\'")
        sel.execute_script(
            "return $('a:contains({})').trigger('click')".format(q_sub))
    else:
        q_root = xpath_quote(root).replace("'", "\\'")
        try:
            sel.element("//button[@data-original-title = {0}] | "
                        "//a[@data-original-title = {0}]".format(q_root))
            sel.execute_script(
                "return $('*[data-original-title={}]').trigger('click')".
                format(q_root))
        except sel.NoSuchElementException:
            try:
                sel.element("//button[@title={}]".format(q_root))
                sel.execute_script(
                    "return $('button[title={}]').trigger('click')".format(
                        q_root))
            except sel.NoSuchElementException:
                try:
                    sel.element(
                        "//button[contains(@title, {})]".format(q_root))
                    sel.execute_script(
                        "return $('button:contains({})').trigger('click')".
                        format(q_root))
                except sel.NoSuchElementException:
                    # The view selection buttons?
                    sel.click(
                        "//li/a[@title={}]/*[self::i or self::img]/../..".
                        format(q_root))

    if not invokes_alert:
        sel.wait_for_ajax()
    return True
示例#11
0
def old_select(root, sub=None, invokes_alert=False):
    """ Clicks on a button by calling the dhtmlx toolbar callEvent.

    Args:
        root: The root button's name as a string.
        sub: The sub button's name as a string. (optional)
        invokes_alert: If ``True``, then the behaviour is little bit different. After the last
            click, no ajax wait and no move away is done to be able to operate the alert that
            appears after click afterwards. Defaults to ``False``.
    Returns: ``True`` if everything went smoothly
    Raises: :py:class:`cfme.exceptions.ToolbarOptionGreyedOrUnavailable`
    """
    # wait for ajax on select to prevent pickup up a toolbar button in the middle of a page change
    sel.wait_for_ajax()
    if isinstance(root, dict):
        root = version.pick(root)
    if sub is not None and isinstance(sub, dict):
        sub = version.pick(sub)

    root_obj = 'ManageIQ.toolbars'

    if sub:
        search = sub_loc(sub)
    else:
        search = root_loc(root)

    eles = sel.elements(search)

    for ele in eles:
        idd = sel.get_attribute(ele, 'idd')
        if idd:
            break
    else:
        raise ToolbarOptionGreyedOrUnavailable(
            "Toolbar button {}/{} is greyed or unavailable!".format(root, sub))

    buttons = sel.execute_script('return {}'.format(root_obj))
    tb_name = None
    for tb_key, tb_obj in buttons.iteritems():
        for btn_key, btn_obj in tb_obj['buttons'].iteritems():
            if btn_obj['name'] == idd:
                tb_name = tb_key
    if not tb_name:
        raise ToolbarOptionGreyedOrUnavailable(
            "Toolbar button {}/{} is greyed or unavailable!".format(root, sub))

    sel.execute_script("{}['{}']['obj'].callEvent('onClick', ['{}'])".format(
        root_obj, tb_name, idd))

    if not invokes_alert:
        sel.wait_for_ajax()
    return True
示例#12
0
def old_select(root, sub=None, invokes_alert=False):
    """ Clicks on a button by calling the dhtmlx toolbar callEvent.

    Args:
        root: The root button's name as a string.
        sub: The sub button's name as a string. (optional)
        invokes_alert: If ``True``, then the behaviour is little bit different. After the last
            click, no ajax wait and no move away is done to be able to operate the alert that
            appears after click afterwards. Defaults to ``False``.
    Returns: ``True`` if everything went smoothly
    Raises: :py:class:`cfme.exceptions.ToolbarOptionGreyedOrUnavailable`
    """
    # wait for ajax on select to prevent pickup up a toolbar button in the middle of a page change
    sel.wait_for_ajax()
    if isinstance(root, dict):
        root = version.pick(root)
    if sub is not None and isinstance(sub, dict):
        sub = version.pick(sub)

    root_obj = 'ManageIQ.toolbars'

    if sub:
        search = sub_loc(sub)
    else:
        search = root_loc(root)

    eles = sel.elements(search)

    for ele in eles:
        idd = sel.get_attribute(ele, 'idd')
        if idd:
            break
    else:
        raise ToolbarOptionGreyedOrUnavailable(
            "Toolbar button {}/{} is greyed or unavailable!".format(root, sub))

    buttons = sel.execute_script('return {}'.format(root_obj))
    tb_name = None
    for tb_key, tb_obj in buttons.iteritems():
        for btn_key, btn_obj in tb_obj['buttons'].iteritems():
            if btn_obj['name'] == idd:
                tb_name = tb_key
    if not tb_name:
        raise ToolbarOptionGreyedOrUnavailable(
            "Toolbar button {}/{} is greyed or unavailable!".format(root, sub))

    sel.execute_script(
        "{}['{}']['obj'].callEvent('onClick', ['{}'])".format(root_obj, tb_name, idd))

    if not invokes_alert:
        sel.wait_for_ajax()
    return True
示例#13
0
def test_evmserverd_stop(appliance):
    """Tests whether stopping the evmserverd really stops the CFME server processes.

    Steps:
        * Remember all server names from ``service evmserverd status`` command.
            * Or the bin/rake evm:status on 5.5+ since the systemd status does not show that, this
                applies also for next references to status.
        * Issue a ``service evmserverd stop`` command.
        * Periodically check output of ``service evmserverd status`` that all servers are stopped.
        * For 5.5+: Really call ``service evmserverd status`` and check that the mentions of
            stopping the service are present.
    """

    server_name_key = version.pick({
        version.LOWEST: 'Server Name',
        '5.8': 'Server'
    })

    server_names = {server[server_name_key] for server in appliance.ssh_client.status["servers"]}
    assert appliance.ssh_client.run_command("systemctl stop evmserverd").rc == 0

    @wait_for_decorator(timeout="2m", delay=5)
    def servers_stopped():
        status = {
            server[server_name_key]: server for server in appliance.ssh_client.status["servers"]
        }
        for server_name in server_names:
            if status[server_name]["Status"] != "stopped":
                return False
        return True

    if version.current_version() >= "5.5":
        status = appliance.ssh_client.run_command("systemctl status evmserverd")
        assert "Stopped EVM server daemon" in status.output
        assert "code=exited" in status.output
示例#14
0
def test_crud_service_dialog(appliance):
    element_data = {
        'element_information': {
            'ele_label': "ele_" + fauxfactory.gen_alphanumeric(),
            'ele_name': fauxfactory.gen_alphanumeric(),
            'ele_desc': fauxfactory.gen_alphanumeric(),
            'choose_type': "Text Box"
        },
        'options': {
            'default_text_box': "Default text"
        }
    }

    dialog, element = create_dialog(appliance, element_data)
    view_cls = navigator.get_class(element.parent, 'Add').VIEW
    view = element.appliance.browser.create_view(view_cls)
    flash_message = version.pick({
        '5.8':
        'Dialog "{}" was added'.format(dialog.label),
        '5.9':
        '{} was saved'.format(dialog.label)
    })
    view.flash.assert_message(flash_message)
    with update(dialog):
        dialog.description = "my edited description"
    dialog.delete()
def myservice(appliance, setup_provider, provider, catalog_item, request):
    """Tests my service

    Metadata:
        test_flag: provision
    """
    vm_name = version.pick({
        version.LOWEST:
        catalog_item.provisioning_data["vm_name"] + '_0001',
        '5.7':
        catalog_item.provisioning_data["vm_name"] + '0001'
    })
    catalog_item.create()
    service_catalogs = ServiceCatalogs(appliance, catalog_item.catalog,
                                       catalog_item.name)
    service_catalogs.order()
    logger.info('Waiting for cfme provision request for service %s',
                catalog_item.name)
    request_description = catalog_item.name
    service_request = appliance.collections.requests.instantiate(
        request_description, partial_check=True)
    service_request.wait_for_request()
    assert service_request.is_succeeded()

    yield catalog_item.name, vm_name

    cleanup_vm(vm_name, provider)
示例#16
0
    def assign_to(self, assign, selections=None, tag_category=None):
        """Assigns this Alert Profile to specified objects.

        Args:
            assign: Where to assign (The Enterprise, ...).
            selections: What items to check in the tree. N/A for The Enteprise.
            tag_category: Only for choices starting with Tagged. N/A for The Enterprise.
        """
        view = navigate_to(self, "Edit assignments")
        changed = view.fill({
            "assign_to": assign,
            "tag_category": tag_category,
            "selections": selections
        })
        if changed:
            view.save_button.click()
        else:
            view.cancel_button.click()
        view = self.create_view(AlertProfileDetailsView)
        assert view.is_displayed
        view.flash.assert_no_error()
        if changed:
            view.flash.assert_message(
                'Alert Profile "{}" assignments {} saved'.format(
                    self.description,
                    version.pick({
                        version.LOWEST: "succesfully",
                        "5.8": "successfully",
                    })))
        else:
            view.flash.assert_message(
                'Edit of Alert Profile "{}" was cancelled by the user'.format(
                    self.description))
def test_service_dialog_duplicate_name(appliance, request):
    element_data = {
        'element_information': {
            'ele_label': "ele_" + fauxfactory.gen_alphanumeric(),
            'ele_name': fauxfactory.gen_alphanumeric(),
            'ele_desc': fauxfactory.gen_alphanumeric(),
            'choose_type': "Text Box"
        },
        'options': {
            'default_text_box': "Default text"
        }
    }
    label = 'duplicate_' + fauxfactory.gen_alphanumeric()
    dialog, element = create_dialog(appliance, element_data, label=label)
    request.addfinalizer(dialog.delete_if_exists)
    region_number = appliance.server.zone.region.number
    error_message = version.pick({
        '5.8': 'Validation failed: Label is not unique within region {}'.format(region_number),
        '5.9': 'There was an error editing this dialog: '
               'Failed to create a new dialog - Validation failed: '
               'Name is not unique within region {}'.format(region_number)})
    with pytest.raises(AssertionError):
        create_dialog(appliance, element_data, label=label)
        view_cls = navigator.get_class(element.parent, 'Add').VIEW
        view = element.appliance.browser.create_view(view_cls)
        view.flash.assert_message(error_message)
示例#18
0
    def _fill_provider_attributes(self, provider_attributes):
        """Fills provider data.

        Helper method for ``self.create_rest``
        """
        if getattr(self, "region", None):
            provider_attributes["provider_region"] = version.pick(
                self.region) if isinstance(self.region, dict) else self.region
        if getattr(self, "project", None):
            provider_attributes["project"] = self.project

        if self.type_name in ('openstack_infra', 'openstack'):
            if getattr(self, 'api_version', None):
                version = 'v3' if 'v3' in self.api_version else 'v2'
                provider_attributes['api_version'] = version
                if version == 'v3' and getattr(self, 'keystone_v3_domain_id',
                                               None):
                    provider_attributes['uid_ems'] = self.keystone_v3_domain_id

        if self.type_name == "azure":
            provider_attributes["uid_ems"] = self.tenant_id
            provider_attributes["provider_region"] = self.region.lower(
            ).replace(" ", "")
            if getattr(self, "subscription_id", None):
                provider_attributes["subscription"] = self.subscription_id
示例#19
0
def test_service_dialog_duplicate_name(appliance, request):
    element_data = {
        'element_information': {
            'ele_label': "ele_" + fauxfactory.gen_alphanumeric(),
            'ele_name': fauxfactory.gen_alphanumeric(),
            'ele_desc': fauxfactory.gen_alphanumeric(),
            'choose_type': "Text Box"
        },
        'options': {
            'default_text_box': "Default text"
        }
    }
    label = 'duplicate_' + fauxfactory.gen_alphanumeric()
    dialog, element = create_dialog(appliance, element_data, label=label)
    request.addfinalizer(dialog.delete_if_exists)
    region_number = appliance.server.zone.region.number
    error_message = version.pick({
        '5.8':
        'Validation failed: Label is not unique within region {}'.format(
            region_number),
        '5.9':
        'There was an error editing this dialog: '
        'Failed to create a new dialog - Validation failed: '
        'Name is not unique within region {}'.format(region_number)
    })
    with pytest.raises(AssertionError):
        create_dialog(appliance, element_data, label=label)
        view_cls = navigator.get_class(element.parent, 'Add').VIEW
        view = element.appliance.browser.create_view(view_cls)
        view.flash.assert_message(error_message)
示例#20
0
def test_permission_edit(appliance, request, product_features, action):
    """
    Ensures that changes in permissions are enforced on next login
    Args:
        appliance: cfme appliance fixture
        request: pytest request fixture
        product_features: product features to set for test role
        action: reference to a function to execute under the test user context
    """
    product_features = version.pick(product_features)
    request.addfinalizer(appliance.server.login_admin)
    role_name = fauxfactory.gen_alphanumeric()
    role = appliance.collections.roles.create(name=role_name,
                vm_restriction=None,
                product_features=[(['Everything'], False)] +  # role_features
                                 [(k, True) for k in product_features])
    group_description = 'grp{}'.format(fauxfactory.gen_alphanumeric())
    group = group_collection(appliance).create(description=group_description, role=role.name)
    user = new_user(appliance, group=group)
    with user:
        try:
            action()
        except Exception:
            pytest.fail('Incorrect permissions set')
    appliance.server.login_admin()
    role.update({'product_features': [(['Everything'], True)] +
                                     [(k, False) for k in product_features]
                 })
    with user:
        try:
            with error.expected(Exception):
                action()
        except error.UnexpectedSuccessException:
            pytest.fail('Permissions have not been updated')
def backup(appliance, provider):
    volume_collection = appliance.collections.volumes
    storage_manager = version.pick({
        '5.8':
        '{} Cinder Manager'.format(provider.name),
        version.LOWEST:
        None
    })
    backup_collection = appliance.collections.volume_backups.filter(
        {'provider': provider})

    # create new volume
    volume = volume_collection.create(
        name=fauxfactory.gen_alpha(),
        storage_manager=storage_manager,
        tenant=provider.data['provisioning']['cloud_tenant'],
        size=STORAGE_SIZE,
        provider=provider)

    # create new backup for crated volume
    backup_name = fauxfactory.gen_alpha()
    volume.create_backup(backup_name)
    backup = backup_collection.instantiate(backup_name, provider)

    yield backup

    try:
        if backup.exists:
            backup_collection.delete(backup)
        if volume.exists:
            volume.delete(wait=False)
    except Exception:
        logger.warning('Exception during volume deletion - skipping..')
示例#22
0
    def view_value_mapping(self):
        out = {
            'hostname': self.hostname,
            'password': self.token,
            'api_port': self.api_port,
            'sec_protocol': self.sec_protocol
        }

        if self.sec_protocol.lower() == 'ssl trusting custom ca' and hasattr(
                self, 'get_ca_cert'):
            out['trusted_ca_certificates'] = self.get_ca_cert({
                "username":
                self.ssh_creds.principal,
                "password":
                self.ssh_creds.secret,
                "hostname":
                self.master_hostname
            })

        out['confirm_password'] = version.pick({
            version.LOWEST: self.token,
            '5.9': None
        })

        return out
示例#23
0
def orchestration_templates(request, rest_api, num=2):
    data = []
    for _ in range(num):
        uniq = fauxfactory.gen_alphanumeric(5)
        data.append({
            'name':
            'test_{}'.format(uniq),
            'description':
            'Test Template {}'.format(uniq),
            'type':
            version.pick({
                version.LOWEST:
                'OrchestrationTemplateCfn',
                '5.9':
                'ManageIQ::Providers::Amazon::CloudManager::OrchestrationTemplate'
            }),
            'orderable':
            False,
            'draft':
            False,
            'content':
            TEMPLATE_TORSO.replace('CloudFormation', uniq)
        })

    return _creating_skeleton(request, rest_api, 'orchestration_templates',
                              data)
 def _refresh_flash_msg(self):
     return version.pick({
         '5.7':
         'Refresh Provider initiated for 1 provider ({})'.format(self.type),
         '5.8':
         'Refresh Provider initiated for 1 provider'
     })
示例#25
0
def pf_select(root, sub=None, invokes_alert=False):
    """ Clicks on a button by calling the click event with the jquery trigger.

    Args:
        root: The root button's name as a string.
        sub: The sub button's name as a string. (optional)
        invokes_alert: If ``True``, then the behaviour is little bit different. After the last
            click, no ajax wait and no move away is done to be able to operate the alert that
            appears after click afterwards. Defaults to ``False``.
    Returns: ``True`` if everything went smoothly
    Raises: :py:class:`cfme.exceptions.ToolbarOptionGreyedOrUnavailable`
    """

    sel.wait_for_ajax()
    if isinstance(root, dict):
        root = version.pick(root)
    if isinstance(sub, dict):
        sub = version.pick(sub)

    if sub:
        q_sub = xpath_quote(sub).replace("'", "\\'")
        sel.execute_script(
            "return $('a:contains({})').trigger('click')".format(q_sub))
    else:
        q_root = xpath_quote(root).replace("'", "\\'")
        try:
            sel.element("//button[@data-original-title = {0}] | "
                        "//a[@data-original-title = {0}]".format(q_root))
            sel.execute_script(
                "return $('*[data-original-title={}]').trigger('click')".format(q_root))
        except sel.NoSuchElementException:
            try:
                sel.element("//button[@title={}]".format(q_root))
                sel.execute_script(
                    "return $('button[title={}]').trigger('click')".format(q_root))
            except sel.NoSuchElementException:
                try:
                    sel.element("//button[contains(@title, {})]".format(q_root))
                    sel.execute_script(
                        "return $('button:contains({})').trigger('click')".format(q_root))
                except sel.NoSuchElementException:
                    # The view selection buttons?
                    sel.click("//li/a[@title={}]/*[self::i or self::img]/../..".format(q_root))

    if not invokes_alert:
        sel.wait_for_ajax()
    return True
示例#26
0
 def retire(self):
     self.load_details(refresh=True)
     lcl_btn(self.TO_RETIRE, invokes_alert=True)
     sel.handle_alert()
     flash.assert_success_message(
         'Retirement initiated for 1 VM and Instance from the {} Database'.format(version.pick({
             version.LOWEST: 'CFME',
             'upstream': 'ManageIQ'})))
    def type(self):
        """Returns presumed type of the manager based on CFME version

        Note:
            We cannot actually know the type of the provider from the UI.
            This represents the supported type by CFME version and is to be used in navigation.
            """
        return version.pick({version.LOWEST: 'Red Hat Satellite', version.LATEST: 'Foreman'})
示例#28
0
    def type(self):
        """Returns presumed type of the manager based on CFME version

        Note:
            We cannot actually know the type of the provider from the UI.
            This represents the supported type by CFME version and is to be used in navigation.
            """
        return version.pick({version.LOWEST: 'Red Hat Satellite', version.LATEST: 'Foreman'})
def test_vm_retire_extend(request, testing_vm, soft_assert,
                          retire_extend_button):
    """ Tests extending a retirement using an AE method.

    Prerequisities:
        * A running VM on any provider.

    Steps:
        * It creates a button pointing to ``Request/vm_retire_extend`` instance. The button should
            live in the VM and Instance button group.
        * Then it sets a retirement date for the VM
        * Then it waits until the retirement date is set
        * Then it clicks the button that was created and it waits for the retirement date to extend.

    Metadata:
        test_flag: retire, provision
    """
    num_days = 5
    soft_assert(testing_vm.retirement_date == 'Never',
                "The retirement date is not 'Never'!")
    retirement_date = generate_retirement_date(delta=num_days)
    testing_vm.set_retirement_date(retirement_date)
    wait_for(lambda: testing_vm.retirement_date != 'Never',
             message="retirement date set")
    set_date = testing_vm.retirement_date
    if not BZ(1419150, forced_streams='5.6').blocks:
        soft_assert(
            set_date == retirement_date.strftime(pick(VM.RETIRE_DATE_FMT)),
            "The retirement date '{}' did not match expected date '{}'".format(
                set_date, retirement_date.strftime(pick(VM.RETIRE_DATE_FMT))))

    # Create the vm_retire_extend button and click on it
    retire_extend_button()

    # CFME automate vm_retire_extend method defaults to extending the date by 14 days
    extend_duration_days = 14
    extended_retirement_date = retirement_date + timedelta(
        days=extend_duration_days)

    # Check that the WebUI updates with the correct date
    wait_for(
        lambda: testing_vm.retirement_date >= extended_retirement_date.
        strftime(pick(VM.RETIRE_DATE_FMT)),
        num_sec=60,
        message="Check for extension of the VM retirement date by {} days".
        format(extend_duration_days))
示例#30
0
    def _install_simplecov(self):
        self.log.info('Installing coverage gem on appliance')
        self.ipapp.ssh_client.put_file(gemfile.strpath, bundler_d.strpath)

        # gem install for more recent downstream builds
        def _gem_install():
            self.ipapp.ssh_client.run_command(
                'gem install --install-dir /opt/rh/cfme-gemset/ -v0.9.2 simplecov')

        # bundle install for old downstream and upstream builds
        def _bundle_install():
            self.ipapp.ssh_client.run_command('yum -y install git')
            self.ipapp.ssh_client.run_command('cd {}; bundle'.format(rails_root))

        version.pick({
            version.LOWEST: _gem_install,
            version.LATEST: _bundle_install,
        })()
示例#31
0
    def view_value_mapping(self):

        out = {}

        out['hostname'] = version.pick({
            version.LOWEST: None,
            '5.9': self.hostname})
        out['api_port'] = version.pick({
            version.LOWEST: None,
            '5.9': self.api_port})
        out['sec_protocol'] = version.pick({
            version.LOWEST: None,
            '5.9': self.sec_protocol})

        if out['sec_protocol'] and self.sec_protocol.lower() == 'ssl trusting custom ca':
            out['trusted_ca_certificates'] = OpenshiftDefaultEndpoint.get_ca_cert()

        return out
示例#32
0
    def _install_simplecov(self):
        self.log.info('Installing coverage gem on appliance')
        self.ipapp.ssh_client.put_file(gemfile.strpath, bundler_d.strpath)

        # gem install for more recent downstream builds
        def _gem_install():
            self.ipapp.ssh_client.run_command(
                'gem install --install-dir /opt/rh/cfme-gemset/ -v0.9.2 simplecov')

        # bundle install for old downstream and upstream builds
        def _bundle_install():
            self.ipapp.ssh_client.run_command('yum -y install git')
            self.ipapp.ssh_client.run_command('cd {}; bundle'.format(rails_root))

        version.pick({
            version.LOWEST: _gem_install,
            version.LATEST: _bundle_install,
        })()
示例#33
0
 def view_value_mapping(self):
     """Maps values to view attrs"""
     region = pick(self.region) if isinstance(self.region, dict) else self.region
     return {
         'name': self.name,
         'prov_type': 'Azure',
         'region': region,
         'tenant_id': self.tenant_id,
         'subscription': getattr(self, 'subscription_id', None)
     }
示例#34
0
 def view_value_mapping(self):
     return {
         'name':
         self.name,
         'prov_type':
         version.pick({
             version.LOWEST: 'Red Hat Virtualization Manager',
             '5.8.0.10': 'Red Hat Virtualization'
         }),
     }
示例#35
0
 def view_value_mapping(self):
     """
     used for filling forms like add/edit provider form
     Returns: dict
     """
     return {
         'token': self.token,
         'verify_token': version.pick({version.LOWEST: self.verify_token,
                                       '5.9': None})
     }
def test_vm_retire_extend(request, testing_vm, soft_assert, retire_extend_button):
    """ Tests extending a retirement using an AE method.

    Prerequisities:
        * A running VM on any provider.

    Steps:
        * It creates a button pointing to ``Request/vm_retire_extend`` instance. The button should
            live in the VM and Instance button group.
        * Then it sets a retirement date for the VM
        * Then it waits until the retirement date is set
        * Then it clicks the button that was created and it waits for the retirement date to extend.

    Metadata:
        test_flag: retire, provision
    """
    num_days = 5
    soft_assert(testing_vm.retirement_date == 'Never', "The retirement date is not 'Never'!")
    retirement_date = generate_retirement_date(delta=num_days)
    testing_vm.set_retirement_date(retirement_date)
    wait_for(lambda: testing_vm.retirement_date != 'Never', message="retirement date set")
    set_date = testing_vm.retirement_date
    if not BZ(1419150, forced_streams='5.6').blocks:
        soft_assert(set_date == retirement_date.strftime(pick(VM.RETIRE_DATE_FMT)),
                    "The retirement date '{}' did not match expected date '{}'"
                    .format(set_date, retirement_date.strftime(pick(VM.RETIRE_DATE_FMT))))

    # Create the vm_retire_extend button and click on it
    retire_extend_button()

    # CFME automate vm_retire_extend method defaults to extending the date by 14 days
    extend_duration_days = 14
    extended_retirement_date = retirement_date + timedelta(days=extend_duration_days)

    # Check that the WebUI updates with the correct date
    wait_for(
        lambda: testing_vm.retirement_date >= extended_retirement_date.strftime(
            pick(VM.RETIRE_DATE_FMT)),
        num_sec=60,
        message="Check for extension of the VM retirement date by {} days".format(
            extend_duration_days)
    )
示例#37
0
 def retire(self):
     self.load_details(refresh=True)
     lcl_btn(self.TO_RETIRE, invokes_alert=True)
     sel.handle_alert()
     flash.assert_success_message(
         'Retirement initiated for 1 VM and Instance from the {} Database'.
         format(
             version.pick({
                 version.LOWEST: 'CFME',
                 'upstream': 'ManageIQ'
             })))
示例#38
0
 def view_value_mapping(self):
     """
     used for filling forms like add/edit provider form
     Returns: dict
     """
     return {
         'username': self.principal,
         'password': self.secret,
         'confirm_password': version.pick({version.LOWEST: self.verify_secret,
                                           '5.9': None})
     }
示例#39
0
 def paged_table(self):
     _paged_table_template = '//div[@id="list_grid"]/div[@class="{}"]/table/tbody'
     return version.pick({
         version.LOWEST:
         SplitPagedTable(header_data=(_paged_table_template.format("xhdr"),
                                      1),
                         body_data=(_paged_table_template.format("objbox"),
                                    0)),
         "5.5":
         PagedTable('//table'),
     })
示例#40
0
 def delete(self):
     view = navigate_to(self, "Details")
     view.configuration.item_select(
         version.pick({
             version.LOWEST: 'Remove Item from the VMDB',
             '5.7': 'Remove Catalog'}),
         handle_alert=True)
     view = self.create_view(CatalogsView)
     assert view.is_displayed
     view.flash.assert_no_error()
     view.flash.assert_success_message(
         'Catalog "{}": Delete successful'.format(self.description or self.name))
示例#41
0
    def view_value_mapping(self):

        out = {}

        out['hostname'] = version.pick({
            version.LOWEST: None,
            '5.9': self.hostname})
        out['api_port'] = version.pick({
            version.LOWEST: None,
            '5.9': self.api_port})
        out['sec_protocol'] = version.pick({
            version.LOWEST: None,
            '5.9': self.sec_protocol})

        if out['sec_protocol'] and self.sec_protocol.lower() == 'ssl trusting custom ca':
            out['trusted_ca_certificates'] = OpenshiftDefaultEndpoint.get_ca_cert(
                {"username": self.ssh_creds.principal,
                 "password": self.ssh_creds.secret,
                 "hostname": self.master_hostname})

        return out
示例#42
0
 def delete(self):
     view = navigate_to(self, "Details")
     view.configuration.item_select(version.pick({
         version.LOWEST:
         'Remove Item from the VMDB',
         '5.7':
         'Remove Catalog Item'
     }),
                                    handle_alert=True)
     view = self.create_view(AllCatalogItemView)
     assert view.is_displayed
     view.flash.assert_success_message(
         'The selected Catalog Item was deleted')
示例#43
0
    def delete(self, cancel=True):
        """
        Deletes a provider from CFME

        Args:
            cancel: Whether to cancel the deletion, defaults to True
        """
        view = navigate_to(self, 'Details')
        item_title = version.pick({'5.9': 'Remove this {} Provider from Inventory',
                                   version.LOWEST: 'Remove this {} Provider'})
        view.toolbar.configuration.item_select(item_title.format("Infrastructure"),
                                               handle_alert=not cancel)
        if not cancel:
            view.flash.assert_no_error()
示例#44
0
    def delete(self, cancel=True):
        """
        Deletes a provider from CFME

        Args:
            cancel: Whether to cancel the deletion, defaults to True
        """
        view = navigate_to(self, 'Details')
        item_title = version.pick({'5.9': 'Remove this {} Provider from Inventory',
                                   version.LOWEST: 'Remove this {} Provider'})
        view.toolbar.configuration.item_select(item_title.format("Infrastructure"),
                                               handle_alert=not cancel)
        if not cancel:
            view.flash.assert_no_error()
示例#45
0
    def view_value_mapping(self):
        out = {'hostname': self.hostname,
               'password': self.token,
               'api_port': self.api_port,
               'sec_protocol': self.sec_protocol}

        if self.sec_protocol.lower() == 'ssl trusting custom ca' and hasattr(self, 'get_ca_cert'):
            out['trusted_ca_certificates'] = self.get_ca_cert()

        out['confirm_password'] = version.pick({
            version.LOWEST: self.token,
            '5.9': None})

        return out
示例#46
0
def orchestration_templates(request, rest_api, num=2):
    data = []
    for _ in range(num):
        uniq = fauxfactory.gen_alphanumeric(5)
        data.append({
            'name': 'test_{}'.format(uniq),
            'description': 'Test Template {}'.format(uniq),
            'type': version.pick({
                version.LOWEST: 'OrchestrationTemplateCfn',
                '5.9': 'ManageIQ::Providers::Amazon::CloudManager::OrchestrationTemplate'}),
            'orderable': False,
            'draft': False,
            'content': TEMPLATE_TORSO.replace('CloudFormation', uniq)})

    return _creating_skeleton(request, rest_api, 'orchestration_templates', data)
示例#47
0
    def delete(self, cancel=True):
        """
        Deletes a provider from CFME

        Args:
            cancel: Whether to cancel the deletion, defaults to True
        """
        view = navigate_to(self, 'Details')
        item_title = version.pick({'5.9': 'Remove this {} Provider from Inventory',
                                   version.LOWEST: 'Remove this {} Provider'})
        view.toolbar.configuration.item_select(item_title.format(self.string_name),
                                               handle_alert=not cancel)
        if not cancel:
            msg = ('Delete initiated for 1 {} Provider from '
                   'the {} Database'.format(self.string_name, self.appliance.product_name))
            view.flash.assert_success_message(msg)
示例#48
0
    def _fill_provider_attributes(self, provider_attributes):
        """Fills provider data.

        Helper method for ``self.create_rest``
        """
        if getattr(self, "region", None):
            provider_attributes["provider_region"] = version.pick(
                self.region) if isinstance(self.region, dict) else self.region
        if getattr(self, "project", None):
            provider_attributes["project"] = self.project

        if self.type_name == "azure":
            provider_attributes["uid_ems"] = self.tenant_id
            provider_attributes["provider_region"] = self.region.lower().replace(" ", "")
            if getattr(self, "subscription_id", None):
                provider_attributes["subscription"] = self.subscription_id
def test_properties(provider, appliance, test_item, soft_assert):

    instances = test_item.collection_object(appliance).get_random_instances(count=2)

    for instance in instances:

        if isinstance(test_item.expected_fields, dict):
            expected_fields = version.pick(test_item.expected_fields)
        else:
            expected_fields = test_item.expected_fields
        for field in expected_fields:
            view = navigate_to(instance, 'Details')
            try:
                soft_get(view.entities.properties.read(), field, dict_=True)
            except AttributeError:
                soft_assert(False, '{} "{}" properties table has missing field - "{}"'
                                   .format(test_item.obj.__name__, instance.name, field))
示例#50
0
    def num_container(self):
        # Containers are linked to providers through container definitions and then through pods
        query = version.pick({
            version.LOWEST: "SELECT count(*) "
            "FROM ext_management_systems, container_groups, container_definitions, containers "
            "WHERE containers.container_definition_id=container_definitions.id "
            "AND container_definitions.container_group_id=container_groups.id "
            "AND container_groups.ems_id=ext_management_systems.id "
            "AND ext_management_systems.name='{}'".format(self.name),

            '5.9': "SELECT count(*) "
            "FROM ext_management_systems, container_groups, containers "
            "WHERE containers.container_group_id=container_groups.id "
            "AND container_groups.ems_id=ext_management_systems.id "
            "AND ext_management_systems.name='{}'".format(self.name)
        })
        res = self.appliance.db.client.engine.execute(query)
        return int(res.first()[0])
示例#51
0
def test_service_dialog_duplicate_name(appliance, request):
    element_data = {
        'element_information': {
            'ele_label': "ele_" + fauxfactory.gen_alphanumeric(),
            'ele_name': fauxfactory.gen_alphanumeric(),
            'ele_desc': fauxfactory.gen_alphanumeric(),
            'choose_type': "Text Box"
        },
        'options': {
            'default_text_box': "Default text"
        }
    }
    label = 'duplicate_' + fauxfactory.gen_alphanumeric()
    dialog = create_dialog(appliance, element_data, label=label)
    request.addfinalizer(dialog.delete_if_exists)
    error_message = version.pick({
        '5.8': 'Validation failed: Label is not unique within region 0',
        '5.9': 'There was an error editing this dialog: '
               'Failed to create a new dialog - Validation failed: '
               'Name is not unique within region 0'})
    with error.expected(error_message):
        create_dialog(appliance, element_data, label=label)
def test_crud_service_dialog(appliance):
    element_data = {
        'element_information': {
            'ele_label': "ele_" + fauxfactory.gen_alphanumeric(),
            'ele_name': fauxfactory.gen_alphanumeric(),
            'ele_desc': fauxfactory.gen_alphanumeric(),
            'choose_type': "Text Box"
        },
        'options': {
            'default_text_box': "Default text"
        }
    }

    dialog, element = create_dialog(appliance, element_data)
    view_cls = navigator.get_class(element.parent, 'Add').VIEW
    view = element.appliance.browser.create_view(view_cls)
    flash_message = version.pick({
        '5.8': 'Dialog "{}" was added'.format(dialog.label),
        '5.9': '{} was saved'.format(dialog.label)})
    view.flash.assert_message(flash_message)
    with update(dialog):
        dialog.description = "my edited description"
    dialog.delete()
 def none(self):
     if self._none:
         return version.pick(self._none)
     else:
         return None
示例#54
0
 def pick(self, appliance_version):
     from cfme.utils.version import pick
     return pick(self.version_dict, appliance_version)
示例#55
0
 def g(m, *args, **kwargs):
     if isinstance(m, dict):
         m = version.pick(m)
     return f(m, *args, **kwargs)
def _d(l, **kwargs):
    """Resolve version-specific locators."""
    return elements(version.pick(l), **kwargs)
示例#57
0
    def run_command(
            self, command, timeout=RUNCMD_TIMEOUT, reraise=False, ensure_host=False,
            ensure_user=False):
        """Run a command over SSH.

        Args:
            command: The command. Supports taking dicts as version picking.
            timeout: Timeout after which the command execution fails.
            reraise: Does not muffle the paramiko exceptions in the log.
            ensure_host: Ensure that the command is run on the machine with the IP given, not any
                container or such that we might be using by default.
            ensure_user: Ensure that the command is run as the user we logged in, so in case we are
                not root, setting this to True will prevent from running sudo.

        Returns:
            A :py:class:`SSHResult` instance.
        """
        if isinstance(command, dict):
            command = version.pick(command, active_version=self.vmdb_version)
        original_command = command
        uses_sudo = False
        logger.info("Running command %r", command)
        if self.is_pod and not ensure_host:
            # This command will be executed in the context of the host provider
            command_to_run = 'source /etc/default/evm; ' + command
            oc_cmd = 'oc exec --namespace={proj} {pod} -- bash -c {cmd}'.format(
                proj=self._project, pod=self._container, cmd=quote(command_to_run))
            command = oc_cmd
            ensure_host = True
        elif self.is_container and not ensure_host:
            command = 'docker exec {} bash -c {}'.format(self._container, quote(
                'source /etc/default/evm; ' + command))

        if self.username != 'root' and not ensure_user:
            # We need sudo
            command = 'sudo -i bash -c {command}'.format(command=quote(command))
            uses_sudo = True

        if command != original_command:
            logger.info("> Actually running command %r", command)
        command += '\n'

        output = []
        try:
            session = self.get_transport().open_session()
            if uses_sudo:
                # We need a pseudo-tty for sudo
                session.get_pty()
            if timeout:
                session.settimeout(float(timeout))

            session.exec_command(command)
            stdout = session.makefile()
            stderr = session.makefile_stderr()
            while True:
                if session.recv_ready:
                    for line in stdout:
                        output.append(line)
                        if self._streaming:
                            self.f_stdout.write(line)

                if session.recv_stderr_ready:
                    for line in stderr:
                        output.append(line)
                        if self._streaming:
                            self.f_stderr.write(line)

                if session.exit_status_ready():
                    break
            exit_status = session.recv_exit_status()
            return SSHResult(exit_status, ''.join(output))
        except paramiko.SSHException:
            if reraise:
                raise
            else:
                logger.exception('Exception happened during SSH call')
        except socket.timeout:
            logger.exception(
                "Command %r timed out. Output before it failed was:\n%r",
                command,
                ''.join(output))
            raise

        # Returning two things so tuple unpacking the return works even if the ssh client fails
        # Return whatever we have in the output
        return SSHResult(1, ''.join(output))
示例#58
0
 def view_value_mapping(self):
     return {
         'name': self.name,
         'prov_type': version.pick({version.LOWEST: 'Red Hat Virtualization Manager',
                                    '5.8.0.10': 'Red Hat Virtualization'}),
     }
示例#59
0
    def run_command(
            self, command, timeout=RUNCMD_TIMEOUT, reraise=False, ensure_host=False,
            ensure_user=False, container=None):
        """Run a command over SSH.

        Args:
            command: The command. Supports taking dicts as version picking.
            timeout: Timeout after which the command execution fails.
            reraise: Does not muffle the paramiko exceptions in the log.
            ensure_host: Ensure that the command is run on the machine with the IP given, not any
                container or such that we might be using by default.
            ensure_user: Ensure that the command is run as the user we logged in, so in case we are
                not root, setting this to True will prevent from running sudo.
            container: allows to temporarily override default container
        Returns:
            A :py:class:`SSHResult` instance.
        """
        if isinstance(command, dict):
            command = version.pick(command, active_version=self.vmdb_version)
        original_command = command
        uses_sudo = False
        logger.info("Running command %r", command)
        container = container or self._container
        if self.is_pod and not ensure_host:
            # This command will be executed in the context of the host provider
            command_to_run = '[[ -f /etc/default/evm ]] && source /etc/default/evm; ' + command
            oc_cmd = 'oc exec --namespace={proj} {pod} -- bash -c {cmd}'.format(
                proj=self._project, pod=container, cmd=quote(command_to_run))
            command = oc_cmd
            ensure_host = True
        elif self.is_container and not ensure_host:
            command = 'docker exec {} bash -c {}'.format(container, quote(
                'source /etc/default/evm; ' + command))

        if self.username != 'root' and not ensure_user:
            # We need sudo
            command = 'sudo -i bash -c {command}'.format(command=quote(command))
            uses_sudo = True

        if command != original_command:
            logger.info("> Actually running command %r", command)
        command += '\n'

        output = []
        try:
            session = self.get_transport().open_session()
            if uses_sudo:
                # We need a pseudo-tty for sudo
                session.get_pty()
            if timeout:
                session.settimeout(float(timeout))

            session.exec_command(command)
            stdout = session.makefile()
            stderr = session.makefile_stderr()

            def write_output(line, file):
                output.append(line)
                if self._streaming:
                    file.write(line)

            while True:
                if session.exit_status_ready():
                    break

                # While the program is running loop through collecting line by line so that we don't
                # fill the buffers up without a newline.
                # Also, note that for long running programs if we try to read output when there
                # is none (and in the case of stderr may never be any)
                # we risk blocking so long that the write buffer on the remote side will fill
                # and the remote program will block on a write.
                # The blocking on our side occurs in paramiko's buffered_pipe.py's read() call,
                # which will block if its internal buffer is empty.
                if session.recv_ready():
                    try:
                        line = next(stdout)
                        write_output(line, self.f_stdout)
                    except StopIteration:
                        pass

                if session.recv_stderr_ready():
                    try:
                        line = next(stdout)
                        write_output(line, self.f_stderr)
                    except StopIteration:
                        pass

            # When the program finishes, we need to grab the rest of the output that is left.
            # Also, we don't have the issue of blocking reads because since the command is
            # finished, any pending reads of SSH encrypted data will finish shortly and put in
            # the buffer or for an empty file EOF will be reached as it will be closed.
            for line in stdout:
                write_output(line, self.f_stdout)
            for line in stderr:
                write_output(line, self.f_stderr)

            exit_status = session.recv_exit_status()
            if exit_status != 0:
                logger.warning('Exit code %d!', exit_status)
            return SSHResult(rc=exit_status, output=''.join(output), command=command)
        except paramiko.SSHException:
            if reraise:
                raise
            else:
                logger.exception('Exception happened during SSH call')
        except socket.timeout:
            logger.exception(
                "Command %r timed out. Output before it failed was:\n%r",
                command,
                ''.join(output))
            raise

        # Returning two things so tuple unpacking the return works even if the ssh client fails
        # Return whatever we have in the output
        return SSHResult(rc=1, output=''.join(output), command=command)