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)
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 )
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")]
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])
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
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")]
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))
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))
'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)
def step(self): sel.click(InfoBlock('Overview', 'Topology'))
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')),
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
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
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")),
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))
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))
def open_details(self, properties=None): """Clicks on details infoblock""" self.load_details(refresh=True) sel.click(InfoBlock(*properties))
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()