예제 #1
0
class CloudProvider(Pretty, CloudInfraProvider):
    """
    Abstract model of a cloud provider in cfme. See EC2Provider or OpenStackProvider.

    Args:
        name: Name of the provider.
        details: a details record (see EC2Details, OpenStackDetails inner class).
        credentials (:py:class:`Credential`): see Credential class.
        key: The CFME key of the provider in the yaml.

    Usage:

        myprov = EC2Provider(name='foo',
                             region='us-west-1',
                             credentials=Credential(principal='admin', secret='foobar'))
        myprov.create()

    """
    provider_types = {}
    in_version = (version.LOWEST, version.LATEST)
    category = "cloud"
    pretty_attrs = ['name', 'credentials', 'zone', 'key']
    STATS_TO_MATCH = ['num_template', 'num_vm']
    string_name = "Cloud"
    page_name = "clouds"
    templates_destination_name = "Images"
    quad_name = "cloud_prov"
    vm_name = "Instances"
    template_name = "Images"
    _properties_region = prop_region  # This will get resolved in common to a real form
    db_types = ["CloudManager"]
    # Specific Add button
    add_provider_button = deferred_verpick({
        version.LOWEST:
        form_buttons.FormButton("Add this Cloud Provider"),
        '5.5':
        form_buttons.add
    })
    save_button = deferred_verpick({
        version.LOWEST: form_buttons.save,
        '5.5': form_buttons.angular_save
    })

    def __init__(self,
                 name=None,
                 credentials=None,
                 zone=None,
                 key=None,
                 appliance=None):
        Navigatable.__init__(self, appliance=appliance)
        if not credentials:
            credentials = {}
        self.name = name
        self.credentials = credentials
        self.zone = zone
        self.key = key

    def _form_mapping(self, create=None, **kwargs):
        return {'name_text': kwargs.get('name')}
예제 #2
0
class ContainerImageCondition(BaseCondition):

    TREE_NODE = "Container Image"
    PRETTY = deferred_verpick({
        version.LOWEST: "Image",
        '5.7': "Container Image",
    })
    FIELD_VALUE = deferred_verpick({
        version.LOWEST: "Image",
        '5.7': "Container Image",
    })
예제 #3
0
class Provider(Pretty, CloudInfraProvider):
    """
    Abstract model of a cloud provider in cfme. See EC2Provider or OpenStackProvider.

    Args:
        name: Name of the provider.
        details: a details record (see EC2Details, OpenStackDetails inner class).
        credentials (Credential): see Credential inner class.
        key: The CFME key of the provider in the yaml.

    Usage:

        myprov = EC2Provider(name='foo',
                             region='us-west-1',
                             credentials=Provider.Credential(principal='admin', secret='foobar'))
        myprov.create()

    """
    pretty_attrs = ['name', 'credentials', 'zone', 'key']
    STATS_TO_MATCH = ['num_template', 'num_vm']
    string_name = "Cloud"
    page_name = "clouds"
    quad_name = "cloud_prov"
    vm_name = "Instances"
    template_name = "Images"
    properties_form = properties_form
    # Specific Add button
    add_provider_button = deferred_verpick({
        version.LOWEST:
        form_buttons.FormButton("Add this Cloud Provider"),
        '5.5':
        form_buttons.FormButton("Add")
    })
    save_button = deferred_verpick({
        version.LOWEST:
        form_buttons.FormButton("Save Changes"),
        '5.5':
        form_buttons.FormButton("Save changes")
    })

    def __init__(self, name=None, credentials=None, zone=None, key=None):
        if not credentials:
            credentials = {}
        self.name = name
        self.credentials = credentials
        self.zone = zone
        self.key = key

    def _form_mapping(self, create=None, **kwargs):
        return {'name_text': kwargs.get('name')}
예제 #4
0
class ProviderCondition(BaseCondition):

    TREE_NODE = deferred_verpick({
        version.LOWEST: "Container Provider",
        '5.7.2': "Provider",
    })
    PRETTY = "Provider"
    FIELD_VALUE = "Provider"
예제 #5
0
class Provider(BaseProvider, Pretty):
    pretty_attrs = ['name', 'key', 'zone']
    STATS_TO_MATCH = [
        'num_project', 'num_service', 'num_replication_controller', 'num_pod',
        'num_node', 'num_container', 'num_image'
    ]
    # TODO add 'num_image_registry' and 'num_volume'
    string_name = "Containers"
    page_name = "containers"
    detail_page_suffix = 'provider_detail'
    edit_page_suffix = 'provider_edit_detail'
    refresh_text = "Refresh items and relationships"
    quad_name = None
    _properties_region = prop_region  # This will get resolved in common to a real form
    add_provider_button = deferred_verpick({
        version.LOWEST:
        form_buttons.FormButton("Add this Containers Provider"),
        '5.6':
        form_buttons.add
    })
    save_button = deferred_verpick({
        version.LOWEST: form_buttons.save,
        '5.6': form_buttons.angular_save
    })

    def __init__(self,
                 name=None,
                 credentials=None,
                 key=None,
                 zone=None,
                 hostname=None,
                 port=None,
                 provider_data=None):
        if not credentials:
            credentials = {}
        self.name = name
        self.credentials = credentials
        self.key = key
        self.zone = zone
        self.hostname = hostname
        self.port = port
        self.provider_data = provider_data

    def _on_detail_page(self):
        """ Returns ``True`` if on the providers detail page, ``False`` if not."""
        ensure_browser_open()
        return sel.is_displayed(
            '//div//h1[contains(., "{} (Summary)")]'.format(self.name))

    def load_details(self, refresh=False):
        if not self._on_detail_page():
            self.navigate(detail=True)
        elif refresh:
            tb.refresh()

    def navigate(self, detail=True):
        if detail is True:
            if not self._on_detail_page():
                sel.force_navigate('containers_provider_detail',
                                   context={'provider': self})
        else:
            sel.force_navigate('containers_provider',
                               context={'provider': self})

    def get_detail(self, *ident):
        """ Gets details from the details infoblock

        Args:
            *ident: An InfoBlock title, followed by the Key name, e.g. "Relationships", "Images"
        Returns: A string representing the contents of the InfoBlock's value.
        """
        self.navigate(detail=True)
        return details_page.infoblock.text(*ident)

    @variable(alias='db')
    def num_project(self):
        return self._num_db_generic('container_projects')

    @num_project.variant('ui')
    def num_project_ui(self):
        return int(self.get_detail("Relationships", "Projects"))

    @variable(alias='db')
    def num_service(self):
        return self._num_db_generic('container_services')

    @num_service.variant('ui')
    def num_service_ui(self):
        return int(self.get_detail("Relationships", "Services"))

    @variable(alias='db')
    def num_replication_controller(self):
        return self._num_db_generic('container_replicators')

    @num_replication_controller.variant('ui')
    def num_replication_controller_ui(self):
        return int(self.get_detail("Relationships", "Replicators"))

    @variable(alias='db')
    def num_container_group(self):
        return self._num_db_generic('container_groups')

    @num_container_group.variant('ui')
    def num_container_group_ui(self):
        return int(self.get_detail("Relationships", "Pods"))

    @variable(alias='db')
    def num_pod(self):
        # potato tomato
        return self.num_container_group()

    @num_pod.variant('ui')
    def num_pod_ui(self):
        # potato tomato
        return self.num_container_group(method='ui')

    @variable(alias='db')
    def num_node(self):
        return self._num_db_generic('container_nodes')

    @num_node.variant('ui')
    def num_node_ui(self):
        return int(self.get_detail("Relationships", "Nodes"))

    @variable(alias='db')
    def num_container(self):
        # Containers are linked to providers through container definitions and then through pods
        res = cfmedb().engine.execute(
            "SELECT count(*) "
            "FROM ext_management_systems, container_groups, container_definitions, containers "
            "WHERE containers.container_definition_id=container_definitions.id "
            "AND container_definitions.container_group_id=container_groups.id "
            "AND container_groups.ems_id=ext_management_systems.id "
            "AND ext_management_systems.name='{}'".format(self.name))
        return int(res.first()[0])

    @num_container.variant('ui')
    def num_container_ui(self):
        return int(self.get_detail("Relationships", "Containers"))

    @variable(alias='db')
    def num_image(self):
        return self._num_db_generic('container_images')

    @num_image.variant('ui')
    def num_image_ui(self):
        return int(self.get_detail("Relationships", "Images"))

    @variable(alias='db')
    def num_image_registry(self):
        return self._num_db_generic('container_image_registries')

    @num_image_registry.variant('ui')
    def num_image_registry_ui(self):
        return int(self.get_detail("Relationships", "Image Registries"))
예제 #6
0
class ContainersProvider(BaseProvider, Pretty):
    PLURAL = 'Providers'
    provider_types = {}
    in_version = ('5.5', version.LATEST)
    category = "container"
    pretty_attrs = ['name', 'key', 'zone']
    STATS_TO_MATCH = [
        'num_project', 'num_service', 'num_replication_controller', 'num_pod',
        'num_node', 'num_image_registry', 'num_container'
    ]
    # TODO add 'num_volume'
    string_name = "Containers"
    page_name = "containers"
    detail_page_suffix = 'provider_detail'
    edit_page_suffix = 'provider_edit_detail'
    refresh_text = "Refresh items and relationships"
    quad_name = None
    db_types = ["ContainerManager"]
    _properties_region = prop_region  # This will get resolved in common to a real form
    add_provider_button = deferred_verpick({
        version.LOWEST:
        form_buttons.FormButton("Add this Containers Provider"),
        '5.6':
        form_buttons.add
    })
    save_button = deferred_verpick({
        version.LOWEST: form_buttons.save,
        '5.6': form_buttons.angular_save
    })

    def __init__(self,
                 name=None,
                 credentials=None,
                 key=None,
                 zone=None,
                 hawkular=None,
                 hostname=None,
                 api_port=None,
                 sec_protocol=None,
                 hawkular_sec_protocol=None,
                 hawkular_hostname=None,
                 hawkular_api_port=None,
                 provider_data=None,
                 appliance=None):
        Navigatable.__init__(self, appliance=appliance)
        if not credentials:
            credentials = {}
        self.name = name
        self.credentials = credentials
        self.key = key
        self.zone = zone
        self.hawkular = hawkular
        self.hostname = hostname
        self.api_port = api_port
        self.sec_protocol = sec_protocol
        self.hawkular_sec_protocol = hawkular_sec_protocol
        self.hawkular_hostname = hawkular_hostname
        self.hawkular_api_port = hawkular_api_port
        self.provider_data = provider_data

    def _on_detail_page(self):
        """ Returns ``True`` if on the providers detail page, ``False`` if not."""
        ensure_browser_open()
        return sel.is_displayed(
            '//div//h1[contains(., "{} (Summary)")]'.format(self.name))

    def load_details(self, refresh=False):
        navigate_to(self, 'Details')
        if refresh:
            tb.refresh()

    def get_detail(self, *ident):
        """ Gets details from the details infoblock

        Args:
            *ident: An InfoBlock title, followed by the Key name, e.g. "Relationships", "Images"
        Returns: A string representing the contents of the InfoBlock's value.
        """
        navigate_to(self, 'Details')
        return details_page.infoblock.text(*ident)

    @variable(alias='db')
    def num_project(self):
        return self._num_db_generic('container_projects')

    @num_project.variant('ui')
    def num_project_ui(self):
        return int(self.get_detail("Relationships", "Projects"))

    @variable(alias='db')
    def num_service(self):
        return self._num_db_generic('container_services')

    @num_service.variant('ui')
    def num_service_ui(self):
        if self.appliance.version < "5.7":
            name = "Services"
        else:
            name = "Container Services"
        return int(self.get_detail("Relationships", name))

    @variable(alias='db')
    def num_replication_controller(self):
        return self._num_db_generic('container_replicators')

    @num_replication_controller.variant('ui')
    def num_replication_controller_ui(self):
        return int(self.get_detail("Relationships", "Replicators"))

    @variable(alias='db')
    def num_container_group(self):
        return self._num_db_generic('container_groups')

    @num_container_group.variant('ui')
    def num_container_group_ui(self):
        return int(self.get_detail("Relationships", "Pods"))

    @variable(alias='db')
    def num_pod(self):
        # potato tomato
        return self.num_container_group()

    @num_pod.variant('ui')
    def num_pod_ui(self):
        # potato tomato
        return self.num_container_group(method='ui')

    @variable(alias='db')
    def num_node(self):
        return self._num_db_generic('container_nodes')

    @num_node.variant('ui')
    def num_node_ui(self):
        return int(self.get_detail("Relationships", "Nodes"))

    @variable(alias='db')
    def num_container(self):
        # Containers are linked to providers through container definitions and then through pods
        res = self.appliance.db.client.engine.execute(
            "SELECT count(*) "
            "FROM ext_management_systems, container_groups, container_definitions, containers "
            "WHERE containers.container_definition_id=container_definitions.id "
            "AND container_definitions.container_group_id=container_groups.id "
            "AND container_groups.ems_id=ext_management_systems.id "
            "AND ext_management_systems.name='{}'".format(self.name))
        return int(res.first()[0])

    @num_container.variant('ui')
    def num_container_ui(self):
        return int(self.get_detail("Relationships", "Containers"))

    @variable(alias='db')
    def num_image(self):
        return self._num_db_generic('container_images')

    @num_image.variant('ui')
    def num_image_ui(self):
        if self.appliance.version < "5.7":
            name = "Images"
        else:
            name = "Container Images"
        return int(self.get_detail("Relationships", name))

    @variable(alias='db')
    def num_image_registry(self):
        return self._num_db_generic('container_image_registries')

    @num_image_registry.variant('ui')
    def num_image_registry_ui(self):
        return int(self.get_detail("Relationships", "Image Registries"))

    def pods_per_ready_status(self):
        """Grabing the Container Statuses Summary of the pods from API"""
        #  TODO: Add later this logic to wrapanapi
        entities = self.mgmt.api.get('pod')[1]['items']
        out = {}
        for entity_j in entities:
            out[entity_j['metadata']['name']] = {
                condition['type']: eval_strings([condition['status']]).pop()
                for condition in entity_j['status'].get('conditions', [])
            }
        return out
