def test_drift_analysis(request, provider, instance, soft_assert):
    """ Tests drift analysis is correct

    Metadata:
        test_flag: vm_analysis
    """

    instance.load_details()
    drift_num_orig = 0
    drift_orig = InfoBlock("Relationships", "Drift History").text
    if drift_orig != 'None':
        drift_num_orig = int(drift_orig)
    instance.smartstate_scan()
    wait_for(lambda: is_vm_analysis_finished(instance.name),
             delay=15, timeout="35m", fail_func=lambda: toolbar.select('Reload'))
    instance.load_details()
    wait_for(
        lambda: int(InfoBlock("Relationships", "Drift History").text) == drift_num_orig + 1,
        delay=20,
        num_sec=120,
        message="Waiting for Drift History count to increase",
        fail_func=sel.refresh
    )
    drift_new = int(InfoBlock("Relationships", "Drift History").text)

    # add a tag and a finalizer to remove it
    tag = ('Department', 'Accounting')
    instance.add_tag(tag, single_value=False)
    request.addfinalizer(lambda: instance.remove_tag(tag))

    instance.smartstate_scan()
    wait_for(lambda: is_vm_analysis_finished(instance.name),
             delay=15, timeout="35m", fail_func=lambda: toolbar.select('Reload'))
    instance.load_details()
    wait_for(
        lambda: int(InfoBlock("Relationships", "Drift History").text) == drift_new + 1,
        delay=20,
        num_sec=120,
        message="Waiting for Drift History count to increase",
        fail_func=sel.refresh
    )

    # check drift difference
    soft_assert(not instance.equal_drift_results('Department (1)', 'My Company Tags', 0, 1),
                "Drift analysis results are equal when they shouldn't be")

    # Test UI features that modify the drift grid
    d_grid = DriftGrid()

    # Accounting tag should not be displayed, because it was changed to True
    toolbar.select("Attributes with same values")
    with error.expected(sel.NoSuchElementException):
        d_grid.get_cell('Accounting', 0)

    # Accounting tag should be displayed now
    toolbar.select("Attributes with different values")
    d_grid.get_cell('Accounting', 0)
 def current_snapshot_name(self):
     """Returns the current snapshot name."""
     self.load_details(refresh=True)
     sel.click(InfoBlock("Properties", "Snapshots"))
     text = sel.text(
         "//a[contains(normalize-space(.), '(Active)')]").strip()
     return re.sub(r"\s*\(Active\)$", "", text)
Exemple #3
0
def test_rh_registration(request, unset_org_id,
                         reg_method, reg_data, proxy_url, proxy_creds):

    if reg_method in ('rhsm', 'sat6'):
        repo_or_channel = reg_data.get('enable_repo', None)
    else:
        repo_or_channel = reg_data.get('add_channel', None)

    if not repo_or_channel:
        set_default_repo = True
    else:
        set_default_repo = False

    if proxy_url:
        use_proxy = True
        proxy_username = proxy_creds['username']
        proxy_password = proxy_creds['password']
    else:
        use_proxy = False
        proxy_url = None
        proxy_username = None
        proxy_password = None

    red_hat_updates.update_registration(
        service=reg_method,
        url=reg_data['url'],
        username=conf.credentials[reg_method]['username'],
        password=conf.credentials[reg_method]['password'],
        repo_name=repo_or_channel,
        organization=reg_data.get('organization', None),
        use_proxy=use_proxy,
        proxy_url=proxy_url,
        proxy_username=proxy_username,
        proxy_password=proxy_password,
        set_default_repository=set_default_repo,
        # Satellite 6 registration requires validation to be able to choose organization
        validate=False if reg_method != 'sat6' else True
    )

    used_repo_or_channel = InfoBlock('Red Hat Software Updates', version.pick({
        version.LOWEST: 'Update Repository',
        "5.4": 'Channel Name(s)' if reg_method == 'sat5' else 'Repository Name(s)'})
    ).text

    red_hat_updates.register_appliances()  # Register all

    if reg_method == 'rhsm':
        request.addfinalizer(rhsm_unregister)
    elif reg_method == 'sat5':
        request.addfinalizer(sat5_unregister)
    else:
        request.addfinalizer(sat6_unregister)

    wait_for(
        func=is_registration_complete,
        func_args=[used_repo_or_channel],
        delay=40,
        num_sec=400,
        fail_func=red_hat_updates.register_appliances
    )
