Source code for cfme.infrastructure.provider

""" A model of an Infrastructure Provider in CFME
"""
import attr

from navmazing import NavigateToSibling, NavigateToAttribute
from widgetastic.exceptions import MoveTargetOutOfBoundsException
from widgetastic.utils import Fillable

from cfme.base.ui import Server
from cfme.common import TagPageView
from cfme.common.provider import CloudInfraProvider, provider_types
from cfme.common.provider_views import (InfraProviderAddView,
                                        InfraProviderEditView,
                                        InfraProviderDetailsView,
                                        ProviderTimelinesView,
                                        InfraProvidersDiscoverView,
                                        InfraProvidersView,
                                        ProviderNodesView,
                                        ProviderTemplatesView,
                                        ProviderVmsView)
from cfme.exceptions import DestinationNotFound
from cfme.infrastructure.cluster import ClusterView, ClusterToolbar
from cfme.infrastructure.host import Host
from cfme.modeling.base import BaseCollection
from cfme.utils import conf
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.varmeth import variable
from cfme.utils.wait import wait_for
from widgetastic_manageiq import BreadCrumb, BaseEntitiesView, View, NoSuchElementException


[docs]class ProviderClustersView(ClusterView): """The all view page for clusters open from provider detail page""" @property def is_displayed(self): """Determine if this page is currently being displayed""" return ( self.logged_in_as_current_user and self.entities.title.text == '{p}(All Clusters)'.format(p=self.context['object'].name)) toolbar = View.nested(ClusterToolbar) breadcrumb = BreadCrumb() including_entities = View.include(BaseEntitiesView, use_parent=True)
@attr.s(hash=False)
[docs]class InfraProvider(Pretty, CloudInfraProvider, Fillable): """ Abstract model of an infrastructure provider in cfme. See VMwareProvider or RHEVMProvider. Args: name: Name of the provider. details: a details record (see VMwareDetails, RHEVMDetails inner class). key: The CFME key of the provider in the yaml. endpoints: one or several provider endpoints like DefaultEndpoint. it should be either dict in format dict{endpoint.name, endpoint, endpoint_n.name, endpoint_n}, list of endpoints or mere one endpoint Usage: credentials = Credential(principal='bad', secret='reallybad') endpoint = DefaultEndpoint(hostname='some_host', api_port=65536, credentials=credentials) myprov = VMwareProvider(name='foo', region='us-west-1' endpoints=endpoint) myprov.create() """ provider_types = {} category = "infra" pretty_attrs = ['name', 'key', 'zone'] STATS_TO_MATCH = ['num_template', 'num_vm', 'num_datastore', 'num_host', 'num_cluster'] string_name = "Infrastructure" templates_destination_name = "Templates" template_name = "Templates" db_types = ["InfraManager"] hosts_menu_item = "Hosts" vm_name = "Virtual Machines" name = attr.ib(default=None) key = attr.ib(default=None) zone = attr.ib(default=None) # hostname = attr.ib(default=None) # defined in CloudInfraProvider class # ip_address = attr.ib(default=None) # defined in CloudInfraProvider class start_ip = attr.ib(default=None) end_ip = attr.ib(default=None) provider_data = attr.ib(default=None) def __attrs_post_init__(self): super(InfraProvider, self).__attrs_post_init__() self.parent = self.appliance.collections.infra_providers @variable(alias='db') def num_datastore(self): """ Returns the providers number of templates, as shown on the Details page.""" results = list(self.appliance.db.client.engine.execute( 'SELECT DISTINCT storages.name, hosts.ems_id ' 'FROM ext_management_systems ems, hosts, storages st, host_storages hst' 'WHERE hosts.id=hst.host_id AND ' 'st.id=hst.storage_id AND ' 'hosts.ems_id=ems.id AND ' 'ems.name=\'{}\''.format(self.name))) return len(results) @num_datastore.variant('ui') def num_datastore_ui(self): view = navigate_to(self, "Details") return int(view.entities.summary("Relationships").get_text_of("Datastores")) @variable(alias='rest') def num_host(self): provider = self.appliance.rest_api.collections.providers.find_by(name=self.name)[0] num_host = 0 for host in self.appliance.rest_api.collections.hosts: if host['ems_id'] == provider.id: num_host += 1 return num_host @num_host.variant('db') def num_host_db(self): ext_management_systems = self.appliance.db.client["ext_management_systems"] hosts = self.appliance.db.client["hosts"] hostlist = list(self.appliance.db.client.session.query(hosts.name) .join(ext_management_systems, hosts.ems_id == ext_management_systems.id) .filter(ext_management_systems.name == self.name)) return len(hostlist) @num_host.variant('ui') def num_host_ui(self): view = navigate_to(self, "Details") try: num = view.entities.summary("Relationships").get_text_of("Hosts") except NoSuchElementException: logger.error("Couldn't find number of hosts using key [Hosts] trying Nodes") num = view.entities.summary("Relationships").get_text_of("Nodes") return int(num) @variable(alias='rest') def num_cluster(self): provider = self.appliance.rest_api.collections.providers.find_by(name=self.name)[0] num_cluster = 0 for cluster in self.appliance.rest_api.collections.clusters: if cluster['ems_id'] == provider.id: num_cluster += 1 return num_cluster @num_cluster.variant('db') def num_cluster_db(self): """ Returns the providers number of templates, as shown on the Details page.""" ext_management_systems = self.appliance.db.client["ext_management_systems"] clusters = self.appliance.db.client["ems_clusters"] clulist = list(self.appliance.db.client.session.query(clusters.name) .join(ext_management_systems, clusters.ems_id == ext_management_systems.id) .filter(ext_management_systems.name == self.name)) return len(clulist) @num_cluster.variant('ui') def num_cluster_ui(self): view = navigate_to(self, "Details") return int(view.entities.summary("Relationships").get_text_of("Clusters")) @property def hosts(self): """Returns list of :py:class:`cfme.infrastructure.host.Host` that should belong to this provider according to the YAML """ result = [] host_collection = self.appliance.collections.hosts for host in self.data.get("hosts", []): creds = conf.credentials.get(host["credentials"], {}) cred = Host.Credential( principal=creds["username"], secret=creds["password"], verify_secret=creds["password"], ) result.append(host_collection.instantiate(name=host["name"], credentials=cred, provider=self)) return result
[docs] def get_clusters(self): """returns the list of clusters belonging to the provider""" view = navigate_to(self, 'Clusters') col = self.appliance.collections.clusters return [col.instantiate(e.name, self) for e in view.entities.get_all(surf_pages=True)]
[docs] def as_fill_value(self): return self.name
@property def view_value_mapping(self): return {'name': self.name}
# todo: update all docstrings @attr.s
[docs]class InfraProviderCollection(BaseCollection): """Collection object for InfraProvider object """ ENTITY = InfraProvider
[docs] def all(self): view = navigate_to(self, 'All') provs = view.entities.get_all(surf_pages=True) # trying to figure out provider type and class # todo: move to all providers collection later def _get_class(pid): prov_type = self.appliance.rest_api.collections.providers.get(id=pid)['type'] for prov_class in provider_types('infra').values(): if prov_class.db_types[0] in prov_type: return prov_class return [self.instantiate(prov_class=_get_class(p.data['id']), name=p.name) for p in provs]
[docs] def instantiate(self, prov_class, *args, **kwargs): return prov_class.from_collection(self, *args, **kwargs)
[docs] def create(self, prov_class, *args, **kwargs): # ugly workaround until I move everything to main class class_attrs = [at.name for at in attr.fields(prov_class)] init_kwargs = {} create_kwargs = {} for name, value in kwargs.items(): if name not in class_attrs: create_kwargs[name] = value else: init_kwargs[name] = value obj = self.instantiate(prov_class, *args, **init_kwargs) obj.create(**create_kwargs) return obj
[docs] def discover(self, discover_cls, cancel=False, start_ip=None, end_ip=None): """ Discover infrastructure providers. Note: only starts discovery, doesn't wait for it to finish. Args: discover_cls: Instance of provider class cancel: Whether to cancel out of the discover UI. start_ip: String start of the IP range for discovery end_ip: String end of the IP range for discovery """ form_data = {} if discover_cls: form_data.update(discover_cls.discover_dict) if start_ip: # TODO: add support of IPv6 for idx, octet in enumerate(start_ip.split('.'), start=1): key = 'from_ip{idx}'.format(idx=idx) form_data.update({key: octet}) if end_ip: end_octet = end_ip.split('.')[-1] form_data.update({'to_ip4': end_octet}) view = navigate_to(self, 'Discover') view.fill(form_data) if cancel: view.cancel.click() else: view.start.click()
[docs] def wait_for_a_provider(self): view = navigate_to(self, 'All') logger.info('Waiting for a provider to appear...') wait_for(lambda: int(view.entities.paginator.items_amount), fail_condition=0, message="Wait for any provider to appear", num_sec=1000, fail_func=view.browser.refresh)
@navigator.register(InfraProviderCollection, 'All') @navigator.register(Server, 'InfraProviders') @navigator.register(InfraProvider, 'All')
[docs]class All(CFMENavigateStep): VIEW = InfraProvidersView prerequisite = NavigateToAttribute('appliance.server', 'LoggedIn')
[docs] def step(self): self.prerequisite_view.navigation.select('Compute', 'Infrastructure', 'Providers')
[docs] def resetter(self): # Reset view and selection self.appliance.browser.widgetastic.browser.refresh() tb = self.view.toolbar if 'Grid View' not in tb.view_selector.selected: tb.view_selector.select('Grid View') self.view.entities.paginator.reset_selection()
@navigator.register(InfraProviderCollection, 'Add') @navigator.register(InfraProvider, 'Add')
[docs]class Add(CFMENavigateStep): VIEW = InfraProviderAddView prerequisite = NavigateToSibling('All')
[docs] def step(self): try: self.prerequisite_view.toolbar.configuration.item_select('Add a New ' 'Infrastructure Provider') except MoveTargetOutOfBoundsException: # TODO: Remove once fixed 1475303 self.prerequisite_view.toolbar.configuration.item_select('Add a New ' 'Infrastructure Provider')
@navigator.register(InfraProviderCollection, 'Discover')
[docs]class Discover(CFMENavigateStep): VIEW = InfraProvidersDiscoverView prerequisite = NavigateToSibling('All')
[docs] def step(self): try: self.prerequisite_view.toolbar.configuration.item_select('Discover ' 'Infrastructure Providers') except MoveTargetOutOfBoundsException: # TODO: Remove once fixed 1475303 self.prerequisite_view.toolbar.configuration.item_select('Discover ' 'Infrastructure Providers')
@navigator.register(InfraProvider, 'Details')
[docs]class Details(CFMENavigateStep): VIEW = InfraProviderDetailsView prerequisite = NavigateToSibling('All')
[docs] def step(self): self.prerequisite_view.entities.get_entity(name=self.obj.name, surf_pages=True).click()
[docs] def resetter(self): """Reset view and selection""" view_selector = self.view.toolbar.view_selector if view_selector.selected != 'Summary View': view_selector.select('Summary View')
@navigator.register(InfraProvider, 'EditTags')
[docs]class EditTags(CFMENavigateStep): VIEW = TagPageView prerequisite = NavigateToSibling('All')
[docs] def step(self): self.prerequisite_view.entities.get_entity(name=self.obj.name, surf_pages=True).check() try: self.prerequisite_view.toolbar.policy.item_select('Edit Tags') except MoveTargetOutOfBoundsException: # TODO: Remove once fixed 1475303 self.prerequisite_view.toolbar.policy.item_select('Edit Tags')
@navigator.register(InfraProvider, 'Edit')
[docs]class Edit(CFMENavigateStep): VIEW = InfraProviderEditView prerequisite = NavigateToSibling('All')
[docs] def step(self): self.prerequisite_view.entities.get_entity(name=self.obj.name, surf_pages=True).check() try: self.prerequisite_view.toolbar.configuration.item_select( 'Edit Selected Infrastructure Providers') except MoveTargetOutOfBoundsException: # TODO: Remove once fixed 1475303 self.prerequisite_view.toolbar.configuration.item_select( 'Edit Selected Infrastructure Providers')
@navigator.register(InfraProvider, 'Timelines')
[docs]class Timelines(CFMENavigateStep): VIEW = ProviderTimelinesView prerequisite = NavigateToSibling('Details')
[docs] def step(self): mon = self.prerequisite_view.toolbar.monitoring try: mon.item_select('Timelines') except MoveTargetOutOfBoundsException: # TODO: Remove once fixed 1475303 mon.item_select('Timelines')
@navigator.register(InfraProvider, 'Clusters')
[docs]class DetailsFromProvider(CFMENavigateStep): VIEW = ProviderClustersView prerequisite = NavigateToSibling('Details')
[docs] def step(self, *args, **kwargs): """Navigate to the correct view""" self.prerequisite_view.entities.summary('Relationships').click_at('Clusters')
@navigator.register(InfraProvider, 'ProviderNodes') # matching other infra class destinations
[docs]class ProviderNodes(CFMENavigateStep): VIEW = ProviderNodesView prerequisite = NavigateToSibling('Details')
[docs] def step(self): try: self.prerequisite_view.entities.summary('Relationships').click_at( self.obj.hosts_menu_item) except NameError: raise DestinationNotFound( "{} aren't present on details page of this provider" .format(self.obj.hosts_menu_item))
@navigator.register(InfraProvider, 'ProviderTemplates')
[docs]class ProviderTemplates(CFMENavigateStep): VIEW = ProviderTemplatesView prerequisite = NavigateToSibling('Details')
[docs] def step(self): self.prerequisite_view.entities.summary('Relationships').click_at('Templates')
@navigator.register(InfraProvider, 'ProviderVms')
[docs]class ProviderVms(CFMENavigateStep): VIEW = ProviderVmsView prerequisite = NavigateToSibling('Details')
[docs] def step(self): self.prerequisite_view.entities.summary('Relationships').click_at('Virtual Machines')