예제 #7
0
class FolderManager(Pretty):
    """Class used in Reports/Edit Reports menus."""
    _fields = deferred_verpick({
        version.LOWEST:
        ".//div[@id='folder_grid']/div[contains(@class, 'objbox')]/table/tbody/tr/td",
        "5.5.0.7": ".//div[@id='folder_grid']/ul/li"
    })
    _field = deferred_verpick({
        version.LOWEST:
        ".//div[@id='folder_grid']/div[contains(@class, 'objbox')]/table/tbody/tr"
        "/td[normalize-space(.)='{}']",
        "5.5.0.7":
        ".//div[@id='folder_grid']/ul/li[normalize-space(.)='{}']"
    })
    pretty_attrs = ['root']

    # Keep the number of items in versions the same as buttons' and actions' values!
    # If a new version arrives, extend all the tuples :)
    versions = (version.LOWEST, "5.5.0.7")
    actions = ("_click_button", "_click_button_i")
    buttons = {
        "move_top": ("Move selected folder top", "fa-angle-double-up"),
        "move_bottom":
        ("Move selected folder to bottom", "fa-angle-double-down"),
        "move_up": ("Move selected folder up", "fa-angle-up"),
        "move_down": ("Move selected folder down", "fa-angle-down"),
        "delete_folder":
        ("Delete selected folder and its contents", "fa-times"),
        "add_subfolder": ("Add subfolder to selected folder", "fa-plus"),
    }

    class _BailOut(Exception):
        pass

    def __init__(self, root):
        self.root = lambda: sel.element(root)

    def __repr__(self):
        return "{}({})".format(type(self).__name__, str(repr(self.root)))

    @classmethod
    def bail_out(cls):
        """If something gets wrong, you can use this method to cancel editing of the items in
        the context manager.

        Raises: :py:class:`FolderManager._BailOut` exception
        """
        raise cls._BailOut()

    def _click_button(self, alt):
        sel.click(sel.element(".//img[@alt='{}']".format(alt), root=self.root))

    def _click_button_i(self, klass):
        sel.click(sel.element("i.{}".format(klass), root=self.root))

    def __getattr__(self, attr):
        """Resulve the button clicking action."""
        try:
            a_tuple = self.buttons[attr]
        except KeyError:
            raise AttributeError("Action {} does not exist".format(attr))
        action = version.pick(dict(zip(self.versions, self.actions)))
        action_meth = getattr(self, action)
        action_data = version.pick(dict(zip(self.versions, a_tuple)))

        def _click_function():
            action_meth(action_data)

        return _click_function

    def commit(self):
        self._click_button("Commit expression element changes")

    def discard(self):
        self._click_button("Discard expression element changes")

    @property
    def _all_fields(self):
        return sel.elements(self._fields, root=self.root)

    @property
    def fields(self):
        """Returns all fields' text values"""
        return map(lambda el: sel.text(el).encode("utf-8"), self._all_fields)

    @property
    def selected_field_element(self):
        """Return selected field's element.

        Returns: :py:class:`WebElement` if field is selected, else `None`
        """
        if version.current_version() < "5.5.0.7":
            active = "cellselected"
        else:
            active = "active"
        selected_fields = filter(
            lambda el: active in sel.get_attribute(el, "class"),
            self._all_fields)
        if len(selected_fields) == 0:
            return None
        else:
            return selected_fields[0]

    @property
    def selected_field(self):
        """Return selected field's text.

        Returns: :py:class:`str` if field is selected, else `None`
        """
        sf = self.selected_field_element
        return None if sf is None else sel.text(sf).encode("utf-8").strip()

    def add(self, subfolder):
        self.add_subfolder()
        wait_for(lambda: self.selected_field_element is not None,
                 num_sec=5,
                 delay=0.1)
        sel.double_click(self.selected_field_element, wait_ajax=False)
        if version.current_version() < "5.5.0.7":
            input = wait_for(lambda: sel.elements(
                "./input", root=self.selected_field_element),
                             num_sec=5,
                             delay=0.1,
                             fail_condition=[])[0][0]
            sel.set_text(input, subfolder)
            sel.send_keys(input, Keys.RETURN)
        else:
            sel.handle_alert(prompt=subfolder)

    def select_field(self, field):
        """Select field by text.

        Args:
            field: Field text.
        """
        sel.click(sel.element(self._field.format(field), root=self.root))
        wait_for(lambda: self.selected_field is not None, num_sec=5, delay=0.1)

    def has_field(self, field):
        """Returns if the field is present.

        Args:
            field: Field to check.
        """
        try:
            self.select_field(field)
            return True
        except (NoSuchElementException, TimedOutError):
            return False

    def delete_field(self, field):
        self.select_field(field)
        self.delete_folder()

    def move_first(self, field):
        self.select_field(field)
        self.move_top()

    def move_last(self, field):
        self.select_field(field)
        self.move_bottom()

    def clear(self):
        for field in self.fields:
            self.delete_field(field)
예제 #8
0
class PolicyProfileAssignable(object):
    """This class can be inherited by anything that provider load_details method.

    It provides functionality to assign and unassign Policy Profiles"""
    manage_policies_tree = deferred_verpick({
        version.LOWEST:
        CheckboxTree("//div[@id='protect_treebox']/ul"),
        "5.7":
        BootstrapTreeview("protectbox")
    })

    @property
    def assigned_policy_profiles(self):
        try:
            return self._assigned_policy_profiles
        except AttributeError:
            self._assigned_policy_profiles = set([])
            return self._assigned_policy_profiles

    def assign_policy_profiles(self, *policy_profile_names):
        """ Assign Policy Profiles to this object.

        Args:
            policy_profile_names: :py:class:`str` with Policy Profile names. After Control/Explorer
                coverage goes in, PolicyProfile objects will be also passable.
        """
        map(self.assigned_policy_profiles.add, policy_profile_names)
        self._assign_unassign_policy_profiles(True, *policy_profile_names)

    def unassign_policy_profiles(self, *policy_profile_names):
        """ Unssign Policy Profiles to this object.

        Args:
            policy_profile_names: :py:class:`str` with Policy Profile names. After Control/Explorer
                coverage goes in, PolicyProfile objects will be also passable.
        """
        for pp_name in policy_profile_names:
            try:
                self.assigned_policy_profiles.remove(pp_name)
            except KeyError:
                pass
        self._assign_unassign_policy_profiles(False, *policy_profile_names)

    def _assign_unassign_policy_profiles(self, assign, *policy_profile_names):
        """DRY function for managing policy profiles.

        See :py:func:`assign_policy_profiles` and :py:func:`assign_policy_profiles`

        Args:
            assign: Wheter to assign or unassign.
            policy_profile_names: :py:class:`str` with Policy Profile names.
        """
        self.load_details(refresh=True)
        pol_btn("Manage Policies")
        for policy_profile in policy_profile_names:
            if assign:
                self.manage_policies_tree.check_node(policy_profile)
            else:
                self.manage_policies_tree.uncheck_node(policy_profile)
        form_buttons.save()
        flash.assert_no_errors()