Exemple #4
0
class Widget(Updateable, Pretty):
    TITLE = None
    DETAIL_PAGE = None
    WAIT_STATES = {"Queued", "Running"}
    status_info = InfoBlock("Status")

    @property
    def on_widget_page(self):
        return sel.is_displayed(
            "//div[contains(@class, 'dhtmlxPolyInfoBar')]"
            "/div[contains(@class, 'dhtmlxInfoBarLabel') and normalize-space(.)={}]"
            .format(
                quoteattr("{} Widget \"{}\"".format(self.TITLE, self.title))))

    def go_to_detail(self):
        if self.on_widget_page:
            toolbar.select("Reload current display")
        else:
            sel.force_navigate(type(self).DETAIL_PAGE,
                               context={"widget": self})

    def generate(self, wait=True, **kwargs):
        self.go_to_detail()
        toolbar.select("Configuration",
                       "Generate Widget content now",
                       invokes_alert=True)
        sel.handle_alert()
        flash.assert_message_match(
            "Content generation for this Widget has been initiated")
        flash.assert_no_errors()
        if wait:
            self.wait_generated(**kwargs)

    def wait_generated(self, timeout=600):
        wait_for(self.check_status,
                 num_sec=timeout,
                 delay=5,
                 fail_condition=lambda result: result != "Complete")

    def check_status(self):
        self.go_to_detail()
        return self.status_info("Current Status").text

    @classmethod
    def detect(cls, t, *args, **kwargs):
        # Can't be in class because it does not see child classes
        MAPPING = {
            "rss_widget": RSSFeedWidget,
            "rssbox": RSSFeedWidget,
            "chart_widget": ChartWidget,
            "chartbox": ChartWidget,
            "report_widget": ReportWidget,
            "reportbox": ReportWidget,
            "menu_widget": MenuWidget,
            # TODO: menubox?
        }
        if t not in MAPPING:
            raise ValueError(t)
        return MAPPING[t](*args, **kwargs)
    def get_hosts(self):
        """ Returns names of hosts (from quadicons) that use this datastore

        Returns: List of strings with names or `[]` if no hosts found.
        """
        self.load_details()
        sel.click(InfoBlock('Relationships', 'Hosts'))
        return [q.name for q in Quadicon.all("host")]
Exemple #6
0
 def current_snapshot_name(self):
     """Returns the current snapshot name."""
     self.load_details(refresh=True)
     sel.click(InfoBlock("Properties", "Snapshots"))
     text = sel.text("//a[contains(normalize-space(.), '(Active)')]|"
         "//li[contains(normalize-space(.), '(Active)')]").strip()
     # In 5.6 the locator returns the entire tree string, snapshot name is after a newline
     return re.sub(r"\s*\(Active\)$", "", text.split('\n')[-1:][0])
Exemple #7
0
    def compliance_status(self):
        """Returns the title of the compliance infoblock. The title contains datetime so it can be
        compared.

        Returns:
            :py:class:`NoneType` if no title is present (no compliance checks before), otherwise str
        """
        self.load_details(refresh=True)
        return InfoBlock("Compliance", "Status").title
Exemple #8
0
 def current_snapshot_description(self):
     """Returns the current snapshot description."""
     self.load_details(refresh=True)
     sel.click(InfoBlock("Properties", "Snapshots"))
     l = "|".join([
         # Newer
         "//label[normalize-space(.)='Description']/../div/p",
         # Older
         "//td[@class='key' and normalize-space(.)='Description']/.."
         "/td[not(contains(@class, 'key'))]"])
     return sel.text(l).strip()
def test_ssa_packages(provider, instance, soft_assert):
    """ Tests SSA fetches correct results for packages

    Metadata:
        test_flag: vm_analysis
    """

    if instance.system_type == WINDOWS:
        pytest.skip("Windows has no packages")

    expected = None
    if 'package' not in instance.system_type.keys():
        pytest.skip("Don't know how to update packages for {}".format(
            instance.system_type))

    package_name = instance.system_type['package']
    package_command = instance.system_type['install-command']
    package_number_command = instance.system_type['package-number']

    cmd = package_command.format(package_name)
    output = instance.ssh.run_command(cmd.format(package_name)).output
    logger.info("{0} output:\n{1}".format(cmd, output))

    expected = instance.ssh.run_command(package_number_command).output.strip(
        '\n')

    instance.smartstate_scan()
    wait_for(lambda: is_vm_analysis_finished(instance.name),
             delay=15,
             timeout="10m",
             fail_func=lambda: tb.select('Reload'))

    # Check that all data has been fetched
    current = instance.get_detail(properties=('Configuration', 'Packages'))
    assert current == expected

    # Make sure new package is listed
    sel.click(InfoBlock("Configuration", "Packages"))
    template = '//div[@id="list_grid"]/div[@class="{}"]/table/tbody'
    packages = version.pick({
        version.LOWEST:
        SplitTable(header_data=(template.format("xhdr"), 1),
                   body_data=(template.format("objbox"), 0)),
        "5.5":
        Table('//table'),
    })

    for page in paginator.pages():
        if packages.find_row('Name', package_name):
            return
    pytest.fail("Package {0} was not found".format(package_name))
    def get_vms(self):
        """ Returns names of VMs (from quadicons) that use this datastore

        Returns: List of strings with names or `[]` if no vms found.
        """
        self.load_details()
        try:
            list_acc.select('Relationships',
                            "VMs",
                            by_title=False,
                            partial=True)
        except (sel.NoSuchElementException, ListAccordionLinkNotFound):
            sel.click(InfoBlock('Relationships', 'Managed VMs'))
        return [q.name for q in Quadicon.all("vm")]
Exemple #11
0
    def equal_drift_results(self, row_text, section, *indexes):
        """ Compares drift analysis results of a row specified by it's title text

        Args:
            row_text: Title text of the row to compare
            section: Accordion section where the change happened; this section will be activated
            indexes: Indexes of results to compare starting with 0 for first row (latest result).
                     Compares all available drifts, if left empty (default).

        Note:
            There have to be at least 2 drift results available for this to work.

        Returns:
            ``True`` if equal, ``False`` otherwise.
        """
        # mark by indexes or mark all
        self.load_details(refresh=True)
        sel.click(InfoBlock("Properties", "Drift History"))
        if indexes:
            drift_table.select_rows_by_indexes(*indexes)
        else:
            # We can't compare more than 10 drift results at once
            # so when selecting all, we have to limit it to the latest 10
            if len(list(drift_table.rows())) > 10:
                drift_table.select_rows_by_indexes(*range(0, min(10, len)))
            else:
                drift_table.select_all()
        tb.select("Select up to 10 timestamps for Drift Analysis")

        # Make sure the section we need is active/open
        sec_apply_btn = "//div[@id='accordion']/a[contains(normalize-space(text()), 'Apply')]"

        # Deselect other sections
        for other_section in drift_section.child_items():
            drift_section.check_node(other_section.text)
            drift_section.uncheck_node(other_section.text)

        # Activate the required section
        drift_section.check_node(section)
        sel.click(sec_apply_btn)

        if not tb.is_active("All attributes"):
            tb.select("All attributes")
        drift_grid = DriftGrid()
        if any(
                drift_grid.cell_indicates_change(row_text, i)
                for i in range(0, len(indexes))):
            return False
        return True
