from __future__ import absolute_import
import time
import re
from navmazing import NavigateToSibling, NavigateToAttribute
from selenium.webdriver.common.keys import Keys
from widgetastic.widget import View, Table, Text, Image, FileInput
from widgetastic_patternfly import (Accordion, Input, Button, Dropdown,
FlashMessages, BootstrapSelect, Tab)
from cfme.base.credential import Credential
from cfme.base.login import BaseLoggedInPage
from cfme.configure.about import AboutView
from cfme.configure.configuration.server_settings import (
ServerInformationView, ServerAuthenticationView)
from cfme.configure.documentation import DocView
from cfme.configure.tasks import TasksView
from cfme.dashboard import DashboardView
from cfme.exceptions import BugException
from cfme.exceptions import ZoneNotFound, DestinationNotFound
from cfme.intelligence.chargeback import ChargebackView
from cfme.intelligence.rss import RSSView
from cfme.utils import conf
from cfme.utils.appliance import MiqImplementationContext
from cfme.utils.appliance.implementations.ui import navigator, CFMENavigateStep, ViaUI, navigate_to
from cfme.utils.blockers import BZ
from cfme.utils.log import logger
from widgetastic_manageiq import (ManageIQTree, Checkbox, AttributeValueForm, TimelinesView,
ParametrizedSummaryTable, SummaryFormItem)
from . import Server, Region, Zone, ZoneCollection
@MiqImplementationContext.external_for(Server.address, ViaUI)
[docs]def address(self):
logger.info("USING UI ADDRESS")
return self.appliance.url
[docs]class LoginPage(View):
flash = FlashMessages('//div[@class="flash_text_div"]')
class details(View): # noqa
region = Text('.//p[normalize-space(text())="Region:"]/span')
zone = Text('.//p[normalize-space(text())="Zone:"]/span')
appliance = Text('.//p[normalize-space(text())="Appliance:"]/span')
change_password = Text('.//a[normalize-space(.)="Update password"]')
back = Text('.//a[normalize-space(.)="Back"]')
username = Input(name='user_name')
password = Input(name='user_password')
new_password = Input(name='user_new_password')
verify_password = Input(name='user_verify_password')
login = Button(id='login')
[docs] def show_update_password(self):
if not self.new_password.is_displayed:
self.change_password.click()
[docs] def hide_update_password(self):
if self.new_password.is_displayed:
self.back.click()
[docs] def login_admin(self, **kwargs):
username = conf.credentials['default']['username']
password = conf.credentials['default']['password']
cred = Credential(principal=username, secret=password)
user = self.extra.appliance.collections.users.instantiate(
credential=cred, name='Administrator'
)
return self.log_in(user, **kwargs)
[docs] def submit_login(self, method='click_on_login'):
if method == 'click_on_login':
self.login.click()
elif method == 'press_enter_after_password':
self.browser.send_keys(Keys.ENTER, self.password)
elif method == '_js_auth_fn':
self.browser.execute_script('miqAjaxAuth();')
else:
raise ValueError('Unknown method {}'.format(method))
if self.flash.is_displayed:
self.flash.assert_no_error()
[docs] def log_in(self, user, method='click_on_login'):
self.fill({
'username': user.credential.principal,
'password': user.credential.secret,
})
self.submit_login(method)
logged_in_view = self.browser.create_view(BaseLoggedInPage)
if logged_in_view.logged_in:
if user.name is None:
name = logged_in_view.current_fullname
self.logger.info(
'setting the appliance.user.name to %r because it was not specified', name)
user.name = name
self.extra.appliance.user = user
[docs] def update_password(
self, username, password, new_password, verify_password=None,
method='click_on_login'):
self.show_update_password()
self.fill({
'username': username,
'password': password,
'new_password': new_password,
'verify_password': verify_password if verify_password is not None else new_password
})
self.submit_login(method)
[docs] def logged_in_as_user(self, user):
return False
@property
def logged_in_as_current_user(self):
return False
@property
def current_username(self):
return None
@property
def current_fullname(self):
return None
@property
def current_groupname(self):
return None
@property
def logged_in(self):
return not self.logged_out
@property
def logged_out(self):
return self.username.is_displayed and self.password.is_displayed and self.login.is_displayed
@property
def is_displayed(self):
return self.logged_out
@MiqImplementationContext.external_for(Server.logged_in, ViaUI)
[docs]def logged_in(self):
return self.appliance.browser.create_view(BaseLoggedInPage).logged_in
LOGIN_METHODS = ['click_on_login', 'press_enter_after_password', '_js_auth_fn']
@MiqImplementationContext.external_for(Server.login, ViaUI)
# for selenim3 v_js_auth_fn doesn't sent info to the server
[docs]def login(self, user=None, method=LOGIN_METHODS[1]):
"""
Login to CFME with the given username and password.
Optionally, submit_method can be press_enter_after_password
to use the enter key to login, rather than clicking the button.
Args:
user: The username to fill in the username field.
password: The password to fill in the password field.
submit_method: A function to call after the username and password have been input.
Raises:
RuntimeError: If the login fails, ie. if a flash message appears
"""
# Circular import
if not user:
username = conf.credentials['default']['username']
password = conf.credentials['default']['password']
cred = Credential(principal=username, secret=password)
user = self.appliance.collections.users.instantiate(credential=cred, name='Administrator')
logged_in_view = self.appliance.browser.create_view(BaseLoggedInPage)
if not logged_in_view.logged_in_as_user(user):
if logged_in_view.logged_in:
logged_in_view.logout()
from cfme.utils.appliance.implementations.ui import navigate_to
login_view = navigate_to(self.appliance.server, 'LoginScreen')
time.sleep(1)
logger.debug('Logging in as user %s', user.credential.principal)
login_view.flush_widget_cache()
login_view.log_in(user, method=method)
logged_in_view.flush_widget_cache()
try:
assert logged_in_view.is_displayed
assert logged_in_view.logged_in_as_user
user.name = logged_in_view.current_fullname
self.appliance.user = user
except AssertionError:
login_view.flash.assert_no_error()
return logged_in_view
@MiqImplementationContext.external_for(Server.login_admin, ViaUI)
[docs]def login_admin(self, **kwargs):
"""
Convenience function to log into CFME using the admin credentials from the yamls.
Args:
kwargs: A dict of keyword arguments to supply to the :py:meth:`login` method.
"""
username = conf.credentials['default']['username']
password = conf.credentials['default']['password']
cred = Credential(principal=username, secret=password)
user = self.appliance.collections.users.instantiate(credential=cred, name='Administrator')
logged_in_page = self.login(user, **kwargs)
return logged_in_page
@MiqImplementationContext.external_for(Server.logout, ViaUI)
[docs]def logout(self):
"""
Logs out of CFME.
"""
logged_in_view = self.appliance.browser.create_view(BaseLoggedInPage)
if logged_in_view.logged_in:
logged_in_view.logout()
self.appliance.user = None
@MiqImplementationContext.external_for(Server.current_full_name, ViaUI)
[docs]def current_full_name(self):
""" Returns the current username.
Returns: the current username.
"""
logged_in_view = self.appliance.browser.create_view(BaseLoggedInPage)
if logged_in_view.logged_in:
return logged_in_view.current_fullname
else:
return None
@MiqImplementationContext.external_for(Server.current_group_name, ViaUI)
[docs]def current_group_name(self):
"""Returns current groupname from settings dropdown nav if logged in, or None"""
view = self.appliance.browser.create_view(BaseLoggedInPage)
return view.current_groupname if view.logged_in else None
@MiqImplementationContext.external_for(Server.group_names, ViaUI)
[docs]def group_names(self):
"""Returns group names selectable for current user from settings dropdown if logged in"""
view = self.appliance.browser.create_view(BaseLoggedInPage)
return view.group_names if view.logged_in else None
# ######################## SERVER NAVS ################################
@navigator.register(Server)
[docs]class LoginScreen(CFMENavigateStep):
VIEW = LoginPage
[docs] def prerequisite(self):
from cfme.utils.browser import ensure_browser_open
ensure_browser_open(self.obj.appliance.server.address())
[docs] def step(self):
# Can be either blank or logged in
from cfme.utils import browser
logged_in_view = self.create_view(BaseLoggedInPage)
if logged_in_view.logged_in:
logged_in_view.logout()
if not self.view.is_displayed:
# Something is wrong
del self.view # In order to unbind the browser
browser.quit()
browser.ensure_browser_open(self.obj.appliance.server.address())
if not self.view.is_displayed:
raise Exception('Could not open the login screen')
@navigator.register(Server)
[docs]class LoggedIn(CFMENavigateStep):
VIEW = BaseLoggedInPage
prerequisite = NavigateToSibling('LoginScreen')
[docs] def step(self):
user = self.obj.appliance.user
self.prerequisite_view.log_in(user)
[docs]class ConfigurationView(BaseLoggedInPage):
title = Text('#explorer_title_text')
@View.nested
class accordions(View): # noqa
@View.nested
class settings(Accordion): # noqa
ACCORDION_NAME = "Settings"
INDIRECT = True
tree = ManageIQTree()
@View.nested
class accesscontrol(Accordion): # noqa
ACCORDION_NAME = "Access Control"
tree = ManageIQTree()
@View.nested
class diagnostics(Accordion): # noqa
ACCORDION_NAME = "Diagnostics"
tree = ManageIQTree()
@View.nested
class database(Accordion): # noqa
ACCORDION_NAME = "Database"
tree = ManageIQTree()
@property
def in_configuration(self):
return (
self.accordions.settings.is_displayed and
self.accordions.accesscontrol.is_displayed and
self.accordions.diagnostics.is_displayed and
self.accordions.database.is_displayed)
@property
def is_displayed(self):
# TODO: We will need a better ID of this location when we have user permissions in effect
return self.in_configuration
@navigator.register(Server)
[docs]class Configuration(CFMENavigateStep):
VIEW = ConfigurationView
prerequisite = NavigateToSibling('LoggedIn')
[docs] def step(self):
if self.obj.appliance.version > '5.7':
self.prerequisite_view.settings.select_item('Configuration')
self.prerequisite_view.browser.handle_alert(wait=2, cancel=False, squash=True)
else:
self.prerequisite_view.navigation.select('Settings', 'Configuration')
@navigator.register(Server)
[docs]class About(CFMENavigateStep):
VIEW = AboutView
prerequisite = NavigateToSibling('LoggedIn')
[docs] def step(self):
self.prerequisite_view.help.select_item('About')
@navigator.register(Server)
@navigator.register(Server)
[docs]class Documentation(CFMENavigateStep):
VIEW = DocView
prerequisite = NavigateToSibling('LoggedIn')
[docs] def step(self):
self.prerequisite_view.help.select_item('Documentation')
@navigator.register(Server)
[docs]class Tasks(CFMENavigateStep):
VIEW = TasksView
prerequisite = NavigateToSibling('LoggedIn')
[docs] def step(self):
if self.obj.appliance.version > '5.7':
self.prerequisite_view.settings.select_item('Tasks')
else:
self.prerequisite_view.navigation.select('Settings', 'Tasks')
@navigator.register(Server)
[docs]class Dashboard(CFMENavigateStep):
VIEW = DashboardView
prerequisite = NavigateToSibling('LoggedIn')
[docs] def step(self):
self.prerequisite_view.navigation.select('Cloud Intel', 'Dashboard')
@navigator.register(Server)
[docs]class Chargeback(CFMENavigateStep):
VIEW = ChargebackView
prerequisite = NavigateToSibling('LoggedIn')
[docs] def step(self, *args, **kwargs):
self.prerequisite_view.navigation.select('Cloud Intel', 'Chargeback')
[docs]class ServerView(ConfigurationView):
@View.nested
class server(Tab): # noqa
TAB_NAME = "Server"
including_view = View.include(ServerInformationView, use_parent=True)
@View.nested
class authentication(Tab): # noqa
TAB_NAME = "Authentication"
including_view = View.include(ServerAuthenticationView, use_parent=True)
@View.nested
class workers(Tab): # noqa
TAB_NAME = "Workers"
# TODO move workers tab view into server_settings as ServerWorkersView
generic_worker_count = BootstrapSelect("generic_worker_count")
generic_worker_threshold = BootstrapSelect("generic_worker_threshold")
cu_data_collector_worker_count = BootstrapSelect("ems_metrics_collector_worker_count")
cu_data_collector_worker_threshold = BootstrapSelect(
"ems_metrics_collector_worker_threshold")
event_monitor_worker_threshold = BootstrapSelect("event_catcher_threshold")
connection_broker_worker_threshold = BootstrapSelect("vim_broker_worker_threshold")
ui_worker_count = BootstrapSelect("ui_worker_count")
reporting_worker_count = BootstrapSelect("reporting_worker_count")
reporting_worker_threshold = BootstrapSelect("reporting_worker_threshold")
web_service_worker_count = BootstrapSelect("web_service_worker_count")
web_service_worker_threshold = BootstrapSelect("web_service_worker_threshold")
priority_worker_count = BootstrapSelect("priority_worker_count")
priority_worker_threshold = BootstrapSelect("priority_worker_threshold")
cu_data_processor_worker_count = BootstrapSelect("ems_metrics_processor_worker_count")
cu_data_processor_worker_threshold = BootstrapSelect(
"ems_metrics_processor_worker_threshold")
refresh_worker_threshold = BootstrapSelect("ems_refresh_worker_threshold")
vm_analysis_collectors_worker_count = BootstrapSelect("proxy_worker_count")
vm_analysis_collectors_worker_threshold = BootstrapSelect("proxy_worker_threshold")
websocket_worker_count = BootstrapSelect("websocket_worker_count")
save = Button('Save')
reset = Button('Reset')
@View.nested
class customlogos(Tab): # noqa
TAB_NAME = "Custom Logos"
@View.nested
class advanced(Tab): # noqa
TAB_NAME = "Advanced"
@property
def is_displayed(self):
selected_list = [
self.context['object'].zone.region.settings_string,
"Zones",
"Zone: {} (current)".format(self.context['object'].zone.description),
"Server: {} [{}] (current)".format(self.context['object'].name,
self.context['object'].sid)]
return (
self.in_configuration and
self.accordions.settings.is_displayed and
self.accordions.settings.tree.currently_selected == selected_list)
@navigator.register(Server)
[docs]class Details(CFMENavigateStep):
VIEW = ServerView
prerequisite = NavigateToSibling('Configuration')
[docs] def step(self):
self.prerequisite_view.accordions.settings.tree.click_path(
self.obj.zone.region.settings_string,
"Zones",
"Zone: {} (current)".format(self.obj.appliance.server.zone.description),
"Server: {} [{}] (current)".format(self.obj.appliance.server.name,
self.obj.sid))
@navigator.register(Server, 'Server')
[docs]class ServerDetails(CFMENavigateStep):
VIEW = ServerInformationView
prerequisite = NavigateToSibling('Details')
[docs] def am_i_here(self):
return (
self.view.is_displayed and
self.prerequisite_view.is_displayed and
self.view.server.is_active)
[docs] def step(self):
self.prerequisite_view.server.select()
@navigator.register(Server)
[docs]class Authentication(CFMENavigateStep):
VIEW = ServerAuthenticationView
prerequisite = NavigateToSibling('Details')
[docs] def step(self):
self.prerequisite_view.authentication.select()
@navigator.register(Server)
[docs]class Workers(CFMENavigateStep):
VIEW = ServerView
prerequisite = NavigateToSibling('Details')
[docs] def am_i_here(self):
return (
self.view.is_displayed and self.view.workers.is_displayed and
self.view.workers.is_active)
[docs] def step(self):
self.prerequisite_view.workers.select()
@navigator.register(Server)
[docs]class CustomLogos(CFMENavigateStep):
VIEW = ServerView
prerequisite = NavigateToSibling('Details')
[docs] def am_i_here(self):
return (
self.view.is_displayed and self.view.custom_logos.is_displayed and
self.view.custom_logos.is_active)
[docs] def step(self):
self.prerequisite_view.customlogos.select()
@navigator.register(Server)
[docs]class Advanced(CFMENavigateStep):
VIEW = ServerView
prerequisite = NavigateToSibling('Details')
[docs] def am_i_here(self):
return (
self.view.is_displayed and self.view.advanced.is_displayed and
self.view.advanced.is_active)
[docs] def step(self):
self.prerequisite_view.advanced.select()
[docs]class ServerDatabaseView(ConfigurationView):
@View.nested
class summary(Tab): # noqa
TAB_NAME = "Summary"
@View.nested
class tables(Tab): # noqa
TAB_NAME = "Tables"
@View.nested
class indexes(Tab): # noqa
TAB_NAME = "Indexes"
@View.nested
class settings(Tab): # noqa
TAB_NAME = "Settings"
@View.nested
class client_connections(Tab): # noqa
TAB_NAME = "Client Connections"
@View.nested
class utilization(Tab): # noqa
TAB_NAME = "Utilization"
@property
def is_displayed(self):
return (
self.in_configuration and
self.summary.is_displayed and
self.title.text == 'VMDB Summary'
)
[docs]class DatabaseSummaryView(ServerDatabaseView):
summary = ParametrizedSummaryTable()
@property
def is_displayed(self):
return (
self.summary.is_displayed and
self.summary.is_active() and
self.title.text == 'VMDB Summary'
)
[docs]class DatabaseTablesView(ServerDatabaseView):
@property
def is_displayed(self):
return self.tables.is_active() and self.title.text == 'All VMDB Tables'
[docs]class DatabaseIndexesView(ServerDatabaseView):
@property
def is_displayed(self):
return self.indexes.is_active() and self.title.text == 'All VMDB Indexes'
[docs]class DatabaseSettingsView(ServerDatabaseView):
@property
def is_displayed(self):
return self.settings.is_active() and self.title.text == 'VMDB Settings'
[docs]class DatabaseClientConnectionsView(ServerDatabaseView):
@property
def is_displayed(self):
return self.client_connections.is_active() and self.title.text == 'VMDB Client Connections'
[docs]class DatabaseUtilizationView(ServerDatabaseView):
@property
def is_displayed(self):
return self.utilization.is_active() and self.title.text == 'VMDB Utilization'
@navigator.register(Server)
[docs]class Database(CFMENavigateStep):
VIEW = ServerDatabaseView
prerequisite = NavigateToSibling('Configuration')
[docs] def step(self):
self.prerequisite_view.accordions.database.tree.click_path('VMDB')
@navigator.register(Server)
[docs]class DatabaseSummary(CFMENavigateStep):
VIEW = DatabaseSummaryView
prerequisite = NavigateToSibling('Database')
[docs] def step(self):
self.prerequisite_view.summary.select()
@navigator.register(Server)
[docs]class DatabaseTables(CFMENavigateStep):
VIEW = DatabaseTablesView
prerequisite = NavigateToSibling('Database')
[docs] def step(self):
self.prerequisite_view.tables.select()
@navigator.register(Server)
[docs]class DatabaseIndexes(CFMENavigateStep):
VIEW = DatabaseIndexesView
prerequisite = NavigateToSibling('Database')
[docs] def step(self):
self.prerequisite_view.indexes.select()
@navigator.register(Server)
[docs]class DatabaseSettings(CFMENavigateStep):
VIEW = DatabaseSettingsView
prerequisite = NavigateToSibling('Database')
[docs] def step(self):
self.prerequisite_view.settings.select()
@navigator.register(Server)
[docs]class DatabaseClientConnections(CFMENavigateStep):
VIEW = DatabaseClientConnectionsView
prerequisite = NavigateToSibling('Database')
[docs] def step(self):
self.prerequisite_view.client_connections.select()
@navigator.register(Server)
[docs]class DatabaseUtilization(CFMENavigateStep):
VIEW = DatabaseUtilizationView
prerequisite = NavigateToSibling('Database')
[docs] def step(self):
self.prerequisite_view.utilization.select()
[docs]class ServerDiagnosticsView(ConfigurationView):
@View.nested
class summary(Tab): # noqa
TAB_NAME = "Summary"
started_on = SummaryFormItem('EVM', 'Started On')
@View.nested
class workers(Tab): # noqa
TAB_NAME = "Workers"
@View.nested
class collectlogs(Tab): # noqa
TAB_NAME = "Collect Logs"
@View.nested
class cfmelog(Tab): # noqa
TAB_NAME = "CFME Log"
@View.nested
class auditlog(Tab): # noqa
TAB_NAME = "Audit Log"
@View.nested
class productionlog(Tab): # noqa
TAB_NAME = "Production Log"
@View.nested
class utilization(Tab): # noqa
TAB_NAME = "Utilization"
@View.nested
class timelines(Tab, TimelinesView): # noqa
TAB_NAME = "Timelines"
configuration = Dropdown('Configuration')
@property
def is_displayed(self):
return self.prerequisite_view.accordions.diagnostics.tree.currently_selected == [
self.context['object'].zone.region.settings_string,
"Zone: {} (current)".format(self.context['object'].zone.description),
"Server: {} [{}] (current)".format(
self.context['object'].name, self.context['object'].sid)]
[docs]class ServerDiagnosticsCollectLogsView(ServerDiagnosticsView):
@property
def is_displayed(self):
return self.collectlogs.is_active()
@navigator.register(Server)
[docs]class Diagnostics(CFMENavigateStep):
VIEW = ServerDiagnosticsView
prerequisite = NavigateToSibling('Configuration')
[docs] def step(self):
self.prerequisite_view.accordions.diagnostics.tree.click_path(
self.obj.zone.region.settings_string,
"Zone: {} (current)".format(self.obj.zone.description),
"Server: {} [{}] (current)".format(
self.obj.name, self.obj.sid))
@navigator.register(Server)
[docs]class DiagnosticsDetails(CFMENavigateStep):
VIEW = ServerDiagnosticsView
prerequisite = NavigateToSibling('Diagnostics')
[docs] def am_i_here(self):
return (
self.view.is_displayed and self.view.summary.is_displayed and
self.view.summary.is_active)
[docs] def step(self):
self.prerequisite_view.summary.select()
@navigator.register(Server)
[docs]class DiagnosticsWorkers(CFMENavigateStep):
VIEW = ServerDiagnosticsView
prerequisite = NavigateToSibling('Diagnostics')
[docs] def am_i_here(self):
return (
self.view.is_displayed and self.view.workers.is_displayed and
self.view.workers.is_active)
[docs] def step(self):
self.prerequisite_view.workers.select()
@navigator.register(Server)
[docs]class ServerDiagnosticsCollectLogs(CFMENavigateStep):
VIEW = ServerDiagnosticsCollectLogsView
prerequisite = NavigateToSibling('Diagnostics')
[docs] def am_i_here(self):
return (
self.view.is_displayed and self.view.cfmelog.is_displayed and
self.view.cfmelog.is_active)
[docs] def step(self):
self.prerequisite_view.cfmelog.select()
@navigator.register(Server)
[docs]class CFMELog(CFMENavigateStep):
VIEW = ServerDiagnosticsView
prerequisite = NavigateToSibling('Diagnostics')
[docs] def am_i_here(self):
return (
self.view.is_displayed and self.view.cfmelog.is_displayed and
self.view.cfmelog.is_active)
[docs] def step(self):
self.prerequisite_view.cfmelog.select()
@navigator.register(Server)
[docs]class AuditLog(CFMENavigateStep):
VIEW = ServerDiagnosticsView
prerequisite = NavigateToSibling('Diagnostics')
[docs] def am_i_here(self):
return (
self.view.is_displayed and self.view.auditlog.is_displayed and
self.view.auditlog.is_active)
[docs] def step(self):
self.prerequisite_view.auditlog.select()
@navigator.register(Server)
[docs]class ProductionLog(CFMENavigateStep):
VIEW = ServerDiagnosticsView
prerequisite = NavigateToSibling('Diagnostics')
[docs] def am_i_here(self):
return (
self.view.is_displayed and self.view.productionlog.is_displayed and
self.view.productionlog.is_active)
[docs] def step(self):
self.prerequisite_view.productionlog.select()
@navigator.register(Server)
[docs]class Utilization(CFMENavigateStep):
VIEW = ServerDiagnosticsView
prerequisite = NavigateToSibling('Diagnostics')
[docs] def am_i_here(self):
return (
self.view.is_displayed and self.view.utilization.is_displayed and
self.view.utilization.is_active)
[docs] def step(self):
self.prerequisite_view.utilization.select()
@navigator.register(Server)
[docs]class Timelines(CFMENavigateStep):
VIEW = ServerDiagnosticsView
prerequisite = NavigateToSibling('Diagnostics')
[docs] def am_i_here(self):
return (
self.view.is_displayed and self.view.timelines.is_displayed and
self.view.timelines.is_active)
[docs] def step(self):
self.prerequisite_view.timelines.select()
# ######################## REGION NAVS ################################
[docs]class CompanyCategories(Tab):
TAB_NAME = "My Company Categories"
class ImportTags(Tab):
TAB_NAME = "Import Tags"
[docs]class ImportVariable(Tab):
TAB_NAME = "Import Variables"
[docs]class RegionView(ConfigurationView):
@View.nested
class details(Tab): # noqa
TAB_NAME = "Details"
table = Table(locator='.//table[./tbody/tr/td[@title="Edit this Region"]]')
@View.nested
class candu_collection(Tab): # noqa
TAB_NAME = "C & U Collection"
@View.nested
class redhat_updates(Tab): # noqa
TAB_NAME = "Red Hat Updates"
@View.nested
class imports(Tab): # noqa
TAB_NAME = "Import Variables"
@View.nested
class replication(Tab): # noqa
TAB_NAME = "Replication"
@View.nested
class help_menu(Tab): # noqa
TAB_NAME = "Help Menu"
company_categories = View.nested(CompanyCategories)
company_tags = View.nested(CompanyTags)
import_tags = View.nested(ImportTags)
map_tags = View.nested(MapTags)
# available starting from 5.9 version, not available in 5.7, 5.8
tags = View.nested(TagsView)
@property
def is_displayed(self):
return self.accordions.settings.tree.currently_selected == [self.obj.settings_string]
[docs]class RegionChangeNameView(RegionView):
region_description = Input("region_description")
save = Button('Save')
reset = Button('Reset')
cancel = Button('Cancel')
@property
def is_displayed(self):
return self.region_description.is_displayed and super(RegionChangeNameView,
self).is_displayed
@navigator.register(Region, 'Details')
[docs]class RegionDetails(CFMENavigateStep):
VIEW = RegionView
prerequisite = NavigateToAttribute('appliance.server', 'Configuration')
[docs] def step(self):
# TODO: This string can now probably be built up with the relevant server, zone,
# region objects
self.prerequisite_view.accordions.settings.tree.click_path(self.obj.settings_string)
self.view.details.select()
@navigator.register(Region)
[docs]class ChangeRegionName(CFMENavigateStep):
VIEW = RegionChangeNameView
prerequisite = NavigateToSibling('Details')
[docs] def step(self):
self.view.details.table.row().click()
@navigator.register(Region)
@navigator.register(Region)
[docs]class Import(CFMENavigateStep):
VIEW = RegionView
prerequisite = NavigateToSibling('Details')
[docs] def am_i_here(self):
return False
[docs] def step(self):
if self.obj.appliance.version < '5.9':
self.prerequisite_view.imports.select()
else:
self.prerequisite_view.tags.imports.select()
@navigator.register(Region)
[docs]class ZoneListView(ConfigurationView):
configuration = Dropdown('Configuration')
table = Table('//div[@id="settings_list"]/table')
@property
def is_displayed(self):
return (
self.accordions.settings.is_opened and
self.accordions.settings.tree.currently_selected == [
self.context['object'].settings_string, 'Zones'] and
self.title.text == 'Settings Zones' and
self.table.is_displayed)
@navigator.register(Region, 'Zones')
[docs]class RegionZones(CFMENavigateStep):
VIEW = ZoneListView
prerequisite = NavigateToAttribute('appliance.server', 'Configuration')
[docs] def step(self):
self.prerequisite_view.accordions.settings.tree.click_path(
self.obj.settings_string, 'Zones')
if not self.view.is_displayed:
# Zones is too smart and does not reload upon clicking, this helps
self.prerequisite_view.accordions.accesscontrol.open()
self.prerequisite_view.accordions.settings.tree.click_path(
self.obj.settings_string, 'Zones')
[docs]class RegionDiagnosticsView(ConfigurationView):
@View.nested
class zones(Tab): # noqa
TAB_NAME = "Zones"
@View.nested
class rolesbyservers(Tab): # noqa
TAB_NAME = "Roles by Servers"
@View.nested
class replication(Tab): # noqa
TAB_NAME = "Replication"
@View.nested
class serversbyroles(Tab): # noqa
TAB_NAME = "Servers by Roles"
@View.nested
class servers(Tab): # noqa
TAB_NAME = "Servers"
@View.nested
class database(Tab): # noqa
TAB_NAME = "Database"
@View.nested
class orphaneddata(Tab): # noqa
TAB_NAME = "Orphaned Data"
@property
def is_displayed(self):
return (
self.accordions.diagnostics.is_opened and
self.accordions.diagnostics.tree.currently_selected == [
self.context['object'].settings_string] and
self.title.text.startswith('Diagnostics Region '))
[docs]class RegionDiagnosticsDatabaseView(RegionDiagnosticsView):
db_backup_settings_type = BootstrapSelect(id='log_protocol')
submit_db_garbage_collection_button = Button(alt="Run Database Garbage Collection Now")
@property
def is_displayed(self):
return self.database.is_active()
@navigator.register(Region, 'Diagnostics')
[docs]class RegionDiagnostics(CFMENavigateStep):
VIEW = RegionDiagnosticsView
prerequisite = NavigateToAttribute('appliance.server', 'Configuration')
[docs] def step(self):
self.prerequisite_view.accordions.diagnostics.tree.click_path(self.obj.settings_string)
@navigator.register(Region, 'DiagnosticsZones')
[docs]class RegionDiagnosticsZones(CFMENavigateStep):
VIEW = RegionDiagnosticsView
prerequisite = NavigateToSibling('Diagnostics')
[docs] def am_i_here(self):
return False
[docs] def step(self):
self.prerequisite_view.zones.select()
@navigator.register(Region, 'RolesByServers')
[docs]class RegionDiagnosticsRolesByServers(CFMENavigateStep):
VIEW = RegionDiagnosticsView
prerequisite = NavigateToSibling('Diagnostics')
[docs] def am_i_here(self):
return False
[docs] def step(self):
self.prerequisite_view.rolesbyservers.select()
@navigator.register(Region, 'Replication')
[docs]class RegionDiagnosticsReplication(CFMENavigateStep):
VIEW = RegionDiagnosticsView
prerequisite = NavigateToSibling('Diagnostics')
[docs] def am_i_here(self):
return False
[docs] def step(self):
if self.obj.appliance.version < '5.7':
self.prerequisite_view.replication.select()
else:
raise DestinationNotFound('Replication destination is absent in 5.7')
@navigator.register(Region, 'ServersByRoles')
[docs]class RegionDiagnosticsServersByRoles(CFMENavigateStep):
VIEW = RegionDiagnosticsView
prerequisite = NavigateToSibling('Diagnostics')
[docs] def am_i_here(self):
return False
[docs] def step(self):
self.prerequisite_view.serversbyroles.select()
@navigator.register(Region, 'Servers')
[docs]class RegionDiagnosticsServers(CFMENavigateStep):
VIEW = RegionDiagnosticsView
prerequisite = NavigateToSibling('Diagnostics')
[docs] def am_i_here(self):
return False
[docs] def step(self):
self.prerequisite_view.servers.select()
@navigator.register(Region, 'Database')
[docs]class RegionDiagnosticsDatabase(CFMENavigateStep):
VIEW = RegionDiagnosticsDatabaseView
prerequisite = NavigateToSibling('Diagnostics')
[docs] def am_i_here(self):
return False
[docs] def step(self):
self.prerequisite_view.database.select()
@navigator.register(Region, 'OrphanedData')
[docs]class RegionDiagnosticsOrphanedData(CFMENavigateStep):
VIEW = RegionDiagnosticsView
prerequisite = NavigateToSibling('Diagnostics')
[docs] def am_i_here(self):
return False
[docs] def step(self):
self.prerequisite_view.orphaneddata.select()
# ################################## ZONE NAVS ##############################
[docs]class ZoneView(ConfigurationView):
@View.nested
class zone(Tab): # noqa
TAB_NAME = "Zone"
@View.nested
class smart_proxy_affinity(Tab): # noqa
TAB_NAME = "SmartProxy Affinity"
# Zone Details #
[docs]class ZoneDetailsView(ZoneView):
configuration = Dropdown('Configuration')
@property
def is_displayed(self):
return self.title.text.startswith(
'Settings Zone "{}"'.format(self.context['object'].description))
[docs]class ZoneSmartProxyAffinityView(ZoneView):
@property
def is_displayed(self):
return self.smart_proxy_affinity.is_active()
@navigator.register(Zone, 'Details')
[docs]class ZoneDetails(CFMENavigateStep):
VIEW = ZoneDetailsView
prerequisite = NavigateToAttribute('appliance.server.zone.region', 'Zones')
[docs] def step(self):
rows = self.prerequisite_view.table.rows((1, re.compile(r'Zone\s?\:\s?{}'.format(
self.obj.description))))
for row in rows:
row.click()
self.view.zone.select()
break
else:
raise ZoneNotFound(
"No unique Zones with the description '{}'".format(self.obj.description))
@navigator.register(Zone, 'SmartProxyAffinity')
[docs]class SmartProxyAffinity(CFMENavigateStep):
VIEW = ZoneSmartProxyAffinityView
prerequisite = NavigateToAttribute('appliance.server.zone.region', 'Zones')
[docs] def step(self):
rows = self.prerequisite_view.table.rows((1, re.compile(r'Zone\s?\:\s?{}'.format(
self.obj.description))))
for row in rows:
row.click()
self.view.smart_proxy_affinity.select()
break
else:
raise ZoneNotFound(
"No unique Zones with the description '{}'".format(self.obj.description))
# Zone Add #
[docs]class ZoneAddView(ZoneForm):
add_button = Button('Add')
@property
def is_displayed(self):
return self.title.text == 'Adding a new Zone'
@navigator.register(ZoneCollection, 'Add')
[docs]class ZoneAdd(CFMENavigateStep):
VIEW = ZoneAddView
prerequisite = NavigateToAttribute('appliance.server.zone.region', 'Zones')
[docs] def step(self):
self.prerequisite_view.configuration.item_select("Add a new Zone")
# Zone Edit #
[docs]class ZoneEditView(ZoneForm):
save_button = Button('Save')
@property
def is_displayed(self):
return self.title.text == 'Editing Zone "{}"'.format(self.context['object'].description)
@navigator.register(Zone, 'Edit')
[docs]class ZoneEdit(CFMENavigateStep):
VIEW = ZoneEditView
prerequisite = NavigateToSibling('Details')
[docs] def step(self):
self.prerequisite_view.configuration.item_select("Edit this Zone")
# Zone Diags #
[docs]class ZoneDiagnosticsView(ConfigurationView):
@View.nested
class rolesbyservers(Tab): # noqa
TAB_NAME = "Roles by Servers"
@View.nested
class serversbyroles(Tab): # noqa
TAB_NAME = "Servers by Roles"
@View.nested
class servers(Tab): # noqa
TAB_NAME = "Servers"
@View.nested
class collectlogs(Tab): # noqa
TAB_NAME = "Collect Logs"
@View.nested
class candugapcollection(Tab): # noqa
TAB_NAME = "C & U Gap Collection"
@property
def is_displayed(self):
return (
self.title.text == 'Diagnostics Zone "{}" (current)'.format(
self.context['object'].description))
[docs]class ZoneCollectLogsView(ZoneDiagnosticsView):
@property
def is_displayed(self):
return self.collectlogs.is_active()
@navigator.register(Zone, 'Diagnostics')
[docs]class ZoneDiagnostics(CFMENavigateStep):
VIEW = ZoneDiagnosticsView
prerequisite = NavigateToAttribute('appliance.server', 'Configuration')
[docs] def step(self):
self.prerequisite_view.accordions.diagnostics.tree.click_path(
self.obj.region.settings_string,
"Zone: {} (current)".format(self.obj.description))
@navigator.register(Zone, 'RolesByServers')
[docs]class ZoneDiagnosticsRolesByServers(CFMENavigateStep):
VIEW = ZoneDiagnosticsView
prerequisite = NavigateToSibling('Diagnostics')
[docs] def step(self):
self.prerequisite_view.rolesbyservers.select()
@navigator.register(Zone, 'ServersByRoles')
[docs]class ZoneDiagnosticsServersByRoles(CFMENavigateStep):
VIEW = ZoneDiagnosticsView
prerequisite = NavigateToSibling('Diagnostics')
[docs] def step(self):
self.prerequisite_view.serversbyroles.select()
@navigator.register(Zone, 'Servers')
[docs]class ZoneDiagnosticsServers(CFMENavigateStep):
VIEW = ZoneDiagnosticsView
prerequisite = NavigateToSibling('Diagnostics')
[docs] def step(self):
self.prerequisite_view.servers.select()
@navigator.register(Zone, 'CANDUGapCollection')
[docs]class ZoneCANDUGapCollection(CFMENavigateStep):
VIEW = ZoneDiagnosticsView
prerequisite = NavigateToSibling('Diagnostics')
[docs] def step(self):
self.prerequisite_view.candugapcollection.select()
@navigator.register(Zone)
[docs]class ZoneCollectLogs(CFMENavigateStep):
VIEW = ZoneCollectLogsView
prerequisite = NavigateToSibling('Diagnostics')
[docs] def step(self):
self.prerequisite_view.collectlogs.select()
@Zone.exists.external_getter_implemented_for(ViaUI)
[docs]def exists(self):
try:
navigate_to(self, 'Details')
return True
except ZoneNotFound:
return False
@MiqImplementationContext.external_for(Zone.update, ViaUI)
[docs]def update(self, updates):
view = navigate_to(self, 'Edit')
changed = view.fill(updates)
if changed:
view.save_button.click()
else:
view.cancel_button.click()
view = self.create_view(ZoneDetailsView)
# assert view.is_displayed
view.flash.assert_no_error()
if changed:
view.flash.assert_message(
'Zone "{}" was saved'.format(updates.get('name', self.name)))
else:
view.flash.assert_message(
'Edit of Zone "{}" was cancelled by the user'.format(self.name))
@MiqImplementationContext.external_for(Zone.delete, ViaUI)
[docs]def delete(self, cancel=False):
""" Delete the Zone represented by this object.
Args:
cancel: Whether to click on the cancel button in the pop-up.
"""
view = navigate_to(self, 'Details')
view.configuration.item_select('Delete this Zone', handle_alert=not cancel)
if not cancel:
view.flash.assert_message('Zone "{}": Delete successful'.format(self.name))
@MiqImplementationContext.external_for(ZoneCollection.create, ViaUI)
[docs]def create(self, name=None, description=None, smartproxy_ip=None, ntp_servers=None,
max_scans=None, user=None, cancel=False):
if BZ(1509452).blocks:
raise BugException(1509452, 'creating zones')
add_page = navigate_to(self, 'Add')
if not ntp_servers:
ntp_servers = []
fill_dict = {
k: v
for k, v in {
'name': name,
'description': description,
'smartproxy_ip': smartproxy_ip,
'ntp_server_1': ntp_servers[0] if len(ntp_servers) > 0 else None,
'ntp_server_2': ntp_servers[1] if len(ntp_servers) > 1 else None,
'ntp_server_3': ntp_servers[2] if len(ntp_servers) > 2 else None,
'max_scans': max_scans,
'user': user.principal if user else None,
'password': user.secret if user else None,
'verify': user.secret if user else None
}.items()
if v is not None}
add_page.fill(fill_dict)
if cancel:
add_page.cancel_button.click()
add_page.flash.assert_no_error()
add_page.flash.assert_message('Add of new Zone was cancelled by the user')
return None
else:
add_page.add_button.click()
add_page.flash.assert_no_error()
add_page.flash.assert_message('Zone "{}" was added'.format(name))
return self.instantiate(
name=name, description=description, smartproxy_ip=smartproxy_ip,
ntp_servers=ntp_servers, max_scans=max_scans, user=user
)
# AUTOMATE
[docs]class AutomateSimulationView(BaseLoggedInPage):
@property
def is_displayed(self):
return (
self.logged_in_as_current_user and
self.navigation.currently_selected == automate_menu_name(
self.context['object'].appliance) + ['Simulation'])
instance = BootstrapSelect('instance_name')
message = Input(name='object_message')
request = Input(name='object_request')
target_type = BootstrapSelect('target_class')
target_object = BootstrapSelect('target_id')
execute_methods = Checkbox(name='readonly')
avp = AttributeValueForm('attribute_', 'value_')
submit_button = Button(title='Submit Automation Simulation with the specified options')
result_tree = ManageIQTree(tree_id='ae_simulation_treebox')
@navigator.register(Server)
[docs]class AutomateSimulation(CFMENavigateStep):
VIEW = AutomateSimulationView
prerequisite = NavigateToSibling('LoggedIn')
[docs] def step(self):
self.prerequisite_view.navigation.select(
*automate_menu_name(self.obj.appliance) + ['Simulation'])
[docs]class AutomateImportExportBaseView(BaseLoggedInPage):
# TODO This is currently overiding the base flash and should be renamed and efforts made
# to update assocaited tests
flash = FlashMessages('div.import-flash-message')
title = Text('.//div[@id="main-content"]//h1')
@property
def in_import_export(self):
return (
self.logged_in_as_current_user and
self.navigation.currently_selected == automate_menu_name(
self.context['object'].appliance) + ['Import / Export'] and
self.title.text == 'Import / Export')
@property
def is_displayed(self):
return self.in_import_export
[docs]class AutomateImportExportView(AutomateImportExportBaseView):
class import_file(View): # noqa
file = FileInput(name='upload_file')
upload = Button('Upload')
class import_git(View): # noqa
ROOT = './/form[@id="retrieve-git-datastore-form"]'
url = Input(name='git_url')
username = Input(name='git_username')
password = Input(name='git_password')
verify_ssl = Checkbox(name='git_verify_ssl')
submit = Button(id='git-url-import')
export_all = Image('.//input[@title="Export all classes and instances"]')
reset_all = Image('.//img[starts-with(@alt, "Reset all components in the following domains:")]')
@property
def is_displayed(self):
return self.in_import_export and self.export_all.is_displayed
@navigator.register(Server)
[docs]class AutomateImportExport(CFMENavigateStep):
VIEW = AutomateImportExportView
prerequisite = NavigateToSibling('LoggedIn')
[docs] def step(self):
self.prerequisite_view.navigation.select(
*automate_menu_name(self.obj.appliance) + ['Import / Export'])