예제 #9
0
class Host(Updateable, Pretty, Navigatable, PolicyProfileAssignable):
    """
    Model of an infrastructure host in cfme.

    Args:
        name: Name of the host.
        hostname: Hostname of the host.
        ip_address: The IP address as a string.
        custom_ident: The custom identifiter.
        host_platform: Included but appears unused in CFME at the moment.
        ipmi_address: The IPMI address.
        mac_address: The mac address of the system.
        credentials (:py:class:`Credential`): see Credential inner class.
        ipmi_credentials (:py:class:`Credential`): see Credential inner class.

    Usage:

        myhost = Host(name='vmware',
                      credentials=Provider.Credential(principal='admin', secret='foobar'))
        myhost.create()

    """
    pretty_attrs = ['name', 'hostname', 'ip_address', 'custom_ident']

    forced_saved = deferred_verpick({
        version.LOWEST:
        form_buttons.FormButton("Save Changes",
                                dimmed_alt="Save",
                                force_click=True),
        '5.5':
        form_buttons.FormButton("Save changes",
                                dimmed_alt="Save changes",
                                force_click=True)
    })

    def __init__(self,
                 name=None,
                 hostname=None,
                 ip_address=None,
                 custom_ident=None,
                 host_platform=None,
                 ipmi_address=None,
                 mac_address=None,
                 credentials=None,
                 ipmi_credentials=None,
                 interface_type='lan',
                 provider=None,
                 appliance=None):
        Navigatable.__init__(self, appliance=appliance)
        self.name = name
        self.quad_name = 'host'
        self.hostname = hostname
        self.ip_address = ip_address
        self.custom_ident = custom_ident
        self.host_platform = host_platform
        self.ipmi_address = ipmi_address
        self.mac_address = mac_address
        self.credentials = credentials
        self.ipmi_credentials = ipmi_credentials
        self.interface_type = interface_type
        self.db_id = None
        self.provider = provider

    def _form_mapping(self, create=None, **kwargs):
        return {
            'name_text': kwargs.get('name'),
            'hostname_text': kwargs.get('hostname'),
            'ipaddress_text': kwargs.get('ip_address'),
            'custom_ident_text': kwargs.get('custom_ident'),
            'host_platform': kwargs.get('host_platform'),
            'ipmi_address_text': kwargs.get('ipmi_address'),
            'mac_address_text': kwargs.get('mac_address')
        }

    class Credential(cfme.Credential, Updateable):
        """Provider credentials

           Args:
             **kwargs: If using IPMI type credential, ipmi = True"""
        def __init__(self, **kwargs):
            super(Host.Credential, self).__init__(**kwargs)
            self.ipmi = kwargs.get('ipmi')

    def _submit(self, cancel, submit_button):
        if cancel:
            sel.click(form_buttons.cancel)
            # sel.wait_for_element(page.configuration_btn)
        else:
            sel.click(submit_button)
            flash.assert_no_errors()

    def create(self, cancel=False, validate_credentials=False):
        """
        Creates a host in the UI

        Args:
           cancel (boolean): Whether to cancel out of the creation.  The cancel is done
               after all the information present in the Host has been filled in the UI.
           validate_credentials (boolean): Whether to validate credentials - if True and the
               credentials are invalid, an error will be raised.
        """
        navigate_to(self, 'Add')
        fill(properties_form, self._form_mapping(True, **self.__dict__))
        fill(credential_form, self.credentials, validate=validate_credentials)
        fill(credential_form,
             self.ipmi_credentials,
             validate=validate_credentials)
        self._submit(cancel, host_add_btn)

    def update(self, updates, cancel=False, validate_credentials=False):
        """
        Updates a host in the UI.  Better to use utils.update.update context
        manager than call this directly.

        Args:
           updates (dict): fields that are changing.
           cancel (boolean): whether to cancel out of the update.
        """

        navigate_to(self, 'Edit')
        change_stored_password()
        fill(credential_form,
             updates.get('credentials', None),
             validate=validate_credentials)

        logger.debug("Trying to save update for host with id: " +
                     str(self.get_db_id))
        self._submit(cancel, self.forced_saved)

    def delete(self, cancel=True):
        """
        Deletes a host from CFME

        Args:
            cancel: Whether to cancel the deletion, defaults to True
        """

        navigate_to(self, 'Details')
        if self.appliance.version >= '5.7':
            btn_name = "Remove item"
        else:
            btn_name = "Remove from the VMDB"
        cfg_btn(btn_name, invokes_alert=True)
        sel.handle_alert(cancel=cancel)

    def load_details(self, refresh=False):
        """To be compatible with the Taggable and PolicyProfileAssignable mixins."""
        navigate_to(self, 'Details')
        if refresh:
            sel.refresh()

    def execute_button(self, button_group, button, cancel=True):
        navigate_to(self, 'Details')
        host_btn = partial(tb.select, button_group)
        host_btn(button, invokes_alert=True)
        sel.click(form_buttons.submit)
        flash.assert_success_message("Order Request was Submitted")
        host_btn(button, invokes_alert=True)
        sel.click(form_buttons.cancel)
        flash.assert_success_message("Service Order was cancelled by the user")

    def power_on(self):
        navigate_to(self, 'Details')
        pow_btn('Power On', invokes_alert=True)
        sel.handle_alert()

    def power_off(self):
        navigate_to(self, 'Details')
        pow_btn('Power Off', invokes_alert=True)
        sel.handle_alert()

    def get_power_state(self):
        return self.get_detail('Properties', 'Power State')
        # return str(find_quadicon(self.name, do_not_navigate=True).state)
        # return state.split()[1]

    def refresh(self, cancel=False):
        tb.select("Configuration",
                  "Refresh Relationships and Power States",
                  invokes_alert=True)
        sel.handle_alert(cancel=cancel)

    # TODO remove provider_crud when issue #4137 fixed,host linked with provider
    def wait_for_host_state_change(self,
                                   desired_state,
                                   timeout=300,
                                   provider_crud=None):
        """Wait for Host to come to desired state.
        This function waits just the needed amount of time thanks to wait_for.
        Args:
            self: self
            desired_state: 'on' or 'off'
            timeout: Specify amount of time (in seconds) to wait until TimedOutError is raised
            provider_crud: provider object where vm resides on (optional)
        """
        def _looking_for_state_change():
            tb.refresh()
            return 'currentstate-' + desired_state in find_quadicon(
                self.name, do_not_navigate=False).state

        navigate_and_select_all_hosts(self.name, provider_crud)
        return wait_for(_looking_for_state_change, num_sec=timeout)

    def get_ipmi(self):
        return IPMI(hostname=self.ipmi_address,
                    username=self.ipmi_credentials.principal,
                    password=self.ipmi_credentials.secret,
                    interface_type=self.interface_type)

    def get_detail(self, *ident):
        """ Gets details from the details infoblock

        The function first ensures that we are on the detail page for the specific host.

        Args:
            *ident: An InfoBlock title, followed by the Key name, e.g. "Relationships", "Images"
        Returns: A string representing the contents of the InfoBlock's value.
        """
        navigate_to(self, 'Details')
        return details_page.infoblock.text(*ident)

    @property
    def exists(self):
        navigate_to(self, 'All')
        for page in paginator.pages():
            if sel.is_displayed(Quadicon(self.name, 'host')):
                return True
        else:
            return False

    @property
    def has_valid_credentials(self):
        """ Check if host has valid credentials saved

        Returns: ``True`` if credentials are saved and valid; ``False`` otherwise
        """
        navigate_to(self, 'All')
        quad = Quadicon(self.name, 'host')
        return 'checkmark' in quad.creds

    def get_datastores(self):
        """ Gets list of all datastores used by this host"""
        navigate_to(self, 'Details')
        list_acc.select('Relationships',
                        'Datastores',
                        by_title=False,
                        partial=True)
        return [q.name for q in Quadicon.all("datastore")]

    @property
    def get_db_id(self):
        if self.db_id is None:
            self.db_id = self.appliance.host_id(self.name)
            return self.db_id
        else:
            return self.db_id

    def run_smartstate_analysis(self):
        """ Runs smartstate analysis on this host

        Note:
            The host must have valid credentials already set up for this to work.
        """
        navigate_to(self, 'Details')
        tb.select('Configuration',
                  'Perform SmartState Analysis',
                  invokes_alert=True)
        sel.handle_alert()
        flash.assert_message_contain(
            '"{}": Analysis successfully initiated'.format(self.name))

    def check_compliance(self, timeout=240):
        """Initiates compliance check and waits for it to finish."""
        navigate_to(self, 'Details')
        original_state = self.compliance_status
        tb.select('Policy',
                  '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))

    @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
        """
        sel.refresh()
        return self.get_detail('Compliance', 'Status')

    @property
    def is_compliant(self):
        """Check if the Host is compliant

        Returns:
            :py:class:`bool`
        """
        text = self.compliance_status.strip().lower()
        if 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 equal_drift_results(self, row_text, section, *indexes):
        """ Compares drift analysis results of a row specified by it's title text

        Args:
            row_text: Title text of the row to compare
            section: Accordion section where the change happened; this section must be activated
            indexes: Indexes of results to compare starting with 0 for first row (latest result).
                     Compares all available drifts, if left empty (default).

        Note:
            There have to be at least 2 drift results available for this to work.

        Returns:
            ``True`` if equal, ``False`` otherwise.
        """
        # mark by indexes or mark all
        navigate_to(self, 'Details')
        list_acc.select(
            'Relationships',
            version.pick({
                version.LOWEST: 'Show host drift history',
                '5.4': 'Show Host drift history'
            }))
        if indexes:
            drift_table.select_rows_by_indexes(*indexes)
        else:
            # We can't compare more than 10 drift results at once
            # so when selecting all, we have to limit it to the latest 10
            if len(list(drift_table.rows())) > 10:
                drift_table.select_rows_by_indexes(*range(0, 10))
            else:
                drift_table.select_all()
        tb.select("Select up to 10 timestamps for Drift Analysis")

        # Make sure the section we need is active/open
        sec_loc_map = {
            'Properties': 'Properties',
            'Security': 'Security',
            'Configuration': 'Configuration',
            'My Company Tags': 'Categories'
        }
        active_sec_loc = "//div[@id='all_sections_treebox']//li[contains(@id, 'group_{}')]"\
            "/span[contains(@class, 'dynatree-selected')]".format(sec_loc_map[section])
        sec_checkbox_loc = "//div[@id='all_sections_treebox']//li[contains(@id, 'group_{}')]"\
            "//span[contains(@class, 'dynatree-checkbox')]".format(sec_loc_map[section])
        sec_apply_btn = "//div[@id='accordion']/a[contains(normalize-space(text()), 'Apply')]"

        # If the section is not active yet, activate it
        if not sel.is_displayed(active_sec_loc):
            sel.click(sec_checkbox_loc)
            sel.click(sec_apply_btn)

        if not tb.is_active("All attributes"):
            tb.select("All attributes")
        d_grid = DriftGrid()
        if any(
                d_grid.cell_indicates_change(row_text, i)
                for i in range(0, len(indexes))):
            return False
        return True

    def tag(self, tag, **kwargs):
        """Tags the system by given tag"""
        navigate_to(self, 'Details')
        mixins.add_tag(tag, **kwargs)

    def untag(self, tag):
        """Removes the selected tag off the system"""
        navigate_to(self, 'Details')
        mixins.remove_tag(tag)
예제 #10
0
class Tenant(Updateable, Pretty, Navigatable):
    """ 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 = deferred_verpick({
        '5.7': form_buttons.angular_save,
        '5.8': form_buttons.simple_save
    })

    # TODO:
    # Temporary defining elements with "//input" as Input() is not working.Seems to be
    # with html elements,looking into it.
    quota_form = Form(
        fields=[('cpu_cb', CFMECheckbox('cpu_allocated')
                 ), ('cpu_txt', "//input[@id='id_cpu_allocated']"
                     ), ('memory_cb', CFMECheckbox('mem_allocated')
                         ), ('memory_txt', "//input[@id='id_mem_allocated']"),
                ('storage_cb', CFMECheckbox('storage_allocated')
                 ), ('storage_txt', "//input[@id='id_storage_allocated']"
                     ), ('vm_cb', CFMECheckbox('vms_allocated')
                         ), ('vm_txt', "//input[@id='id_vms_allocated']"),
                ('template_cb', CFMECheckbox('templates_allocated')
                 ), ('template_txt', "//input[@id='id_templates_allocated']")])

    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,
                 appliance=None):
        Navigatable.__init__(self, appliance=appliance)
        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:
            navigate_to(self, 'Details')
            return True
        except CandidateNotFound:
            return False

    @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):
        if self._default:
            raise ValueError("Cannot create the root tenant {}".format(
                self.name))

        navigate_to(self, 'Add')
        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):
        navigate_to(self, 'Edit')
        # 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):
        navigate_to(self, 'Details')
        tb_select("Delete this item", invokes_alert=True)
        sel.handle_alert(cancel=cancel)
        flash.assert_success_message('Tenant "{}": Delete successful'.format(
            self.description))

    def set_quota(self, **kwargs):
        navigate_to(self, 'ManageQuotas')
        # Workaround - form is appearing after short delay
        sel.wait_for_element(self.quota_form.cpu_txt)
        fill(self.quota_form, {
            'cpu_cb': kwargs.get('cpu_cb'),
            'cpu_txt': kwargs.get('cpu'),
            'memory_cb': kwargs.get('memory_cb'),
            'memory_txt': kwargs.get('memory'),
            'storage_cb': kwargs.get('storage_cb'),
            'storage_txt': kwargs.get('storage'),
            'vm_cb': kwargs.get('vm_cb'),
            'vm_txt': kwargs.get('vm'),
            'template_cb': kwargs.get('template_cb'),
            'template_txt': kwargs.get('template')
        },
             action=self.save_changes)
        flash.assert_success_message(
            'Quotas for Tenant "{}" were saved'.format(self.name))
