import attr
import re
from navmazing import NavigateToAttribute, NavigateToSibling
from widgetastic.utils import Version, VersionPick
from widgetastic.widget import View
from widgetastic_patternfly import (
BreadCrumb, BootstrapSwitch, Button, CheckableBootstrapTreeview, Dropdown, Input, Tab)
from cfme.base import BaseEntity, BaseCollection
from cfme.base.login import BaseLoggedInPage
from cfme.utils.appliance import NavigatableMixin
from cfme.utils.appliance.implementations.ui import navigator, CFMENavigateStep, navigate_to
from cfme.utils.log import logger
from cfme.utils.pretty import Pretty
from cfme.utils.update import Updateable
from widgetastic_manageiq import Table, BootstrapSelect, Text, ViewButtonGroup
[docs]class TimeProfileEntities(View):
table = Table('//div[@id="main_div"]//table')
breadcrumb = BreadCrumb()
title = Text('//div[@id="main-content"]//h3')
[docs]class TimeProfileView(BaseLoggedInPage):
entities = View.nested(TimeProfileEntities)
configuration = Dropdown('Configuration')
[docs]class TimeProfileAddView(TimeProfileView):
@View.nested
class form(TimeProfileForm): # noqa
add = Button(VersionPick({Version.lowest(): 'Save', '5.9': 'Add'}))
@property
def is_displayed(self):
return self.entities.title == 'Time Profile Information'
[docs]class TimeProfileEditView(TimeProfileView):
@View.nested
class form(TimeProfileForm): # noqa
reset = Button('Reset')
save = Button('Save')
@property
def is_displayed(self):
return 'Edit' in self.entities.breadcrumb.active_location
@attr.s
[docs]class TimeProfile(Updateable, BaseEntity):
description = attr.ib(default=None)
scope = attr.ib(default=None)
days = attr.ib(default=True)
hours = attr.ib(default=True)
timezone = attr.ib(default=None)
[docs] def update(self, updates):
"""
This method is used for updating the time_profile
Args:
updates: It the object of the time_profile that we need to update.
"""
view = navigate_to(self, 'Edit')
changed = view.form.fill({
'description': updates.get('description'),
'scope': updates.get('scope'),
'days': updates.get('days'),
'hours': updates.get('hours'),
'timezone': updates.get('timezone'),
})
if changed:
view.form.save.click()
view.flash.assert_no_error()
[docs] def copy(self, description=None, cancel=False):
"""
This method performs the copy of the provided time profile object.
Args:
description (str) : It's the descriptive name of the new copied time_profile.
cancel (bool) : This variable performs cancel operation while copy.
return: It returns the object of the copied time_profile.
"""
view = navigate_to(self, 'Copy')
if description is not None:
new_time_profile = self.parent.instantiate(description=description, scope=self.scope)
changed = view.form.fill({
'description': description,
'scope': self.scope,
})
else:
new_time_profile = self.parent.instantiate(
description="{} copy".format(self.description),
scope=self.scope
)
changed = view.form.fill({
'description': "{} copy".format(self.description),
'scope': self.scope,
})
if not cancel and changed:
view.form.add.click()
view.flash.assert_no_error()
return new_time_profile
@attr.s
[docs]class TimeProfileCollection(BaseCollection):
ENTITY = TimeProfile
[docs] def create(self, description=None, scope=None, days=True, hours=True,
timezone=None, cancel=False):
"""
Args:
description (str): It's the descriptive name of the time_profile.
scope: It's the option 'All User' or 'Current User' from dropdown.
days (bool): It's the option to switch on or switch off the days Bootstrap switch.
hours (bool): It's the option to switch on or switch off the hours Bootstrap switch.
timezone: It's the required Time Zone for the time_profile.
cancel (bool) : It's a flag used to cancel or not the create operation.
return: It returns the object of the newly created time_profile object.
"""
time_profile = self.instantiate(description, scope, days, hours, timezone)
view = navigate_to(self, 'Add')
view.form.fill({
'description': description,
'scope': scope,
'days': days,
'hours': hours,
'timezone': timezone,
})
if not cancel:
view.form.add.click()
view.flash.assert_no_error()
return time_profile
[docs] def delete(self, cancel=False, *time_objs):
"""
This method performs the delete operation.
Args:
cancel (bool) : It's a flag used for selecting Ok or Cancel from delete confirmation
dialogue box
time_objs : It's time profile object.
"""
view = navigate_to(self, 'All')
for time_obj in time_objs:
view.entities.table.row(Description=time_obj.description)[0].check()
view.configuration.item_select("Delete selected Time Profiles",
handle_alert=not cancel)
view.flash.assert_no_error()
[docs]class TimeProfilesView(BaseLoggedInPage):
table = Table("//div[@id='main_div']//table")
help_block = Text("//span[contains(@class, 'help-block')]")
configuration = Dropdown('Configuration')
[docs]class Visual(Updateable, NavigatableMixin):
def __init__(self, appliance, my_settings):
self.appliance = appliance
self.my_settings = my_settings
@property
def grid_view_limit(self):
view = navigate_to(self.my_settings, 'Visual')
value = re.findall('\d+', view.tabs.visual.default_items_per_page.grid_view.read())
return int(value[0])
@grid_view_limit.setter
def grid_view_limit(self, value):
view = navigate_to(self.my_settings, 'Visual')
value_to_fill = str(value)
if view.tabs.visual.default_items_per_page.grid_view.fill(value_to_fill):
view.tabs.visual.save.click()
@property
def tile_view_limit(self):
view = navigate_to(self.my_settings, 'Visual')
value = re.findall('\d+', view.tabs.visual.default_items_per_page.tile_view.read())
return int(value[0])
@tile_view_limit.setter
def tile_view_limit(self, value):
view = navigate_to(self.my_settings, 'Visual')
value_to_fill = str(value)
if view.tabs.visual.default_items_per_page.tile_view.fill(value_to_fill):
view.tabs.visual.save.click()
@property
def list_view_limit(self):
view = navigate_to(self.my_settings, 'Visual')
value = re.findall('\d+', view.tabs.visual.default_items_per_page.list_view.read())
return int(value[0])
@list_view_limit.setter
def list_view_limit(self, value):
view = navigate_to(self.my_settings, 'Visual')
value_to_fill = str(value)
if view.tabs.visual.default_items_per_page.list_view.fill(value_to_fill):
view.tabs.visual.save.click()
@property
def report_view_limit(self):
view = navigate_to(self.my_settings, 'Visual')
value = re.findall('\d+', view.tabs.visual.default_items_per_page.reports.read())
return int(value[0])
@report_view_limit.setter
def report_view_limit(self, value):
view = navigate_to(self.my_settings, 'Visual')
value_to_fill = str(value)
if view.tabs.visual.default_items_per_page.reports.fill(value_to_fill):
view.tabs.visual.save.click()
@property
def login_page(self):
view = navigate_to(self.my_settings, 'Visual')
return view.tabs.visual.start_page.show_at_login.read()
@login_page.setter
def login_page(self, value):
view = navigate_to(self.my_settings, 'Visual')
if view.tabs.visual.start_page.show_at_login.fill(value):
view.tabs.visual.save.click()
@property
def infra_provider_quad(self):
view = navigate_to(self.my_settings, 'Visual')
return view.tabs.visual.grid_tile_icons.infra_provider_quad.read()
@infra_provider_quad.setter
def infra_provider_quad(self, value):
view = navigate_to(self.my_settings, 'Visual')
if view.tabs.visual.grid_tile_icons.infra_provider_quad.fill(value):
view.tabs.visual.save.click()
@property
def host_quad(self):
view = navigate_to(self.my_settings, 'Visual')
return view.tabs.visual.grid_tile_icons.host_quad.read()
@host_quad.setter
def host_quad(self, value):
view = navigate_to(self.my_settings, 'Visual')
if view.tabs.visual.grid_tile_icons.host_quad.fill(value):
view.tabs.visual.save.click()
@property
def datastore_quad(self):
view = navigate_to(self.my_settings, 'Visual')
return view.tabs.visual.grid_tile_icons.datastore_quad.read()
@datastore_quad.setter
def datastore_quad(self, value):
view = navigate_to(self.my_settings, 'Visual')
if view.tabs.visual.grid_tile_icons.datastore_quad.fill(value):
view.tabs.visual.save.click()
@property
def vm_quad(self):
view = navigate_to(self.my_settings, 'Visual')
return view.tabs.visual.grid_tile_icons.vm_quad.read()
@vm_quad.setter
def vm_quad(self, value):
view = navigate_to(self.my_settings, 'Visual')
if view.tabs.visual.grid_tile_icons.vm_quad.fill(value):
view.tabs.visual.save.click()
@property
def template_quad(self):
view = navigate_to(self.my_settings, 'Visual')
return view.tabs.visual.grid_tile_icons.template_quad.read()
@template_quad.setter
def template_quad(self, value):
view = navigate_to(self.my_settings, 'Visual')
if view.tabs.visual.grid_tile_icons.template_quad.fill(value):
view.tabs.visual.save.click()
@property
def cloud_provider_quad(self):
view = navigate_to(self.my_settings, 'Visual')
return view.tabs.visual.grid_tile_icons.cloud_provider_quad.read()
@cloud_provider_quad.setter
def cloud_provider_quad(self, value):
view = navigate_to(self.my_settings, 'Visual')
if view.tabs.visual.grid_tile_icons.cloud_provider_quad.fill(value):
view.tabs.visual.save.click()
@property
def timezone(self):
view = navigate_to(self.my_settings, 'Visual')
return view.tabs.visual.display_settings.time_zone.read()
@timezone.setter
def timezone(self, value):
view = navigate_to(self.my_settings, 'Visual')
if view.tabs.visual.display_settings.time_zone.fill(value):
view.tabs.visual.save.click()
@property
def grid_view_entities(self):
view = navigate_to(self.my_settings, 'Visual')
values = view.tabs.visual.default_items_per_page.grid_view.all_options
text = [value.text for value in values]
return text
@property
def tile_view_entities(self):
view = navigate_to(self.my_settings, 'Visual')
values = view.tabs.visual.default_items_per_page.tile_view.all_options
text = [value.text for value in values]
return text
@property
def list_view_entities(self):
view = navigate_to(self.my_settings, 'Visual')
values = view.tabs.visual.default_items_per_page.list_view.all_options
text = [value.text for value in values]
return text
@property
def report_view_entities(self):
view = navigate_to(self.my_settings, 'Visual')
values = view.tabs.visual.default_items_per_page.reports.all_options
text = [value.text for value in values]
return text
[docs]class DefaultViews(Updateable, NavigatableMixin):
# Basic class for navigation to default views screen
look_up = {
'Flavors': ['clouds', 'flavors'],
'Instances': ['clouds', 'instances'],
'Availability Zones': ['clouds', 'availability_zones'],
'Images': ['clouds', 'images'],
'Cloud Providers': ['clouds', 'providers'],
'Stacks': ['clouds', 'stacks'],
'Compare': ['general', 'compare'],
'Compare Mode': ['general', 'compare_mode'],
'Infrastructure Providers': ['infrastructure', 'infrastructure_providers'],
'Configuration Management Providers': ['infrastructure',
'configuration_management_providers'],
'VMs': ['infrastructure', 'vms'],
'My Services': ['services', 'my_services'],
'Catalog Items': ['services', 'catalog_items'],
'Templates & Images': ['services', 'templates'],
'VMs & Instances': ['services', 'vms_instances'],
'Containers Providers': ['containers', 'providers'],
'Nodes': ['containers', 'nodes'],
'Pods': ['containers', 'pods'],
'Services': ['containers', 'services'],
'Routes': ['containers', 'routes'],
'Containers': ['containers', 'containers'],
'Projects': ['containers', 'projects'],
'Replicators': ['containers', 'replicators'],
'Container Images': ['containers', 'images'],
'Image Registries': ['containers', 'image_registries'],
'Builds': ['containers', 'builds'],
'Volumes': ['containers', 'volumes'],
'Templates': ['containers', 'templates']
}
def __init__(self, appliance, my_settings):
self.appliance = appliance
self.my_settings = my_settings
[docs] def set_default_view(self, button_group_names, defaults, fieldset=None):
"""This function sets default views for the objects.
Args:
button_group_names: either the name of the button_group_name
or list of the button groups to set the
default view for.
defaults: the default view to set. in case that button_group_names
is a list, you can either set 1 view and it'll be set
for all the button_group_names or you can use a list
(default view per button_group_name).
Raises:
AssertionError
"""
if not isinstance(button_group_names, (list, tuple)):
button_group_names = [button_group_names]
if not isinstance(defaults, (list, tuple)):
defaults = [defaults] * len(button_group_names)
assert len(button_group_names) == len(defaults)
for button_group_name, default in zip(button_group_names, defaults):
view = navigate_to(self.my_settings, 'DefaultViews')
value = view.tabs.default_views
for attribute in self.look_up[button_group_name]:
value = getattr(value, attribute)
if value.active_button != default:
if value.fill(default):
view.tabs.default_views.save.click()
[docs] def get_default_view(self, button_group_name, fieldset=None):
view = navigate_to(self.my_settings, 'DefaultViews')
value = view.tabs.default_views
for attribute in self.look_up[button_group_name]:
value = getattr(value, attribute)
return value.active_button
[docs] def set_default_view_switch_on(self):
view = navigate_to(self.my_settings, 'DefaultViews')
if view.tabs.default_views.vm_visibility.show_vms.fill(True):
view.tabs.default_views.save.click()
return True
[docs] def set_default_view_switch_off(self):
view = navigate_to(self.my_settings, 'DefaultViews')
if view.tabs.default_views.vm_visibility.show_vms.fill(False):
view.tabs.default_views.save.click()
return True
[docs]class DefaultFilters(Updateable, Pretty, NavigatableMixin):
pretty_attrs = ['name', 'filters']
def __init__(self, appliance, my_settings, name=None, filters=None):
self.appliance = appliance
self.my_settings = my_settings
self.name = name
self.filters = filters or []
[docs] def update(self, updates):
"""
Args:
updates: Dictionary containing 'filters' key. Values are tuples of ([path], bool)
Where bool is whether to check or uncheck the filter
Returns: None
"""
view = navigate_to(self.my_settings, 'DefaultFilters')
tree = view.tabs.default_filters.tree
for path, check in updates['filters']:
fill_value = tree.CheckNode(path) if check else tree.UncheckNode(path)
if tree.fill(fill_value):
view.tabs.default_filters.save.click()
else:
logger.info('No change to filter on update, not saving form.')
[docs]class TimeProfiles(Updateable, NavigatableMixin):
def __init__(self, appliance, my_settings):
self.appliance = appliance
self.my_settings = my_settings
[docs]class MySettingsEntities(View):
table = Table('//div[@id="main_div"]//table')
breadcrumb = BreadCrumb()
title = Text('//div[@id="main-content"]//h3')
[docs]class MySettingsView(BaseLoggedInPage):
"""The My Settings page"""
entities = View.nested(MySettingsEntities)
configuration = Dropdown('Configuration')
@View.nested
class tabs(View): # noqa
"""The tabs on the page"""
@View.nested
class visual(Tab): # noqa
TAB_NAME = 'Visual'
including_entities = View.include(VisualForm, use_parent=True)
@View.nested
class default_views(Tab): # noqa
TAB_NAME = 'Default Views'
including_entities = View.include(DefaultViewsForm, use_parent=True)
@View.nested
class default_filters(Tab): # noqa
TAB_NAME = 'Default Filters'
including_entities = View.include(DefaultFiltersForm, use_parent=True)
@View.nested
class time_profiles(Tab): # noqa
TAB_NAME = 'Time Profiles'
including_entities = View.include(TimeProfilesView, use_parent=True)
@property
def is_displayed(self):
return (
self.tabs.visual.is_displayed and
self.tabs.default_views.is_displayed and
self.tabs.default_filters.is_displayed and
self.tabs.time_profiles.is_displayed)
[docs]class MySettings(Updateable, NavigatableMixin):
"""The 'My Settings' page"""
def __init__(self, appliance):
self.appliance = appliance
@property
def visual(self):
"""The 'Visual' tab on the 'My Settings' page"""
return Visual(self.appliance, self)
@property
def default_views(self):
"""The 'Default Views' tab on the 'My Settings' page"""
return DefaultViews(self.appliance, self)
@property
def default_filters(self):
"""The 'Default Filters' tab on the 'My Settings' page"""
return DefaultFilters(self.appliance, self)
@property
def time_profiles(self):
"""The 'Time Profiles' tab on the 'My Settings' page"""
return TimeProfiles(self.appliance, self)
@navigator.register(MySettings, 'MySettings')
[docs]class MySettingsStep(CFMENavigateStep):
VIEW = MySettingsView
prerequisite = NavigateToAttribute('appliance.server', 'LoggedIn')
[docs] def step(self):
"""Go to the My Settings view"""
self.prerequisite_view.settings.select_item('My Settings')
@navigator.register(MySettings, 'Visual')
[docs]class VisualStep(CFMENavigateStep):
VIEW = MySettingsView
prerequisite = NavigateToSibling('MySettings')
[docs] def step(self):
self.prerequisite_view.tabs.visual.select()
[docs] def resetter(self):
self.view.tabs.visual.select()
@navigator.register(MySettings, 'DefaultViews')
[docs]class DefaultViewsStep(CFMENavigateStep):
VIEW = MySettingsView
prerequisite = NavigateToSibling('MySettings')
[docs] def step(self):
self.prerequisite_view.tabs.default_views.select()
[docs] def resetter(self):
self.view.tabs.default_views.select()
@navigator.register(MySettings, 'DefaultFilters')
@navigator.register(TimeProfileCollection, 'All')
[docs]class TimeProfileCollectionAll(CFMENavigateStep):
VIEW = MySettingsView
prerequisite = NavigateToAttribute('appliance.user.my_settings', 'MySettings')
[docs] def step(self):
self.prerequisite_view.tabs.time_profiles.select()
[docs] def resetter(self):
self.view.tabs.time_profiles.select()
@navigator.register(TimeProfileCollection, 'Add')
[docs]class TimeProfileAdd(CFMENavigateStep):
VIEW = TimeProfileAddView
prerequisite = NavigateToSibling('All')
[docs] def step(self):
self.prerequisite_view.tabs.time_profiles.select()
self.prerequisite_view.configuration.item_select('Add a new Time Profile')
@navigator.register(TimeProfile, 'Edit')
[docs]class TimeProfileEdit(CFMENavigateStep):
VIEW = TimeProfileEditView
prerequisite = NavigateToAttribute('parent', 'All')
[docs] def step(self):
self.view.entities.table.row(Description=self.obj.description)[0].check()
self.view.configuration.item_select('Edit selected Time Profile')
@navigator.register(TimeProfile, 'Copy')
[docs]class TimeProfileCopy(CFMENavigateStep):
VIEW = TimeProfileAddView
prerequisite = NavigateToAttribute('parent', 'All')
[docs] def step(self):
self.view.entities.table.row(Description=self.obj.description)[0].check()
self.view.configuration.item_select('Copy selected Time Profile')