def test_ssa_users(provider, instance, soft_assert):
    """ Tests SSA fetches correct results for users list

    Metadata:
        test_flag: vm_analysis
    """
    username = fauxfactory.gen_alphanumeric()
    expected = None

    # In windows case we can't add new users (yet)
    # So we simply check that user list doesn't cause any Rails errors
    if instance.system_type != WINDOWS:
        # Add a new user
        instance.ssh.run_command("userdel {0} || useradd {0}".format(username))
        expected = instance.ssh.run_command(
            "cat /etc/passwd | wc -l").output.strip('\n')

    instance.smartstate_scan()
    wait_for(lambda: is_vm_analysis_finished(instance.name),
             delay=15,
             timeout="10m",
             fail_func=lambda: tb.select('Reload'))

    # Check that all data has been fetched
    current = instance.get_detail(properties=('Security', 'Users'))
    if instance.system_type != WINDOWS:
        assert current == expected

    # Make sure created user is in the list
    sel.click(InfoBlock("Security", "Users"))
    template = '//div[@id="list_grid"]/div[@class="{}"]/table/tbody'
    users = version.pick({
        version.LOWEST:
        SplitTable(header_data=(template.format("xhdr"), 1),
                   body_data=(template.format("objbox"), 0)),
        "5.5":
        Table('//table'),
    })

    for page in paginator.pages():
        sel.wait_for_element(users)
        if users.find_row('Name', username):
            return
    if instance.system_type != WINDOWS:
        pytest.fail("User {0} was not found".format(username))
def test_ssa_groups(provider, instance, soft_assert):
    """ Tests SSA fetches correct results for groups

    Metadata:
        test_flag: vm_analysis
    """
    group = fauxfactory.gen_alphanumeric()
    expected = None

    if instance.system_type != WINDOWS:
        # Add a new group
        instance.ssh.run_command("groupdel {0} || groupadd {0}".format(group))
        expected = instance.ssh.run_command(
            "cat /etc/group | wc -l").output.strip('\n')

    instance.smartstate_scan()
    wait_for(lambda: is_vm_analysis_finished(instance.name),
             delay=15,
             timeout="10m",
             fail_func=lambda: tb.select('Reload'))

    # Check that all data has been fetched
    current = instance.get_detail(properties=('Security', 'Groups'))
    if instance.system_type != WINDOWS:
        assert current == expected

    # Make sure created group is in the list
    sel.click(InfoBlock("Security", "Groups"))
    template = '//div[@id="list_grid"]/div[@class="{}"]/table/tbody'
    groups = version.pick({
        version.LOWEST:
        SplitTable(header_data=(template.format("xhdr"), 1),
                   body_data=(template.format("objbox"), 0)),
        "5.5":
        Table('//table'),
    })

    for page in paginator.pages():
        sel.wait_for_element(groups)
        if groups.find_row('Name', group):
            return
    if instance.system_type != WINDOWS:
        pytest.fail("Group {0} was not found".format(group))
Exemple #14
0
def test_server_name():
    """Tests that changing the server name updates the about page"""
    form_infoblocks = InfoBlock('form')
    flash_msg = 'Configuration settings saved for CFME Server "{}'

    sel.force_navigate('cfg_settings_currentserver_server')
    old_server_name = sel.value(
        BasicInformation.basic_information.appliance_name)
    new_server_name = old_server_name + "-CFME"
    settings_pg = BasicInformation(appliance_name=new_server_name)
    settings_pg.update()
    flash.assert_message_contain(flash_msg.format(new_server_name))

    sel.force_navigate('about')
    assert new_server_name == form_infoblocks.text('Session Information', 'Server Name'),\
        "Server name in About section does not match the new name"

    settings_pg = BasicInformation(appliance_name=old_server_name)
    settings_pg.update()
    flash.assert_message_contain(flash_msg.format(old_server_name))
Exemple #15
0
                    'containers_provider_edit':
                    lambda _: cfg_btn('Edit Selected Containers Provider'),
                    'containers_provider_edit_tags':
                    lambda _: pol_btn('Edit Tags')
                }
        ],
        'containers_provider_detail': [
            lambda ctx: sel.click(Quadicon(ctx['provider'].name, None)), {
                'containers_provider_edit_detail':
                lambda _: cfg_btn('Edit this Containers Provider'),
                'containers_provider_timelines_detail':
                lambda _: mon_btn('Timelines'),
                'containers_provider_edit_tags_detail':
                lambda _: pol_btn('Edit Tags'),
                'containers_provider_topology_detail':
                lambda _: sel.click(InfoBlock('Overview', 'Topology'))
            }
        ]
    })