예제 #11
0
class OpenStackInstance(Instance):
    # CFME & provider power control options
    START = "Start"  # START also covers RESUME and UNPAUSE (same as in CFME 5.4+ web UI)
    POWER_ON = START  # For compatibility with the infra objects.
    SUSPEND = "Suspend"
    TERMINATE = "Terminate"
    # CFME-only power control options
    SOFT_REBOOT = "Soft Reboot"
    HARD_REBOOT = "Hard Reboot"
    # Provider-only power control options
    STOP = "Stop"
    PAUSE = "Pause"
    RESTART = "Restart"

    # CFME power states
    STATE_ON = "on"
    STATE_OFF = "off"
    STATE_ERROR = "non-operational"
    STATE_PAUSED = deferred_verpick({
        version.LOWEST: "off",
        "5.4": "paused",
    })
    STATE_SUSPENDED = deferred_verpick({
        version.LOWEST: "off",
        "5.4": "suspended",
    })
    STATE_UNKNOWN = "unknown"

    def create(self, email=None, first_name=None, last_name=None, cloud_network=None,
               instance_type=None, cancel=False, **prov_fill_kwargs):
        """Provisions an OpenStack instance with the given properties through CFME

        Args:
            email: Email of the requester
            first_name: Name of the requester
            last_name: Surname of the requester
            cloud_network: Name of the cloud network the instance should belong to
            instance_type: Type of the instance
            cancel: Clicks the cancel button if `True`, otherwise clicks the submit button
                    (Defaults to `False`)
        Note:
            For more optional keyword arguments, see
            :py:data:`cfme.cloud.provisioning.provisioning_form`
        """
        from cfme.provisioning import provisioning_form
        sel.force_navigate('clouds_provision_instances', context={
            'provider': self.provider,
            'template_name': self.template_name,
        })

        # not supporting multiselect now, just take first value
        security_groups = prov_fill_kwargs.pop('security_groups', None)
        if security_groups:
            prov_fill_kwargs['security_groups'] = security_groups[0]

        fill(provisioning_form, dict(
            email=email,
            first_name=first_name,
            last_name=last_name,
            instance_name=self.name,
            instance_type=instance_type,
            cloud_network=cloud_network,
            **prov_fill_kwargs
        ))

        if cancel:
            sel.click(provisioning_form.cancel_button)
            flash.assert_success_message(
                "Add of new VM Provision Request was cancelled by the user")
        else:
            sel.click(provisioning_form.submit_button)
            flash.assert_success_message(
                "VM Provision Request was Submitted, you will be notified when your VMs are ready")

        row_description = 'Provision from [%s] to [%s]' % (self.template_name, self.name)
        cells = {'Description': row_description}
        row, __ = wait_for(requests.wait_for_request, [cells],
                           fail_func=requests.reload, num_sec=600, delay=20)
        assert row.last_message.text == version.pick(
            {version.LOWEST: 'VM Provisioned Successfully',
             "5.3": 'Vm Provisioned Successfully', })

    def power_control_from_provider(self, option):
        """Power control the instance from the provider

        Args:
            option: power control action to take against instance

        Raises:
            OptionNotAvailable: option param must have proper value
        """
        if option == OpenStackInstance.START:
            self.provider.mgmt.start_vm(self.name)
        elif option == OpenStackInstance.STOP:
            self.provider.mgmt.stop_vm(self.name)
        elif option == OpenStackInstance.SUSPEND:
            self.provider.mgmt.suspend_vm(self.name)
        elif option == OpenStackInstance.RESUME:
            self.provider.mgmt.resume_vm(self.name)
        elif option == OpenStackInstance.PAUSE:
            self.provider.mgmt.pause_vm(self.name)
        elif option == OpenStackInstance.UNPAUSE:
            self.provider.mgmt.unpause_vm(self.name)
        elif option == OpenStackInstance.RESTART:
            self.provider.mgmt.restart_vm(self.name)
        elif option == OpenStackInstance.TERMINATE:
            self.provider.mgmt.delete_vm(self.name)
        else:
            raise OptionNotAvailable(option + " is not a supported action")
예제 #12
0
class Host(Updateable, Pretty):
    """
    Model of an infrastructure host in cfme.

    Args:
        name: Name of the host.
        hostname: Hostname of the host.
        ip_address: The IP address as a string.
        custom_ident: The custom identifiter.
        host_platform: Included but appears unused in CFME at the moment.
        ipmi_address: The IPMI address.
        mac_address: The mac address of the system.
        credentials (Credential): see Credential inner class.
        ipmi_credentials (Credential): see Credential inner class.

    Usage:

        myhost = Host(name='vmware',
                      credentials=Provider.Credential(principal='admin', secret='foobar'))
        myhost.create()

    """
    pretty_attrs = ['name', 'hostname', 'ip_address', 'custom_ident']

    forced_saved = deferred_verpick(
        {version.LOWEST: form_buttons.FormButton(
            "Save Changes", dimmed_alt="Save", force_click=True),
         '5.5': form_buttons.FormButton(
            "Save changes", dimmed_alt="Save changes", force_click=True)})

    def __init__(self, name=None, hostname=None, ip_address=None, custom_ident=None,
                 host_platform=None, ipmi_address=None, mac_address=None, credentials=None,
                 ipmi_credentials=None, interface_type='lan'):
        self.name = name
        self.hostname = hostname
        self.ip_address = ip_address
        self.custom_ident = custom_ident
        self.host_platform = host_platform
        self.ipmi_address = ipmi_address
        self.mac_address = mac_address
        self.credentials = credentials
        self.ipmi_credentials = ipmi_credentials
        self.interface_type = interface_type
        self.db_id = None

    def _form_mapping(self, create=None, **kwargs):
        return {'name_text': kwargs.get('name'),
                'hostname_text': kwargs.get('hostname'),
                'ipaddress_text': kwargs.get('ip_address'),
                'custom_ident_text': kwargs.get('custom_ident'),
                'host_platform': kwargs.get('host_platform'),
                'ipmi_address_text': kwargs.get('ipmi_address'),
                'mac_address_text': kwargs.get('mac_address')}

    class Credential(cfme.Credential, Updateable):
        """Provider credentials

           Args:
             **kwargs: If using IPMI type credential, ipmi = True"""

        def __init__(self, **kwargs):
            super(Host.Credential, self).__init__(**kwargs)
            self.ipmi = kwargs.get('ipmi')

    def _submit(self, cancel, submit_button):
        if cancel:
            sel.click(form_buttons.cancel)
            # sel.wait_for_element(page.configuration_btn)
        else:
            sel.click(submit_button)
            flash.assert_no_errors()

    def create(self, cancel=False, validate_credentials=False):
        """
        Creates a host in the UI

        Args:
           cancel (boolean): Whether to cancel out of the creation.  The cancel is done
               after all the information present in the Host has been filled in the UI.
           validate_credentials (boolean): Whether to validate credentials - if True and the
               credentials are invalid, an error will be raised.
        """
        sel.force_navigate('infrastructure_host_new')
        fill(properties_form, self._form_mapping(True, **self.__dict__))
        fill(credential_form, self.credentials, validate=validate_credentials)
        fill(credential_form, self.ipmi_credentials, validate=validate_credentials)
        self._submit(cancel, host_add_btn)

    def update(self, updates, cancel=False, validate_credentials=False):
        """
        Updates a host in the UI.  Better to use utils.update.update context
        manager than call this directly.

        Args:
           updates (dict): fields that are changing.
           cancel (boolean): whether to cancel out of the update.
        """

        sel.force_navigate('infrastructure_host_edit', context={'host': self})
        change_stored_password()
        fill(credential_form, updates.get('credentials', None), validate=validate_credentials)

        # Workaround for issue with form_button staying dimmed.
        try:
            logger.debug("Trying to save update for host with id: " + str(self.get_db_id))
            self._submit(cancel, self.forced_saved)
            logger.debug("save worked, no exception")
        except Exception as e:
            logger.debug("exception detected: " + str(e))
            sel.browser().execute_script(
                "$j.ajax({type: 'POST', url: '/host/form_field_changed/%s',"
                " data: {'default_userid':'%s'}})" %
                (str(sel.current_url().split('/')[5]), updates.get('credentials', None).principal))
            sel.browser().execute_script(
                "$j.ajax({type: 'POST', url: '/host/form_field_changed/%s',"
                " data: {'default_password':'******'}})" %
                (str(sel.current_url().split('/')[5]), updates.get('credentials', None).secret))
            sel.browser().execute_script(
                "$j.ajax({type: 'POST', url: '/host/form_field_changed/%s',"
                " data: {'default_verify':'%s'}})" %
                (str(sel.current_url().split('/')[5]),
                    updates.get('credentials', None).verify_secret))
            self._submit(cancel, self.forced_saved)

    def delete(self, cancel=True):
        """
        Deletes a host from CFME

        Args:
            cancel: Whether to cancel the deletion, defaults to True
        """

        sel.force_navigate('infrastructure_host', context={'host': self})
        cfg_btn('Remove from the VMDB', invokes_alert=True)
        sel.handle_alert(cancel=cancel)

    def execute_button(self, button_group, button, cancel=True):
        sel.force_navigate('infrastructure_host', context={'host': self})
        host_btn = partial(tb.select, button_group)
        host_btn(button, invokes_alert=True)
        sel.click(form_buttons.submit)
        flash.assert_success_message("Order Request was Submitted")
        host_btn(button, invokes_alert=True)
        sel.click(form_buttons.cancel)
        flash.assert_success_message("Service Order was cancelled by the user")

    def power_on(self):
        sel.force_navigate('infrastructure_host', context={'host': self})
        pow_btn('Power On')
        sel.handle_alert()

    def power_off(self):
        sel.force_navigate('infrastructure_host', context={'host': self})
        pow_btn('Power Off')
        sel.handle_alert()

    def get_ipmi(self):
        return IPMI(hostname=self.ipmi_address, username=self.ipmi_credentials.principal,
                    password=self.ipmi_credentials.secret, interface_type=self.interface_type)

    def get_detail(self, *ident):
        """ Gets details from the details infoblock

        The function first ensures that we are on the detail page for the specific host.

        Args:
            *ident: An InfoBlock title, followed by the Key name, e.g. "Relationships", "Images"
        Returns: A string representing the contents of the InfoBlock's value.
        """
        if not self._on_detail_page():
            sel.force_navigate('infrastructure_host', context={'host': self})
        return details_page.infoblock.text(*ident)

    def _on_detail_page(self):
        """ Returns ``True`` if on the hosts detail page, ``False`` if not."""
        return sel.is_displayed('//div[@class="dhtmlxInfoBarLabel-2"][contains(., "%s")]'
                                % self.name)

    @property
    def exists(self):
        sel.force_navigate('infrastructure_hosts')
        for page in paginator.pages():
            if sel.is_displayed(Quadicon(self.name, 'host')):
                return True
        else:
            return False

    @property
    def has_valid_credentials(self):
        """ Check if host has valid credentials saved

        Returns: ``True`` if credentials are saved and valid; ``False`` otherwise
        """
        sel.force_navigate('infrastructure_hosts')
        quad = Quadicon(self.name, 'host')
        return quad.creds == 'checkmark'

    def _assign_unassign_policy_profiles(self, assign, *policy_profile_names):
        """DRY function for managing policy profiles.

        See :py:func:`assign_policy_profiles` and :py:func:`assign_policy_profiles`

        Args:
            assign: Wheter to assign or unassign.
            policy_profile_names: :py:class:`str` with Policy Profile names.
        """
        sel.force_navigate('infrastructure_host_policy_assignment', context={'host': self})
        for policy_profile in policy_profile_names:
            if assign:
                manage_policies_tree.check_node(policy_profile)
            else:
                manage_policies_tree.uncheck_node(policy_profile)
        sel.click(form_buttons.save)

    def assign_policy_profiles(self, *policy_profile_names):
        """ Assign Policy Profiles to this Host.

        Args:
            policy_profile_names: :py:class:`str` with Policy Profile names. After Control/Explorer
                coverage goes in, PolicyProfile objects will be also passable.
        """
        self._assign_unassign_policy_profiles(True, *policy_profile_names)

    def unassign_policy_profiles(self, *policy_profile_names):
        """ Unssign Policy Profiles to this Host.

        Args:
            policy_profile_names: :py:class:`str` with Policy Profile names. After Control/Explorer
                coverage goes in, PolicyProfile objects will be also passable.
        """
        self._assign_unassign_policy_profiles(False, *policy_profile_names)

    def get_datastores(self):
        """ Gets list of all datastores used by this host"""
        sel.force_navigate('infrastructure_host', context={'host': self})
        list_acc.select('Relationships', version.pick({version.LOWEST: 'Show Datastores',
                                                       '5.3': 'Show all Datastores'}))

        datastores = set([])
        for page in paginator.pages():
            for title in sel.elements(
                    "//div[@id='quadicon']/../../../tr/td/a[contains(@href,'storage/show')]"):
                datastores.add(sel.get_attribute(title, "title"))
        return datastores

    @property
    def get_db_id(self):
        if self.db_id is None:
            self.db_id = get_host_id(self.name)
            return self.db_id
        else:
            return self.db_id

    def run_smartstate_analysis(self):
        """ Runs smartstate analysis on this host

        Note:
            The host must have valid credentials already set up for this to work.
        """
        sel.force_navigate('infrastructure_host', context={'host': self})
        tb.select('Configuration', 'Perform SmartState Analysis', invokes_alert=True)
        sel.handle_alert()
        flash.assert_message_contain('"{}": Analysis successfully initiated'.format(self.name))

    def equal_drift_results(self, row_text, section, *indexes):
        """ Compares drift analysis results of a row specified by it's title text

        Args:
            row_text: Title text of the row to compare
            section: Accordion section where the change happened; this section must be activated
            indexes: Indexes of results to compare starting with 0 for first row (latest result).
                     Compares all available drifts, if left empty (default).

        Note:
            There have to be at least 2 drift results available for this to work.

        Returns:
            ``True`` if equal, ``False`` otherwise.
        """
        # mark by indexes or mark all
        sel.force_navigate('infrastructure_host', context={'host': self})
        list_acc.select('Relationships',
            version.pick({
                version.LOWEST: 'Show host drift history',
                '5.4': 'Show Host drift history'}))
        if indexes:
            drift_table.select_rows_by_indexes(*indexes)
        else:
            # We can't compare more than 10 drift results at once
            # so when selecting all, we have to limit it to the latest 10
            if len(list(drift_table.rows())) > 10:
                drift_table.select_rows_by_indexes(*range(0, 10))
            else:
                drift_table.select_all()
        tb.select("Select up to 10 timestamps for Drift Analysis")

        # Make sure the section we need is active/open
        sec_loc_map = {
            'Properties': 'Properties',
            'Security': 'Security',
            'Configuration': 'Configuration',
            'My Company Tags': 'Categories'}
        active_sec_loc = "//div[@id='all_sections_treebox']//li[contains(@id, 'group_{}')]"\
            "/span[contains(@class, 'dynatree-selected')]".format(sec_loc_map[section])
        sec_checkbox_loc = "//div[@id='all_sections_treebox']//li[contains(@id, 'group_{}')]"\
            "//span[contains(@class, 'dynatree-checkbox')]".format(sec_loc_map[section])
        sec_apply_btn = "//div[@id='accordion']/a[contains(normalize-space(text()), 'Apply')]"

        # If the section is not active yet, activate it
        if not sel.is_displayed(active_sec_loc):
            sel.click(sec_checkbox_loc)
            sel.click(sec_apply_btn)

        if not tb.is_active("All attributes"):
            tb.select("All attributes")
        d_grid = DriftGrid()
        if any(d_grid.cell_indicates_change(row_text, i) for i in range(0, len(indexes))):
            return False
        return True

    def tag(self, tag, **kwargs):
        """Tags the system by given tag"""
        sel.force_navigate('infrastructure_host', context={'host': self})
        mixins.add_tag(tag, **kwargs)

    def untag(self, tag):
        """Removes the selected tag off the system"""
        sel.force_navigate('infrastructure_host', context={'host': self})
        mixins.remove_tag(tag)
