Source code for cfme.automate.explorer.instance

# -*- coding: utf-8 -*-
import attr

import re

from cached_property import cached_property

from navmazing import NavigateToAttribute, NavigateToSibling
from widgetastic.utils import ParametrizedLocator, ParametrizedString
from widgetastic.widget import Text, ParametrizedView
from widgetastic_manageiq import Table
from widgetastic_patternfly import CandidateNotFound, Input, Button

from cfme.exceptions import ItemNotFound
from cfme.modeling.base import BaseCollection, BaseEntity
from cfme.utils.appliance.implementations.ui import navigator, CFMENavigateStep, navigate_to

from . import AutomateExplorerView, check_tree_path
from .common import Copiable, CopyViewBase
from .klass import ClassDetailsView


[docs]class InstanceCopyView(AutomateExplorerView, CopyViewBase): @property def is_displayed(self): return ( self.in_explorer and self.title.text == 'Copy Automate Instance' and self.datastore.is_opened and check_tree_path( self.datastore.tree.currently_selected, self.context['object'].tree_path))
[docs]class InstanceDetailsView(AutomateExplorerView): title = Text('#explorer_title_text') table = Table('#instance_fields_grid') @property def is_displayed(self): return ( self.in_explorer and self.title.text.startswith('Automate Instance [{}'.format( self.context['object'].display_name or self.context['object'].name)) and self.datastore.is_opened and check_tree_path( self.datastore.tree.currently_selected, self.context['object'].tree_path))
[docs]class InstanceAddView(AutomateExplorerView): title = Text('#explorer_title_text') name = Input(name='cls_inst_name') display_name = Input(name='cls_inst_display_name') description = Input(name='cls_inst_description') @ParametrizedView.nested class fields(ParametrizedView): # noqa PARAMETERS = ('name', ) ROOT = ParametrizedLocator('.//tr[./td[1][contains(normalize-space(.), "({name})")]]') ALL_FIELDS = './/table//tr/td[1]' @cached_property def row_id(self): attr = self.browser.get_attribute( 'id', './td/input[starts-with(@id, "cls_inst_value_")]', parent=self) return int(attr.rsplit('_', 1)[-1]) value = Input(name=ParametrizedString('cls_inst_value_{@row_id}')) on_entry = Input(name=ParametrizedString('cls_inst_on_entry_{@row_id}')) on_exit = Input(name=ParametrizedString('cls_inst_on_exit_{@row_id}')) on_error = Input(name=ParametrizedString('cls_inst_on_error_{@row_id}')) collect = Input(name=ParametrizedString('cls_inst_collect_{@row_id}')) @classmethod def all(cls, browser): results = [] for e in browser.elements(cls.ALL_FIELDS): text = re.sub(r'^\(|\)$', '', browser.text(e)) results.append((text, )) return results add_button = Button('Add') cancel_button = Button('Cancel') @property def is_displayed(self): return ( self.in_explorer and self.title.text == 'Datastore' and self.datastore.is_opened and self.title.text == 'Adding a new Automate Instance')
[docs]class InstanceEditView(AutomateExplorerView): title = Text('#explorer_title_text') name = Input(name='inst_name') display_name = Input(name='inst_display_name') description = Input(name='inst_description') @ParametrizedView.nested class fields(ParametrizedView): # noqa PARAMETERS = ('name', ) ROOT = ParametrizedLocator('//h3[normalize-space(.)="Fields"]' '/following-sibling::table' '//tr[./td[1][contains(normalize-space(.), "({name})")]]') ALL_FIELDS = './/table//tr/td[1]' @cached_property def row_id(self): attr = self.browser.get_attribute( 'id', './td/input[starts-with(@id, "inst_value_")]', parent=self) return int(attr.rsplit('_', 1)[-1]) value = Input(name=ParametrizedString('inst_value_{@row_id}')) on_entry = Input(name=ParametrizedString('inst_on_entry_{@row_id}')) on_exit = Input(name=ParametrizedString('inst_on_exit_{@row_id}')) on_error = Input(name=ParametrizedString('inst_on_error_{@row_id}')) collect = Input(name=ParametrizedString('inst_collect_{@row_id}')) @classmethod def all(cls, browser): results = [] for e in browser.elements(cls.ALL_FIELDS): text = re.sub(r'^\(|\)$', '', browser.text(e)) results.append((text, )) return results save_button = Button('Save') cancel_button = Button('Cancel') @property def is_displayed(self): return ( self.in_explorer and self.title.text == 'Editing Automate Instance "{}"'.format(self.obj.name))
[docs]class Instance(BaseEntity, Copiable): ICON_NAME = 'fa-file-text-o' def __init__(self, collection, name, display_name=None, description=None, fields=None): super(Instance, self).__init__(collection) self.name = name if display_name is not None: self.display_name = display_name if description is not None: self.description = description self.fields = fields __repr__ = object.__repr__ @cached_property def display_name(self): return self.db_object.display_name @cached_property def description(self): return self.db_object.description @cached_property def db_id(self): table = self.appliance.db.client['miq_ae_instances'] try: return self.appliance.db.client.session.query(table.id).filter( table.name == self.name, table.class_id == self.klass.db_id)[0] # noqa except IndexError: raise ItemNotFound('Instance named {} not found in the database'.format(self.name)) @property def db_object(self): table = self.appliance.db.client['miq_ae_instances'] return self.appliance.db.client.session.query(table).filter(table.id == self.db_id).first() @property def klass(self): return self.parent_obj @property def namespace(self): return self.klass.namespace @property def parent_obj(self): return self.parent.parent @property def domain(self): return self.parent_obj.domain @property def tree_path(self): if self.display_name: return self.parent_obj.tree_path + [ (self.ICON_NAME, '{} ({})'.format(self.display_name, self.name))] else: return self.parent_obj.tree_path + [(self.ICON_NAME, self.name)] @property def tree_path_name_only(self): return self.parent_obj.tree_path_name_only + [self.name]
[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(InstanceDetailsView, override=updates) assert view.is_displayed view.flash.assert_no_error() if changed: view.flash.assert_message( 'Automate Instance "{}" was saved'.format(updates.get('name', self.name))) else: view.flash.assert_message( 'Edit of Automate Instance "{}" was cancelled by the user'.format(self.name))
[docs] def delete(self, cancel=False): # Ensure this has correct data self.description # Do it! details_page = navigate_to(self, 'Details') details_page.configuration.item_select('Remove this Instance', handle_alert=not cancel) if cancel: assert details_page.is_displayed details_page.flash.assert_no_error() else: result_view = self.create_view(ClassDetailsView, self.parent_obj) assert result_view.is_displayed result_view.flash.assert_no_error() result_view.flash.assert_message( 'Automate Instance "{}": Delete successful'.format(self.description or self.name))
@property def exists(self): try: navigate_to(self, 'Details') return True except CandidateNotFound: return False
[docs] def delete_if_exists(self): if self.exists: self.delete()
@attr.s
[docs]class InstanceCollection(BaseCollection): ENTITY = Instance @property def tree_path(self): return self.parent.tree_path
[docs] def create(self, name=None, display_name=None, description=None, fields=None, cancel=False): add_page = navigate_to(self, 'Add') fill_dict = { k: v for k, v in { 'name': name, 'display_name': display_name, 'description': description, 'fields': fields, }.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 Automate Instance was cancelled by the user') return None else: add_page.add_button.click() add_page.flash.assert_no_error() add_page.flash.assert_message('Automate Instance "{}" was added'.format(name)) return self.instantiate( name=name, display_name=display_name, description=description, fields=fields)
[docs] def delete(self, *instances): all_page = navigate_to(self.parent, 'Details') all_page.instances.select() instances = list(instances) parents = set() for instance in instances: parents.add(instance.parent) if len(parents) > 1: raise ValueError('You passed instances that are not under one class.') checked_instances = [] if not all_page.instances.table.is_displayed: raise ValueError('No instance found!') all_page.instances.table.uncheck_all() for row in all_page.instances.table: name = row[2].text for instance in instances: if ( (instance.display_name and instance.display_name == name) or instance.name == name): checked_instances.append(instance) row[0].check() break if set(instances) == set(checked_instances): break if set(instances) != set(checked_instances): raise ValueError('Some of the instances were not found in the UI.') all_page.configuration.item_select('Remove Instances', handle_alert=True) all_page.flash.assert_no_error() for instance in checked_instances: all_page.flash.assert_message( 'Automate Instance "{}": Delete successful'.format(instance.name))
@navigator.register(InstanceCollection)
[docs]class Add(CFMENavigateStep): VIEW = InstanceAddView prerequisite = NavigateToAttribute('parent', 'Details')
[docs] def step(self): self.prerequisite_view.instances.select() self.prerequisite_view.configuration.item_select('Add a New Instance')
@navigator.register(Instance)
[docs]class Details(CFMENavigateStep): VIEW = InstanceDetailsView prerequisite = NavigateToAttribute('appliance.server', 'AutomateExplorer')
[docs] def step(self): self.prerequisite_view.datastore.tree.click_path(*self.obj.tree_path)
@navigator.register(Instance)
[docs]class Edit(CFMENavigateStep): VIEW = InstanceEditView prerequisite = NavigateToSibling('Details')
[docs] def step(self): self.prerequisite_view.configuration.item_select('Edit this Instance')
@navigator.register(Instance)
[docs]class Copy(CFMENavigateStep): VIEW = InstanceCopyView prerequisite = NavigateToSibling('Details')
[docs] def step(self): self.prerequisite_view.configuration.item_select('Copy this Instance')