properties_form = Form(fields=[(
    'type_select',
    AngularSelect('server_emstype')), (
        'name_text',
        Input('name')), ('hostname_text',
                         Input('hostname')), ('port_text', Input('port'))])

properties_form_56 = TabStripForm(
    fields=[('type_select', AngularSelect('ems_type')),
            ('name_text', Input('name'))],
    tab_fields={
def test_rh_registration(appliance, request, reg_method, reg_data, proxy_url,
                         proxy_creds):
    """ Tests whether an appliance can be registered againt RHSM and SAT6 """
    repo = reg_data.get('enable_repo')
    if not repo:
        set_default_repo = True
    else:
        set_default_repo = False

    if proxy_url:
        use_proxy = True
        proxy_username = proxy_creds['username']
        proxy_password = proxy_creds['password']
    else:
        use_proxy = False
        proxy_url = None
        proxy_username = None
        proxy_password = None

    red_hat_updates.update_registration(
        service=reg_method,
        url=reg_data['url'],
        username=conf.credentials[reg_method]['username'],
        password=conf.credentials[reg_method]['password'],
        repo_name=repo,
        organization=reg_data.get('organization'),
        use_proxy=use_proxy,
        proxy_url=proxy_url,
        proxy_username=proxy_username,
        proxy_password=proxy_password,
        set_default_repository=set_default_repo,
        # Satellite 6 registration requires validation to be able to fetch organization
        validate=False if reg_method != 'sat6' else True)

    used_repo_or_channel = InfoBlock('Red Hat Software Updates',
                                     'Repository Name(s)').text

    red_hat_updates.register_appliances()  # Register all

    request.addfinalizer(appliance.unregister)

    wait_for(func=red_hat_updates.is_registering,
             func_args=[appliance.server.name],
             delay=10,
             num_sec=100,
             fail_func=red_hat_updates.refresh)
    '''if/else added to overcome bz #1463588 these can be removed once fixed'''

    if reg_method == 'rhsm':
        wait_for(func=red_hat_updates.is_registered,
                 handle_exception=True,
                 func_args=[appliance.server.name],
                 delay=40,
                 num_sec=400,
                 fail_func=red_hat_updates.refresh)
    else:
        # First registration with sat6 fails; we need to click it after this failure
        wait_for(func=red_hat_updates.is_registered,
                 func_args=[appliance.server.name],
                 delay=50,
                 num_sec=1200,
                 fail_func=red_hat_updates.register_appliances)

    wait_for(func=appliance.is_registration_complete,
             func_args=[used_repo_or_channel],
             delay=20,
             num_sec=400)
Exemple #17
0
 def step(self):
     sel.click(InfoBlock('Overview', 'Topology'))
Exemple #18
0
from selenium.common.exceptions import NoSuchElementException
import utils.conf as conf
from utils.datafile import load_data_file
from utils.log import logger
from utils.path import project_path
from utils.update import Updateable
from utils.wait import wait_for
from utils.pretty import Pretty
from utils.db import cfmedb
from utils.varmeth import variable

cfg_btn = partial(tb.select, 'Configuration')

pxe_server_table_exist = Table('//div[@id="records_div"]/table/tbody/tr/td')
pxe_details_page = Region(locators=dict(
    last_refreshed=InfoBlock("Basic Information", "Last Refreshed On"),
    pxe_image_type=InfoBlock("Basic Information", "Type")))

pxe_properties_form = Form(fields=[
    ('name_text', Input('name')),
    ('log_protocol', Select("//select[@id='log_protocol']")),
    ('uri_text', Input('uri')),
    ('userid_text', Input('log_userid')),
    ('password_text', Input('log_password')),
    ('verify_text', Input('log_verify')),
    ('validate_btn', "//a[@id='val']"),
    ('access_url_text', Input('access_url')),
    ('pxe_dir_text', Input('pxe_directory')),
    ('windows_dir_text', Input('windows_images_directory')),
    ('customize_dir_text', Input('customization_directory')),
    ('pxe_menu_text', Input('pxemenu_0')),
Exemple #19
0
 def step(self):
     navigate_to(self.obj.provider, 'Details')
     sel.click(InfoBlock('Relationships', 'Cloud volumes').element)
def get_tag():
    return InfoBlock('Smart Management', 'My Company Tags').text
Exemple #21
0
    def equal_drift_results(self, row_text, section, *indexes):
        """ Compares drift analysis results of a row specified by it's title text

        Args:
            row_text: Title text of the row to compare
            section: Accordion section where the change happened; this section will be activated
            indexes: Indexes of results to compare starting with 0 for first row (latest result).
                     Compares all available drifts, if left empty (default).

        Note:
            There have to be at least 2 drift results available for this to work.

        Returns:
            ``True`` if equal, ``False`` otherwise.
        """
        # mark by indexes or mark all
        self.load_details(refresh=True)
        sel.click(InfoBlock("Properties", "Drift History"))
        if indexes:
            drift_table.select_rows_by_indexes(*indexes)
        else:
            # We can't compare more than 10 drift results at once
            # so when selecting all, we have to limit it to the latest 10
            if len(list(drift_table.rows())) > 10:
                drift_table.select_rows_by_indexes(*range(0, min(10, len)))
            else:
                drift_table.select_all()
        tb.select("Select up to 10 timestamps for Drift Analysis")

        # Make sure the section we need is active/open
        sec_loc_map = {
            'Properties': 'Properties',
            'Security': 'Security',
            'Configuration': 'Configuration',
            'My Company Tags': 'Categories'}
        sec_loc_template = "//div[@id='all_sections_treebox']//li[contains(@id, 'group_{}')]" \
                           "//span[contains(@class, 'dynatree-checkbox')]"
        sec_checkbox_loc = "//div[@id='all_sections_treebox']//li[contains(@id, 'group_{}')]" \
            "//span[contains(@class, 'dynatree-checkbox')]".format(sec_loc_map[section])
        sec_apply_btn = "//div[@id='accordion']/a[contains(normalize-space(text()), 'Apply')]"

        # Deselect other sections
        for other_section in sec_loc_map.keys():
            other_section_loc = sec_loc_template.format(sec_loc_map[other_section])
            other_section_classes = sel.get_attribute(other_section_loc + '/..', "class")
            if other_section != section and 'dynatree-partsel' in other_section_classes:
                # Element needs to be checked out if it has no dynatree-selected
                if 'dynatree-selected' not in other_section_classes:
                    sel.click(other_section_loc)
                sel.click(other_section_loc)

        # Activate the required section
        sel.click(sec_checkbox_loc)
        sel.click(sec_apply_btn)

        if not tb.is_active("All attributes"):
            tb.select("All attributes")
        drift_grid = DriftGrid()
        if any(drift_grid.cell_indicates_change(row_text, i) for i in range(0, len(indexes))):
            return False
        return True
Exemple #22
0
database_tree = partial(accordion.tree, "Database")
settings_tree = partial(accordion.tree, "Settings")
diagnostics_tree = partial(accordion.tree, "Diagnostics")

replication_worker = Form(fields=[
    ('database', Input("replication_worker_dbname")),
    ('port', Input("replication_worker_port")),
    ('username', Input("replication_worker_username")),
    ('password', Input("replication_worker_password")),
    ('password_verify', Input("replication_worker_verify")),
    ('host', Input("replication_worker_host")),
])

replication_process = UIRegion(
    locators={
        "status": InfoBlock("Replication Process", "Status"),
        "current_backlog": InfoBlock("Replication Process", "Current Backlog"),
    })

server_roles = Form(fields=[
    # TODO embedded_ansible is only present in CFME 5.8 (MIQ Fine+)
    ('embedded_ansible', CFMECheckbox("server_roles_embedded_ansible")),
    ('ems_metrics_coordinator',
     CFMECheckbox("server_roles_ems_metrics_coordinator")),
    ('ems_operations', CFMECheckbox("server_roles_ems_operations")),
    ('ems_metrics_collector',
     CFMECheckbox("server_roles_ems_metrics_collector")),
    ('reporting', CFMECheckbox("server_roles_reporting")),
    ('ems_metrics_processor',
     CFMECheckbox("server_roles_ems_metrics_processor")),
    ('scheduler', CFMECheckbox("server_roles_scheduler")),
Exemple #23
0
def test_run_datastore_analysis(request, setup_provider, provider,
                                datastore_type, datastore_name, soft_assert):
    """Tests smarthost analysis

    Metadata:
        test_flag: datastore_analysis
    """
    test_datastore = datastore.Datastore(datastore_name, provider.key)

    # Check if there is a host with valid credentials
    host_names = test_datastore.get_hosts()
    assert len(host_names) != 0, "No hosts attached to this datastore found"
    for host_name in host_names:
        host_qi = Quadicon(host_name, 'host')
        if host_qi.creds == 'checkmark':
            break
    else:
        # If not, get credentials for one of the present hosts
        found_host = False
        for host_name in host_names:
            host_data = get_host_data_by_name(provider.key, host_name)
            if host_data is None:
                continue

            found_host = True
            test_host = host.Host(name=host_name)

            # Add them to the host
            wait_for(lambda: test_host.exists,
                     delay=10,
                     num_sec=120,
                     fail_func=sel.refresh)
            if not test_host.has_valid_credentials:
                test_host.update(
                    updates={
                        'credentials':
                        host.get_credentials_from_config(
                            host_data['credentials'])
                    })
                wait_for(lambda: test_host.has_valid_credentials,
                         delay=10,
                         num_sec=120,
                         fail_func=sel.refresh)

                # And remove them again when the test is finished
                def test_host_remove_creds():
                    test_host.update(
                        updates={
                            'credentials':
                            host.Host.Credential(
                                principal="", secret="", verify_secret="")
                        })

                request.addfinalizer(test_host_remove_creds)
            break

        assert found_host,\
            "No credentials found for any of the hosts attached to datastore {}"\
            .format(datastore_name)

    # TODO add support for events
    # register_event(
    #     None,
    #     "datastore",
    #     datastore_name,
    #     ["datastore_analysis_request_req", "datastore_analysis_complete_req"]
    # )

    # Initiate analysis
    sel.force_navigate('infrastructure_datastore',
                       context={
                           'datastore': test_datastore,
                           'provider': test_datastore.provider
                       })
    tb.select('Configuration',
              'Perform SmartState Analysis',
              invokes_alert=True)
    sel.handle_alert()
    flash.assert_message_contain(
        '"{}": scan successfully initiated'.format(datastore_name))

    # Wait for the task to finish
    def is_datastore_analysis_finished():
        """ Check if analysis is finished - if not, reload page
        """
        if not sel.is_displayed(tasks.tasks_table) or not tabs.is_tab_selected(
                'All Other Tasks'):
            sel.force_navigate('tasks_all_other')
        host_analysis_finished = tasks.tasks_table.find_row_by_cells({
            'task_name':
            "SmartState Analysis for [{}]".format(datastore_name),
            'state':
            'Finished'
        })
        return host_analysis_finished is not None

    wait_for(is_datastore_analysis_finished,
             delay=10,
             num_sec=300,
             fail_func=lambda: tb.select('Reload'))

    # Delete the task
    tasks.tasks_table.select_row_by_cells({
        'task_name':
        "SmartState Analysis for [{}]".format(datastore_name),
        'state':
        'Finished'
    })
    tb.select('Delete Tasks', 'Delete', invokes_alert=True)
    sel.handle_alert()

    c_datastore = test_datastore.get_detail('Properties', 'Datastore Type')
    # Check results of the analysis and the datastore type
    soft_assert(
        c_datastore == datastore_type.upper(),
        'Datastore type does not match the type defined in yaml:' +
        'expected "{}" but was "{}"'.format(datastore_type.upper(),
                                            c_datastore))
    for row_name in CONTENT_ROWS_TO_CHECK:
        value = InfoBlock('Content', row_name).text
        soft_assert(value != '0',
                    'Expected value for {} to be non-empty'.format(row_name))
def test_db_backup_schedule(request, db_backup_data):
    """ Test scheduled one-type backup on given machines using smb/nfs
    """

    # ---- Create new db backup schedule set to run in the next 6 min
    dt = get_schedulable_datetime()
    # the dash is there to make strftime not use a leading zero
    hour = dt.strftime('%-H')
    minute = dt.strftime('%-M')

    sched_args = {
        'name': db_backup_data.schedule_name,
        'description': db_backup_data.schedule_description,
        'active': True,
        'run_type': "Once",
        'run_every': None,
        'time_zone': "UTC",
        'start_date': dt,
        'start_hour': hour,
        'start_min': minute
    }

    if db_backup_data.protocol_type == 'smb':
        sched_args.update({
            'protocol':
            'Samba',
            'uri':
            db_backup_data.hostname,
            'username':
            db_backup_data.credentials['username'],
            'password':
            db_backup_data.credentials['password'],
            'password_verify':
            db_backup_data.credentials['password']
        })
    else:
        sched_args.update({
            'protocol': 'Network File System',
            'uri': db_backup_data.hostname,
        })

    if db_backup_data.protocol_type == 'nfs':
        path_on_host = urlparse('nfs://' + db_backup_data.hostname).path
    else:
        path_on_host = db_backup_data.path_on_host
    full_path = get_full_path_to_file(path_on_host,
                                      db_backup_data.schedule_name)

    sched = DatabaseBackupSchedule(**sched_args)
    # Fails on upstream - BZ1099341
    sched.create()
    flash.assert_message_contain('Schedule "{}" was saved'.format(
        db_backup_data.schedule_name))

    # ----

    # ---- Add cleanup finalizer
    def delete_sched_and_files():
        with get_ssh_client(db_backup_data.hostname,
                            db_backup_data.credentials) as ssh:
            ssh.run_command('rm -rf {}'.format(full_path))
        sched.delete()
        flash.assert_message_contain('Schedule "{}": Delete successful'.format(
            db_backup_data.schedule_description))

    request.addfinalizer(delete_sched_and_files)
    # ----

    # ---- Wait for schedule to run
    sel.force_navigate('cfg_settings_schedule',
                       context={'schedule_name': db_backup_data.schedule_name})
    form_infoblocks = InfoBlock('form')
    wait_for(
        lambda: form_infoblocks.text('Schedule Info', 'Last Run Time') != '',
        num_sec=600,
        delay=30,
        fail_func=sel.refresh,
        message='Schedule failed to run in 10mins from being set up')
    # ----

    # ---- Check if the db backup file exists
    with get_ssh_client(db_backup_data.hostname,
                        db_backup_data.credentials) as ssh:

        assert ssh.run_command('cd "{}"'.format(path_on_host))[0] == 0,\
            "Could not cd into '{}' over ssh".format(path_on_host)
        # Find files no more than 5 minutes old, count them and remove newline
        file_check_cmd = "find {}/* -cmin -5 | wc -l | tr -d '\n' ".format(
            full_path)

        wait_for(lambda: ssh.run_command(file_check_cmd)[1] == '1',
                 delay=5,
                 num_sec=60,
                 message="File '{}' not found on share".format(full_path))
Exemple #25
0
def test_run_datastore_analysis(request, setup_provider, provider, datastore,
                                soft_assert):
    """Tests smarthost analysis

    Metadata:
        test_flag: datastore_analysis
    """

    # Check if there is a host with valid credentials
    host_names = datastore.get_hosts()
    assert len(host_names) != 0, "No hosts attached to this datastore found"
    for host_name in host_names:
        host_qi = Quadicon(host_name, 'host')
        if 'checkmark' in host_qi.creds:
            break
    else:
        # If not, get credentials for one of the present hosts
        found_host = False
        for host_name in host_names:
            host_data = get_host_data_by_name(provider.key, host_name)
            if host_data is None:
                continue

            found_host = True
            test_host = host.Host(name=host_name)

            # Add them to the host
            wait_for(lambda: test_host.exists,
                     delay=10,
                     num_sec=120,
                     fail_func=sel.refresh)
            if not test_host.has_valid_credentials:
                test_host.update(
                    updates={
                        'credentials':
                        host.get_credentials_from_config(
                            host_data['credentials'])
                    })
                wait_for(lambda: test_host.has_valid_credentials,
                         delay=10,
                         num_sec=120,
                         fail_func=sel.refresh)

                # And remove them again when the test is finished
                def test_host_remove_creds():
                    test_host.update(
                        updates={
                            'credentials':
                            host.Host.Credential(
                                principal="", secret="", verify_secret="")
                        })

                request.addfinalizer(test_host_remove_creds)
            break

        assert found_host,\
            "No credentials found for any of the hosts attached to datastore {}"\
            .format(datastore.name)

    # TODO add support for events
    # register_event(
    #     None,
    #     "datastore",
    #     datastore_name,
    #     ["datastore_analysis_request_req", "datastore_analysis_complete_req"]
    # )

    # Initiate analysis
    datastore.run_smartstate_analysis()
    wait_for(lambda: is_datastore_analysis_finished(datastore.name),
             delay=15,
             timeout="10m",
             fail_func=lambda: toolbar.select('Reload'))

    c_datastore = datastore.get_detail('Properties', 'Datastore Type')
    # Check results of the analysis and the datastore type
    soft_assert(
        c_datastore == datastore.type.upper(),
        'Datastore type does not match the type defined in yaml:' +
        'expected "{}" but was "{}"'.format(datastore.type.upper(),
                                            c_datastore))
    for row_name in CONTENT_ROWS_TO_CHECK:
        value = InfoBlock('Content', row_name).text
        soft_assert(value != '0',
                    'Expected value for {} to be non-empty'.format(row_name))
Exemple #26
0
 def open_details(self, properties=None):
     """Clicks on details infoblock"""
     self.load_details(refresh=True)
     sel.click(InfoBlock(*properties))
Exemple #27
0
 def _wait_func():
     self.navigate()
     return InfoBlock("Properties",
                      "Last Update Status").text.strip().lower() == "ok"
class Schedule(Updateable):
    """Represents a schedule in Intelligence/Reports/Schedules.

    Args:
        name: Schedule name.
        description: Schedule description.
        filter: 3-tuple with filter selection (see the UI).
        active: Whether is this schedule active.
        run: Specifies how often this schedule runs. It can be either string "Once", or a tuple,
            which maps to the two selects in UI ("Hourly", "Every hour")...
        time_zone: Specify time zone.
        start_date: Specify the start date.
        start_time: Specify the start time either as a string ("0:15") or tuple ("0", "15")
        send_email: If specifies, turns on e-mail sending. Can be string, or list or set.
    """
    form = Form(fields=[
        ("name", "//input[@id='name']"),
        ("description", "//input[@id='description']"),
        ("active", "//input[@id='enabled']"),
        ("filter", ShowingInputs(
            Select("//select[@id='filter_typ']"),
            Select("//select[@id='subfilter_typ']"),
            Select("//select[@id='repfilter_typ']"),
            min_values=3
        )),
        ("timer", Timer()),
        ("send_email", "//input[@id='send_email_cb']"),
        ("emails", EmailSelectForm())
    ])

    _run_mapping = {
        "Once": None,
        "Hourly": "run_hours",
        "Daily": "run_days",
        "Weekly": "run_weekly",
        "Monthly": "run_months"
    }

    info_block = InfoBlock("detail")

    def __init__(
            self,
            name,
            description,
            filter,
            active=None,
            timer=None,
            send_email=None):
        self.name = name
        self.description = description
        self.filter = filter
        self.active = active
        self.timer = timer
        self.send_email = send_email

    @property
    def exists(self):
        schedules = cfmedb["miq_schedules"]
        return cfmedb.session\
            .query(schedules.name)\
            .filter(schedules.name == self.name)\
            .count() > 0

    def _fill(self, action):
        fill(
            self.form,
            self._create_fill_dict(),
            action=action
        )

    def create(self, cancel=False):
        sel.force_navigate("schedule_add")
        self._fill(form_buttons.add if not cancel else form_buttons.cancel)
        flash.assert_no_errors()
        assert self.exists, "Schedule does not exist!"

    def update(self, updates):
        sel.force_navigate("schedule_edit", context={"schedule": self})
        fill(self.form, updates, action=form_buttons.save)
        flash.assert_no_errors()
        assert self.exists, "Schedule does not exist!"

    def delete(self, cancel=False):
        sel.force_navigate("schedule", context={"schedule": self})
        cfg_btn("Delete this Schedule from the Database", invokes_alert=True)
        sel.handle_alert(cancel)
        flash.assert_no_errors()
        assert not self.exists, "Schedule does not exist!"

    def table_item(self, item):
        """Works both up- and downstream.

        I think this should be incorporated into InfoBlock somehow. Currently there is the fieldset
        issue.
        """
        return "//td[preceding-sibling::td[contains(@class, 'key') and .='{}']]".format(item)

    def queue(self, wait_for_finish=False):
        """Queue this schedule.

        Args:
            wait_for_finish: If True, then this function blocks until the action is finished.
        """
        if not self.exists:
            self.create()
        sel.force_navigate("schedule", context={"schedule": self})
        last_run = sel.text(self.table_item("Last Run Time")).strip()
        cfg_btn("Queue up this Schedule to run now")
        flash.assert_no_errors()
        if wait_for_finish:
            wait_for(
                lambda: sel.text(self.table_item("Last Run Time")).strip() != last_run,
                delay=2,
                fail_func=lambda: toolbar.select("Reload current display"),
                message="wait for report queue finish"
            )

    def _create_fill_dict(self):
        """Handle the values, create dictionary for form"""
        # Simple values come
        fields = {
            "name": self.name,
            "description": self.description,
            "active": self.active,
            "filter": self.filter,
            "timer": self.timer,
        }

        # Send e-mail
        if self.send_email is not None:
            fields["send_email"] = True
            fields["emails"] = self.send_email

        return fields

    # Methods for all schedules
    @classmethod
    def _select_schedules(cls, schedules):
        """Select schedules in the table.

        Args:
            schedules: Schedules to select.
        Raises: :py:class:`NameError` when some of the schedules were not found.
        """
        sel.force_navigate("schedules")
        failed_selections = []
        for schedule in schedules:
            if isinstance(schedule, cls):
                name = schedule.name
            else:
                name = str(schedule)
            if not schedules_table.select_row("Name", name):
                failed_selections.append(name)
        if failed_selections:
            raise NameError("These schedules were not found: {}.".format(
                ", ".join(failed_selections)
            ))

    @classmethod
    def _action_on_schedules(cls, action, schedules, cancel=None):
        """Select schedules and perform an action on them

        Args:
            action: Action in Configuration to perform.
            schedules: List of schedules.
            cancel: If specified, the nalert is expected after clicking on action and value of the
                variable specifies handling behaviour.
        Raises: :py:class:`NameError` when some of the schedules were not found.
        """
        cls._select_schedules(schedules)
        if cancel is None:
            cfg_btn(action)
        else:
            cfg_btn(action, invokes_alert=True)
            sel.handle_alert(bool(cancel))
        flash.assert_no_errors()

    @classmethod
    def enable_schedules(cls, *schedules):
        """Select and enable specified schedules.

        Args:
            *schedules: Schedules to enable. Can be objects or strings.
        Raises: :py:class:`NameError` when some of the schedules were not found.
        """
        return cls._action_on_schedules("Enable the selected Schedules", schedules)

    @classmethod
    def disable_schedules(cls, *schedules):
        """Select and disable specified schedules.

        Args:
            *schedules: Schedules to disable. Can be objects or strings.
        Raises: :py:class:`NameError` when some of the schedules were not found.
        """
        return cls._action_on_schedules("Disable the selected Schedules", schedules)

    @classmethod
    def queue_schedules(cls, *schedules):
        """Select and queue specified schedules.

        Args:
            *schedules: Schedules to queue. Can be objects or strings.
        Raises: :py:class:`NameError` when some of the schedules were not found.
        """
        return cls._action_on_schedules("Queue up selected Schedules to run now", schedules)

    @classmethod
    def delete_schedules(cls, *schedules, **kwargs):
        """Select and delete specified schedules from VMDB.

        Args:
            *schedules: Schedules to delete. Can be objects or strings.
        Keywords:
            cancel: Whether to cancel the deletion (Default: False)
        Raises: :py:class:`NameError` when some of the schedules were not found.
        """
        return cls._action_on_schedules(
            "Delete the selected Schedules from the VMDB", schedules, kwargs.get("cancel", False)
        )
class Widget(Updateable, Pretty, Navigatable):
    TITLE = None
    DETAIL_PAGE = None
    WAIT_STATES = {"Queued", "Running"}
    status_info = InfoBlock("Status")

    def __init__(self,
                 title,
                 description=None,
                 active=None,
                 shortcuts=None,
                 visibility=None,
                 appliance=None):
        Navigatable.__init__(self, appliance)
        self.title = title
        self.description = description
        self.active = active
        self.shortcuts = shortcuts
        self.visibility = visibility

    def generate(self, wait=True, **kwargs):
        navigate_to(self, 'Details')
        toolbar.select("Configuration",
                       "Generate Widget content now",
                       invokes_alert=True)
        sel.handle_alert()
        flash.assert_message_match(
            "Content generation for this Widget has been initiated")
        flash.assert_no_errors()
        if wait:
            self.wait_generated(**kwargs)

    def wait_generated(self, timeout=600):
        wait_for(self.check_status,
                 num_sec=timeout,
                 delay=5,
                 fail_condition=lambda result: result != "Complete",
                 fail_func=toolbar.refresh)

    def check_status(self):
        navigate_to(self, 'Details')
        return self.status_info("Current Status").text

    @classmethod
    def detect(cls, t, *args, **kwargs):
        # Can't be in class because it does not see child classes
        MAPPING = {
            "rss_widget": RSSFeedWidget,
            "rssbox": RSSFeedWidget,
            "chart_widget": ChartWidget,
            "chartbox": ChartWidget,
            "report_widget": ReportWidget,
            "reportbox": ReportWidget,
            "menu_widget": MenuWidget,
            # TODO: menubox?
        }
        if t not in MAPPING:
            raise ValueError(t)
        return MAPPING[t](*args, **kwargs)

    def create(self, cancel=False):
        navigate_to(self, 'Add')
        fill(self.form,
             self.__dict__,
             action=form_buttons.cancel if cancel else form_buttons.add)
        flash.assert_no_errors()

    def update(self, updates):
        navigate_to(self, 'Edit')
        fill(self.form, updates, action=form_buttons.save)
        flash.assert_no_errors()

    def delete(self, cancel=False):
        navigate_to(self, 'Details')
        toolbar.select("Configuration",
                       "Delete this Widget from the Database",
                       invokes_alert=True)
        sel.handle_alert(cancel)
        flash.assert_no_errors()