예제 #13
0
class OpenStackInstance(Instance):
    # CFME & provider power control options
    START = "Start"  # START also covers RESUME and UNPAUSE (same as in CFME 5.4+ web UI)
    POWER_ON = START  # For compatibility with the infra objects.
    SUSPEND = "Suspend"
    DELETE = "Delete"
    TERMINATE = deferred_verpick({
        version.LOWEST: 'Terminate',
        '5.6.1': 'Delete',
    })
    # CFME-only power control options
    SOFT_REBOOT = "Soft Reboot"
    HARD_REBOOT = "Hard Reboot"
    # Provider-only power control options
    STOP = "Stop"
    PAUSE = "Pause"
    RESTART = "Restart"
    SHELVE = "Shelve"
    SHELVE_OFFLOAD = "Shelve Offload"

    # CFME power states
    STATE_ON = "on"
    STATE_OFF = "off"
    STATE_ERROR = "non-operational"
    STATE_PAUSED = "paused"
    STATE_SUSPENDED = "suspended"
    STATE_REBOOTING = "reboot_in_progress"
    STATE_SHELVED = "shelved"
    STATE_SHELVED_OFFLOAD = "shelved_offloaded"
    STATE_UNKNOWN = "unknown"
    STATE_ARCHIVED = "archived"
    STATE_TERMINATED = "terminated"

    @property
    def ui_powerstates_available(self):
        return {
            'on':
            [self.SUSPEND, self.SOFT_REBOOT, self.HARD_REBOOT, self.TERMINATE],
            'off': [self.START, self.TERMINATE]
        }

    @property
    def ui_powerstates_unavailable(self):
        return {
            'on': [self.START],
            'off': [self.SUSPEND, self.SOFT_REBOOT, self.HARD_REBOOT]
        }

    def create(self, cancel=False, **prov_fill_kwargs):
        """Provisions an OpenStack instance with the given properties through CFME

        Args:
            cancel: Clicks the cancel button if `True`, otherwise clicks the submit button
                    (Defaults to `False`)
            prov_fill_kwargs: dictionary of provisioning field/value pairs
        Note:
            For more optional keyword arguments, see
            :py:data:`cfme.cloud.provisioning.ProvisioningForm`
        """
        super(OpenStackInstance, self).create(form_values=prov_fill_kwargs,
                                              cancel=cancel)

    def power_control_from_provider(self, option):
        """Power control the instance from the provider

        Args:
            option: power control action to take against instance

        Raises:
            OptionNotAvailable: option param must have proper value
        """
        if option == OpenStackInstance.START:
            self.provider.mgmt.start_vm(self.name)
        elif option == OpenStackInstance.STOP:
            self.provider.mgmt.stop_vm(self.name)
        elif option == OpenStackInstance.SUSPEND:
            self.provider.mgmt.suspend_vm(self.name)
        elif option == OpenStackInstance.PAUSE:
            self.provider.mgmt.pause_vm(self.name)
        elif option == OpenStackInstance.SHELVE:
            # TODO: rewrite it once wrapanapi will get shelve
            # and shelve_offload methods
            self.provider.mgmt._find_instance_by_name(self.name).shelve()
        elif option == OpenStackInstance.SHELVE_OFFLOAD:
            self.provider.mgmt._find_instance_by_name(
                self.name).shelve_offload()
        elif option == OpenStackInstance.RESTART:
            self.provider.mgmt.restart_vm(self.name)
        elif option == OpenStackInstance.TERMINATE:
            self.provider.mgmt.delete_vm(self.name)
        else:
            raise OptionNotAvailable(option + " is not a supported action")
예제 #14
0
class Timeprofile(Updateable, Navigatable):
    timeprofile_form = Form(
        fields=[
            ("description", Input("description")),
            ("scope", AngularSelect("profile_type")),
            ("timezone", AngularSelect("profile_tz")),
            ("days", CFMECheckbox("all_days")),
            ("hours", CFMECheckbox("all_hours")),
        ]
    )
    save_edit_button = deferred_verpick({'5.7': form_buttons.FormButton('Save changes'),
                                         '5.8': form_buttons.FormButton('Save')})
    save_button = deferred_verpick({
        version.LOWEST: form_buttons.FormButton("Add this Time Profile"),
        '5.7': form_buttons.FormButton('Add'),
        '5.8': form_buttons.FormButton('Save')
    })

    def __init__(self, description=None, scope=None, days=None, hours=None, timezone=None,
            appliance=None):
        Navigatable.__init__(self, appliance=appliance)
        self.description = description
        self.scope = scope
        self.days = days
        self.hours = hours
        self.timezone = timezone

    def create(self, cancel=False):
        navigate_to(self, 'Add')
        fill(self.timeprofile_form, {'description': self.description,
                                     'scope': self.scope,
                                     'days': self.days,
                                     'hours': self.hours,
                                     'timezone': self.timezone,
                                     })
        if not cancel:
            sel.click(self.save_button)
            end = "saved" if self.appliance.version > '5.7' else "added"
            flash.assert_success_message('Time Profile "{}" was {}'
                                         .format(self.description, end))

    def update(self, updates):
        navigate_to(self, 'Edit')
        fill(self.timeprofile_form, {'description': updates.get('description'),
                                     'scope': updates.get('scope'),
                                     'timezone': updates.get('timezone')},
             action={version.LOWEST: form_buttons.save,
                     '5.7': self.save_edit_button})
        flash.assert_success_message(
            'Time Profile "{}" was saved'.format(updates.get('description', self.description)))

    def copy(self):
        navigate_to(self, 'All')
        row = timeprofile_table.find_row_by_cells({'description': self.description})
        sel.check(sel.element(".//input[@type='checkbox']", root=row[0]))
        cfg_btn('Copy selected Time Profile')
        new_timeprofile = Timeprofile(description=self.description + "copy",
                         scope=self.scope)
        fill(self.timeprofile_form, {'description': new_timeprofile.description,
                              'scope': new_timeprofile.scope},
             action=self.save_button)
        end = "saved" if self.appliance.version > '5.7' else "added"
        flash.assert_success_message('Time Profile "{}" was {}'
                                     .format(new_timeprofile.description, end))
        return new_timeprofile

    def delete(self):
        navigate_to(self, 'All')
        row = timeprofile_table.find_row_by_cells({'description': self.description})
        sel.check(sel.element(".//input[@type='checkbox']", root=row[0]))
        cfg_btn('Delete selected Time Profiles', invokes_alert=True)
        sel.handle_alert()
        flash.assert_success_message(
            'Time Profile "{}": Delete successful'.format(self.description))
