class DefaultDashboard(Updateable, Pretty): form = Form(fields=[ ("title", Input("description")), ("locked", Input("locked")), ("widgets", DashboardWidgetSelector("//div[@id='form_widgets_div']")), ]) pretty_attrs = ['title', 'widgets'] def __init__(self, title=None, locked=None, widgets=None): self.locked = locked self.widgets = widgets def update(self, updates): sel.force_navigate("reports_default_dashboard_edit") fill(self.form, updates, action=form_buttons.save) flash.assert_no_errors() def delete(self, cancel=False): sel.force_navigate("reports_default_dashboard") toolbar.select("Configuration", "Delete this Dashboard from the Database", invokes_alert=True) sel.handle_alert(cancel) flash.assert_no_errors()
class Role(Updateable, Pretty): form = Form( fields=[ ('name_txt', Input('name')), ('vm_restriction_select', { version.LOWEST: Select("//*[@id='vm_restriction']"), '5.5': AngularSelect('vm_restriction')}), ('product_features_tree', CheckboxTree("//div[@id='features_treebox']/ul")), ]) pretty_attrs = ['name', 'product_features'] def __init__(self, name=None, vm_restriction=None, product_features=None): self.name = name self.vm_restriction = vm_restriction self.product_features = product_features or [] def create(self): sel.force_navigate('cfg_accesscontrol_role_add') fill(self.form, {'name_txt': self.name, 'vm_restriction_select': self.vm_restriction, 'product_features_tree': self.product_features}, action=form_buttons.add) flash.assert_success_message('Role "{}" was saved'.format(self.name)) def update(self, updates): sel.force_navigate("cfg_accesscontrol_role_edit", context={"role": self}) fill(self.form, {'name_txt': updates.get('name'), 'vm_restriction_select': updates.get('vm_restriction'), 'product_features_tree': updates.get('product_features')}, action=form_buttons.save) flash.assert_success_message('Role "{}" was saved'.format(updates.get('name', self.name))) def delete(self): sel.force_navigate("cfg_accesscontrol_role_ed", context={"role": self}) tb_select('Delete this Role', invokes_alert=True) sel.handle_alert() flash.assert_success_message('Role "{}": Delete successful'.format(self.name)) def copy(self, name=None): if not name: name = self.name + "copy" sel.force_navigate("cfg_accesscontrol_role_ed", context={"role": self}) tb.select('Configuration', 'Copy this Role to a new Role') new_role = Role(name=name) fill(self.form, {'name_txt': new_role.name}, action=form_buttons.add) flash.assert_success_message('Role "{}" was saved'.format(new_role.name)) return new_role
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')), ]) pxe_image_type_form = Form(fields=[('image_type', Select("//select[@id='image_typ']"))])
# -*- coding: utf-8 -*- """This module operates the `Advanced search` box located on multiple pages.""" import re from functools import partial from cfme.fixtures import pytest_selenium as sel from cfme.web_ui import expression_editor as exp_ed from cfme.web_ui import Input, Region, Select, fill from cfme.web_ui.form_buttons import FormButton from utils.version import current_version from utils.wait import wait_for search_box = Region(locators=dict( # Filter of results, the search field that is normally visible search_field=Input("search_text", "search[text]"), # The icon buttons for searching search_icon={ "5.3": "//div[@id='searchbox']//*[@id='searchicon']", "5.4": "//div[@id='searchbox']//div[contains(@class, 'form-group')]" "/*[self::a or (self::button and @type='submit')]" }, # The arrow opening/closing the advanced search box toggle_advanced={ "5.3": "//img[@id='adv_search_img']", "5.4": "(//button | //a)[@id='adv_search']" },
def form(self): fields = [ ('token_secret_55', Input('bearer_token')), ('google_service_account', Input('service_account')), ] tab_fields = { ("Default", ('default_when_no_tabs', )): [ ('default_principal', Input("default_userid")), ('default_secret', Input("default_password")), ('default_verify_secret', Input("default_verify")), ('token_secret', { version.LOWEST: Input('bearer_password'), '5.6': Input('default_password') }), ('token_verify_secret', { version.LOWEST: Input('bearer_verify'), '5.6': Input('default_verify') }), ], "RSA key pair": [ ('ssh_user', Input("ssh_keypair_userid")), ('ssh_key', FileInput("ssh_keypair_password")), ], "C & U Database": [ ('candu_principal', Input("metrics_userid")), ('candu_secret', Input("metrics_password")), ('candu_verify_secret', Input("metrics_verify")), ], "Hawkular": [ ('hawkular_validate_btn', form_buttons.validate), ] } fields_end = [ ('validate_btn', form_buttons.validate), ] if version.current_version() >= '5.6': amevent = "Events" else: amevent = "AMQP" tab_fields[amevent] = [] if version.current_version() >= "5.6": tab_fields[amevent].append( ('event_selection', Radio('event_stream_selection'))) tab_fields[amevent].extend([ ('amqp_principal', Input("amqp_userid")), ('amqp_secret', Input("amqp_password")), ('amqp_verify_secret', Input("amqp_verify")), ]) return TabStripForm(fields=fields, tab_fields=tab_fields, fields_end=fields_end)
import cfme.web_ui.flash as flash import cfme.web_ui.tabstrip as tabs import cfme.web_ui.toolbar as tb from cfme.web_ui import (accordion, Quadicon, Form, Input, fill, form_buttons, mixins, Table, Region, AngularSelect, match_location) from utils import version, conf from utils.appliance.implementations.ui import navigator, CFMENavigateStep, navigate_to from utils.appliance import Navigatable from utils.log import logger from utils.pretty import Pretty from utils.update import Updateable from utils.wait import wait_for properties_form = Form(fields=[( 'name_text', Input('name')), ('type_select', AngularSelect("provider_type")), ( 'url_text', Input('url')), ('ssl_checkbox', Input('verify_ssl'))]) credential_form = Form( fields=[('principal_text', Input('log_userid')), ('secret_pass', Input('log_password')), ('verify_secret_pass', Input('log_verify')), ('validate_btn', form_buttons.validate)]) def cfm_mgr_table(): return Table("//div[@id='main_div']//div[@id='list_grid']/table")
class Group(Updateable, Pretty): group_form = Form( fields=[('description_txt', Input('description')), ('role_select', { version.LOWEST: Select("//*[@id='group_role']"), "5.5": AngularSelect("group_role") }), ('group_tenant', AngularSelect("group_tenant"), { "appeared_in": "5.5" })]) pretty_attrs = ['description', 'role'] def __init__(self, description=None, role=None, tenant="My Company"): self.description = description self.role = role self.tenant = tenant def create(self): sel.force_navigate('cfg_accesscontrol_group_add') fill(self.group_form, { 'description_txt': self.description, 'role_select': self.role, 'group_tenant': self.tenant }, action=form_buttons.add) flash.assert_success_message('Group "{}" was saved'.format( self.description)) def update(self, updates): sel.force_navigate("cfg_accesscontrol_group_edit", context={"group": self}) fill(self.group_form, { 'description_txt': updates.get('description'), 'role_select': updates.get('role'), 'group_tenant': updates.get('tenant') }, action=form_buttons.save) flash.assert_success_message('Group "{}" was saved'.format( updates.get('description', self.description))) def delete(self): sel.force_navigate("cfg_accesscontrol_group_ed", context={"group": self}) tb_select('Delete this Group', invokes_alert=True) sel.handle_alert() flash.assert_success_message( 'EVM Group "{}": Delete successful'.format(self.description)) def edit_tags(self, tag, value): sel.force_navigate("cfg_accesscontrol_group_ed", context={"group": self}) pol_btn("Edit 'My Company' Tags for this Group", invokes_alert=True) fill(edit_tags_form, { 'select_tag': tag, 'select_value': value }, action=form_buttons.save) flash.assert_success_message('Tag edits were successfully saved') def remove_tag(self, tag, value): sel.force_navigate("cfg_accesscontrol_group_ed", context={"group": self}) pol_btn("Edit 'My Company' Tags for this Group", invokes_alert=True) row = tag_table.find_row_by_cells( { 'category': tag, 'assigned_value': value }, partial_check=True) sel.click(row[0]) form_buttons.save() flash.assert_success_message('Tag edits were successfully saved')
from utils.signals import fire from utils.wait import wait_for, RefreshTimer from utils.stats import tol_check from utils.update import Updateable from utils.varmeth import variable from . import PolicyProfileAssignable, Taggable cfg_btn = partial(tb.select, 'Configuration') manage_policies_tree = CheckboxTree("//div[@id='protect_treebox']/ul") details_page = Region(infoblock_type='detail') credential_form = TabStripForm( fields=[('token_secret', Input('bearer_token'))], tab_fields={ "Default": [ ('default_principal', Input("default_userid")), ('default_secret', Input("default_password")), ('default_verify_secret', Input("default_verify")), ], "AMQP": [ ('amqp_principal', Input("amqp_userid")), ('amqp_secret', Input("amqp_password")), ('amqp_verify_secret', Input("amqp_verify")), ], "RSA key pair": [ ('ssh_user', Input("ssh_keypair_userid")), ('ssh_key', Input("ssh_keypair_password")), ],
cfg_btn = partial(tb.select, "Configuration") accordion_tree = partial(accordion.tree, "Catalog Items") policy_btn = partial(tb.select, "Policy") dynamic_tree = Tree( "//div[@id='basic_info_div']//ul[@class='dynatree-container']") template_select_form = Form( fields=[('template_table', Table('//div[@id="prov_vm_div"]/table') ), ('add_button', form_buttons.add), ('cancel_button', form_buttons.cancel)]) # Forms basic_info_form = Form( fields=[('name_text', Input("name")), ( 'description_text', Input("description")), ('display_checkbox', Input("display")), ('select_catalog', Select("//select[@id='catalog_id']") ), ('select_dialog', Select("//select[@id='dialog_id']")), ('select_orch_template', Select("//select[@id='template_id']") ), ('select_provider', Select("//select[@id='manager_id']")), ('select_config_template', Select("//select[@id='template_id']") ), ('field_entry_point', Input("fqname")), ('edit_button', form_buttons.save), ('apply_btn', { version.LOWEST: '//a[@title="Apply"]', '5.5.0.6': '//a[normalize-space(.)="Apply"]' })]) edit_tags_form = Form(fields=[(
def provider_credential_form(): # todo: to remove it when all providers are moved to widgetastic fields = [ ('token_secret_55', Input('bearer_token')), ('google_service_account', Input('service_account')), ] tab_fields = { ("Default", ('default_when_no_tabs', )): [ ('default_principal', Input("default_userid")), ('default_secret', Input("default_password")), ('default_verify_secret', Input("default_verify")), ('token_secret', Input('default_password')), ('token_verify_secret', Input('default_verify')), ], "RSA key pair": [ ('ssh_user', Input("ssh_keypair_userid")), ('ssh_key', FileInput("ssh_keypair_password")), ], "C & U Database": [ ('candu_principal', Input("metrics_userid")), ('candu_secret', Input("metrics_password")), ('candu_verify_secret', Input("metrics_verify")), ], "Hawkular": [ ('hawkular_validate_btn', form_buttons.validate), ] } fields_end = [ ('validate_btn', form_buttons.validate), ] tab_fields["Events"] = [ ('event_selection', Radio('event_stream_selection')), ('amqp_principal', Input("amqp_userid")), ('amqp_secret', Input("amqp_password")), ('amqp_verify_secret', Input("amqp_verify"))] return TabStripForm(fields=fields, tab_fields=tab_fields, fields_end=fields_end)
t_msgr.properties).join(t_md, t_msgr.domain_id == t_md.id) if name: query = query.filter(t_msgr.name == name) if feed: query = query.filter(t_msgr.feed == feed) query = query.filter(t_md.name == domain.name) return query def _get_server_groups_page(domain): navigate_to(domain, 'DomainServerGroups') timeout_form = Form(fields=[( "timeout", Input("timeout", use_id=True)), ( 'suspend_button', FormButton("Suspend")), ( 'stop_button', FormButton("Stop")), ('cancel_button', FormButton("Cancel"))]) class MiddlewareServerGroup(MiddlewareBase, Taggable, Container, Navigatable): """ MiddlewareServerGroup class provides actions and details on Server Group page. Class method available to get existing server groups list Args: name: name of the server group domain: Domain (MiddlewareDomain) object to which belongs server group profile: Profile of the server group
from cfme import dashboard, Credential from cfme.configure.access_control import User from cfme.web_ui import Region, Form, fill, Input from utils import conf, version from utils.browser import ensure_browser_open, quit from utils.log import logger from fixtures.pytest_store import store page = Region( # TODO: Make title defer it's resolution title={ version.LOWEST: "Dashboard", '5.5': "Login" }, locators={ 'username': Input("user_name"), 'password': Input("user_password"), 'submit_button': '//a[@id="login"]|//button[normalize-space(.)="Login"]/..', # Login page has an abnormal flash div 'flash': '//div[@id="flash_div"]', 'logout': '//a[contains(@href, "/logout")]', 'update_password': '******', 'back': '//a[@title="Back"]', 'user_new_password': Input("user_new_password"), 'user_verify_password': Input("user_verify_password") }, identifying_loc='submit_button') _form_fields = ('username', 'password', 'user_new_password', 'user_verify_password')
def test_vmware_vimapi_hotadd_disk(request, testing_group, provider, testing_vm, domain, cls): """ Tests hot adding a disk to vmware vm. This test exercises the ``VMware_HotAdd_Disk`` method, located in ``/Integration/VMware/VimApi`` Steps: * It creates an instance in ``System/Request`` that can be accessible from eg. a button. * Then it creates a service dialog that contains a field with the desired disk size, the text field name should be ``size`` * Then it creates a button, that refers to the ``VMware_HotAdd_Disk`` in ``Request``. The button shall belong in the VM and instance button group. * After the button is created, it goes to a VM's summary page, clicks the button, enters the size of the disk and submits the dialog. * The test waits until the number of disks is raised. Metadata: test_flag: hotdisk, provision """ meth = Method(name='parse_dialog_value_{}'.format(fauxfactory.gen_alpha()), data=dedent('''\ # Transfers the dialog value to the root so the VMware method can use it. $evm.root['size'] = $evm.object['dialog_size'] exit MIQ_OK '''), cls=cls) @request.addfinalizer def _remove_method(): if meth.exists(): meth.delete() meth.create() # Instance that calls the method and is accessible from the button instance = Instance( name="VMware_HotAdd_Disk_{}".format(fauxfactory.gen_alpha()), values={ "meth4": { 'on_entry': meth.name }, # To preparse the value "rel5": "/Integration/VMware/VimApi/VMware_HotAdd_Disk", }, cls=cls) @request.addfinalizer def _remove_instance(): if instance.exists(): instance.delete() instance.create() # Dialog to put the disk capacity element_data = { 'ele_label': "Disk size", 'ele_name': "size", 'ele_desc': "Disk size", 'choose_type': "Text Box", 'default_text_box': "Default text" } dialog = ServiceDialog( label=fauxfactory.gen_alphanumeric(), description=fauxfactory.gen_alphanumeric(), submit=True, tab_label=fauxfactory.gen_alphanumeric(), tab_desc=fauxfactory.gen_alphanumeric(), box_label=fauxfactory.gen_alphanumeric(), box_desc=fauxfactory.gen_alphanumeric(), ) dialog.create(element_data) request.addfinalizer(dialog.delete) # Button that will invoke the dialog and action button_name = fauxfactory.gen_alphanumeric() button = Button(group=testing_group, text=button_name, hover=button_name, dialog=dialog, system="Request", request=instance.name) request.addfinalizer(button.delete_if_exists) button.create() def _get_disk_count(): return int( testing_vm.get_detail(properties=("Datastore Allocation Summary", "Number of Disks")).strip()) original_disk_count = _get_disk_count() logger.info('Initial disk count: {}'.format(original_disk_count)) toolbar.select(testing_group.text, button.text) fill(Input("size"), '1') pytest.sel.click(submit) flash.assert_no_errors() try: wait_for(lambda: original_disk_count + 1 == _get_disk_count(), num_sec=180, delay=5) finally: logger.info('End disk count: {}'.format(_get_disk_count()))
REQUEST_FINISHED_STATES = {'Migrated', 'Finished'} buttons = Region(locators=dict( approve="//*[@title='Approve this Request']", deny="//*[@title='Deny this Request']", copy="//*[@title='Copy original Request']", edit="//*[@title='Edit the original Request']", delete="//*[@title='Delete this Request']", submit="//span[@id='buttons_on']/a[@title='Submit']", cancel="//a[@title='Cancel']", )) request_table = Table('//*[@id="list_grid"]/table') fields = Region( locators=dict(reason=Input("reason"), request_list=request_table)) match_page = partial(match_location, controller='miq_request', title='Requests') # TODO Refactor these module methods and their callers for a proper request class class Request(Navigatable): def __init__(self, appliance=None): Navigatable.__init__(self, appliance=appliance) @navigator.register(Request, 'All') class RequestAll(CFMENavigateStep): prerequisite = NavigateToAttribute('appliance.server', 'LoggedIn')
class ProvisioningDialog(Updateable, Pretty): form = Form(fields=[ ("name", Input('name')), ("description", Input('description')), ("type", DialogTypeSelect("//select[@id='dialog_type']")), ("content", "//textarea[@id='content_data']"), ]) HOST_PROVISION = ("host_provision", "Host Provision") VM_MIGRATE = ("vm_migrate", "VM Migrate") VM_PROVISION = ("vm_provision", "VM Provision") ALLOWED_TYPES = {HOST_PROVISION, VM_MIGRATE, VM_PROVISION} pretty_attrs = ['name', 'description', 'content'] def __init__(self, type, name=None, description=None, content=None): self.name = name self.description = description self.type = type self.content = content def __str__(self): return self.name @property def type_nav(self): return self.type[0] @property def exists(self): try: sel.force_navigate("{}_dialog".format(self.type_nav), context={"dialog": self}) return True except CandidateNotFound: return False def create(self, cancel=False): sel.force_navigate("{}_dialogs".format(self.type_nav)) cfg_btn("Add a new Dialog") fill(self.form, self.__dict__, action=form_buttons.cancel if cancel else form_buttons.add) flash.assert_no_errors() def update(self, updates): sel.force_navigate("{}_dialog".format(self.type_nav), context={"dialog": self}) cfg_btn("Edit this Dialog") fill(self.form, updates, action=form_buttons.save) flash.assert_no_errors() def delete(self, cancel=False): sel.force_navigate("{}_dialog".format(self.type_nav), context={"dialog": self}) cfg_btn("Remove from the VMDB", invokes_alert=True) sel.handle_alert(cancel) def change_type(self, new_type): """Safely changes type of the dialog. It would normally mess up the navigation""" sel.force_navigate("{}_dialog".format(self.type_nav), context={"dialog": self}) cfg_btn("Edit this Dialog") self.type = new_type fill(self.form, {"type": new_type}, action=form_buttons.save) flash.assert_no_errors()
import cfme.web_ui.tabstrip as tabs import cfme.web_ui.toolbar as tb from cfme.web_ui import ( accordion, Quadicon, Form, Input, fill, form_buttons, mixins, SplitTable, Table, Region, AngularSelect, Select ) from utils import version, conf from utils.log import logger from utils.pretty import Pretty from utils.update import Updateable from utils.wait import wait_for properties_form = Form( fields=[ ('name_text', Input('name')), ('type_select', { version.LOWEST: Select("select#provtype"), '5.6': AngularSelect("provider_type")}), ('url_text', Input('url')), ('ssl_checkbox', Input('verify_ssl')) ]) credential_form = Form( fields=[ ('principal_text', Input('log_userid')), ('secret_pass', Input('log_password')), ('verify_secret_pass', Input('log_verify')), ('validate_btn', form_buttons.validate) ])
from utils.log import logger from utils.wait import wait_for paged_tbl = PagedTable(table_locator="//div[@id='list_grid']//table") cfg_btn = partial(tb.select, 'Configuration') mon_btn = partial(tb.select, 'Monitoring') pol_btn = partial(tb.select, 'Policy') details_page = Region(infoblock_type='detail') 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={ "Default": [ ('hostname_text', Input("default_hostname")), ('port_text', Input("default_api_port")), ('sec_protocol', AngularSelect("default_security_protocol", exact=True)), ], "Hawkular": [('hawkular_hostname', Input("hawkular_hostname")), ('hawkular_api_port', Input("hawkular_api_port"))], })
class BaseVM(Pretty, Updateable, PolicyProfileAssignable, Taggable, SummaryMixin, Navigatable): """Base VM and Template class that holds the largest common functionality between VMs, instances, templates and images. In order to inherit these, you have to implement the ``on_details`` method. """ pretty_attrs = ['name', 'provider', 'template_name'] # Forms edit_form = Form(fields=[ ('custom_ident', Input("custom_1")), ('description_tarea', "//textarea[@id='description']"), ('parent_sel', { version.LOWEST: Select("//select[@name='chosen_parent']"), "5.5": AngularSelect("chosen_parent") }), ('child_sel', Select("//select[@id='kids_chosen']", multi=True)), ('vm_sel', Select("//select[@id='choices_chosen']", multi=True)), ('add_btn', "//img[@alt='Move selected VMs to left']"), ('remove_btn', "//img[@alt='Move selected VMs to right']"), ('remove_all_btn', "//img[@alt='Move all VMs to right']"), ]) ### # Factory class methods # @classmethod def factory(cls, vm_name, provider, template_name=None, template=False): """Factory class method that determines the correct subclass for given provider. For reference how does that work, refer to the entrypoints in the setup.py Args: vm_name: Name of the VM/Instance as it appears in the UI provider: The provider object (not the string!) template_name: Source template name. Useful when the VM/Instance does not exist and you want to create it. template: Whether the generated object class should be VM/Instance or a template class. """ try: return all_types(template)[provider.type](vm_name, provider, template_name) except KeyError: # Matching via provider type failed. Maybe we have some generic classes for infra/cloud? try: return all_types(template)[provider.category](vm_name, provider, template_name) except KeyError: raise UnknownProviderType( 'Unknown type of provider CRUD object: {}'.format( provider.__class__.__name__)) ### # To be set or implemented # ALL_LIST_LOCATION = None TO_OPEN_EDIT = None # Name of the item in Configuration that puts you in the form QUADICON_TYPE = "vm" # Titles of the delete buttons in configuration REMOVE_SELECTED = { '5.6': 'Remove selected items', '5.6.2.2': 'Remove selected items from the VMDB', '5.7': 'Remove selected items' } REMOVE_SINGLE = { '5.6': 'Remove Virtual Machine', '5.6.2.2': 'Remove from the VMDB', '5.7': 'Remove Virtual Machine' } RETIRE_DATE_FMT = { version.LOWEST: parsetime.american_date_only_format, '5.7': parsetime.american_minutes_with_utc } _param_name = ParamClassName('name') ### # Shared behaviour # def __init__(self, name, provider, template_name=None, appliance=None): super(BaseVM, self).__init__() Navigatable.__init__(self, appliance=appliance) if type(self) in {BaseVM, VM, Template}: raise NotImplementedError('This class cannot be instantiated.') self.name = name self.provider = provider self.template_name = template_name ### # Properties # @property def is_vm(self): return not isinstance(self, _TemplateMixin) @property def quadicon_type(self): return self.QUADICON_TYPE @property 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'), }) ### # Methods # def check_compliance(self, timeout=240): """Initiates compliance check and waits for it to finish.""" original_state = self.compliance_status cfg_btn("Refresh Relationships and Power States", invokes_alert=True) sel.handle_alert() flash.assert_no_errors() pol_btn("Check Compliance of Last Known Configuration", invokes_alert=True) sel.handle_alert() flash.assert_no_errors() wait_for(lambda: self.compliance_status != original_state, num_sec=timeout, delay=5, message="compliance of {} checked".format(self.name)) return self.compliant @property 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 @property def compliant(self): """Check if the VM is compliant Returns: :py:class:`NoneType` if the VM was never verified, otherwise :py:class:`bool` """ text = self.get_detail(properties=("Compliance", "Status")).strip().lower() if text == "never verified": return None elif text.startswith("non-compliant"): return False elif text.startswith("compliant"): return True else: raise ValueError( "{} is not a known state for compliance".format(text)) @property def console_handle(self): ''' The basic algorithm for getting the consoles window handle is to get the appliances window handle and then iterate through the window_handles till we find one that is not the appliances window handle. Once we find this check that it has a canvas widget with a specific ID ''' browser = self.appliance.browser.widgetastic appliance_handle = browser.window_handle cur_handles = browser.selenium.window_handles logger.info("Current Window Handles: {}".format(cur_handles)) for handle in cur_handles: if handle != appliance_handle: # FIXME: Add code to verify the tab has the correct widget # for a console tab. return handle def delete(self, cancel=False, from_details=False): """Deletes the VM/Instance from the VMDB. Args: cancel: Whether to cancel the action in the alert. from_details: Whether to use the details view or list view. """ if from_details: self.load_details(refresh=True) cfg_btn(self.REMOVE_SINGLE, invokes_alert=True) else: self.find_quadicon().check() cfg_btn(self.REMOVE_SELECTED, invokes_alert=True) sel.handle_alert(cancel=cancel) @property def exists(self): """Checks presence of the quadicon in the CFME.""" try: self.find_quadicon() return True except VmOrInstanceNotFound: return False @property def ip_address(self): """Fetches IP Address of VM""" return self.provider.mgmt.get_ip_address(self.name) @property def is_retired(self): """"Check retirement status of vm""" self.summary.reload() if self.summary.lifecycle.retirement_date.text_value.lower( ) != 'never': try: return self.summary.lifecycle.retirement_state.text_value.lower( ) == 'retired' except AttributeError: return False else: return False def find_quadicon(self, from_any_provider=False, use_search=True): """Find and return a quadicon belonging to a specific vm Args: from_any_provider: Whether to look for it anywhere (root of the tree). Useful when looking up archived or orphaned VMs Returns: entity of appropriate type Raises: VmOrInstanceNotFound """ # todo :refactor this method replace it with vm methods like get_state if from_any_provider: view = navigate_to(self, 'All') else: view = navigate_to(self, 'AllForProvider', use_resetter=False) view.toolbar.view_selector.select('Grid View') if use_search: search.normal_search(self.name) try: return view.entities.get_entity(by_name=self.name, surf_pages=True) except ItemNotFound: raise VmOrInstanceNotFound("VM '{}' not found in UI!".format( self.name)) def get_detail(self, properties=None, icon_href=False): """Gets details from the details infoblock The function first ensures that we are on the detail page for the specific VM/Instance. Args: properties: An InfoBlock title, followed by the Key name, e.g. "Relationships", "Images" Returns: A string representing the contents of the InfoBlock's value. """ self.load_details(refresh=True) if icon_href: return InfoBlock.icon_href(*properties) else: return InfoBlock.text(*properties) def open_console(self, console='VM Console', invokes_alert=False, cancel=False): """ Initiates the opening of one of the console types supported by the Access button. Presently we only support VM Console, which is the HTML5 Console. In case of VMware provider it could be VMRC, VNC/HTML5, WebMKS, but we only support VNC/HTML5. Possible values for 'console' could be 'VM Console' and 'Web Console', but Web Console is not supported as well. Args: console: one of the supported console types given by the Access button. invokes_alert: If the particular console will invoke a CFME popup/alert setting this to true will handle this. cancel: Allows one to cancel the operation if the popup/alert occurs. """ # TODO: implement vmrc vm console if console not in ['VM Console']: raise NotImplementedError( 'Not supported console type: {}'.format(console)) view = navigate_to(self, 'Details') # Click console button given by type view.toolbar.access.item_select( console, handle_alert=None if invokes_alert is False else True) # Get the consoles window handle, and then create a VMConsole object, and store # the VMConsole object aside. console_handle = self.console_handle if console_handle is None: raise TypeError("Console handle should not be None") appliance_handle = self.appliance.browser.widgetastic.window_handle logger.info("Creating VMConsole:") logger.info(" appliance_handle: {}".format(appliance_handle)) logger.info(" console_handle: {}".format(console_handle)) logger.info(" name: {}".format(self.name)) self.vm_console = VMConsole(appliance_handle=appliance_handle, console_handle=console_handle, vm=self) def open_details(self, properties=None): """Clicks on details infoblock""" self.load_details(refresh=True) sel.click(InfoBlock(*properties)) @classmethod def get_first_vm_title(cls, do_not_navigate=False, provider=None): """Get the title of first VM/Instance.""" if not do_not_navigate: if provider is None: navigate_to(cls, 'All') else: provider.load_all_provider_vms() return Quadicon.get_first_quad_title() @property def last_analysed(self): """Returns the contents of the ``Last Analysed`` field in summary""" return self.get_detail(properties=('Lifecycle', 'Last Analyzed')).strip() def load_details(self, refresh=False): """Navigates to an VM's details page. Args: refresh: Refreshes the VM page if already there Raises: VmOrInstanceNotFound: When unable to find the VM passed """ navigate_to(self, 'Details', use_resetter=False) if refresh: toolbar.refresh() self.browser.plugin.ensure_page_safe() def open_edit(self): """Loads up the edit page of the object.""" self.load_details(refresh=True) cfg_btn(self.TO_OPEN_EDIT) def open_timelines(self): """Navigates to an VM's timeline page. Returns: :py:class:`TimelinesView` object """ return navigate_to(self, 'Timelines') def rediscover(self): """Deletes the VM from the provider and lets it discover again""" self.delete(from_details=True) self.wait_for_delete() self.provider.refresh_provider_relationships() self.wait_to_appear() def rediscover_if_analysis_data_present(self): """Rediscovers the object if it has some analysis data present. Returns: Boolean if the rediscovery happened. """ if self.last_analysed.lower() != 'never': self.rediscover() return True return False def refresh_relationships(self, from_details=False, cancel=False, from_any_provider=False): """Executes a refresh of relationships. Args: from_details: Whether or not to perform action from instance details page cancel: Whether or not to cancel the refresh relationships action """ if from_details: self.load_details() else: self.find_quadicon(from_any_provider=from_any_provider).check() cfg_btn('Refresh Relationships and Power States', invokes_alert=True) sel.handle_alert(cancel=cancel) @property def retirement_date(self): """Returns the retirement date of the selected machine, or 'Never' Returns: :py:class:`str` object """ return self.get_detail(properties=("Lifecycle", "Retirement Date")).strip() def smartstate_scan(self, cancel=False, from_details=False): """Initiates fleecing from the UI. Args: cancel: Whether or not to cancel the refresh relationships action from_details: Whether or not to perform action from instance details page """ if from_details: self.load_details(refresh=True) else: self.find_quadicon().check() cfg_btn('Perform SmartState Analysis', invokes_alert=True) sel.handle_alert(cancel=cancel) def wait_to_disappear(self, timeout=600, load_details=True): """Wait for a VM to disappear within CFME Args: timeout: time (in seconds) to wait for it to appear """ wait_for(lambda: self.exists, num_sec=timeout, delay=30, fail_func=sel.refresh, fail_condition=True, message="wait for vm to not exist") wait_for_delete = wait_to_disappear # An alias for more fitting verbosity def wait_to_appear(self, timeout=600, load_details=True): """Wait for a VM to appear within CFME Args: timeout: time (in seconds) to wait for it to appear load_details: when found, should it load the vm details """ wait_for(lambda: self.exists, num_sec=timeout, delay=30, fail_func=self.provider.refresh_provider_relationships, message="wait for vm to appear") if load_details: self.load_details() def set_ownership(self, user=None, group=None, click_cancel=False, click_reset=False): """Set ownership of the VM/Instance or Template/Image""" self.find_quadicon(use_search=False).click() cfg_btn('Set Ownership') if click_reset: action = form_buttons.reset msg_assert = partial(flash.assert_message_match, 'All changes have been reset') elif click_cancel: action = form_buttons.cancel msg_assert = partial(flash.assert_success_message, 'Set Ownership was cancelled by the user') else: action = form_buttons.save msg_assert = partial( flash.assert_success_message, 'Ownership saved for selected {}'.format(self.VM_TYPE)) fill(set_ownership_form, { 'user_name': user, 'group_name': group }, action=action) msg_assert() def unset_ownership(self): """Unset ownership of the VM/Instance or Template/Image""" # choose the vm code comes here self.find_quadicon(use_search=False).click() cfg_btn('Set Ownership') fill(set_ownership_form, { 'user_name': '<No Owner>', 'group_name': 'EvmGroup-administrator' }, action=form_buttons.save) flash.assert_success_message('Ownership saved for selected {}'.format( self.VM_TYPE))
class BaseVM(Pretty, Updateable, PolicyProfileAssignable, Taggable, SummaryMixin): """Base VM and Template class that holds the largest common functionality between VMs, instances, templates and images. In order to inherit these, you have to implement the ``on_details`` method. """ pretty_attrs = ['name', 'provider', 'template_name'] _registered_types = {} # Forms edit_form = Form( fields=[ ('custom_ident', Input("custom_1")), ('description_tarea', "//textarea[@id='description']"), ('parent_sel', { version.LOWEST: Select("//select[@name='chosen_parent']"), "5.5": AngularSelect("chosen_parent")}), ('child_sel', Select("//select[@id='kids_chosen']", multi=True)), ('vm_sel', Select("//select[@id='choices_chosen']", multi=True)), ('add_btn', "//img[@alt='Move selected VMs to left']"), ('remove_btn', "//img[@alt='Move selected VMs to right']"), ('remove_all_btn', "//img[@alt='Move all VMs to right']"), ]) ### # Factory class methods # @classmethod def register_for_provider_type(cls, *provider_types): """This method is used to assign the subclasses to certain providers. Usage as follows: .. code-block:: python @BaseVM.register_for_provider_type("cloud") class Instance(BaseVM): pass @BaseVM.register_for_provider_type("ec2") class EC2Instance(BaseVM): pass @BaseVM.register_for_provider_type("infra") class VM(BaseVM): pass You can use both the types of providers and also the general classes (infra, cloud). Args: *provider_types: The provider types to assign this class to """ def f(klass): for provider_type in provider_types: if provider_type not in cls._registered_types: cls._registered_types[provider_type] = {} if issubclass(klass, _TemplateMixin): cls._registered_types[provider_type]["template"] = klass else: cls._registered_types[provider_type]["vm"] = klass return klass return f @classmethod def factory(cls, vm_name, provider, template_name=None, template=False): """Factory class method that determines the correct subclass for given provider. For reference how does that work, refer to :py:meth:`register_for_provider_type` Args: vm_name: Name of the VM/Instance as it appears in the UI provider: The provider object (not the string!) template_name: Source template name. Useful when the VM/Instance does not exist and you want to create it. template: Whether the generated object class should be VM/Instance or a template class. """ # Ensure the classes are loaded: import cfme.cloud.instance # NOQA import cfme.infrastructure.virtual_machines # NOQA try: return ( cls._registered_types[provider.type]["template" if template else "vm"] (vm_name, provider, template_name)) except KeyError: # Matching via provider type failed. Maybe we have some generic classes for infra/cloud? try: return ( cls._registered_types[provider.category]["template" if template else "vm"] (vm_name, provider, template_name)) except KeyError: raise UnknownProviderType( 'Unknown type of cloud provider CRUD object: {}' .format(provider.__class__.__name__)) ### # To be set or implemented # ALL_LIST_LOCATION = None TO_OPEN_EDIT = None # Name of the item in Configuration that puts you in the form QUADICON_TYPE = "vm" # Titles of the delete buttons in configuration REMOVE_MULTI = "Remove selected items from the VMDB" # For multiple items REMOVE_SINGLE = "Remove from the VMDB" # For single item ### # Shared behaviour # def __init__(self, name, provider, template_name=None): super(BaseVM, self).__init__() if type(self) in {BaseVM, VM, Template}: raise NotImplementedError('This class cannot be instantiated.') self.name = name self.provider = provider self.template_name = template_name if not hasattr(self, "on_details"): raise NotImplementedError( "You need to implement on_details method in {}!".format(type(self).__name__)) ### # Properties # @property def is_vm(self): return not isinstance(self, _TemplateMixin) @property def quadicon_type(self): return self.QUADICON_TYPE @property 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'), }) ### # Methods # def check_compliance(self): """Clicks the Check compliance button.""" self.load_details(refresh=True) pol_btn("Check Compliance of Last Known Configuration", invokes_alert=True) sel.handle_alert() flash.assert_no_errors() @contextmanager def check_compliance_wrapper(self, timeout=240): """This wrapper takes care of waiting for the compliance status to change Args: timeout: Wait timeout in seconds. """ self.load_details(refresh=True) original_state = self.compliance_status yield wait_for( lambda: self.compliance_status != original_state, num_sec=timeout, delay=5, message="compliance of {} checked".format(self.name), fail_func=lambda: toolbar.select("Reload")) def check_compliance_and_wait(self, timeout=240): """Initiates compliance check and waits for it to finish.""" with self.check_compliance_wrapper(timeout=timeout): self.check_compliance() return self.compliant @property 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 @property def compliant(self): """Check if the VM is compliant Returns: :py:class:`NoneType` if the VM was never verified, otherwise :py:class:`bool` """ text = self.get_detail(properties=("Compliance", "Status")).strip().lower() if text == "never verified": return None elif text.startswith("non-compliant"): return False elif text.startswith("compliant"): return True else: raise ValueError("{} is not a known state for compliance".format(text)) def delete(self, cancel=False, from_details=False): """Deletes the VM/Instance from the VMDB. Args: cancel: Whether to cancel the action in the alert. from_details: Whether to use the details view or list view. """ if from_details: self.load_details(refresh=True) cfg_btn(self.REMOVE_SINGLE, invokes_alert=True) else: self.find_quadicon(mark=True) cfg_btn(self.REMOVE_MULTI, invokes_alert=True) sel.handle_alert(cancel=cancel) @property def exists(self): """Checks presence of the quadicon in the CFME.""" try: self.find_quadicon() return True except VmOrInstanceNotFound: return False def find_quadicon( self, do_not_navigate=False, mark=False, refresh=True, from_any_provider=False, use_search=True): """Find and return a quadicon belonging to a specific vm Args: from_any_provider: Whether to look for it anywhere (root of the tree). Useful when looking up archived or orphaned VMs Returns: :py:class:`cfme.web_ui.Quadicon` instance Raises: VmOrInstanceNotFound """ quadicon = Quadicon(self.name, self.quadicon_type) if not do_not_navigate: if from_any_provider: sel.force_navigate(self.ALL_LIST_LOCATION) elif self.is_vm: self.provider.load_all_provider_vms() else: self.provider.load_all_provider_templates() toolbar.select('Grid View') else: # Search requires navigation, we shouldn't use it then use_search = False if refresh: sel.refresh() if not paginator.page_controls_exist(): if self.is_vm: raise VmOrInstanceNotFound("VM '{}' not found in UI!".format(self.name)) else: raise TemplateNotFound("Template '{}' not found in UI!".format(self.name)) # this is causing some issues in 5.5.0.9, commenting out for a bit # paginator.results_per_page(1000) if use_search: try: if not search.has_quick_search_box(): # We don't use provider-specific page (vm_templates_provider_branch) here # as those don't list archived/orphaned VMs if self.is_vm: sel.force_navigate(self.provider.instances_page_name) else: sel.force_navigate(self.provider.templates_page_name) search.normal_search(self.name) except Exception as e: logger.warning("Failed to use search: %s", str(e)) for page in paginator.pages(): if sel.is_displayed(quadicon, move_to=True): if mark: sel.check(quadicon.checkbox()) return quadicon else: raise VmOrInstanceNotFound("VM '{}' not found in UI!".format(self.name)) def get_detail(self, properties=None, icon_href=False): """Gets details from the details infoblock The function first ensures that we are on the detail page for the specific VM/Instance. Args: properties: An InfoBlock title, followed by the Key name, e.g. "Relationships", "Images" Returns: A string representing the contents of the InfoBlock's value. """ self.load_details(refresh=True) if icon_href: return InfoBlock.icon_href(*properties) else: return InfoBlock.text(*properties) def open_details(self, properties=None): """Clicks on details infoblock""" self.load_details(refresh=True) sel.click(InfoBlock(*properties)) @classmethod def get_first_vm_title(cls, do_not_navigate=False, provider=None): """Get the title of first VM/Instance.""" if not do_not_navigate: if provider is None: sel.force_navigate(cls.ALL_LIST_LOCATION) else: provider.load_all_provider_vms() return Quadicon.get_first_quad_title() @property def last_analysed(self): """Returns the contents of the ``Last Analysed`` field in summary""" return self.get_detail(properties=('Lifecycle', 'Last Analyzed')).strip() def load_details(self, refresh=False): """Navigates to an VM's details page. Args: refresh: Refreshes the VM page if already there Raises: VmOrInstanceNotFound: When unable to find the VM passed """ if not self.on_details(): logger.debug("load_details: not on details already") sel.click(self.find_quadicon()) else: if refresh: toolbar.refresh() def open_edit(self): """Loads up the edit page of the object.""" self.load_details(refresh=True) cfg_btn(self.TO_OPEN_EDIT) def open_timelines(self): self.load_details(refresh=True) mon_btn("Timelines") def rediscover(self): """Deletes the VM from the provider and lets it discover again""" self.delete(from_details=True) self.wait_for_delete() self.provider.refresh_provider_relationships() self.wait_to_appear() def rediscover_if_analysis_data_present(self): """Rediscovers the object if it has some analysis data present. Returns: Boolean if the rediscovery happened. """ if self.last_analysed.lower() != 'never': self.rediscover() return True return False def refresh_relationships(self, from_details=False, cancel=False): """Executes a refresh of relationships. Args: from_details: Whether or not to perform action from instance details page cancel: Whether or not to cancel the refresh relationships action """ if from_details: self.load_details() else: self.find_quadicon(mark=True) cfg_btn('Refresh Relationships and Power States', invokes_alert=True) sel.handle_alert(cancel=cancel) @property def retirement_date(self): """Returns the retirement date of the selected machine. Returns: :py:class:`NoneType` if there is none, or :py:class:`utils.timeutil.parsetime` """ date_str = self.get_detail(properties=("Lifecycle", "Retirement Date")).strip() if date_str.lower() == "never": return None return parsetime.from_american_date_only(date_str) def smartstate_scan(self, cancel=False, from_details=False): """Initiates fleecing from the UI. Args: cancel: Whether or not to cancel the refresh relationships action from_details: Whether or not to perform action from instance details page """ if from_details: self.load_details(refresh=True) else: self.find_quadicon(mark=True) cfg_btn('Perform SmartState Analysis', invokes_alert=True) sel.handle_alert(cancel=cancel) def wait_to_disappear(self, timeout=600, load_details=True): """Wait for a VM to disappear within CFME Args: timeout: time (in seconds) to wait for it to appear """ wait_for( lambda: self.exists, num_sec=timeout, delay=30, fail_func=sel.refresh, fail_condition=True) wait_for_delete = wait_to_disappear # An alias for more fitting verbosity def wait_to_appear(self, timeout=600, load_details=True): """Wait for a VM to appear within CFME Args: timeout: time (in seconds) to wait for it to appear from_details: when found, should it load the vm details """ wait_for(lambda: self.exists, num_sec=timeout, delay=30, fail_func=sel.refresh) if load_details: self.load_details() def set_ownership(self, user=None, group=None, click_cancel=False, click_reset=False): """Set ownership of the VM/Instance or Template/Image""" sel.click(self.find_quadicon(False, False, False)) cfg_btn('Set Ownership') if click_reset: action = form_buttons.reset msg_assert = lambda: flash.assert_message_match( 'All changes have been reset') elif click_cancel: action = form_buttons.cancel msg_assert = lambda: flash.assert_success_message( 'Set Ownership was cancelled by the user') else: action = form_buttons.save msg_assert = lambda: flash.assert_success_message( 'Ownership saved for selected {}'.format(self.VM_TYPE)) fill(set_ownership_form, {'user_name': user, 'group_name': group}, action=action) msg_assert() def unset_ownership(self): """Unset ownership of the VM/Instance or Template/Image""" # choose the vm code comes here sel.click(self.find_quadicon(False, False, False)) cfg_btn('Set Ownership') fill(set_ownership_form, {'user_name': '<No Owner>', 'group_name': 'EvmGroup-administrator'}, action=form_buttons.save) flash.assert_success_message('Ownership saved for selected {}'.format(self.VM_TYPE))
def get_expression_as_text(): """ Returns whole expression as represented visually. """ return sel.text("//div[@id='exp_editor_div']/fieldset/div").encode( "utf-8").strip() ### # Form handling # field_form = Form(fields=[ ("type", Select("select#chosen_typ")), ("field", Select("select#chosen_field")), ("key", Select("select#chosen_key")), ("value", Input("chosen_value")), ("user_input", Input("user_input")), ]) field_date_form = Form( fields=[("dropdown_select", Select("select#chosen_from_1") ), ("input_select_date", Calendar("miq_date_1_0") ), ("input_select_time", Select("select#miq_time_1_0"))]) count_form = Form(fields=[ ("type", Select("select#chosen_typ")), ("count", Select("select#chosen_count")), ("key", Select("select#chosen_key")), ("value", Input("chosen_value")), ("user_input", Input("user_input")), ])
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={ "Default": [ ('hostname_text', Input("default_hostname")), ('port_text', Input("default_api_port")), ('sec_protocol', AngularSelect("default_security_protocol")), ], "Hawkular": [('hawkular_hostname', Input("hawkular_hostname")), ('hawkular_api_port', Input("hawkular_api_port"))], })
dynamic_tree = Tree({ version.LOWEST: "//div[@class='dhxcont_global_content_area']" "[not(contains(@style, 'display: none'))]/div/div/div/div/div" "/fieldset/div/ul[@class='dynatree-container']", '5.4': "//div[@class='dhxcont_global_content_area']" "[not(contains(@style, 'display: none'))]/div/div/div/div/div/div" "/div/div/div/ul[@class='dynatree-container']", '5.5': "//div[@class='modal-content']/div/div/ul[@class='dynatree-container']" }) label_form = Form( fields=[('label', Input("label")), ('description_text', Input("description")), ('submit_button', Input("chkbx_submit")), ('cancel_button', Input("chkbx_cancel"))]) tab_form = Form( fields=[('tab_label', Input("tab_label")), ('tab_desc', Input("tab_description"))]) box_form = Form( fields=[('box_label', Input("group_label")), ('box_desc', Input("group_description"))]) element_form = Form(fields=[ ('ele_label', Input("field_label")), ('ele_name', Input("field_name")), ('ele_desc', Input("field_description")),
'edit_registration': make_update_button("Edit Registration"), 'refresh': make_update_button("Refresh List"), 'check_updates': make_update_button("Check for Updates"), 'register': make_update_button("Register"), 'apply_updates': make_update_button("Apply CFME Update") }, identifying_loc="edit_registration") registration_form = Form(fields=[ ("service", Select("//select[@id='register_to']")), ("url", Input('server_url')), ("repo_name", Input('repo_name')), ("use_proxy", Input('use_proxy')), ("proxy_url", Input('proxy_address')), ("proxy_username", Input('proxy_userid')), ("proxy_password", Input('proxy_password')), ("proxy_password_verify", Input('proxy_password2')), # 5.4+ ("username", Input('customer_userid')), ("password", Input('customer_password')), ("password_verify", Input('customer_password2')), # 5.4+ ("organization_sat5", Input('customer_org')), ("organization_sat6", Select("//select[@id='customer_org']")) ]) registration_buttons = Region(locators={ 'url_default':
create_button = form_buttons.FormButton("Create") manage_policies_tree = CheckboxTree("//div[@id='protect_treebox']/ul") manage_policies_page = Region(locators={ 'save_button': form_buttons.save, }) template_select_form = Form(fields=[('template_table', Table('//div[@id="pre_prov_div"]//table') ), ('cancel_button', form_buttons.cancel)]) snapshot_form = Form( fields=[('name', Input('name')), ('description', Input('description')), ('snapshot_memory', Input('snap_memory')), ( 'create_button', create_button), ('cancel_button', form_buttons.cancel)]) retirement_date_form = Form( fields=[('retirement_date_text', Calendar("miq_date_1")), ('retirement_warning_select', Select("//select[@id='retirement_warn']"))]) retire_remove_button = "//span[@id='remove_button']/a/img" match_page = partial(match_location, controller='vm_infra', title='Virtual Machines')
lambda ctx: sel.click(Quadicon(ctx['provider'].name)), { 'middleware_provider_edit_detail': lambda _: cfg_btn('Edit this Middleware Provider'), 'middleware_provider_timelines_detail': lambda _: mon_btn('Timelines'), 'middleware_provider_edit_tags_detail': lambda _: pol_btn('Edit Tags'), } ] }) properties_form = Form(fields=[( 'type_select', AngularSelect('server_emstype')), ( 'name_text', Input('name')), ('hostname_text', Input('hostname')), ('port_text', Input('port'))]) class HawkularProvider(MiddlewareBase, BaseProvider): """ HawkularProvider class holds provider data. Used to perform actions on hawkular provider page Args: name: Name of the provider hostname: Hostname/IP of the provider port: http/https port of hawkular provider credentials: see Credential inner class. key: The CFME key of the provider in the yaml. Usage:
form_buttons, paginator, Input) from cfme.web_ui.form_buttons import FormButton from utils.browser import ensure_browser_open from utils.log import logger from utils.update import Updateable from utils.wait import wait_for from utils import version from utils.pretty import Pretty add_infra_provider = FormButton("Add this Infrastructure Provider") details_page = Region(infoblock_type='detail') # Forms discover_form = Form( fields=[('rhevm_chk', Input("discover_type_rhevm") ), ('vmware_chk', Input("discover_type_virtualcenter")), ('scvmm_chk', Input("discover_type_scvmm")), ('from_0', Input("from_first")), ('from_1', Input("from_second")), ('from_2', Input("from_third")), ('from_3', Input("from_fourth")), ('to_3', Input("to_fourth")), ('start_button', FormButton("Start the Host Discovery"))]) properties_form = Form( fields=[('type_select', Select("select#server_emstype")), ( 'name_text', Input("name")), ('hostname_text', Input("hostname")), ('ipaddress_text', Input("ipaddress"), { "removed_since": "5.4.0.0.15" }), ('api_port', Input("port")),
class User(Updateable, Pretty): user_form = Form(fields=[ ('name_txt', Input('name')), ('userid_txt', Input('userid')), ('password_txt', Input('password')), ('password_verify_txt', { version.LOWEST: Input('password2'), "5.5": Input('verify') }), ('email_txt', Input('email')), ('user_group_select', Select("//*[@id='chosen_group']")), ]) pretty_attrs = ['name', 'group'] def __init__(self, name=None, credential=None, email=None, group=None, cost_center=None, value_assign=None): self.name = name self.credential = credential self.email = email self.group = group self.cost_center = cost_center self.value_assign = value_assign self._restore_user = None def __enter__(self): if self._restore_user != store.user: from cfme.login import logout logger.info('Switching to new user: %s', self.credential.principal) self._restore_user = store.user logout() store.user = self def __exit__(self, *args, **kwargs): if self._restore_user != store.user: from cfme.login import logout logger.info('Restoring to old user: %s', self._restore_user.credential.principal) logout() store.user = self._restore_user self._restore_user = None def create(self): sel.force_navigate('cfg_accesscontrol_user_add') fill(self.user_form, { 'name_txt': self.name, 'userid_txt': self.credential.principal, 'password_txt': self.credential.secret, 'password_verify_txt': self.credential.verify_secret, 'email_txt': self.email, 'user_group_select': getattr(self.group, 'description', None) }, action=form_buttons.add) flash.assert_success_message('User "{}" was saved'.format(self.name)) def update(self, updates): sel.force_navigate("cfg_accesscontrol_user_edit", context={"user": self}) change_stored_password() fill(self.user_form, { 'name_txt': updates.get('name'), 'userid_txt': updates.get('credential').principal, 'password_txt': updates.get('credential').secret, 'password_verify_txt': updates.get('credential').verify_secret, 'email_txt': updates.get('email'), 'user_group_select': getattr(updates.get('group'), 'description', None) }, action=form_buttons.save) flash.assert_success_message('User "{}" was saved'.format( updates.get('name', self.name))) def copy(self): sel.force_navigate("cfg_accesscontrol_user_ed", context={"user": self}) tb.select('Configuration', 'Copy this User to a new User') new_user = User(name=self.name + "copy", credential=cfme.Credential(principal='redhat', secret='redhat')) change_stored_password() fill(self.user_form, { 'name_txt': new_user.name, 'userid_txt': new_user.credential.principal, 'password_txt': new_user.credential.secret, 'password_verify_txt': new_user.credential.verify_secret }, action=form_buttons.add) flash.assert_success_message('User "{}" was saved'.format( new_user.name)) return new_user def delete(self): sel.force_navigate("cfg_accesscontrol_user_ed", context={"user": self}) tb.select('Configuration', 'Delete this User', invokes_alert=True) sel.handle_alert() flash.assert_success_message('EVM User "{}": Delete successful'.format( self.name)) def edit_tags(self, tag, value): sel.force_navigate("cfg_accesscontrol_user_ed", context={"user": self}) pol_btn("Edit 'My Company' Tags for this User", invokes_alert=True) fill(edit_tags_form, { 'select_tag': tag, 'select_value': value }, action=form_buttons.save) flash.assert_success_message('Tag edits were successfully saved') def remove_tag(self, tag, value): sel.force_navigate("cfg_accesscontrol_user_ed", context={"user": self}) pol_btn("Edit 'My Company' Tags for this User", invokes_alert=True) row = tag_table.find_row_by_cells( { 'category': tag, 'assigned_value': value }, partial_check=True) sel.click(row[0]) form_buttons.save() flash.assert_success_message('Tag edits were successfully saved') @property def description(self): return self.credential.principal
class Schedule(Updateable, Pretty): """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("name")), ("description", Input("description")), ("active", Input("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("send_email_cb")), ("emails", EmailSelectForm())]) _run_mapping = { "Once": None, "Hourly": "run_hours", "Daily": "run_days", "Weekly": "run_weekly", "Monthly": "run_months" } pretty_attrs = ['name', 'filter'] 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}) delete_label = version.pick({ "5.4": "Delete this Schedule from the Database", "5.5": "Delete this Schedule from the VMDB" }) cfg_btn(delete_label, 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 (version.pick({ version.LOWEST: "//td[preceding-sibling::td[contains(@class, 'key')" " and normalize-space(.)='{}']]", "5.5": "//label[contains(@class, 'control-label')" " and normalize-space(.)='{}']/../div/p" }).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. cancel: (kwarg) 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 Tenant(Updateable, Pretty): """ Class representing CFME tenants in the UI. * Kudos to mfalesni * The behaviour is shared with Project, which is the same except it cannot create more nested tenants/projects. Args: name: Name of the tenant description: Description of the tenant parent_tenant: Parent tenant, can be None, can be passed as string or object """ save_changes = form_buttons.FormButton("Save changes") tenant_form = Form( fields=[('name', Input('name')), ('description', Input('description'))]) pretty_attrs = ["name", "description"] @classmethod def get_root_tenant(cls): return cls(name="My Company", _default=True) def __init__(self, name=None, description=None, parent_tenant=None, _default=False): self.name = name self.description = description self.parent_tenant = parent_tenant self._default = _default @property def parent_tenant(self): if self._default: return None if self._parent_tenant: return self._parent_tenant return self.get_root_tenant() @parent_tenant.setter def parent_tenant(self, tenant): if tenant is not None and isinstance(tenant, Project): # If we try to raise ValueError("Project cannot be a parent object.") if isinstance(tenant, basestring): # If parent tenant is passed as string, # we assume that tenant name was passed instead of object tenant = Tenant(tenant) self._parent_tenant = tenant def __eq__(self, other): if not isinstance(other, type(self)): return False else: return self.name == other.name @property def exists(self): try: sel.force_navigate("cfg_tenant_project", context={"tenant": self}) except CandidateNotFound: return False else: return True @property def tree_path(self): if self._default: return [self.name] else: return self.parent_tenant.tree_path + [self.name] @property def parent_path(self): return self.tree_path[:-1] def create(self, cancel=False): sel.force_navigate("cfg_tenant_project_create", context={"tenant": self}) fill(self.tenant_form, self, action=form_buttons.add) if type(self) is Tenant: flash.assert_success_message('Tenant "{}" was saved'.format( self.name)) elif type(self) is Project: flash.assert_success_message('Project "{}" was saved'.format( self.name)) else: raise TypeError( 'No Tenant or Project class passed to create method{}'.format( type(self).__name__)) def update(self, updates): sel.force_navigate("cfg_tenant_project_edit", context={"tenant": self}) # Workaround - without this, update was failing sometimes sel.wait_for_ajax() # Workaround - form is appearing after short delay sel.wait_for_element(self.tenant_form.description) fill(self.tenant_form, updates, action=self.save_changes) flash.assert_success_message('Project "{}" was saved'.format( updates.get('name', self.name))) def delete(self, cancel=False): sel.force_navigate("cfg_tenant_project", context={"tenant": self}) tb_select("Delete this item", invokes_alert=True) sel.handle_alert(cancel=cancel) flash.assert_success_message('Tenant "{}": Delete successful'.format( self.description))
from utils.wait import wait_for from utils import deferred_verpick, version from utils.pretty import Pretty from utils.appliance.implementations.ui import navigator, CFMENavigateStep, navigate_to from utils.appliance import Navigatable from widgetastic_manageiq import TimelinesView from cfme.common import PolicyProfileAssignable # Page specific locators details_page = Region(infoblock_type='detail') page_title_loc = '//div[@id="center_div" or @id="main-content"]//h1' properties_form = Form(fields=[ ('name_text', Input("name")), ('hostname_text', Input("hostname")), ('ipaddress_text', Input("ipaddress"), { "removed_since": "5.4.0.0.15" }), ('custom_ident_text', Input("custom")), ('host_platform', { version.LOWEST: Select('//select[@id="user_assigned_os"]'), '5.5': AngularSelect('user_assigned_os') }), ('ipmi_address_text', Input("ipmi_address")), ('mac_address_text', Input("mac_address")), ]) credential_form = Form(fields=[ ('default_button', "//div[@id='auth_tabs']/ul/li/a[@href='#default']"),