예제 #15
0
class Provider(Pretty, CloudInfraProvider):
    """
    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).
        credentials (Credential): see Credential inner class.
        key: The CFME key of the provider in the yaml.
        candu: C&U credentials if this is a RHEVMDetails class.

    Usage:

        myprov = VMwareProvider(name='foo',
                             region='us-west-1',
                             credentials=Provider.Credential(principal='admin', secret='foobar'))
        myprov.create()

    """
    type_tclass = "infra"
    pretty_attrs = ['name', 'key', 'zone']
    STATS_TO_MATCH = [
        'num_template', 'num_vm', 'num_datastore', 'num_host', 'num_cluster'
    ]
    string_name = "Infrastructure"
    page_name = "infrastructure"
    instances_page_name = "infra_vm_and_templates"
    templates_page_name = "infra_vm_and_templates"
    quad_name = "infra_prov"
    _properties_region = prop_region  # This will get resolved in common to a real form
    add_provider_button = deferred_verpick({
        version.LOWEST:
        form_buttons.FormButton("Add this Infrastructure Provider"),
        '5.6':
        form_buttons.add
    })
    save_button = deferred_verpick({
        version.LOWEST: form_buttons.save,
        '5.6': form_buttons.angular_save
    })

    def __init__(self,
                 name=None,
                 credentials=None,
                 key=None,
                 zone=None,
                 provider_data=None):
        if not credentials:
            credentials = {}
        self.name = name
        self.credentials = credentials
        self.key = key
        self.provider_data = provider_data
        self.zone = zone
        self.vm_name = version.pick({
            version.LOWEST: "VMs",
            '5.5': "VMs and Instances"
        })
        self.template_name = "Templates"

    def _form_mapping(self, create=None, **kwargs):
        return {'name_text': kwargs.get('name')}

    @variable(alias='db')
    def num_datastore(self):
        storage_table_name = version.pick({
            version.LOWEST: 'hosts_storages',
            '5.5.0.8': 'host_storages'
        })
        """ Returns the providers number of templates, as shown on the Details page."""

        results = list(cfmedb().engine.execute(
            'SELECT DISTINCT storages.name, hosts.ems_id '
            'FROM ext_management_systems, hosts, storages, {} '
            'WHERE hosts.id={}.host_id AND '
            'storages.id={}.storage_id AND '
            'hosts.ems_id=ext_management_systems.id AND '
            'ext_management_systems.name=\'{}\''.format(
                storage_table_name, storage_table_name, storage_table_name,
                self.name)))
        return len(results)

    @num_datastore.variant('ui')
    def num_datastore_ui(self):
        return int(self.get_detail("Relationships", "Datastores"))

    @variable(alias='rest')
    def num_host(self):
        provider = rest_api().collections.providers.find_by(name=self.name)[0]
        num_host = 0
        for host in 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 = cfmedb()["ext_management_systems"]
        hosts = cfmedb()["hosts"]
        hostlist = list(cfmedb().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):
        if version.current_version() < "5.6":
            host_src = "host.png"
            node_src = "node.png"
        else:
            host_src = "host-"
            node_src = "node-"

        try:
            num = int(self.get_detail("Relationships", host_src,
                                      use_icon=True))
        except sel.NoSuchElementException:
            logger.error(
                "Couldn't find number of hosts using key [Hosts] trying Nodes")
            num = int(self.get_detail("Relationships", node_src,
                                      use_icon=True))
        return num

    @variable(alias='rest')
    def num_cluster(self):
        provider = rest_api().collections.providers.find_by(name=self.name)[0]
        num_cluster = 0
        for cluster in 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 = cfmedb()["ext_management_systems"]
        clusters = cfmedb()["ems_clusters"]
        clulist = list(cfmedb().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):
        if version.current_version() < "5.6":
            return int(
                self.get_detail("Relationships", "cluster.png", use_icon=True))
        else:
            return int(
                self.get_detail("Relationships", "cluster-", use_icon=True))

    def discover(self):
        """
        Begins provider discovery from a provider instance

        Usage:
            discover_from_config(utils.providers.get_crud('rhevm'))
        """
        from virtualcenter import VMwareProvider
        from rhevm import RHEVMProvider
        from scvmm import SCVMMProvider
        vmware = isinstance(self, VMwareProvider)
        rhevm = isinstance(self, RHEVMProvider)
        scvmm = isinstance(self, SCVMMProvider)
        discover(rhevm,
                 vmware,
                 scvmm,
                 cancel=False,
                 start_ip=self.start_ip,
                 end_ip=self.end_ip)

    @property
    def hosts(self):
        """Returns list of :py:class:`cfme.infrastructure.host.Host` that should belong to this
        provider according to the YAML
        """
        result = []
        for host in self.get_yaml_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(name=host["name"], credentials=cred))
        return result
예제 #16
0
class GCEInstance(Instance):
    # CFME & provider power control options
    START = "Start"
    POWER_ON = START  # For compatibility with the infra objects.
    STOP = "Stop"
    DELETE = "Delete"
    TERMINATE = deferred_verpick({
        version.LOWEST: 'Terminate',
        '5.6.1': 'Delete',
    })
    # CFME-only power control options
    SOFT_REBOOT = "Soft Reboot"
    # Provider-only power control options
    RESTART = "Restart"

    # CFME power states
    STATE_ON = "on"
    STATE_OFF = "off"
    STATE_SUSPENDED = "suspended"
    STATE_TERMINATED = "terminated"
    STATE_ARCHIVED = "archived"
    STATE_UNKNOWN = "unknown"

    @property
    def ui_powerstates_available(self):
        return {
            'on': [self.STOP, self.SOFT_REBOOT, self.TERMINATE],
            'off': [self.START, self.TERMINATE]
        }

    @property
    def ui_powerstates_unavailable(self):
        return {'on': [self.START], 'off': [self.STOP, self.SOFT_REBOOT]}

    def create(self,
               email=None,
               first_name=None,
               last_name=None,
               availability_zone=None,
               instance_type=None,
               cloud_network=None,
               boot_disk_size=None,
               cancel=False,
               **prov_fill_kwargs):
        """Provisions an GCE instance with the given properties through CFME

        Args:
            email: Email of the requester
            first_name: Name of the requester
            last_name: Surname of the requester
            availability_zone: zone to deploy instance
            cloud_network: Name of the cloud network the instance should belong to
            instance_type: Type of the instance
            boot_disk_size: size of root disk
            cancel: Clicks the cancel button if `True`, otherwise clicks the submit button
                    (Defaults to `False`)
        Note:
            For more optional keyword arguments, see
            :py:data:`cfme.cloud.provisioning.provisioning_form`
        """
        from cfme.provisioning import provisioning_form
        # Nav to provision form and select image
        select_provision_image(template_name=self.template_name,
                               provider=self.provider)

        fill(
            provisioning_form,
            dict(email=email,
                 first_name=first_name,
                 last_name=last_name,
                 instance_name=self.name,
                 availability_zone=availability_zone,
                 instance_type=instance_type,
                 cloud_network=cloud_network,
                 boot_disk_size=boot_disk_size,
                 **prov_fill_kwargs))

        if cancel:
            sel.click(provisioning_form.cancel_button)
            flash.assert_success_message(
                "Add of new VM Provision Request was cancelled by the user")
        else:
            sel.click(provisioning_form.submit_button)
            flash.assert_success_message(
                "VM Provision Request was Submitted, you will be notified when your VMs are ready"
            )

    def power_control_from_provider(self, option):
        """Power control the instance from the provider

        Args:
            option: power control action to take against instance
        Raises:
            OptionNotAvailable: option param must have proper value
        """
        if option == GCEInstance.START:
            self.provider.mgmt.start_vm(self.name)
        elif option == GCEInstance.STOP:
            self.provider.mgmt.stop_vm(self.name)
        elif option == GCEInstance.RESTART:
            self.provider.mgmt.restart_vm(self.name)
        elif option == GCEInstance.TERMINATE:
            self.provider.mgmt.delete_vm(self.name)
        else:
            raise OptionNotAvailable(option + " is not a supported action")
예제 #17
0
class OpenStackInstance(Instance):
    # CFME & provider power control options
    START = "Start"  # START also covers RESUME and UNPAUSE (same as in CFME 5.4+ web UI)
    POWER_ON = START  # For compatibility with the infra objects.
    SUSPEND = "Suspend"
    DELETE = "Delete"
    TERMINATE = deferred_verpick({
        version.LOWEST: 'Terminate',
        '5.6.1': 'Delete',
    })
    # CFME-only power control options
    SOFT_REBOOT = "Soft Reboot"
    HARD_REBOOT = "Hard Reboot"
    # Provider-only power control options
    STOP = "Stop"
    PAUSE = "Pause"
    RESTART = "Restart"
    SHELVE = "Shelve"
    SHELVE_OFFLOAD = "Shelve Offload"

    # CFME power states
    STATE_ON = "on"
    STATE_OFF = "off"
    STATE_ERROR = "non-operational"
    STATE_PAUSED = "paused"
    STATE_SUSPENDED = "suspended"
    STATE_REBOOTING = "reboot_in_progress"
    STATE_SHELVED = "shelved"
    STATE_SHELVED_OFFLOAD = "shelved_offloaded"
    STATE_UNKNOWN = "unknown"
    STATE_ARCHIVED = "archived"
    STATE_TERMINATED = "terminated"

    @property
    def ui_powerstates_available(self):
        return {
            'on': [self.SUSPEND, self.SOFT_REBOOT, self.HARD_REBOOT, self.TERMINATE],
            'off': [self.START, self.TERMINATE]}

    @property
    def ui_powerstates_unavailable(self):
        return {
            'on': [self.START],
            'off': [self.SUSPEND, self.SOFT_REBOOT, self.HARD_REBOOT]}

    def create(self, email=None, first_name=None, last_name=None, cloud_network=None,
               instance_type=None, cancel=False, **prov_fill_kwargs):
        """Provisions an OpenStack instance with the given properties through CFME

        Args:
            email: Email of the requester
            first_name: Name of the requester
            last_name: Surname of the requester
            cloud_network: Name of the cloud network the instance should belong to
            instance_type: Type of the instance
            cancel: Clicks the cancel button if `True`, otherwise clicks the submit button
                    (Defaults to `False`)
        Note:
            For more optional keyword arguments, see
            :py:data:`cfme.cloud.provisioning.provisioning_form`
        """
        from cfme.provisioning import provisioning_form
        # Nav to provision form and select image
        select_provision_image(template_name=self.template_name, provider=self.provider)

        # not supporting multiselect now, just take first value
        security_groups = prov_fill_kwargs.pop('security_groups', None)
        if security_groups:
            prov_fill_kwargs['security_groups'] = security_groups[0]

        fill(provisioning_form, dict(
            email=email,
            first_name=first_name,
            last_name=last_name,
            instance_name=self.name,
            instance_type=instance_type,
            cloud_network=cloud_network,
            **prov_fill_kwargs
        ))

        if cancel:
            sel.click(provisioning_form.cancel_button)
            flash.assert_success_message(
                "Add of new VM Provision Request was cancelled by the user")
        else:
            sel.click(provisioning_form.submit_button)
            flash.assert_success_message(
                "VM Provision Request was Submitted, you will be notified when your VMs are ready")

    def power_control_from_provider(self, option):
        """Power control the instance from the provider

        Args:
            option: power control action to take against instance

        Raises:
            OptionNotAvailable: option param must have proper value
        """
        if option == OpenStackInstance.START:
            self.provider.mgmt.start_vm(self.name)
        elif option == OpenStackInstance.STOP:
            self.provider.mgmt.stop_vm(self.name)
        elif option == OpenStackInstance.SUSPEND:
            self.provider.mgmt.suspend_vm(self.name)
        elif option == OpenStackInstance.PAUSE:
            self.provider.mgmt.pause_vm(self.name)
        elif option == OpenStackInstance.SHELVE:
            # TODO: rewrite it once mgmtsystem will get shelve
            # and shelve_offload methods
            self.provider.mgmt._find_instance_by_name(self.name).shelve()
        elif option == OpenStackInstance.SHELVE_OFFLOAD:
            self.provider.mgmt._find_instance_by_name(self.name).shelve_offload()
        elif option == OpenStackInstance.RESTART:
            self.provider.mgmt.restart_vm(self.name)
        elif option == OpenStackInstance.TERMINATE:
            self.provider.mgmt.delete_vm(self.name)
        else:
            raise OptionNotAvailable(option + " is not a supported action")
예제 #18
0
    class Snapshot(object):
        snapshot_tree = deferred_verpick({
            version.LOWEST:
            Tree("//div[@id='snapshots_treebox']/ul"),
            '5.7.0.1':
            BootstrapTreeview('snapshot_treebox')
        })

        def __init__(self,
                     name=None,
                     description=None,
                     memory=None,
                     parent_vm=None):
            super(Vm.Snapshot, self).__init__()
            self.name = name
            self.description = description
            self.memory = memory
            self.vm = parent_vm

        def _nav_to_snapshot_mgmt(self):
            snapshot_title = '"Snapshots" for Virtual Machine "{}"'.format(
                self.vm.name)
            if summary_title() != snapshot_title:
                self.vm.load_details()
                sel.click(InfoBlock.element("Properties", "Snapshots"))

        @property
        def exists(self):
            self._nav_to_snapshot_mgmt()
            try:
                self.snapshot_tree.find_path_to(
                    re.compile(r"{}.*?".format(self.name or self.description)))
                return True
            except CandidateNotFound:
                return False
            except NoSuchElementException:
                return False

        def _click_tree_path(self, prop):
            """Find and click the given property in a snapshot tree path.

            Args:
                prop (str): Property to check (name or description).

            Returns:
                None
            """
            self.snapshot_tree.click_path(
                *self.snapshot_tree.find_path_to(re.compile(prop)))

        @property
        def active(self):
            """Check if the snapshot is active.

            Returns:
                bool: True if snapshot is active, False otherwise.
            """
            self._nav_to_snapshot_mgmt()
            try:
                self._click_tree_path(self.name or self.description)
                if sel.is_displayed_text("{} (Active)".format(
                        self.name or self.description)):
                    return True
            except CandidateNotFound:
                return False

        def create(self):
            snapshot_dict = {'description': self.description}
            self._nav_to_snapshot_mgmt()
            toolbar.select('Create a new snapshot for this VM')

            if self.name is not None:
                snapshot_dict['name'] = self.name

            if self.vm.provider.mgmt.is_vm_running(self.vm.name):
                snapshot_dict["snapshot_memory"] = self.memory

            fill(snapshot_form,
                 snapshot_dict,
                 action=snapshot_form.create_button)
            wait_for(lambda: self.exists,
                     num_sec=300,
                     delay=20,
                     fail_func=sel.refresh,
                     handle_exception=True)

        def delete(self, cancel=False):
            self._nav_to_snapshot_mgmt()
            toolbar.select('Delete Snapshots',
                           'Delete Selected Snapshot',
                           invokes_alert=True)
            sel.handle_alert(cancel=cancel)
            if not cancel:
                flash.assert_message_match(
                    'Remove Snapshot initiated for 1 '
                    'VM and Instance from the CFME Database')

        def delete_all(self, cancel=False):
            self._nav_to_snapshot_mgmt()
            toolbar.select('Delete Snapshots',
                           'Delete All Existing Snapshots',
                           invokes_alert=True)
            sel.handle_alert(cancel=cancel)
            if not cancel:
                flash.assert_message_match(
                    'Remove All Snapshots initiated for 1 VM and '
                    'Instance from the CFME Database')

        def revert_to(self, cancel=False):
            self._nav_to_snapshot_mgmt()

            self._click_tree_path(self.name or self.description)

            toolbar.select('Revert to selected snapshot', invokes_alert=True)
            sel.handle_alert(cancel=cancel)
            flash.assert_message_match(
                'Revert To Snapshot initiated for 1 VM and Instance from '
                'the CFME Database')
예제 #19
0
class ButtonGroup(Updateable):
    """Create,Edit and Delete Button Groups

    Args:
        text: The button Group name.
        hover: The button group hover text.
        type: The object type.
    """
    CLUSTER = "Cluster"
    DATASTORE = "Datastore"
    HOST = deferred_verpick({version.LOWEST: "Host", '5.4': "Host / Node"})
    PROVIDER = "Provider"
    SERVICE = "Service"
    TEMPLATE = "VM Template and Image"
    VM_INSTANCE = "VM and Instance"

    def __init__(self, text=None, hover=None, type=None):
        self.text = text
        self.hover = hover
        self.type = type

    def create(self):
        sel.force_navigate('new_button_group', context={"buttongroup": self})
        fill(button_group_form, {
            'btn_group_text': self.text,
            'btn_group_hvr_text': self.hover
        })
        if version.current_version() < "5.5":
            select = DHTMLSelect("div#button_div")
        else:
            select = AngularSelect("button_image")
        select.select_by_value(1)
        sel.click(button_group_form.add_button)
        flash.assert_success_message('Buttons Group "{}" was added'.format(
            self.hover))

    def update(self, updates):
        sel.force_navigate('group_button_edit', context={"buttongroup": self})
        edited_hvr_text = updates.get('hover', None)
        fill(button_group_form, {'btn_group_hvr_text': edited_hvr_text})
        sel.click(button_group_form.save_button)
        flash.assert_success_message(
            'Buttons Group "{}" was saved'.format(edited_hvr_text))

    def delete(self):
        sel.force_navigate('button_group', context={"buttongroup": self})
        cfg_btn("Remove this Button Group", invokes_alert=True)
        sel.handle_alert()
        flash.assert_success_message(
            'Buttons Group "{}": Delete successful'.format(self.hover))

    @property
    def exists(self):
        try:
            sel.force_navigate('button_group', context={"buttongroup": self})
            return True
        except CandidateNotFound:
            return False

    def delete_if_exists(self):
        if self.exists:
            self.delete()
예제 #20
0
class Widget(Pretty):
    _name = deferred_verpick({
        version.LOWEST:
        "//div[@id='{}']//span[contains(@class, 'modtitle_text')]",
        "5.5":
        "//div[@id='{}']//h3",
        "5.6":
        "//div[@id='{}']//h2[contains(@class, 'card-pf-title')]"
    })
    _remove = "//div[@id='{}']//a[@title='Remove from Dashboard']"
    _minimize = "//div[@id='{}']//a[@title='Minimize']"
    _restore = "//div[@id='{}']//a[@title='Restore']"
    _footer = deferred_verpick({
        version.LOWEST:
        "//div[@id='{}']//div[@class='modboxfooter' or contains(@class, 'panel-footer')]",
        "5.6":
        "//div[@id='{}']//div[contains(@class, 'card-pf-footer')]"
    })
    _zoom = "//div[@id='{}']//a[@title='Zoom in on this chart']"
    _zoomed_name = deferred_verpick({
        version.LOWEST:
        "//div[@id='lightbox_div']//span[contains(@class, 'modtitle_text')]",
        "5.5": "//div[@id='lightbox_div']//h3"
    })
    _zoomed_close = deferred_verpick({
        version.LOWEST:
        "//div[@id='lightbox_div']//a[@title='Close']",
        "5.5":
        "//div[@id='lightbox_div']//a[@title='Close']/i"
    })
    _all = "//div[@id='modules']//div[contains(@id, 'w_')]"
    _content = deferred_verpick({
        version.LOWEST:
        "//div[@id='{}']//div[contains(@class, 'modboxin')]",
        "5.5":
        "//div[@id='{}']//div[contains(@class,'panel-body')]/div",
        "5.6":
        "//div[@id='{}']//div[contains(@class,'card-pf-body')]/div"
    })
    _content_type = deferred_verpick({
        version.LOWEST:
        "//div[@id='{}']//div[contains(@class, 'modboxin')]/../h2/a[1]",
        "5.5":
        "//div[@id='{}']//div[contains(@class,'panel-body')]",
        "5.6":
        "//div[@id='{}']//div[contains(@class,'card-pf-body')]"
    })

    # 5.5+ updated
    _menu_opener = deferred_verpick({
        version.LOWEST:
        "//div[@id='{}']//a[contains(@class, 'dropdown-toggle')]/i",
        "5.6":
        "//div[@id='{}']//button[contains(@class, 'dropdown-toggle')]"
    })
    _menu_container = "//div[@id='{}']//ul[contains(@class, 'dropdown-menu')]"
    _menu_minmax = _menu_container + "/li/a[contains(@id, 'minmax')]"
    _menu_remove = _menu_container + "/li/a[contains(@id, 'close')]"
    _menu_zoom = _menu_container + "/li/a[contains(@id, 'zoom')]"

    pretty_attrs = ['_div_id']

    def __init__(self, div_id):
        self._div_id = div_id

    @property
    def newer_version(self):
        return version.current_version() >= "5.5"

    @property
    def name(self):
        return sel.text(self._name.format(self._div_id)).encode("utf-8")

    @property
    def content_type(self):
        if version.current_version() <= "5.4":
            return sel.get_attribute(self._content_type.format(self._div_id),
                                     "class").strip()
        else:
            return sel.get_attribute(self._content_type.format(self._div_id),
                                     "class").rsplit(" ", 1)[-1]

    @property
    def content(self):
        if self.content_type in {"rss_widget", "rssbox"}:
            return RSSWidgetContent(self._div_id)
        elif self.content_type in {"report_widget", "reportbox"}:
            return ReportWidgetContent(self._div_id)
        else:
            return BaseWidgetContent(self._div_id)

    @property
    def footer(self):
        cleaned = [
            x.strip() for x in sel.text(self._footer.format(
                self._div_id)).encode("utf-8").strip().split("|")
        ]
        result = {}
        for item in cleaned:
            name, time = item.split(" ", 1)
            time = time.strip()
            if time.lower() == "never":
                result[name.strip().lower()] = None
            else:
                result[name.strip().lower()] = parsetime.from_american_minutes(
                    time.strip())
        return result

    @property
    def time_updated(self):
        return self.footer["updated"]

    @property
    def time_next(self):
        return self.footer["next"]

    @property
    def is_minimized(self):
        self.close_zoom()
        if not self.newer_version:
            return not sel.is_displayed(self._minimize.format(self._div_id))
        else:
            return not sel.is_displayed(self._content.format(self._div_id))

    @property
    def can_zoom(self):
        """Can this Widget be zoomed?"""
        self.close_zoom()
        if not self.newer_version:
            return sel.is_displayed(self._zoom.format(self._div_id))
        else:
            self.open_dropdown_menu()
            zoomable = sel.is_displayed(self._menu_zoom.format(self._div_id))
            self.close_dropdown_menu()
            return zoomable

    def _click_menu_button_by_loc(self, loc):
        self.close_zoom()
        try:
            self.open_dropdown_menu()
            sel.click(loc.format(self._div_id))
        finally:
            self.close_dropdown_menu()

    def remove(self, cancel=False):
        """Remove this Widget."""
        self.close_zoom()
        if not self.newer_version:
            sel.click(self._remove.format(self._div_id),
                      wait_ajax=False)  # alert
            sel.handle_alert(cancel)
        else:
            self._click_menu_button_by_loc(self._menu_remove)

    def minimize(self):
        """Minimize this Widget."""
        self.close_zoom()
        if not self.is_minimized:
            if not self.newer_version:
                sel.click(self._minimize.format(self._div_id))
            else:
                self._click_menu_button_by_loc(self._menu_minmax)

    def restore(self):
        """Return the Widget back from minimalization."""
        self.close_zoom()
        if self.is_minimized:
            if not self.newer_version:
                sel.click(self._restore.format(self._div_id))
            else:
                self._click_menu_button_by_loc(self._menu_minmax)

    def zoom(self):
        """Zoom this Widget."""
        self.close_zoom()
        if not self.is_zoomed():
            if not self.newer_version:
                sel.click(self._zoom.format(self._div_id))
            else:
                self._click_menu_button_by_loc(self._menu_zoom)

    @classmethod
    def is_zoomed(cls):
        return sel.is_displayed(cls._zoomed_name)

    @classmethod
    def get_zoomed_name(cls):
        return sel.text(cls._zoomed_name).encode("utf-8").strip()

    @classmethod
    def close_zoom(cls):
        if cls.is_zoomed():
            sel.click(cls._zoomed_close)
            # Here no ajax, so we have to check it manually
            wait_for(lambda: not cls.is_zoomed(),
                     delay=0.1,
                     num_sec=5,
                     message="cancel zoom")

    @classmethod
    def all(cls):
        """Returns objects with all Widgets currently present."""
        sel.force_navigate('dashboard')
        result = []
        for el in sel.elements(cls._all):
            result.append(cls(sel.get_attribute(el, "id")))
        return result

    @classmethod
    def by_name(cls, name):
        """Returns Widget with specified name."""
        for widget in cls.all():
            if widget.name == name:
                return widget
        else:
            raise NameError(
                "Could not find widget with name {} on current dashboard!".
                format(name))

    @classmethod
    def by_type(cls, content_type):
        """Returns Widget with specified content_type."""
        return filter(lambda w: w.content_type == content_type, cls.all())

    # 5.5+ specific methods
    @property
    def is_dropdown_menu_opened(self):
        return sel.is_displayed(self._menu_container.format(self._div_id))

    @property
    def drag_element(self):
        return sel.element(self._name.format(self._div_id))

    @property
    def drop_element(self):
        return sel.element(self._footer.format(self._div_id))

    def open_dropdown_menu(self):
        if not sel.is_displayed(self._menu_opener.format(self._div_id)):
            return  # Not a 5.5+
        self.close_dropdown_menu()
        sel.click(self._menu_opener.format(self._div_id))
        wait_for(lambda: self.is_dropdown_menu_opened,
                 num_sec=10,
                 delay=0.2,
                 message="widget dropdown menu opend")

    def close_dropdown_menu(self):
        if not sel.is_displayed(self._menu_opener.format(self._div_id)):
            return  # Not a 5.5+
        if self.is_dropdown_menu_opened:
            sel.click("//a[contains(@class, 'navbar-brand')]/img")
            wait_for(lambda: not self.is_dropdown_menu_opened,
                     num_sec=10,
                     delay=0.2,
                     message="widget dropdown menu closed")

    def drag_and_drop(self, widget):
        sel.drag_and_drop(self.drag_element, widget.drop_element)
    class Snapshot(object):
        snapshot_tree = deferred_verpick({
            version.LOWEST:
            Tree("//div[@id='snapshots_treebox']/ul"),
            '5.7.0.1':
            BootstrapTreeview('snapshot_treebox')
        })

        def __init__(self,
                     name=None,
                     description=None,
                     memory=None,
                     parent_vm=None):
            super(Vm.Snapshot, self).__init__()
            self.name = name
            self.description = description
            self.memory = memory
            self.vm = parent_vm

        def _nav_to_snapshot_mgmt(self):
            snapshot_title = '"Snapshots" for Virtual Machine "{}"'.format(
                self.vm.name)
            if summary_title() != snapshot_title:
                self.vm.load_details()
                sel.click(InfoBlock.element("Properties", "Snapshots"))

        def does_snapshot_exist(self):
            self._nav_to_snapshot_mgmt()
            try:
                if self.name is not None:
                    self.snapshot_tree.find_path_to(
                        re.compile(self.name + r".*?"))
                else:
                    self.snapshot_tree.find_path_to(
                        re.compile(self.description + r".*?"))
                return True
            except CandidateNotFound:
                return False
            except NoSuchElementException:
                return False

        def wait_for_snapshot_active(self):
            self._nav_to_snapshot_mgmt()
            try:
                self.snapshot_tree.click_path(
                    *self.snapshot_tree.find_path_to(re.compile(self.name)))
                if sel.is_displayed_text(self.name + " (Active)"):
                    return True
            except CandidateNotFound:
                return False

        def create(self):
            self._nav_to_snapshot_mgmt()
            toolbar.select('Create a new snapshot for this VM')
            if self.name is not None:
                fill(snapshot_form, {
                    'name': self.name,
                    'description': self.description,
                    'snapshot_memory': self.memory
                },
                     action=snapshot_form.create_button)
            else:
                fill(snapshot_form, {
                    'description': self.description,
                    'snapshot_memory': self.memory
                },
                     action=snapshot_form.create_button)
            wait_for(self.does_snapshot_exist,
                     num_sec=300,
                     delay=20,
                     fail_func=sel.refresh,
                     handle_exception=True)

        def delete(self, cancel=False):
            self._nav_to_snapshot_mgmt()
            toolbar.select('Delete Snapshots',
                           'Delete Selected Snapshot',
                           invokes_alert=True)
            sel.handle_alert(cancel=cancel)
            if not cancel:
                flash.assert_message_match(
                    'Remove Snapshot initiated for 1 '
                    'VM and Instance from the CFME Database')

        def delete_all(self, cancel=False):
            self._nav_to_snapshot_mgmt()
            toolbar.select('Delete Snapshots',
                           'Delete All Existing Snapshots',
                           invokes_alert=True)
            sel.handle_alert(cancel=cancel)
            if not cancel:
                flash.assert_message_match(
                    'Remove All Snapshots initiated for 1 VM and '
                    'Instance from the CFME Database')

        def revert_to(self, cancel=False):
            self._nav_to_snapshot_mgmt()
            self.snapshot_tree.click_path(
                *self.snapshot_tree.find_path_to(re.compile(self.name)))
            toolbar.select('Revert to selected snapshot', invokes_alert=True)
            sel.handle_alert(cancel=cancel)
            flash.assert_message_match(
                'Revert To Snapshot initiated for 1 VM and Instance from '
                'the CFME Database')
예제 #22
0
class AzureInstance(Instance):
    # CFME & provider power control options Added by Jeff Teehan on 5-16-2016
    START = "Start"
    POWER_ON = START  # For compatibility with the infra objects.
    STOP = "Stop"
    SUSPEND = "Suspend"
    DELETE = "Delete"
    TERMINATE = deferred_verpick({
        version.LOWEST: 'Terminate',
        '5.6.1': 'Delete',
    })
    # CFME-only power control options
    SOFT_REBOOT = "Soft Reboot"
    # Provider-only power control options
    RESTART = "Restart"

    # CFME power states
    STATE_ON = "on"
    STATE_OFF = "off"
    STATE_SUSPENDED = "suspended"
    STATE_TERMINATED = "terminated"
    STATE_UNKNOWN = "unknown"
    STATE_ARCHIVED = "archived"

    @property
    def ui_powerstates_available(self):
        return {
            'on': [self.STOP, self.SUSPEND, self.SOFT_REBOOT, self.TERMINATE],
            'off': [self.START, self.TERMINATE]
        }

    @property
    def ui_powerstates_unavailable(self):
        return {
            'on': [self.START],
            'off': [self.STOP, self.SUSPEND, self.SOFT_REBOOT]
        }

    def create(self, cancel=False, **prov_fill_kwargs):
        """Provisions an Azure instance with the given properties through CFME

        Args:
            cancel: Clicks the cancel button if `True`, otherwise clicks the submit button
                    (Defaults to `False`)
            prov_fill_kwargs: dictionary of provisioning field/value pairs
        Note:
            For more optional keyword arguments, see
            :py:data:`cfme.cloud.provisioning.ProvisioningForm`
        """
        super(AzureInstance, self).create(form_values=prov_fill_kwargs,
                                          cancel=cancel)

    def power_control_from_provider(self, option):
        """Power control the instance from the provider

        Args:
            option: power control action to take against instance

        Raises:
            OptionNotAvailable: option param must have proper value
        """
        if option == AzureInstance.START:
            self.provider.mgmt.start_vm(self.name)
        elif option == AzureInstance.STOP:
            self.provider.mgmt.stop_vm(self.name)
        elif option == AzureInstance.RESTART:
            self.provider.mgmt.restart_vm(self.name)
        elif option == AzureInstance.SUSPEND:
            self.provider.mgmt.suspend_vm(self.name)
        elif option == AzureInstance.TERMINATE:
            self.provider.mgmt.delete_vm(self.name)
        else:
            raise OptionNotAvailable(option + " is not a supported action")
class Genealogy(object):
    """Class, representing genealogy of an infra object with possibility of data retrieval
    and comparison.

    Args:
        o: The :py:class:`Vm` or :py:class:`Template` object.
    """
    genealogy_tree = deferred_verpick({
        version.LOWEST:
        CheckboxTree("//div[@id='genealogy_treebox']/ul"),
        5.7:
        BootstrapTreeview('genealogy_treebox')
    })

    section_comparison_tree = CheckboxTree(
        "//div[@id='all_sections_treebox']/div/table")
    apply_button = form_buttons.FormButton("Apply sections")

    mode_mapping = {
        "exists": "Exists Mode",
        "details": "Details Mode",
    }

    attr_mapping = {
        "all": "All Attributes",
        "different": "Attributes with different values",
        "same": "Attributes with same values",
    }

    def __init__(self, o):
        self.o = o

    def navigate(self):
        self.o.load_details()
        sel.click(InfoBlock.element("Relationships", "Genealogy"))

    def compare(self, *objects, **kwargs):
        """Compares two or more objects in the genealogy.

        Args:
            *objects: :py:class:`Vm` or :py:class:`Template` or :py:class:`str` with name.

        Keywords:
            sections: Which sections to compare.
            attributes: `all`, `different` or `same`. Default: `all`.
            mode: `exists` or `details`. Default: `exists`."""
        sections = kwargs.get("sections")
        attributes = kwargs.get("attributes", "all").lower()
        mode = kwargs.get("mode", "exists").lower()
        assert len(objects) >= 2, "You must specify at least two objects"
        objects = map(lambda o: o.name
                      if isinstance(o, (Vm, Template)) else o, objects)
        self.navigate()
        for obj in objects:
            if not isinstance(obj, list):
                path = self.genealogy_tree.find_path_to(obj)
            self.genealogy_tree.check_node(*path)
        toolbar.select("Compare selected VMs")
        # COMPARE PAGE
        flash.assert_no_errors()
        if sections is not None:
            map(lambda path: self.section_comparison_tree.check_node(*path),
                sections)
            sel.click(self.apply_button)
            flash.assert_no_errors()
        # Set requested attributes sets
        toolbar.select(self.attr_mapping[attributes])
        # Set the requested mode
        toolbar.select(self.mode_mapping[mode])

    @property
    def tree(self):
        """Returns contents of the tree with genealogy"""
        self.navigate()
        return self.genealogy_tree.read_contents()

    @property
    def ancestors(self):
        """Returns list of ancestors of the represented object."""
        self.navigate()
        path = self.genealogy_tree.find_path_to(
            re.compile(r"^.*?\(Selected\)$"))
        if not path:
            raise ValueError("Something wrong happened, path not found!")
        processed_path = []
        for step in path[:-1]:
            # We will remove the (parent) and (Selected) suffixes
            processed_path.append(
                re.sub(r"\s*(?:\(Current\)|\(Parent\))$", "", step))
        return processed_path