From 3e3b9a5e9f940c542efa31f0dcef5856c2d2b1a1 Mon Sep 17 00:00:00 2001 From: john-dupuy Date: Fri, 6 Sep 2019 15:27:56 -0400 Subject: [PATCH 1/5] initial commit of conversion of ConfigManager to Entity --- cfme/ansible_tower/explorer.py | 30 +- cfme/fixtures/config_manager.py | 32 ++ .../__init__.py} | 494 +++++------------- .../config_management/ansible_tower.py | 126 +++++ .../config_management/config_profiles.py | 62 +++ .../config_systems/__init__.py | 55 ++ .../config_systems/ansible_tower.py | 14 + .../config_systems/satellite.py | 14 + .../config_management/satellite.py | 136 +++++ .../infrastructure/test_config_management.py | 32 +- .../test_config_management_rest.py | 32 +- .../test_ansible_workflow_servicecatalogs.py | 33 +- .../test_config_provider_servicecatalogs.py | 38 +- cfme/utils/testgen.py | 21 - entry_points.txt | 6 +- 15 files changed, 637 insertions(+), 488 deletions(-) create mode 100644 cfme/fixtures/config_manager.py rename cfme/infrastructure/{config_management.py => config_management/__init__.py} (59%) create mode 100644 cfme/infrastructure/config_management/ansible_tower.py create mode 100644 cfme/infrastructure/config_management/config_profiles.py create mode 100644 cfme/infrastructure/config_management/config_systems/__init__.py create mode 100644 cfme/infrastructure/config_management/config_systems/ansible_tower.py create mode 100644 cfme/infrastructure/config_management/config_systems/satellite.py create mode 100644 cfme/infrastructure/config_management/satellite.py diff --git a/cfme/ansible_tower/explorer.py b/cfme/ansible_tower/explorer.py index cd6afb610b..554ff737c5 100644 --- a/cfme/ansible_tower/explorer.py +++ b/cfme/ansible_tower/explorer.py @@ -11,6 +11,7 @@ from cfme.common import Taggable from cfme.common import TaggableCollection from cfme.exceptions import ItemNotFound +from cfme.infrastructure.config_management.config_systems.ansible_tower import AnsibleTowerSystemsCollection # noqa from cfme.modeling.base import BaseCollection from cfme.modeling.base import BaseEntity from cfme.utils.appliance.implementations.ui import CFMENavigateStep @@ -150,26 +151,6 @@ def is_displayed(self): ) -@attr.s -class AnsibleTowerProvider(BaseEntity): - pass - - -@attr.s -class AnsibleTowerProvidersCollection(BaseCollection): - ENTITY = AnsibleTowerProvider - - -@attr.s -class AnsibleTowerSystem(BaseEntity): - pass - - -@attr.s -class AnsibleTowerSystemsCollection(BaseCollection): - ENTITY = AnsibleTowerSystem - - @attr.s class AnsibleTowerJobTemplate(BaseEntity, Taggable): name = attr.ib() @@ -209,15 +190,6 @@ def step(self, *args, **kwargs): self.view.sidebar.providers.tree.click_path('All Ansible Tower Providers') -@navigator.register(AnsibleTowerProvidersCollection, 'All') -class AnsibleTowerExplorerProvidersAll(CFMENavigateStep): - VIEW = TowerExplorerProvidersAllView - prerequisite = NavigateToAttribute('appliance.server', 'AnsibleTowerExplorer') - - def step(self, *args, **kwargs): - self.view.sidebar.providers.tree.click_path('All Ansible Tower Providers') - - @navigator.register(AnsibleTowerSystemsCollection, 'All') class TowerExplorerSystemAll(CFMENavigateStep): VIEW = TowerExplorerSystemsAllView diff --git a/cfme/fixtures/config_manager.py b/cfme/fixtures/config_manager.py new file mode 100644 index 0000000000..eaf3265099 --- /dev/null +++ b/cfme/fixtures/config_manager.py @@ -0,0 +1,32 @@ +import pytest + +from cfme.utils.conf import cfme_data + +CONFIG_MANAGERS = [cfg_mgr_key for cfg_mgr_key in cfme_data.get('configuration_managers', {})] + + +@pytest.fixture(params=CONFIG_MANAGERS) +def config_manager_obj(request, appliance): + collection = "satellite_providers" + if "ansible" in request.param: + collection = "ansible_tower_providers" + + yield getattr(appliance.collections, collection).instantiate(key=request.param) + + +@pytest.fixture(scope="module", params=CONFIG_MANAGERS) +def config_manager_obj_module_scope(request, appliance): + collection = "satellite_providers" + if "ansible" in request.param: + collection = "ansible_tower_providers" + + yield getattr(appliance.collections, collection).instantiate(key=request.param) + + +@pytest.fixture(scope="class", params=CONFIG_MANAGERS) +def config_manager_obj_class_scope(request, appliance): + collection = "satellite_providers" + if "ansible" in request.param: + collection = "ansible_tower_providers" + + yield getattr(appliance.collections, collection).instantiate(key=request.param) diff --git a/cfme/infrastructure/config_management.py b/cfme/infrastructure/config_management/__init__.py similarity index 59% rename from cfme/infrastructure/config_management.py rename to cfme/infrastructure/config_management/__init__.py index 7ab64676ff..85aec537bd 100644 --- a/cfme/infrastructure/config_management.py +++ b/cfme/infrastructure/config_management/__init__.py @@ -1,8 +1,10 @@ +from copy import copy + +import attr from manageiq_client.api import APIException from manageiq_client.api import Entity as RestEntity from navmazing import NavigateToAttribute from navmazing import NavigateToSibling -from widgetastic.exceptions import NoSuchElementException from widgetastic.widget import Checkbox from widgetastic.widget import Text from widgetastic.widget import TextInput @@ -11,13 +13,16 @@ from widgetastic_patternfly import Dropdown from cfme.base.credential import Credential as BaseCredential -from cfme.common import BaseLoggedInPage -from cfme.common import Taggable +from cfme.base.login import BaseLoggedInPage from cfme.common import TagPageView from cfme.exceptions import displayed_not_implemented +from cfme.infrastructure.config_management.config_profiles import ConfigProfile +from cfme.infrastructure.config_management.config_profiles import ConfigProfilesCollection +from cfme.infrastructure.config_management.config_systems import ConfigSystem +from cfme.modeling.base import BaseCollection +from cfme.modeling.base import BaseEntity from cfme.utils import conf from cfme.utils import ParamClassName -from cfme.utils.appliance import NavigatableMixin from cfme.utils.appliance.implementations.ui import CFMENavigateStep from cfme.utils.appliance.implementations.ui import navigate_to from cfme.utils.appliance.implementations.ui import navigator @@ -25,16 +30,12 @@ from cfme.utils.pretty import Pretty from cfme.utils.rest import assert_response from cfme.utils.update import Updateable -from cfme.utils.version import LATEST -from cfme.utils.version import LOWEST -from cfme.utils.version import VersionPicker from cfme.utils.wait import wait_for from widgetastic_manageiq import Accordion from widgetastic_manageiq import BaseEntitiesView from widgetastic_manageiq import Button from widgetastic_manageiq import ItemsToolBarViewSelector from widgetastic_manageiq import ManageIQTree -from widgetastic_manageiq import Search from widgetastic_manageiq import SummaryTable from widgetastic_manageiq import Table from widgetastic_manageiq import WaitTab @@ -83,24 +84,6 @@ class ConfigManagementEntities(BaseEntitiesView): table = Table("//div[@id='gtl_div']//table") -class ConfigManagementProfileEntities(BaseEntitiesView): - """Entities view for the detail page""" - @View.nested - class summary(WaitTab): # noqa - TAB_NAME = 'Summary' - - properties = SummaryTable(title='Properties') - environment = SummaryTable(title='Environment') - operating_system = SummaryTable(title='Operating System') - tenancy = SummaryTable(title='Tenancy') - smart_management = SummaryTable(title='Smart Management') - - @View.nested - class configured_systems(WaitTab): # noqa - TAB_NAME = 'Configured Systems' - elements = Table('//div[@id="main_div"]//div[@id="list_grid" or @id="gtl_div"]//table') - - class ConfigManagementAddForm(View): """Form to add a provider""" name = TextInput('name') @@ -147,13 +130,14 @@ class ConfigManagementEditEntities(View): cancel = Button('Cancel') -class ConfigManagementView(BaseLoggedInPage): - """The base page for both the all and details page""" +class ConfigManagementCollectionView(BaseLoggedInPage): + """ Base page for ALL """ @property def in_config(self): """Determine if we're in the config section""" - if getattr(self.context['object'], 'type', None) == 'Ansible Tower': + object_type = getattr(self.context['object'].ENTITY, 'type', None) + if object_type == 'Ansible Tower': nav_chain = ['Automation', 'Ansible Tower', 'Explorer'] else: nav_chain = ['Configuration', 'Management'] @@ -163,39 +147,20 @@ def in_config(self): ) -class ConfigManagementAllView(ConfigManagementView): - """The main list view""" - toolbar = View.nested(ConfigManagementToolbar) - sidebar = View.nested(ConfigManagementSideBar) - search = View.nested(Search) - including_entities = View.include(ConfigManagementEntities, use_parent=True) - - @property - def is_displayed(self): - """Is this view being displayed?""" - if self.context['object'].type == 'Ansible Tower': - title_text = 'All Ansible Tower Providers' - else: - title_text = 'All Configuration Management Providers' - return ( - self.in_config and - self.entities.title.text == title_text - ) - - -class ConfigSystemAllView(ConfigManagementAllView): - """The config system view has a different title""" +class ConfigManagementView(BaseLoggedInPage): + """The base page for the details page""" @property - def is_displayed(self): - if hasattr(self.context['object'], 'type') and self.context['object'].type == \ - 'Ansible Tower': - title_text = 'All Ansible Tower Configured Systems' + def in_config(self): + """Determine if we're in the config section""" + object_type = getattr(self.context['object'], 'type', None) + if object_type == 'Ansible Tower': + nav_chain = ['Automation', 'Ansible Tower', 'Explorer'] else: - title_text = 'All Configured Systems' + nav_chain = ['Configuration', 'Management'] return ( - self.in_config and - self.entities.title.text == title_text + self.logged_in_as_current_user and + self.navigation.currently_selected == nav_chain ) @@ -219,21 +184,6 @@ def is_displayed(self): ) -class ConfigManagementProfileView(ConfigManagementView): - """The profile page""" - toolbar = View.nested(ConfigManagementDetailsToolbar) - sidebar = View.nested(ConfigManagementSideBar) - including_entities = View.include(ConfigManagementProfileEntities, use_parent=True) - - @property - def is_displayed(self): - """Is this view being displayed?""" - title = 'Configured System ({}) "{}"'.format( - self.context['object'].manager.type, - self.context['object'].name) - return self.in_config and self.entities.title.text == title - - class ConfigManagementAddView(ConfigManagementView): """The add page""" sidebar = View.nested(ConfigManagementSideBar) @@ -247,10 +197,41 @@ class ConfigManagementEditView(ConfigManagementView): entities = View.nested(ConfigManagementEditEntities) is_displayed = displayed_not_implemented - is_displayed = displayed_not_implemented +class ConfigManagementProfileEntities(BaseEntitiesView): + """Entities view for the detail page""" + @View.nested + class summary(WaitTab): # noqa + TAB_NAME = 'Summary' + + properties = SummaryTable(title='Properties') + environment = SummaryTable(title='Environment') + operating_system = SummaryTable(title='Operating System') + tenancy = SummaryTable(title='Tenancy') + smart_management = SummaryTable(title='Smart Management') + + @View.nested + class configured_systems(WaitTab): # noqa + TAB_NAME = 'Configured Systems' + elements = Table('//div[@id="main_div"]//div[@id="list_grid" or @id="gtl_div"]//table') -class ConfigManager(Updateable, Pretty, NavigatableMixin): + +class ConfigManagementProfileView(ConfigManagementView): + """The profile page""" + toolbar = View.nested(ConfigManagementDetailsToolbar) + sidebar = View.nested(ConfigManagementSideBar) + including_entities = View.include(ConfigManagementProfileEntities, use_parent=True) + + @property + def is_displayed(self): + title = 'Configured Systems under {} "{}"'.format( + self.context['object'].type, + self.context['object'].name) + return self.entities.title.text == title + + +@attr.s +class ConfigManager(BaseEntity, Updateable, Pretty): """ This is base class for Configuration manager objects (Red Hat Satellite, Foreman, Ansible Tower) @@ -269,27 +250,17 @@ class ConfigManager(Updateable, Pretty, NavigatableMixin): _param_name = ParamClassName('name') type = None refresh_flash_msg = 'Refresh Provider initiated for 1 provider' + name = attr.ib(default=None) + url = attr.ib(default=None) + credentials = attr.ib(default=None) + key = attr.ib(default=None) - def __init__(self, appliance, name=None, url=None, ssl=None, credentials=None, key=None): - self.appliance = appliance - self.name = name - self.url = url - self.ssl = ssl - self.credentials = credentials - self.key = key or name + _collections = {"config_profiles": ConfigProfilesCollection} class Credential(BaseCredential, Updateable): pass - @property - def ui_name(self): - """Return the name used in the UI""" - if self.type == 'Ansible Tower': - return '{} Automation Manager'.format(self.name) - else: - return '{} Configuration Manager'.format(self.name) - - def create(self, cancel=False, validate_credentials=True, validate=True, force=False): + def create(self, cancel=False, validate_credentials=True, validate=True, force=False, **kwargs): """Creates the manager through UI Args: @@ -302,23 +273,25 @@ def create(self, cancel=False, validate_credentials=True, validate=True, force=F force (bool): Whether to force the creation even if the manager already exists. True will try anyway; False will check for its existence and leave, if present. """ + def config_profiles_loaded(): # Workaround - without this, validation of provider failed config_profiles_names = [prof.name for prof in self.config_profiles] logger.info( "UI: %s\nYAML: %s", - set(config_profiles_names), set(self.yaml_data['config_profiles'])) + set(config_profiles_names), set(self.yaml_data['config_profiles']) + ) # Just validate any profiles from yaml are in UI - not all are displayed return any( [cp in config_profiles_names for cp in self.yaml_data['config_profiles']]) if not force and self.exists: return + form_dict = self.__dict__ form_dict.update(self.credentials.view_value_mapping) - if self.appliance.version < '5.8': - form_dict['provider_type'] = self.type - view = navigate_to(self, 'Add') + + view = navigate_to(self.parent, 'Add') view.entities.form.fill(form_dict) if validate_credentials: view.entities.form.validate.click() @@ -343,7 +316,8 @@ def config_profiles_loaded(): config_profiles_loaded, fail_func=self.refresh_relationships, handle_exception=True, - num_sec=180, delay=30) + num_sec=180, delay=30 + ) def update(self, updates, cancel=False, validate_credentials=False): """Updates the manager through UI @@ -385,7 +359,7 @@ def delete(self, cancel=False, wait_deleted=True, force=False): """ if not force and not self.exists: return - view = navigate_to(self, 'All') + view = navigate_to(self.parent, 'All') view.toolbar.view_selector.select('List View') provider_entity = view.entities.get_entities_by_keys(provider_name=self.ui_name) provider_entity[0].check() @@ -413,32 +387,35 @@ def rest_api_entity(self): name=self.ui_name ).provider_id - return RestEntity( - self.appliance.rest_api.collections.providers, - data={ - "href": self.appliance.url_path( - "/api/providers/{}?provider_class=provider".format(provider_id) - ) - }, - ) + return RestEntity(self.appliance.rest_api.collections.providers, data={ + "href": self.appliance.url_path( + "/api/providers/{}?provider_class=provider".format(provider_id) + ) + }) + # TODO: implement this via Sentaku def create_rest(self): """Create the config manager in CFME using REST""" + include_ssl = False if "ansible_tower" in self.key: config_type = "AnsibleTower" elif "satellite" in self.key: config_type = "Foreman" + include_ssl = True payload = { "type": "ManageIQ::Providers::{}::Provider".format(config_type), "url": self.url, "name": self.name, - "verify_ssl": self.ssl, "credentials": { "userid": self.credentials.view_value_mapping["username"], "password": self.credentials.view_value_mapping["password"], }, } + + if include_ssl: + payload["verify_ssl"] = self.ssl + try: self.appliance.rest_api.post( api_endpoint_url=self.appliance.url_path( @@ -471,18 +448,9 @@ def delete_rest(self): "Provider wasn't deleted, status code {}".format(response.status_code) ) - @property - def exists(self): - """Returns whether the manager exists in the UI or not""" - try: - navigate_to(self, 'Details') - except NoSuchElementException: - return False - return True - def refresh_relationships(self, cancel=False): """Refreshes relationships and power states of this manager""" - view = navigate_to(self, 'All') + view = navigate_to(self.parent, 'All') view.toolbar.view_selector.select('List View') provider_entity = view.entities.get_entities_by_keys(provider_name=self.ui_name)[0] provider_entity.check() @@ -495,232 +463,60 @@ def refresh_relationships(self, cancel=False): @property def config_profiles(self): """Returns 'ConfigProfile' configuration profiles (hostgroups) available on this manager""" - view = navigate_to(self, 'Details') - # TODO - remove it later.Workaround for BZ 1452425 - view.toolbar.view_selector.select('List View') - view.toolbar.refresh.click() - wait_for(lambda: view.entities.elements.is_displayed, fail_func=view.toolbar.refresh.click, - handle_exception=True, num_sec=60, delay=5) - config_profiles = [] - for row in view.entities.elements: - if self.type == 'Ansible Tower': - name = row.name.text - else: - name = row.description.text - if 'unassigned' in name.lower(): - continue - config_profiles.append(ConfigProfile(appliance=self.appliance, name=name, manager=self)) - return config_profiles + return self.collections.config_profiles.all() @property - def systems(self): + def config_systems(self): """Returns 'ConfigSystem' configured systems (hosts) available on this manager""" - return sum([prof.systems for prof in self.config_profiles]) + systems_per_prof = [prof.config_systems for prof in self.config_profiles] + return [item for sublist in systems_per_prof for item in sublist] @property def yaml_data(self): """Returns yaml data for this manager""" return conf.cfme_data.configuration_managers[self.key] - @classmethod - def load_from_yaml(cls, key, appliance): - """Returns 'ConfigManager' object loaded from yamls, based on its key""" - data = conf.cfme_data.configuration_managers[key] - creds = conf.credentials[data['credentials']] - return cls( - appliance=appliance, - name=data['name'], - url=data['url'], - ssl=data['ssl'], - credentials=cls.Credential( - principal=creds['username'], secret=creds['password']), - key=key) + def from_yaml(self, key): + """ Given key, returns an object with yaml properties filled in""" + new_obj = copy(self) + new_obj.key = key + new_obj.name = new_obj.yaml_data.get("name") + new_obj.url = new_obj.yaml_data.get("url") + if hasattr(new_obj, "ssl"): + new_obj.ssl = new_obj.yaml_data.get("ssl") + creds = conf.credentials.get(new_obj.yaml_data.get("credentials")) + new_obj.credentials = self.Credential( + principal=creds["username"], + secret=creds["password"] + ) + return new_obj @property def quad_name(self): - if self.type == 'Ansible Tower': - return '{} Automation Manager'.format(self.name) - else: - return '{} Configuration Manager'.format(self.name) - - -def get_config_manager_from_config(cfg_mgr_key, appliance=None, mgr_type=None): - cfg_mgr = conf.cfme_data.get('configuration_managers', {})[cfg_mgr_key] - if mgr_type and cfg_mgr['type'] != mgr_type: - logger.info(f'Config managers loading type mismatch: {cfg_mgr} ' - f'key does not match desired type: [{mgr_type}]') - return None - if cfg_mgr['type'] == 'satellite': - return Satellite.load_from_yaml(cfg_mgr_key, appliance) - elif cfg_mgr['type'] == 'ansible': - return AnsibleTower.load_from_yaml(cfg_mgr_key, appliance) - else: - raise Exception("Unknown configuration manager key") - - -class ConfigProfile(Pretty, NavigatableMixin): - """Configuration profile object (foreman-side hostgroup) - - Args: - appliance: appliance object - name: Name of the profile - manager: ConfigManager object which this profile is bound to - """ - pretty_attrs = ['name', 'manager'] - - def __init__(self, appliance, name, manager): - self.appliance = appliance - self.name = name - self.manager = manager - - @property - def systems(self): - """Returns 'ConfigSystem' objects that are active under this profile""" - view = navigate_to(self, 'Details') - view.toolbar.view_selector.select('List View') - - # Unassigned config profile has no tabstrip - if 'unassigned' not in self.name.lower(): - view.entities.configured_systems.click() - - if view.entities.configured_systems.elements.is_displayed: - return [ConfigSystem(appliance=self.appliance, name=row.hostname.text, profile=self) - for row in view.entities.configured_systems.elements] - return list() - - -class ConfigSystem(Pretty, NavigatableMixin, Taggable): - """The tags pages of the config system""" - pretty_attrs = ['name', 'manager_key'] - - def __init__(self, appliance, name, profile): - self.appliance = appliance - self.name = name - self.profile = profile - - def get_tags(self, tenant="My Company Tags"): - """Overridden get_tags method to deal with the fact that configured systems don't have a - details view.""" - view = navigate_to(self, 'EditTags') - return [ - self.appliance.collections.categories.instantiate( - display_name=r.category.text.replace('*', '').strip()).collections.tags.instantiate( - display_name=r.assigned_value.text.strip()) - for r in view.form.tags - ] - - -class Satellite(ConfigManager): - """ - Configuration manager object (Red Hat Satellite, Foreman) - - Args: - name: Name of the Satellite/Foreman configuration manager - url: URL, hostname or IP of the configuration manager - ssl: Boolean value; `True` if SSL certificate validity should be checked, `False` otherwise - credentials: Credentials to access the config. manager - key: Key to access the cfme_data yaml data (same as `name` if not specified) - - Usage: - Create provider: - .. code-block:: python - - satellite_cfg_mgr = Satellite('my_satellite', 'my-satellite.example.com', - ssl=False, ConfigManager.Credential(principal='admin', - secret='testing'), key='satellite_yaml_key') - satellite_cfg_mgr.create() - - Update provider: - .. code-block:: python - - with update(satellite_cfg_mgr): - satellite_cfg_mgr.name = 'new_satellite_name' - - Delete provider: - .. code-block:: python - - satellite_cfg_mgr.delete() - """ - - type = VersionPicker({ - LOWEST: 'Red Hat Satellite', - LATEST: 'Foreman' - }) - - def __init__(self, appliance, name=None, url=None, ssl=None, credentials=None, key=None): - super(Satellite, self).__init__(appliance=appliance, - name=name, - url=url, - ssl=ssl, - credentials=credentials, - key=key) - self.key = key or name - - -class AnsibleTower(ConfigManager): - """ - Configuration manager object (Ansible Tower) - - Args: - name: Name of the Ansible Tower configuration manager - url: URL, hostname or IP of the configuration manager - ssl: Boolean value; `True` if SSL certificate validity should be checked, `False` otherwise - credentials: Credentials to access the config. manager - key: Key to access the cfme_data yaml data (same as `name` if not specified) - - Usage: - Create provider: - .. code-block:: python - - tower_cfg_mgr = AnsibleTower('my_tower', 'https://my-tower.example.com/api/v1', - ssl=False, ConfigManager.Credential(principal='admin', - secret='testing'), key='tower_yaml_key') - tower_cfg_mgr.create() - - Update provider: - .. code-block:: python + return self.ui_name - with update(tower_cfg_mgr): - tower_cfg_mgr.name = 'new_tower_name' - Delete provider: - .. code-block:: python - - tower_cfg_mgr.delete() - """ - - type = 'Ansible Tower' - - def __init__(self, appliance, name=None, url=None, ssl=None, credentials=None, key=None): - super(AnsibleTower, self).__init__(appliance=appliance, - name=name, - url=url, - ssl=ssl, - credentials=credentials, - key=key) - self.key = key or name - - @property - def ui_name(self): - """Return the name used in the UI""" - return '{} Automation Manager'.format(self.name) +class ConfigManagersCollection(BaseCollection): + ENTITY = ConfigManager -@navigator.register(ConfigManager, 'All') -class MgrAll(CFMENavigateStep): - VIEW = ConfigManagementAllView - prerequisite = NavigateToAttribute('appliance.server', 'LoggedIn') - def step(self, *args, **kwargs): - if self.obj.type == 'Ansible Tower': - self.prerequisite_view.navigation.select('Automation', 'Ansible Tower', 'Explorer') - self.view.sidebar.providers.tree.click_path('All Ansible Tower Providers') + def instantiate(self, *args, **kwargs): + if kwargs.get("key"): + # pull all yaml data if key is included + obj = self.ENTITY.from_collection(self, *args, **kwargs) + return obj.from_yaml(kwargs.get("key")) else: - self.prerequisite_view.navigation.select('Configuration', 'Management') - self.view.sidebar.providers.tree.click_path('All Configuration Manager Providers') + return self.ENTITY.from_collection(self, *args, **kwargs) + + def create(self, *args, **kwargs): + """ Create/add config manager via UI """ + config_manager = self.instantiate(*args, **kwargs) + config_manager.create(**kwargs) + return config_manager -@navigator.register(ConfigManager, 'Add') +@navigator.register(ConfigManagersCollection, 'Add') class MgrAdd(CFMENavigateStep): VIEW = ConfigManagementAddView prerequisite = NavigateToSibling('All') @@ -732,7 +528,7 @@ def step(self, *args, **kwargs): @navigator.register(ConfigManager, 'Edit') class MgrEdit(CFMENavigateStep): VIEW = ConfigManagementEditView - prerequisite = NavigateToSibling('All') + prerequisite = NavigateToAttribute('parent', 'All') def step(self, *args, **kwargs): self.prerequisite_view.toolbar.view_selector.select('List View') @@ -745,7 +541,7 @@ def step(self, *args, **kwargs): @navigator.register(ConfigManager, 'Details') class MgrDetails(CFMENavigateStep): VIEW = ConfigManagementDetailsView - prerequisite = NavigateToSibling('All') + prerequisite = NavigateToAttribute('parent', 'All') def step(self, *args, **kwargs): self.prerequisite_view.toolbar.view_selector.select('List View') @@ -757,43 +553,16 @@ def step(self, *args, **kwargs): @navigator.register(ConfigManager, 'EditFromDetails') class MgrEditFromDetails(CFMENavigateStep): VIEW = ConfigManagementEditView - prerequisite = NavigateToSibling('Details') + prerequisite = NavigateToAttribute('parent', 'All') def step(self, *args, **kwargs): self.prerequisite_view.toolbar.configuration.item_select('Edit this Provider') -@navigator.register(ConfigProfile, 'Details') -class Details(CFMENavigateStep): - VIEW = ConfigManagementProfileView - prerequisite = NavigateToAttribute('manager', 'Details') - - def step(self, *args, **kwargs): - self.prerequisite_view.toolbar.view_selector.select('List View') - row = self.prerequisite_view.entities.paginator.find_row_on_pages( - self.prerequisite_view.entities.elements, description=self.obj.name) - row.click() - - -@navigator.register(ConfigSystem, 'All') -class SysAll(CFMENavigateStep): - VIEW = ConfigSystemAllView - prerequisite = NavigateToAttribute('appliance.server', 'LoggedIn') - - def step(self, *args, **kwargs): - if hasattr(self.obj, 'type') and self.obj.type == 'Ansible Tower': - self.prerequisite_view.navigation.select('Automation', 'Ansible Tower', 'Explorer') - self.view.sidebar.configured_systems.tree.click_path( - 'All Ansible Tower Configured Systems') - else: - self.prerequisite_view.navigation.select('Configuration', 'Management') - self.view.sidebar.configured_systems.tree.click_path("All Configured Systems") - - @navigator.register(ConfigSystem, 'EditTags') class SysEditTags(CFMENavigateStep): VIEW = TagPageView - prerequisite = NavigateToSibling('All') + prerequisite = NavigateToAttribute('profile', 'Details') def step(self, *args, **kwargs): self.prerequisite_view.toolbar.view_selector.select('List View') @@ -801,3 +570,22 @@ def step(self, *args, **kwargs): self.prerequisite_view.entities.elements, hostname=self.obj.name) row[0].check() self.prerequisite_view.toolbar.policy.item_select('Edit Tags') + + +@navigator.register(ConfigProfile, 'Details') +class Details(CFMENavigateStep): + VIEW = ConfigManagementProfileView + prerequisite = NavigateToAttribute('manager', 'Details') + + def step(self, *args, **kwargs): + self.prerequisite_view.toolbar.view_selector.select('List View') + try: + row = self.prerequisite_view.entities.paginator.find_row_on_pages( + self.prerequisite_view.entities.elements, name=self.obj.name + ) + except NameError: + row = self.prerequisite_view.entities.paginator.find_row_on_pages( + self.prerequisite_view.entities.elements, description=self.obj.name + ) + + row.click() diff --git a/cfme/infrastructure/config_management/ansible_tower.py b/cfme/infrastructure/config_management/ansible_tower.py new file mode 100644 index 0000000000..5b4bb325cc --- /dev/null +++ b/cfme/infrastructure/config_management/ansible_tower.py @@ -0,0 +1,126 @@ +from navmazing import NavigateToAttribute +from widgetastic.widget import View + +from cfme.infrastructure.config_management import ConfigManagementCollectionView +from cfme.infrastructure.config_management import ConfigManagementEntities +from cfme.infrastructure.config_management import ConfigManagementSideBar +from cfme.infrastructure.config_management import ConfigManagementToolbar +from cfme.infrastructure.config_management import ConfigManager +from cfme.infrastructure.config_management import ConfigManagersCollection +from cfme.infrastructure.config_management.config_profiles import ConfigProfilesCollection +from cfme.infrastructure.config_management.config_systems import ConfigSystem +from cfme.utils import conf +from cfme.utils.appliance.implementations.ui import CFMENavigateStep +from cfme.utils.appliance.implementations.ui import navigator +from widgetastic_manageiq import Search + + +class AnsibleTowerProvidersAllView(ConfigManagementCollectionView): + """The main list view""" + toolbar = View.nested(ConfigManagementToolbar) + sidebar = View.nested(ConfigManagementSideBar) + search = View.nested(Search) + including_entities = View.include(ConfigManagementEntities, use_parent=True) + + @property + def is_displayed(self): + title_text = 'All Ansible Tower Providers' + return ( + self.in_config and + self.entities.title.text == title_text + ) + + +class ConfigSystemAllView(AnsibleTowerProvidersAllView): + """The config system view has a different title""" + + @property + def is_displayed(self): + title_text = 'All Ansible Tower Configured Systems' + return ( + self.in_config and + self.entities.title.text == title_text + ) + + +class AnsibleTowerProvider(ConfigManager): + """ + Configuration manager object (Ansible Tower) + + Args: + name: Name of the Ansible Tower configuration manager + url: URL, hostname or IP of the configuration manager + ssl: Boolean value; `True` if SSL certificate validity should be checked, `False` otherwise + credentials: Credentials to access the config. manager + key: Key to access the cfme_data yaml data (same as `name` if not specified) + + Usage: + Create provider: + .. code-block:: python + + tower_cfg_mgr = AnsibleTower('my_tower', 'https://my-tower.example.com/api/v1', + ssl=False, ConfigManager.Credential(principal='admin', + secret='testing'), key='tower_yaml_key') + tower_cfg_mgr.create() + + Update provider: + .. code-block:: python + + with update(tower_cfg_mgr): + tower_cfg_mgr.name = 'new_tower_name' + + Delete provider: + .. code-block:: python + + tower_cfg_mgr.delete() + """ + type = 'Ansible Tower' + + _collections = { + "config_profiles": ConfigProfilesCollection + } + + @property + def ui_name(self): + """Return the name used in the UI""" + return '{} Automation Manager'.format(self.name) + + @classmethod + def from_config(cls, key): + """Returns 'ConfigManager' object loaded from yamls, based on its key""" + data = conf.cfme_data.configuration_managers[key] + creds = conf.credentials[data['credentials']] + return cls.appliance.collections.config_managers.instantiate( + cls, + name=data['name'], + url=data['url'], + credentials=cls.Credential( + principal=creds['username'], secret=creds['password']), + key=key + ) + + +class AnsibleTowerProvidersCollection(ConfigManagersCollection): + ENTITY = AnsibleTowerProvider + + +@navigator.register(AnsibleTowerProvidersCollection, 'All') +class MgrAll(CFMENavigateStep): + VIEW = AnsibleTowerProvidersAllView + prerequisite = NavigateToAttribute('appliance.server', 'LoggedIn') + + def step(self, *args, **kwargs): + self.prerequisite_view.navigation.select('Automation', 'Ansible Tower', 'Explorer') + self.view.sidebar.providers.tree.click_path('All Ansible Tower Providers') + + +@navigator.register(ConfigSystem, 'All') +class SysAll(CFMENavigateStep): + VIEW = ConfigSystemAllView + prerequisite = NavigateToAttribute('appliance.server', 'LoggedIn') + + def step(self, *args, **kwargs): + self.prerequisite_view.navigation.select('Automation', 'Ansible Tower', 'Explorer') + self.view.sidebar.configured_systems.tree.click_path( + 'All Ansible Tower Configured Systems' + ) diff --git a/cfme/infrastructure/config_management/config_profiles.py b/cfme/infrastructure/config_management/config_profiles.py new file mode 100644 index 0000000000..40b83dd2c3 --- /dev/null +++ b/cfme/infrastructure/config_management/config_profiles.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +import attr + +from cfme.infrastructure.config_management.config_systems import ConfigSystemsCollection +from cfme.modeling.base import BaseCollection +from cfme.modeling.base import BaseEntity +from cfme.utils.appliance.implementations.ui import navigate_to +from cfme.utils.pretty import Pretty +from cfme.utils.wait import wait_for + + +@attr.s +class ConfigProfile(BaseEntity, Pretty): + """Configuration profile object (foreman-side hostgroup) + + Args: + name: Name of the profile + manager: ConfigManager object which this profile is bound to + """ + pretty_attrs = ['name', 'manager'] + + name = attr.ib() + manager = attr.ib() + + _collections = {"config_systems": ConfigSystemsCollection} + + @property + def type(self): + kind = "Configuration Profile" + if self.manager.type == "Ansible Tower": + kind = "Inventory Group" + return kind + + @property + def config_systems(self): + """Returns 'ConfigSystem' objects that are active under this profile""" + return self.collections.config_systems.all() + + +@attr.s +class ConfigProfilesCollection(BaseCollection): + """ Collection of ConfigProfiles (nested collection of ConfigManager) """ + ENTITY = ConfigProfile + + def all(self): + """Returns 'ConfigProfile' configuration profiles (hostgroups) available on this manager""" + view = navigate_to(self.parent, "Details") + # TODO - remove it later.Workaround for BZ 1452425 + view.toolbar.view_selector.select('List View') + view.toolbar.refresh.click() + wait_for(lambda: view.entities.elements.is_displayed, fail_func=view.toolbar.refresh.click, + handle_exception=True, num_sec=60, delay=5) + config_profiles = [] + for row in view.entities.elements: + if self.parent.type == 'Ansible Tower': + name = row.name.text + else: + name = row.description.text + if 'unassigned' in name.lower(): + continue + config_profiles.append(self.instantiate(name=name, manager=self.parent)) + return config_profiles diff --git a/cfme/infrastructure/config_management/config_systems/__init__.py b/cfme/infrastructure/config_management/config_systems/__init__.py new file mode 100644 index 0000000000..9cebdf81b9 --- /dev/null +++ b/cfme/infrastructure/config_management/config_systems/__init__.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +import attr + +from cfme.common import Taggable +from cfme.modeling.base import BaseCollection +from cfme.modeling.base import BaseEntity +from cfme.utils.appliance.implementations.ui import navigate_to +from cfme.utils.pretty import Pretty + + +@attr.s +class ConfigSystem(BaseEntity, Pretty, Taggable): + """The tags pages of the config system""" + pretty_attrs = ['name', 'manager_key'] + + name = attr.ib() + profile = attr.ib(default=None) + + def get_tags(self, tenant="My Company Tags"): + """Overridden get_tags method to deal with the fact that configured systems don't have a + details view.""" + view = navigate_to(self, 'EditTags') + return [ + self.appliance.collections.categories.instantiate( + display_name=r.category.text.replace('*', '').strip()).collections.tags.instantiate( + display_name=r.assigned_value.text.strip()) + for r in view.form.tags + ] + + +@attr.s +class ConfigSystemsCollection(BaseCollection): + """ Collection of ConfigSystem objects """ + ENTITY = ConfigSystem + + def all(self): + """Returns 'ConfigSystem' objects that are active under the system's profile""" + if self.filters: + view = navigate_to(self.parent, 'Details') + profile = self.parent + else: + view = navigate_to(self, "All") + profile = None + + view.toolbar.view_selector.select('List View') + # make sure all entities are displayed + view.entities.paginator.set_items_per_page(view.entities.paginator.items_amount) + + if view.entities.elements.is_displayed: + return [ + self.instantiate(row.hostname.text, profile) + for row in view.entities.elements + ] + else: + return [] diff --git a/cfme/infrastructure/config_management/config_systems/ansible_tower.py b/cfme/infrastructure/config_management/config_systems/ansible_tower.py new file mode 100644 index 0000000000..8c5ddeffcd --- /dev/null +++ b/cfme/infrastructure/config_management/config_systems/ansible_tower.py @@ -0,0 +1,14 @@ +import attr + +from cfme.infrastructure.config_management.config_systems import ConfigSystem +from cfme.infrastructure.config_management.config_systems import ConfigSystemsCollection + + +@attr.s +class AnsibleTowerSystem(ConfigSystem): + pass + + +@attr.s +class AnsibleTowerSystemsCollection(ConfigSystemsCollection): + ENTITY = AnsibleTowerSystem diff --git a/cfme/infrastructure/config_management/config_systems/satellite.py b/cfme/infrastructure/config_management/config_systems/satellite.py new file mode 100644 index 0000000000..e004ec349f --- /dev/null +++ b/cfme/infrastructure/config_management/config_systems/satellite.py @@ -0,0 +1,14 @@ +import attr + +from cfme.infrastructure.config_management.config_systems import ConfigSystem +from cfme.infrastructure.config_management.config_systems import ConfigSystemsCollection + + +@attr.s +class SatelliteSystem(ConfigSystem): + pass + + +@attr.s +class SatelliteSystemsCollection(ConfigSystemsCollection): + ENTITY = SatelliteSystem diff --git a/cfme/infrastructure/config_management/satellite.py b/cfme/infrastructure/config_management/satellite.py new file mode 100644 index 0000000000..0fe31d0372 --- /dev/null +++ b/cfme/infrastructure/config_management/satellite.py @@ -0,0 +1,136 @@ +import attr +from navmazing import NavigateToAttribute +from widgetastic.widget import View + +from cfme.infrastructure.config_management import ConfigManagementCollectionView +from cfme.infrastructure.config_management import ConfigManagementEntities +from cfme.infrastructure.config_management import ConfigManagementSideBar +from cfme.infrastructure.config_management import ConfigManagementToolbar +from cfme.infrastructure.config_management import ConfigManager +from cfme.infrastructure.config_management import ConfigManagersCollection +from cfme.infrastructure.config_management.config_profiles import ConfigProfilesCollection +from cfme.infrastructure.config_management.config_systems.satellite import SatelliteSystemsCollection # noqa +from cfme.utils import conf +from cfme.utils.appliance.implementations.ui import CFMENavigateStep +from cfme.utils.appliance.implementations.ui import navigator +from cfme.utils.version import LATEST +from cfme.utils.version import LOWEST +from cfme.utils.version import VersionPicker +from widgetastic_manageiq import Search + + +class SatelliteProvidersAllView(ConfigManagementCollectionView): + """The main list view""" + toolbar = View.nested(ConfigManagementToolbar) + sidebar = View.nested(ConfigManagementSideBar) + search = View.nested(Search) + including_entities = View.include(ConfigManagementEntities, use_parent=True) + + @property + def is_displayed(self): + """Is this view being displayed?""" + title_text = 'All Configuration Management Providers' + return ( + self.in_config and + self.entities.title.text == title_text + ) + + +class SatelliteSystemsAllView(SatelliteProvidersAllView): + """The config system view has a different title""" + + @property + def is_displayed(self): + title_text = 'All Configured Systems' + return ( + self.in_config and + self.entities.title.text == title_text + ) + + +@attr.s +class SatelliteProvider(ConfigManager): + """ + Configuration manager object (Red Hat Satellite, Foreman) + + Args: + name: Name of the Satellite/Foreman configuration manager + url: URL, hostname or IP of the configuration manager + ssl: Boolean value; `True` if SSL certificate validity should be checked, `False` otherwise + credentials: Credentials to access the config. manager + key: Key to access the cfme_data yaml data (same as `name` if not specified) + + Usage: + Create provider: + .. code-block:: python + + satellite_cfg_mgr = Satellite('my_satellite', 'my-satellite.example.com', + ssl=False, ConfigManager.Credential(principal='admin', + secret='testing'), key='satellite_yaml_key') + satellite_cfg_mgr.create() + + Update provider: + .. code-block:: python + + with update(satellite_cfg_mgr): + satellite_cfg_mgr.name = 'new_satellite_name' + + Delete provider: + .. code-block:: python + + satellite_cfg_mgr.delete() + """ + type = VersionPicker({ + LOWEST: 'Red Hat Satellite', + LATEST: 'Foreman' + }) + + ssl = attr.ib(default=None) + + _collections = { + "config_profiles": ConfigProfilesCollection + } + + @property + def ui_name(self): + """Return the name used in the UI""" + return '{} Configuration Manager'.format(self.name) + + @classmethod + def from_config(cls, key): + """Returns 'ConfigManager' object loaded from yamls, based on its key""" + data = conf.cfme_data.configuration_managers[key] + creds = conf.credentials[data['credentials']] + return cls.appliance.collections.config_managers.instantiate( + cls, + name=data['name'], + url=data['url'], + ssl=data['ssl'], + credentials=cls.Credential( + principal=creds['username'], secret=creds['password']), + key=key + ) + + +class SatelliteProvidersCollection(ConfigManagersCollection): + ENTITY = SatelliteProvider + + +@navigator.register(SatelliteProvidersCollection, 'All') +class MgrAll(CFMENavigateStep): + VIEW = SatelliteProvidersAllView + prerequisite = NavigateToAttribute('appliance.server', 'LoggedIn') + + def step(self, *args, **kwargs): + self.prerequisite_view.navigation.select('Configuration', 'Management') + self.view.sidebar.providers.tree.click_path('All Configuration Manager Providers') + + +@navigator.register(SatelliteSystemsCollection, 'All') +class SysAll(CFMENavigateStep): + VIEW = SatelliteSystemsAllView + prerequisite = NavigateToAttribute('appliance.server', 'LoggedIn') + + def step(self, *args, **kwargs): + self.prerequisite_view.navigation.select('Configuration', 'Management') + self.view.sidebar.configured_systems.tree.click_path("All Configured Systems") diff --git a/cfme/tests/infrastructure/test_config_management.py b/cfme/tests/infrastructure/test_config_management.py index ff1f3e417c..fc327022b9 100644 --- a/cfme/tests/infrastructure/test_config_management.py +++ b/cfme/tests/infrastructure/test_config_management.py @@ -3,13 +3,13 @@ from cfme import test_requirements from cfme.ansible_tower.explorer import TowerCreateServiceDialogFromTemplateView -from cfme.infrastructure.config_management import AnsibleTower -from cfme.utils.testgen import config_managers -from cfme.utils.testgen import generate from cfme.utils.update import update -pytest_generate_tests = generate(gen_func=config_managers) +pytestmark = [ + pytest.mark.meta(blockers=[1491704]) +] + TEMPLATE_TYPE = { "job": "Job Template (Ansible Tower)", @@ -28,11 +28,11 @@ def config_manager(config_manager_obj, appliance): @pytest.fixture def config_system(config_manager): - return fauxfactory.gen_choice(config_manager.systems) + return fauxfactory.gen_choice(config_manager.config_systems) @pytest.mark.tier(3) -def test_config_manager_detail_config_btn(request, config_manager): +def test_config_manager_detail_config_btn(config_manager): """ Polarion: assignee: nachandr @@ -124,11 +124,9 @@ def test_config_manager_remove(config_manager): # Disable this test for Tower, no Configuration profiles can be retrieved from Tower side yet # this is all real hackish because configmanager isn't a proper provider. @pytest.mark.tier(3) -@test_requirements.tag -@pytest.mark.uncollectif(lambda config_manager_obj: - isinstance(config_manager_obj, AnsibleTower), - reason='Ansible tower not valid for this test') -def test_config_system_tag(config_system, tag, appliance, config_manager, config_manager_obj): +@pytest.mark.uncollectif(lambda config_manager_obj: "ansible" in config_manager_obj, + reason="Ansible Tower cannot run this test") +def test_config_system_tag(config_system, tag): """ Polarion: assignee: anikifor @@ -140,11 +138,9 @@ def test_config_system_tag(config_system, tag, appliance, config_manager, config @pytest.mark.tier(3) -@test_requirements.tag -@pytest.mark.uncollectif(lambda config_manager_obj: - not isinstance(config_manager_obj, AnsibleTower), +@pytest.mark.uncollectif(lambda config_manager_obj: "satellite" in config_manager_obj, reason='Only Ansible tower is valid for this test') -def test_ansible_tower_job_templates_tag(request, config_manager, tag, config_manager_obj): +def test_ansible_tower_job_templates_tag(request, config_manager, tag): """ Polarion: assignee: anikifor @@ -170,12 +166,10 @@ def test_ansible_tower_job_templates_tag(request, config_manager, tag, config_ma @pytest.mark.tier(3) -@pytest.mark.uncollectif(lambda config_manager_obj: - not isinstance(config_manager_obj, AnsibleTower), +@pytest.mark.uncollectif(lambda config_manager_obj: "satellite" in config_manager_obj, reason='Only Ansible tower is valid for this test') @pytest.mark.parametrize('template_type', TEMPLATE_TYPE.values(), ids=list(TEMPLATE_TYPE.keys())) -def test_ansible_tower_service_dialog_creation_from_template(config_manager, appliance, - template_type, config_manager_obj): +def test_ansible_tower_service_dialog_creation_from_template(config_manager, template_type): """ Polarion: assignee: nachandr diff --git a/cfme/tests/infrastructure/test_config_management_rest.py b/cfme/tests/infrastructure/test_config_management_rest.py index defceca32b..58f281cfd6 100644 --- a/cfme/tests/infrastructure/test_config_management_rest.py +++ b/cfme/tests/infrastructure/test_config_management_rest.py @@ -5,33 +5,29 @@ from cfme.utils.rest import assert_response from cfme.utils.rest import delete_resources_from_detail from cfme.utils.rest import query_resource_attributes -from cfme.utils.testgen import config_managers -from cfme.utils.testgen import generate from cfme.utils.wait import wait_for -pytest_generate_tests = generate( - # config_managers generates list of managers from cfme_data - gen_func=config_managers, - # Filter the config manager types for Tower - managers_type='ansible', - scope='module' -) -pytestmark = [test_requirements.rest] +pytestmark = [ + test_requirements.rest, + pytest.mark.usefixtures('config_manager_obj_module_scope'), + pytest.mark.uncollectif( + lambda config_manager_obj_module_scope: "ansible" not in config_manager_obj_module_scope + ) +] -@pytest.fixture(scope='module') -def config_manager(config_manager_obj, appliance): +@pytest.fixture(scope='class') +def config_manager(config_manager_obj_class_scope): """Fixture that provides a random config manager and sets it up.""" - config_manager_obj.appliance = appliance - if config_manager_obj.type != 'Ansible Tower': + if config_manager_obj_class_scope.type != 'Ansible Tower': pytest.skip('A non "Ansible Tower" configuration manager provider was selected for test,' 'Please verify cfme_data.yaml content.') - config_manager_obj.create(validate=True) # actually add it to CFME - yield config_manager_obj + config_manager_obj_class_scope.create(validate=True) # actually add it to CFME + yield config_manager_obj_class_scope - config_manager_obj.delete() + config_manager_obj_class_scope.delete() @pytest.fixture @@ -233,7 +229,7 @@ def test_authentications_options(self, appliance, config_manager): assert_response(appliance) -@pytest.fixture(scope="module") +@pytest.fixture def config_manager_rest(config_manager_obj): """Creates provider using REST API.""" config_manager_obj.create_rest() diff --git a/cfme/tests/services/test_ansible_workflow_servicecatalogs.py b/cfme/tests/services/test_ansible_workflow_servicecatalogs.py index 7b23d9f51d..a2173756e9 100644 --- a/cfme/tests/services/test_ansible_workflow_servicecatalogs.py +++ b/cfme/tests/services/test_ansible_workflow_servicecatalogs.py @@ -3,7 +3,6 @@ from cfme import test_requirements from cfme.services.myservice import MyService from cfme.services.service_catalogs import ServiceCatalogs -from cfme.utils import testgen from cfme.utils.blockers import BZ from cfme.utils.log import logger @@ -11,6 +10,10 @@ pytestmark = [ test_requirements.service, pytest.mark.tier(2), + pytest.mark.usefixtures("config_manager_obj_module_scope"), + pytest.mark.uncollectif( + lambda config_manager_obj_module_scope: "ansible" not in config_manager_obj_module_scope + ), pytest.mark.parametrize('workflow_type', ['multiple_job_workflow', 'inventory_sync_workflow'], ids=['multiple_job_workflow', 'inventory_sync_workflow'], scope='module'), @@ -18,31 +21,13 @@ ] -def pytest_generate_tests(metafunc): - # Filter out providers without provisioning data or hosts defined - argnames, argvalues, idlist = testgen.config_managers(metafunc) - new_idlist = [] - new_argvalues = [] - for i, argvalue_tuple in enumerate(argvalues): - args = dict(zip(argnames, argvalue_tuple)) - - if not args['config_manager_obj'].yaml_data['provisioning']: - continue - - new_idlist.append(idlist[i]) - new_argvalues.append(argvalues[i]) - - testgen.parametrize(metafunc, argnames, new_argvalues, ids=new_idlist, scope='module') - - @pytest.fixture(scope="module") -def tower_manager(config_manager_obj, appliance): +def tower_manager(config_manager_obj_module_scope): """ Fixture that sets up Ansible Tower provider""" - config_manager_obj.appliance = appliance - if config_manager_obj.type == "Ansible Tower": - config_manager_obj.create(validate=True) - yield config_manager_obj - config_manager_obj.delete() + if config_manager_obj_module_scope.type == "Ansible Tower": + config_manager_obj_module_scope.create(validate=True) + yield config_manager_obj_module_scope + config_manager_obj_module_scope.delete() @pytest.fixture(scope="function") diff --git a/cfme/tests/services/test_config_provider_servicecatalogs.py b/cfme/tests/services/test_config_provider_servicecatalogs.py index 28f21d35b9..479d9f5803 100644 --- a/cfme/tests/services/test_config_provider_servicecatalogs.py +++ b/cfme/tests/services/test_config_provider_servicecatalogs.py @@ -3,7 +3,6 @@ from cfme import test_requirements from cfme.services.myservice import MyService from cfme.services.service_catalogs import ServiceCatalogs -from cfme.utils import testgen from cfme.utils.blockers import BZ from cfme.utils.blockers import GH from cfme.utils.log import logger @@ -11,6 +10,10 @@ pytestmark = [ test_requirements.service, + pytest.mark.usefixtures('config_manager_obj_module_scope'), + pytest.mark.uncollectif( + lambda config_manager_obj_module_scope: "ansible" not in config_manager_obj_module_scope + ), pytest.mark.tier(2), pytest.mark.parametrize('job_type', ['template', 'template_limit', 'template_survey', 'textarea_survey'], @@ -20,33 +23,24 @@ ] -def pytest_generate_tests(metafunc): - # Filter out providers without provisioning data or hosts defined - argnames, argvalues, idlist = testgen.config_managers(metafunc) - new_idlist = [] - new_argvalues = [] - for i, argvalue_tuple in enumerate(argvalues): - args = dict(list(zip(argnames, argvalue_tuple))) +@pytest.fixture +def config_manager_obj(appliance, cfg_mgr_key): + collection = "satellite_providers" + if "ansible" in cfg_mgr_key: + collection = "ansible_tower_providers" - if not args['config_manager_obj'].yaml_data['provisioning']: - continue - - new_idlist.append(idlist[i]) - new_argvalues.append(argvalues[i]) - - testgen.parametrize(metafunc, argnames, new_argvalues, ids=new_idlist, scope='module') + yield getattr(appliance.collections, collection).instantiate(key=cfg_mgr_key) @pytest.fixture(scope="module") -def config_manager(config_manager_obj, appliance): +def config_manager(config_manager_obj_module_scope): """ Fixture that provides a random config manager and sets it up""" - config_manager_obj.appliance = appliance - if config_manager_obj.type == "Ansible Tower": - config_manager_obj.create(validate=True) + if config_manager_obj_module_scope.type == "Ansible Tower": + config_manager_obj_module_scope.create(validate=True) else: - config_manager_obj.create() - yield config_manager_obj - config_manager_obj.delete() + config_manager_obj_module_scope.create() + yield config_manager_obj_module_scope + config_manager_obj_module_scope.delete() @pytest.fixture(scope="function") diff --git a/cfme/utils/testgen.py b/cfme/utils/testgen.py index 5ec3a42687..64eb4e8134 100644 --- a/cfme/utils/testgen.py +++ b/cfme/utils/testgen.py @@ -87,7 +87,6 @@ def gen_providers: import pytest from cfme.common.provider import BaseProvider -from cfme.infrastructure.config_management import get_config_manager_from_config from cfme.roles import group_data from cfme.utils.conf import auth_data from cfme.utils.conf import cfme_data @@ -317,26 +316,6 @@ def auth_groups(metafunc, auth_mode): return argnames, argvalues, idlist -def config_managers(metafunc, managers_type=None): - """Provides config managers - """ - argnames = ['config_manager_obj'] - argvalues = [] - idlist = [] - - data = cfme_data.get('configuration_managers', {}) - - for cfg_mgr_key in data: - config_mgr = get_config_manager_from_config(cfg_mgr_key, - appliance=None, - mgr_type=managers_type) - if config_mgr is None: - continue # type mismatch - argvalues.append([config_mgr]) - idlist.append(cfg_mgr_key) - return argnames, argvalues, idlist - - def pxe_servers(metafunc): """Provides pxe data """ diff --git a/entry_points.txt b/entry_points.txt index 9b74292934..167f9179df 100644 --- a/entry_points.txt +++ b/entry_points.txt @@ -16,8 +16,8 @@ ansible_playbooks = cfme.ansible.playbooks:PlaybooksCollection ansible_repositories = cfme.ansible.repositories:RepositoryCollection ansible_tower_job_templates = cfme.ansible_tower.explorer:AnsibleTowerJobTemplatesCollection ansible_tower_jobs = cfme.ansible_tower.jobs:TowerJobsCollection -ansible_tower_providers = cfme.ansible_tower.explorer:AnsibleTowerProvidersCollection -ansible_tower_systems = cfme.ansible_tower.explorer:AnsibleTowerSystemsCollection +ansible_tower_providers = cfme.infrastructure.config_management.ansible_tower:AnsibleTowerProvidersCollection +ansible_tower_systems = cfme.infrastructure.config_management.config_systems.ansible_tower:AnsibleTowerSystemsCollection automate_import_exports = cfme.automate.import_export:AutomateImportExportsCollection balancers = cfme.networks.balancer:BalancerCollection block_managers = cfme.storage.manager:BlockManagerCollection @@ -105,6 +105,8 @@ reports = cfme.intelligence.reports.reports:ReportsCollection requests = cfme.services.requests:RequestCollection resource_pools = cfme.infrastructure.resource_pool:ResourcePoolCollection roles = cfme.configure.access_control:RoleCollection +satellite_systems = cfme.infrastructure.config_management.config_systems.satellite:SatelliteSystemsCollection +satellite_providers = cfme.infrastructure.config_management.satellite:SatelliteProvidersCollection saved_reports = cfme.intelligence.reports.saved:SavedReportsCollection schedules = cfme.intelligence.reports.schedules:ScheduleCollection security_groups = cfme.cloud.security_groups:SecurityGroupCollection From 0bb530e0157441661357298c48c76e1682a0a078 Mon Sep 17 00:00:00 2001 From: john-dupuy Date: Wed, 23 Oct 2019 15:41:21 -0400 Subject: [PATCH 2/5] First commit of conversion to provider --- .../config_management/__init__.py | 31 ++++++++----------- .../config_management/ansible_tower.py | 18 +++++------ .../config_management/satellite.py | 19 +++++------- .../infrastructure/test_config_management.py | 5 ++- entry_points.txt | 8 +++-- 5 files changed, 37 insertions(+), 44 deletions(-) diff --git a/cfme/infrastructure/config_management/__init__.py b/cfme/infrastructure/config_management/__init__.py index 85aec537bd..79c187d414 100644 --- a/cfme/infrastructure/config_management/__init__.py +++ b/cfme/infrastructure/config_management/__init__.py @@ -13,14 +13,14 @@ from widgetastic_patternfly import Dropdown from cfme.base.credential import Credential as BaseCredential -from cfme.base.login import BaseLoggedInPage +from cfme.common import BaseLoggedInPage from cfme.common import TagPageView +from cfme.common.provider import BaseProvider from cfme.exceptions import displayed_not_implemented from cfme.infrastructure.config_management.config_profiles import ConfigProfile from cfme.infrastructure.config_management.config_profiles import ConfigProfilesCollection from cfme.infrastructure.config_management.config_systems import ConfigSystem from cfme.modeling.base import BaseCollection -from cfme.modeling.base import BaseEntity from cfme.utils import conf from cfme.utils import ParamClassName from cfme.utils.appliance.implementations.ui import CFMENavigateStep @@ -231,7 +231,7 @@ def is_displayed(self): @attr.s -class ConfigManager(BaseEntity, Updateable, Pretty): +class ConfigManagerProvider(BaseProvider, Updateable, Pretty): """ This is base class for Configuration manager objects (Red Hat Satellite, Foreman, Ansible Tower) @@ -246,9 +246,10 @@ class ConfigManager(BaseEntity, Updateable, Pretty): Use Satellite or AnsibleTower classes instead. """ - pretty_attr = ['name', 'url'] + pretty_attr = ['name', 'key'] _param_name = ParamClassName('name') type = None + category = "config_manager" refresh_flash_msg = 'Refresh Provider initiated for 1 provider' name = attr.ib(default=None) url = attr.ib(default=None) @@ -496,18 +497,12 @@ def quad_name(self): return self.ui_name -class ConfigManagersCollection(BaseCollection): +class ConfigManagerProviderCollection(BaseCollection): - ENTITY = ConfigManager + ENTITY = ConfigManagerProvider - - def instantiate(self, *args, **kwargs): - if kwargs.get("key"): - # pull all yaml data if key is included - obj = self.ENTITY.from_collection(self, *args, **kwargs) - return obj.from_yaml(kwargs.get("key")) - else: - return self.ENTITY.from_collection(self, *args, **kwargs) + def instantiate(self, prov_class, *args, **kwargs): + return prov_class.from_collection(self, *args, **kwargs) def create(self, *args, **kwargs): """ Create/add config manager via UI """ @@ -516,7 +511,7 @@ def create(self, *args, **kwargs): return config_manager -@navigator.register(ConfigManagersCollection, 'Add') +@navigator.register(ConfigManagerProviderCollection, 'Add') class MgrAdd(CFMENavigateStep): VIEW = ConfigManagementAddView prerequisite = NavigateToSibling('All') @@ -525,7 +520,7 @@ def step(self, *args, **kwargs): self.prerequisite_view.toolbar.configuration.item_select('Add a new Provider') -@navigator.register(ConfigManager, 'Edit') +@navigator.register(ConfigManagerProvider, 'Edit') class MgrEdit(CFMENavigateStep): VIEW = ConfigManagementEditView prerequisite = NavigateToAttribute('parent', 'All') @@ -538,7 +533,7 @@ def step(self, *args, **kwargs): self.prerequisite_view.toolbar.configuration.item_select('Edit this Provider') -@navigator.register(ConfigManager, 'Details') +@navigator.register(ConfigManagerProvider, 'Details') class MgrDetails(CFMENavigateStep): VIEW = ConfigManagementDetailsView prerequisite = NavigateToAttribute('parent', 'All') @@ -550,7 +545,7 @@ def step(self, *args, **kwargs): row.click() -@navigator.register(ConfigManager, 'EditFromDetails') +@navigator.register(ConfigManagerProvider, 'EditFromDetails') class MgrEditFromDetails(CFMENavigateStep): VIEW = ConfigManagementEditView prerequisite = NavigateToAttribute('parent', 'All') diff --git a/cfme/infrastructure/config_management/ansible_tower.py b/cfme/infrastructure/config_management/ansible_tower.py index 5b4bb325cc..36805fdd8e 100644 --- a/cfme/infrastructure/config_management/ansible_tower.py +++ b/cfme/infrastructure/config_management/ansible_tower.py @@ -5,8 +5,7 @@ from cfme.infrastructure.config_management import ConfigManagementEntities from cfme.infrastructure.config_management import ConfigManagementSideBar from cfme.infrastructure.config_management import ConfigManagementToolbar -from cfme.infrastructure.config_management import ConfigManager -from cfme.infrastructure.config_management import ConfigManagersCollection +from cfme.infrastructure.config_management import ConfigManagerProvider from cfme.infrastructure.config_management.config_profiles import ConfigProfilesCollection from cfme.infrastructure.config_management.config_systems import ConfigSystem from cfme.utils import conf @@ -43,7 +42,7 @@ def is_displayed(self): ) -class AnsibleTowerProvider(ConfigManager): +class AnsibleTowerProvider(ConfigManagerProvider): """ Configuration manager object (Ansible Tower) @@ -75,6 +74,7 @@ class AnsibleTowerProvider(ConfigManager): tower_cfg_mgr.delete() """ type = 'Ansible Tower' + type_name = 'ansible_tower' _collections = { "config_profiles": ConfigProfilesCollection @@ -86,9 +86,9 @@ def ui_name(self): return '{} Automation Manager'.format(self.name) @classmethod - def from_config(cls, key): + def from_config(cls, prov_config, prov_key, appliance=None): """Returns 'ConfigManager' object loaded from yamls, based on its key""" - data = conf.cfme_data.configuration_managers[key] + data = prov_config creds = conf.credentials[data['credentials']] return cls.appliance.collections.config_managers.instantiate( cls, @@ -96,15 +96,11 @@ def from_config(cls, key): url=data['url'], credentials=cls.Credential( principal=creds['username'], secret=creds['password']), - key=key + key=prov_key ) -class AnsibleTowerProvidersCollection(ConfigManagersCollection): - ENTITY = AnsibleTowerProvider - - -@navigator.register(AnsibleTowerProvidersCollection, 'All') +@navigator.register(AnsibleTowerProvider, 'All') class MgrAll(CFMENavigateStep): VIEW = AnsibleTowerProvidersAllView prerequisite = NavigateToAttribute('appliance.server', 'LoggedIn') diff --git a/cfme/infrastructure/config_management/satellite.py b/cfme/infrastructure/config_management/satellite.py index 0fe31d0372..8787c8e359 100644 --- a/cfme/infrastructure/config_management/satellite.py +++ b/cfme/infrastructure/config_management/satellite.py @@ -6,8 +6,7 @@ from cfme.infrastructure.config_management import ConfigManagementEntities from cfme.infrastructure.config_management import ConfigManagementSideBar from cfme.infrastructure.config_management import ConfigManagementToolbar -from cfme.infrastructure.config_management import ConfigManager -from cfme.infrastructure.config_management import ConfigManagersCollection +from cfme.infrastructure.config_management import ConfigManagerProvider from cfme.infrastructure.config_management.config_profiles import ConfigProfilesCollection from cfme.infrastructure.config_management.config_systems.satellite import SatelliteSystemsCollection # noqa from cfme.utils import conf @@ -49,7 +48,7 @@ def is_displayed(self): @attr.s -class SatelliteProvider(ConfigManager): +class SatelliteProvider(ConfigManagerProvider): """ Configuration manager object (Red Hat Satellite, Foreman) @@ -84,7 +83,7 @@ class SatelliteProvider(ConfigManager): LOWEST: 'Red Hat Satellite', LATEST: 'Foreman' }) - + type_name = 'satellite' ssl = attr.ib(default=None) _collections = { @@ -97,9 +96,9 @@ def ui_name(self): return '{} Configuration Manager'.format(self.name) @classmethod - def from_config(cls, key): + def from_config(cls, prov_config, prov_key, appliance=None): """Returns 'ConfigManager' object loaded from yamls, based on its key""" - data = conf.cfme_data.configuration_managers[key] + data = prov_config creds = conf.credentials[data['credentials']] return cls.appliance.collections.config_managers.instantiate( cls, @@ -108,15 +107,11 @@ def from_config(cls, key): ssl=data['ssl'], credentials=cls.Credential( principal=creds['username'], secret=creds['password']), - key=key + key=prov_key ) -class SatelliteProvidersCollection(ConfigManagersCollection): - ENTITY = SatelliteProvider - - -@navigator.register(SatelliteProvidersCollection, 'All') +@navigator.register(SatelliteProvider, 'All') class MgrAll(CFMENavigateStep): VIEW = SatelliteProvidersAllView prerequisite = NavigateToAttribute('appliance.server', 'LoggedIn') diff --git a/cfme/tests/infrastructure/test_config_management.py b/cfme/tests/infrastructure/test_config_management.py index fc327022b9..2f86910cc4 100644 --- a/cfme/tests/infrastructure/test_config_management.py +++ b/cfme/tests/infrastructure/test_config_management.py @@ -3,11 +3,14 @@ from cfme import test_requirements from cfme.ansible_tower.explorer import TowerCreateServiceDialogFromTemplateView +from cfme.infrastructure.config_management.ansible_tower import AnsibleTowerProvider +from cfme.infrastructure.config_management.satellite import SatelliteProvider from cfme.utils.update import update pytestmark = [ - pytest.mark.meta(blockers=[1491704]) + pytest.mark.provider([AnsibleTowerProvider, SatelliteProvider], scope='module'), + pytest.mark.usefixtures('setup_provider') ] diff --git a/entry_points.txt b/entry_points.txt index 167f9179df..6a1f2b9092 100644 --- a/entry_points.txt +++ b/entry_points.txt @@ -16,7 +16,6 @@ ansible_playbooks = cfme.ansible.playbooks:PlaybooksCollection ansible_repositories = cfme.ansible.repositories:RepositoryCollection ansible_tower_job_templates = cfme.ansible_tower.explorer:AnsibleTowerJobTemplatesCollection ansible_tower_jobs = cfme.ansible_tower.jobs:TowerJobsCollection -ansible_tower_providers = cfme.infrastructure.config_management.ansible_tower:AnsibleTowerProvidersCollection ansible_tower_systems = cfme.infrastructure.config_management.config_systems.ansible_tower:AnsibleTowerSystemsCollection automate_import_exports = cfme.automate.import_export:AutomateImportExportsCollection balancers = cfme.networks.balancer:BalancerCollection @@ -42,6 +41,7 @@ cloud_tenants = cfme.cloud.tenant:TenantCollection clusters = cfme.infrastructure.cluster:ClusterCollection compute_rates = cfme.intelligence.chargeback.rates:ComputeRateCollection conditions = cfme.control.explorer.conditions:ConditionCollection +config_managers = cfme.infrastructure.config_management:ConfigManagerProviderCollection container_builds = cfme.containers.build:BuildCollection container_image_registries = cfme.containers.image_registry:ImageRegistryCollection container_images = cfme.containers.image:ImageCollection @@ -106,7 +106,6 @@ requests = cfme.services.requests:RequestCollection resource_pools = cfme.infrastructure.resource_pool:ResourcePoolCollection roles = cfme.configure.access_control:RoleCollection satellite_systems = cfme.infrastructure.config_management.config_systems.satellite:SatelliteSystemsCollection -satellite_providers = cfme.infrastructure.config_management.satellite:SatelliteProvidersCollection saved_reports = cfme.intelligence.reports.saved:SavedReportsCollection schedules = cfme.intelligence.reports.schedules:ScheduleCollection security_groups = cfme.cloud.security_groups:SecurityGroupCollection @@ -141,6 +140,7 @@ saml = cfme.utils.auth:SAMLAuthProvider [manageiq.provider_categories] cloud = cfme.cloud.provider:CloudProvider +config_manager = cfme.infrastructure.config_management:ConfigManagerProvider containers = cfme.containers.provider:ContainersProvider infra = cfme.infrastructure.provider:InfraProvider networks = cfme.networks.provider:NetworkProvider @@ -163,6 +163,10 @@ rhevm = cfme.infrastructure.provider.rhevm:RHEVMProvider scvmm = cfme.infrastructure.provider.scvmm:SCVMMProvider virtualcenter = cfme.infrastructure.provider.virtualcenter:VMwareProvider +[manageiq.provider_types.config_manager] +ansible_tower = cfme.infrastructure.config_management.ansible_tower:AnsibleTowerProvider +satellite = cfme.infrastructure.config_management.satellite:SatelliteProvider + [manageiq.provider_types.networks] nuage = cfme.networks.provider.nuage:NuageProvider From b6dc8f3f4498568f8080a9b95aa1198f9f1c4144 Mon Sep 17 00:00:00 2001 From: john-dupuy Date: Fri, 8 Nov 2019 15:22:34 -0500 Subject: [PATCH 3/5] Config manager as a provider updates --- cfme/fixtures/config_manager.py | 32 ------ .../config_management/__init__.py | 104 ++++++++++-------- .../config_management/ansible_tower.py | 10 +- .../config_management/config_profiles.py | 7 +- .../config_management/satellite.py | 16 ++- .../infrastructure/test_config_management.py | 79 ++++++------- .../test_config_management_rest.py | 38 +++---- .../test_ansible_workflow_servicecatalogs.py | 29 ++--- .../test_config_provider_servicecatalogs.py | 39 ++----- cfme/utils/appliance/__init__.py | 6 +- 10 files changed, 147 insertions(+), 213 deletions(-) delete mode 100644 cfme/fixtures/config_manager.py diff --git a/cfme/fixtures/config_manager.py b/cfme/fixtures/config_manager.py deleted file mode 100644 index eaf3265099..0000000000 --- a/cfme/fixtures/config_manager.py +++ /dev/null @@ -1,32 +0,0 @@ -import pytest - -from cfme.utils.conf import cfme_data - -CONFIG_MANAGERS = [cfg_mgr_key for cfg_mgr_key in cfme_data.get('configuration_managers', {})] - - -@pytest.fixture(params=CONFIG_MANAGERS) -def config_manager_obj(request, appliance): - collection = "satellite_providers" - if "ansible" in request.param: - collection = "ansible_tower_providers" - - yield getattr(appliance.collections, collection).instantiate(key=request.param) - - -@pytest.fixture(scope="module", params=CONFIG_MANAGERS) -def config_manager_obj_module_scope(request, appliance): - collection = "satellite_providers" - if "ansible" in request.param: - collection = "ansible_tower_providers" - - yield getattr(appliance.collections, collection).instantiate(key=request.param) - - -@pytest.fixture(scope="class", params=CONFIG_MANAGERS) -def config_manager_obj_class_scope(request, appliance): - collection = "satellite_providers" - if "ansible" in request.param: - collection = "ansible_tower_providers" - - yield getattr(appliance.collections, collection).instantiate(key=request.param) diff --git a/cfme/infrastructure/config_management/__init__.py b/cfme/infrastructure/config_management/__init__.py index 79c187d414..9ae79e2a43 100644 --- a/cfme/infrastructure/config_management/__init__.py +++ b/cfme/infrastructure/config_management/__init__.py @@ -1,5 +1,3 @@ -from copy import copy - import attr from manageiq_client.api import APIException from manageiq_client.api import Entity as RestEntity @@ -21,7 +19,6 @@ from cfme.infrastructure.config_management.config_profiles import ConfigProfilesCollection from cfme.infrastructure.config_management.config_systems import ConfigSystem from cfme.modeling.base import BaseCollection -from cfme.utils import conf from cfme.utils import ParamClassName from cfme.utils.appliance.implementations.ui import CFMENavigateStep from cfme.utils.appliance.implementations.ui import navigate_to @@ -36,6 +33,7 @@ from widgetastic_manageiq import Button from widgetastic_manageiq import ItemsToolBarViewSelector from widgetastic_manageiq import ManageIQTree +from widgetastic_manageiq import Search from widgetastic_manageiq import SummaryTable from widgetastic_manageiq import Table from widgetastic_manageiq import WaitTab @@ -136,8 +134,8 @@ class ConfigManagementCollectionView(BaseLoggedInPage): @property def in_config(self): """Determine if we're in the config section""" - object_type = getattr(self.context['object'].ENTITY, 'type', None) - if object_type == 'Ansible Tower': + object_type = getattr(self.context['object'], 'type', None) + if object_type == 'ansible_tower': nav_chain = ['Automation', 'Ansible Tower', 'Explorer'] else: nav_chain = ['Configuration', 'Management'] @@ -154,7 +152,7 @@ class ConfigManagementView(BaseLoggedInPage): def in_config(self): """Determine if we're in the config section""" object_type = getattr(self.context['object'], 'type', None) - if object_type == 'Ansible Tower': + if object_type == 'ansible_tower': nav_chain = ['Automation', 'Ansible Tower', 'Explorer'] else: nav_chain = ['Configuration', 'Management'] @@ -216,6 +214,23 @@ class configured_systems(WaitTab): # noqa elements = Table('//div[@id="main_div"]//div[@id="list_grid" or @id="gtl_div"]//table') +class ConfigManagerProvidersAllView(ConfigManagementCollectionView): + """The main list view""" + toolbar = View.nested(ConfigManagementToolbar) + sidebar = View.nested(ConfigManagementSideBar) + search = View.nested(Search) + including_entities = View.include(ConfigManagementEntities, use_parent=True) + + @property + def is_displayed(self): + """Is this view being displayed?""" + title_text = 'All Configuration Management Providers' + return ( + self.in_config and + self.entities.title.text == title_text + ) + + class ConfigManagementProfileView(ConfigManagementView): """The profile page""" toolbar = View.nested(ConfigManagementDetailsToolbar) @@ -230,7 +245,7 @@ def is_displayed(self): return self.entities.title.text == title -@attr.s +@attr.s(cmp=False) class ConfigManagerProvider(BaseProvider, Updateable, Pretty): """ This is base class for Configuration manager objects (Red Hat Satellite, Foreman, Ansible Tower) @@ -238,7 +253,6 @@ class ConfigManagerProvider(BaseProvider, Updateable, Pretty): Args: name: Name of the config. manager url: URL, hostname or IP of the config. manager - ssl: Boolean value; `True` if SSL certificate validity should be checked, `False` otherwise credentials: Credentials to access the config. manager key: Key to access the cfme_data yaml data (same as `name` if not specified) @@ -248,19 +262,30 @@ class ConfigManagerProvider(BaseProvider, Updateable, Pretty): pretty_attr = ['name', 'key'] _param_name = ParamClassName('name') - type = None category = "config_manager" refresh_flash_msg = 'Refresh Provider initiated for 1 provider' name = attr.ib(default=None) url = attr.ib(default=None) credentials = attr.ib(default=None) key = attr.ib(default=None) + ui_type = None _collections = {"config_profiles": ConfigProfilesCollection} class Credential(BaseCredential, Updateable): pass + @property + def exists(self): + """ Returns ``True`` if a provider of the same name exists on the appliance + This overwrite of BaseProvider exists is necessary because MIQ appends + Configuration Manager to the provider name + """ + for name in self.appliance.managed_provider_names: + if self.name in name: + return True + return False + def create(self, cancel=False, validate_credentials=True, validate=True, force=False, **kwargs): """Creates the manager through UI @@ -280,11 +305,11 @@ def config_profiles_loaded(): config_profiles_names = [prof.name for prof in self.config_profiles] logger.info( "UI: %s\nYAML: %s", - set(config_profiles_names), set(self.yaml_data['config_profiles']) + set(config_profiles_names), set(self.data['config_profiles']) ) # Just validate any profiles from yaml are in UI - not all are displayed return any( - [cp in config_profiles_names for cp in self.yaml_data['config_profiles']]) + [cp in config_profiles_names for cp in self.data['config_profiles']]) if not force and self.exists: return @@ -292,7 +317,7 @@ def config_profiles_loaded(): form_dict = self.__dict__ form_dict.update(self.credentials.view_value_mapping) - view = navigate_to(self.parent, 'Add') + view = navigate_to(self, 'Add') view.entities.form.fill(form_dict) if validate_credentials: view.entities.form.validate.click() @@ -303,12 +328,12 @@ def config_profiles_loaded(): else: view.entities.add.wait_displayed('2s') view.entities.add.click() - success_message = '{} Provider "{}" was added'.format(self.type, self.name) + success_message = f'{self.ui_type} Provider "{self.name}" was added' view.flash.assert_success_message(success_message) view.flash.assert_success_message(self.refresh_flash_msg) if validate: try: - self.yaml_data['config_profiles'] + self.data['config_profiles'] except KeyError as e: logger.exception(e) raise @@ -344,7 +369,7 @@ def update(self, updates, cancel=False, validate_credentials=False): else: view.entities.save.click() view.flash.assert_success_message( - '{} Provider "{}" was updated'.format(self.type, updates['name'] or self.name)) + '{} Provider "{}" was updated'.format(self.ui_type, updates['name'] or self.name)) self.__dict__.update(**updates) def delete(self, cancel=False, wait_deleted=True, force=False): @@ -360,8 +385,7 @@ def delete(self, cancel=False, wait_deleted=True, force=False): """ if not force and not self.exists: return - view = navigate_to(self.parent, 'All') - view.toolbar.view_selector.select('List View') + view = navigate_to(self, 'AllOfType') provider_entity = view.entities.get_entities_by_keys(provider_name=self.ui_name) provider_entity[0].check() remove_item = 'Remove selected items from Inventory' @@ -398,9 +422,9 @@ def rest_api_entity(self): def create_rest(self): """Create the config manager in CFME using REST""" include_ssl = False - if "ansible_tower" in self.key: + if self.type == "ansible_tower": config_type = "AnsibleTower" - elif "satellite" in self.key: + else: config_type = "Foreman" include_ssl = True @@ -451,7 +475,7 @@ def delete_rest(self): def refresh_relationships(self, cancel=False): """Refreshes relationships and power states of this manager""" - view = navigate_to(self.parent, 'All') + view = navigate_to(self, 'AllOfType') view.toolbar.view_selector.select('List View') provider_entity = view.entities.get_entities_by_keys(provider_name=self.ui_name)[0] provider_entity.check() @@ -472,26 +496,6 @@ def config_systems(self): systems_per_prof = [prof.config_systems for prof in self.config_profiles] return [item for sublist in systems_per_prof for item in sublist] - @property - def yaml_data(self): - """Returns yaml data for this manager""" - return conf.cfme_data.configuration_managers[self.key] - - def from_yaml(self, key): - """ Given key, returns an object with yaml properties filled in""" - new_obj = copy(self) - new_obj.key = key - new_obj.name = new_obj.yaml_data.get("name") - new_obj.url = new_obj.yaml_data.get("url") - if hasattr(new_obj, "ssl"): - new_obj.ssl = new_obj.yaml_data.get("ssl") - creds = conf.credentials.get(new_obj.yaml_data.get("credentials")) - new_obj.credentials = self.Credential( - principal=creds["username"], - secret=creds["password"] - ) - return new_obj - @property def quad_name(self): return self.ui_name @@ -511,10 +515,10 @@ def create(self, *args, **kwargs): return config_manager -@navigator.register(ConfigManagerProviderCollection, 'Add') +@navigator.register(ConfigManagerProvider, 'Add') class MgrAdd(CFMENavigateStep): VIEW = ConfigManagementAddView - prerequisite = NavigateToSibling('All') + prerequisite = NavigateToSibling('AllOfType') def step(self, *args, **kwargs): self.prerequisite_view.toolbar.configuration.item_select('Add a new Provider') @@ -523,7 +527,7 @@ def step(self, *args, **kwargs): @navigator.register(ConfigManagerProvider, 'Edit') class MgrEdit(CFMENavigateStep): VIEW = ConfigManagementEditView - prerequisite = NavigateToAttribute('parent', 'All') + prerequisite = NavigateToSibling('AllOfType') def step(self, *args, **kwargs): self.prerequisite_view.toolbar.view_selector.select('List View') @@ -536,7 +540,7 @@ def step(self, *args, **kwargs): @navigator.register(ConfigManagerProvider, 'Details') class MgrDetails(CFMENavigateStep): VIEW = ConfigManagementDetailsView - prerequisite = NavigateToAttribute('parent', 'All') + prerequisite = NavigateToSibling('AllOfType') def step(self, *args, **kwargs): self.prerequisite_view.toolbar.view_selector.select('List View') @@ -548,7 +552,7 @@ def step(self, *args, **kwargs): @navigator.register(ConfigManagerProvider, 'EditFromDetails') class MgrEditFromDetails(CFMENavigateStep): VIEW = ConfigManagementEditView - prerequisite = NavigateToAttribute('parent', 'All') + prerequisite = NavigateToSibling('AllOfType') def step(self, *args, **kwargs): self.prerequisite_view.toolbar.configuration.item_select('Edit this Provider') @@ -584,3 +588,13 @@ def step(self, *args, **kwargs): ) row.click() + + +@navigator.register(ConfigManagerProviderCollection, "All") +class ConfigManagerAllPage(CFMENavigateStep): + + def step(self, *args, **kwargs): + raise NotImplementedError( + "There is no page in MIQ that displays all config managers." + " Use 'AllOfType' on a config manager provider instance." + ) diff --git a/cfme/infrastructure/config_management/ansible_tower.py b/cfme/infrastructure/config_management/ansible_tower.py index 36805fdd8e..154f126618 100644 --- a/cfme/infrastructure/config_management/ansible_tower.py +++ b/cfme/infrastructure/config_management/ansible_tower.py @@ -1,3 +1,4 @@ +import attr from navmazing import NavigateToAttribute from widgetastic.widget import View @@ -42,6 +43,7 @@ def is_displayed(self): ) +@attr.s(cmp=False) class AnsibleTowerProvider(ConfigManagerProvider): """ Configuration manager object (Ansible Tower) @@ -73,8 +75,8 @@ class AnsibleTowerProvider(ConfigManagerProvider): tower_cfg_mgr.delete() """ - type = 'Ansible Tower' type_name = 'ansible_tower' + ui_type = 'Ansible Tower' _collections = { "config_profiles": ConfigProfilesCollection @@ -100,7 +102,7 @@ def from_config(cls, prov_config, prov_key, appliance=None): ) -@navigator.register(AnsibleTowerProvider, 'All') +@navigator.register(AnsibleTowerProvider, 'AllOfType') class MgrAll(CFMENavigateStep): VIEW = AnsibleTowerProvidersAllView prerequisite = NavigateToAttribute('appliance.server', 'LoggedIn') @@ -109,6 +111,10 @@ def step(self, *args, **kwargs): self.prerequisite_view.navigation.select('Automation', 'Ansible Tower', 'Explorer') self.view.sidebar.providers.tree.click_path('All Ansible Tower Providers') + def resetter(self, *args, **kwargs): + # Reset view and selection + self.view.toolbar.view_selector.select("List View") + @navigator.register(ConfigSystem, 'All') class SysAll(CFMENavigateStep): diff --git a/cfme/infrastructure/config_management/config_profiles.py b/cfme/infrastructure/config_management/config_profiles.py index 40b83dd2c3..fccf51eeeb 100644 --- a/cfme/infrastructure/config_management/config_profiles.py +++ b/cfme/infrastructure/config_management/config_profiles.py @@ -27,7 +27,7 @@ class ConfigProfile(BaseEntity, Pretty): @property def type(self): kind = "Configuration Profile" - if self.manager.type == "Ansible Tower": + if self.manager.type == "ansible_tower": kind = "Inventory Group" return kind @@ -45,14 +45,11 @@ class ConfigProfilesCollection(BaseCollection): def all(self): """Returns 'ConfigProfile' configuration profiles (hostgroups) available on this manager""" view = navigate_to(self.parent, "Details") - # TODO - remove it later.Workaround for BZ 1452425 - view.toolbar.view_selector.select('List View') - view.toolbar.refresh.click() wait_for(lambda: view.entities.elements.is_displayed, fail_func=view.toolbar.refresh.click, handle_exception=True, num_sec=60, delay=5) config_profiles = [] for row in view.entities.elements: - if self.parent.type == 'Ansible Tower': + if self.parent.ui_type == 'Ansible Tower': name = row.name.text else: name = row.description.text diff --git a/cfme/infrastructure/config_management/satellite.py b/cfme/infrastructure/config_management/satellite.py index 8787c8e359..a65bd6905e 100644 --- a/cfme/infrastructure/config_management/satellite.py +++ b/cfme/infrastructure/config_management/satellite.py @@ -12,9 +12,6 @@ from cfme.utils import conf from cfme.utils.appliance.implementations.ui import CFMENavigateStep from cfme.utils.appliance.implementations.ui import navigator -from cfme.utils.version import LATEST -from cfme.utils.version import LOWEST -from cfme.utils.version import VersionPicker from widgetastic_manageiq import Search @@ -47,7 +44,7 @@ def is_displayed(self): ) -@attr.s +@attr.s(cmp=False) class SatelliteProvider(ConfigManagerProvider): """ Configuration manager object (Red Hat Satellite, Foreman) @@ -79,12 +76,9 @@ class SatelliteProvider(ConfigManagerProvider): satellite_cfg_mgr.delete() """ - type = VersionPicker({ - LOWEST: 'Red Hat Satellite', - LATEST: 'Foreman' - }) type_name = 'satellite' ssl = attr.ib(default=None) + ui_type = 'Red Hat Satellite' _collections = { "config_profiles": ConfigProfilesCollection @@ -111,7 +105,7 @@ def from_config(cls, prov_config, prov_key, appliance=None): ) -@navigator.register(SatelliteProvider, 'All') +@navigator.register(SatelliteProvider, 'AllOfType') class MgrAll(CFMENavigateStep): VIEW = SatelliteProvidersAllView prerequisite = NavigateToAttribute('appliance.server', 'LoggedIn') @@ -120,6 +114,10 @@ def step(self, *args, **kwargs): self.prerequisite_view.navigation.select('Configuration', 'Management') self.view.sidebar.providers.tree.click_path('All Configuration Manager Providers') + def resetter(self, *args, **kwargs): + # Reset view and selection + self.view.toolbar.view_selector.select("List View") + @navigator.register(SatelliteSystemsCollection, 'All') class SysAll(CFMENavigateStep): diff --git a/cfme/tests/infrastructure/test_config_management.py b/cfme/tests/infrastructure/test_config_management.py index 2f86910cc4..4af85142f7 100644 --- a/cfme/tests/infrastructure/test_config_management.py +++ b/cfme/tests/infrastructure/test_config_management.py @@ -5,7 +5,9 @@ from cfme.ansible_tower.explorer import TowerCreateServiceDialogFromTemplateView from cfme.infrastructure.config_management.ansible_tower import AnsibleTowerProvider from cfme.infrastructure.config_management.satellite import SatelliteProvider +from cfme.utils.appliance.implementations.ui import navigate_to from cfme.utils.update import update +from cfme.utils.wait import wait_for pytestmark = [ @@ -21,21 +23,14 @@ @pytest.fixture -def config_manager(config_manager_obj, appliance): - """ Fixture that provides a random config manager and sets it up""" - config_manager_obj.appliance = appliance - config_manager_obj.create() - yield config_manager_obj - config_manager_obj.delete() - - -@pytest.fixture -def config_system(config_manager): - return fauxfactory.gen_choice(config_manager.config_systems) +def config_system(provider): + # by selecting a profile we don't have to select fetch ALL the config systems on a provider + profile = provider.config_profiles[0] + return fauxfactory.gen_choice(profile.config_systems) @pytest.mark.tier(3) -def test_config_manager_detail_config_btn(config_manager): +def test_config_manager_detail_config_btn(provider): """ Polarion: assignee: nachandr @@ -43,23 +38,22 @@ def test_config_manager_detail_config_btn(config_manager): initialEstimate: 1/2h casecomponent: Ansible """ - config_manager.refresh_relationships() + provider.refresh_relationships() @pytest.mark.tier(2) -def test_config_manager_add(request, config_manager_obj): +def test_config_manager_add(provider): """ Polarion: assignee: nachandr casecomponent: Ansible initialEstimate: 1/4h """ - request.addfinalizer(config_manager_obj.delete) - config_manager_obj.create() + navigate_to(provider, "Details") @pytest.mark.tier(3) -def test_config_manager_add_invalid_url(request, config_manager_obj): +def test_config_manager_add_invalid_url(has_no_providers, provider): """ Polarion: assignee: nachandr @@ -67,15 +61,15 @@ def test_config_manager_add_invalid_url(request, config_manager_obj): initialEstimate: 1/15h casecomponent: Ansible """ - request.addfinalizer(config_manager_obj.delete) - config_manager_obj.url = 'https://invalid_url' + wait_for(lambda: not provider.exists, num_sec=60, delay=3) # wait for provider to be deleted + provider.url = 'https://invalid_url' error_message = 'getaddrinfo: Name or service not known' with pytest.raises(Exception, match=error_message): - config_manager_obj.create() + provider.create() @pytest.mark.tier(3) -def test_config_manager_add_invalid_creds(request, config_manager_obj): +def test_config_manager_add_invalid_creds(has_no_providers, provider): """ Polarion: assignee: nachandr @@ -83,19 +77,19 @@ def test_config_manager_add_invalid_creds(request, config_manager_obj): initialEstimate: 1/4h casecomponent: Ansible """ - request.addfinalizer(config_manager_obj.delete) - config_manager_obj.credentials.principal = 'invalid_user' - if config_manager_obj.type == "Ansible Tower": + wait_for(lambda: not provider.exists, num_sec=60, delay=3) # wait for provider to be deleted + provider.credentials.principal = 'invalid_user' + if provider.type == "ansible_tower": msg = ('validation was not successful: {"detail":"Authentication credentials ' 'were not provided. To establish a login session, visit /api/login/."}') else: msg = 'Credential validation was not successful: 401 Unauthorized' with pytest.raises(Exception, match=msg): - config_manager_obj.create() + provider.create() @pytest.mark.tier(3) -def test_config_manager_edit(request, config_manager): +def test_config_manager_edit(request, provider): """ Polarion: assignee: nachandr @@ -104,16 +98,16 @@ def test_config_manager_edit(request, config_manager): casecomponent: Ansible """ new_name = fauxfactory.gen_alpha(8) - old_name = config_manager.name - with update(config_manager): - config_manager.name = new_name - request.addfinalizer(lambda: config_manager.update(updates={'name': old_name})) - assert (config_manager.name == new_name and config_manager.exists),\ + old_name = provider.name + with update(provider): + provider.name = new_name + request.addfinalizer(lambda: provider.update(updates={'name': old_name})) + assert (provider.name == new_name and provider.exists),\ "Failed to update configuration manager's name" @pytest.mark.tier(3) -def test_config_manager_remove(config_manager): +def test_config_manager_remove(request, provider): """ Polarion: assignee: nachandr @@ -121,14 +115,11 @@ def test_config_manager_remove(config_manager): initialEstimate: 1/15h casecomponent: Ansible """ - config_manager.delete() + request.addfinalizer(provider.create) + provider.delete() -# Disable this test for Tower, no Configuration profiles can be retrieved from Tower side yet -# this is all real hackish because configmanager isn't a proper provider. @pytest.mark.tier(3) -@pytest.mark.uncollectif(lambda config_manager_obj: "ansible" in config_manager_obj, - reason="Ansible Tower cannot run this test") def test_config_system_tag(config_system, tag): """ Polarion: @@ -141,9 +132,8 @@ def test_config_system_tag(config_system, tag): @pytest.mark.tier(3) -@pytest.mark.uncollectif(lambda config_manager_obj: "satellite" in config_manager_obj, - reason='Only Ansible tower is valid for this test') -def test_ansible_tower_job_templates_tag(request, config_manager, tag): +@pytest.mark.provider([AnsibleTowerProvider]) +def test_ansible_tower_job_templates_tag(request, provider, tag): """ Polarion: assignee: anikifor @@ -155,7 +145,7 @@ def test_ansible_tower_job_templates_tag(request, config_manager, tag): 1673104 """ try: - job_template = config_manager.appliance.collections.ansible_tower_job_templates.all()[0] + job_template = provider.appliance.collections.ansible_tower_job_templates.all()[0] except IndexError: pytest.skip("No job template was found") job_template.add_tag(tag=tag, details=False) @@ -169,10 +159,9 @@ def test_ansible_tower_job_templates_tag(request, config_manager, tag): @pytest.mark.tier(3) -@pytest.mark.uncollectif(lambda config_manager_obj: "satellite" in config_manager_obj, - reason='Only Ansible tower is valid for this test') +@pytest.mark.provider([AnsibleTowerProvider]) @pytest.mark.parametrize('template_type', TEMPLATE_TYPE.values(), ids=list(TEMPLATE_TYPE.keys())) -def test_ansible_tower_service_dialog_creation_from_template(config_manager, template_type): +def test_ansible_tower_service_dialog_creation_from_template(provider, template_type): """ Polarion: assignee: nachandr @@ -182,7 +171,7 @@ def test_ansible_tower_service_dialog_creation_from_template(config_manager, tem """ try: - job_template = config_manager.appliance.collections.ansible_tower_job_templates.filter( + job_template = provider.appliance.collections.ansible_tower_job_templates.filter( {"job_type": template_type}).all()[0] except IndexError: pytest.skip("No job template was found") diff --git a/cfme/tests/infrastructure/test_config_management_rest.py b/cfme/tests/infrastructure/test_config_management_rest.py index 58f281cfd6..f4ff15274d 100644 --- a/cfme/tests/infrastructure/test_config_management_rest.py +++ b/cfme/tests/infrastructure/test_config_management_rest.py @@ -2,6 +2,7 @@ import pytest from cfme import test_requirements +from cfme.infrastructure.config_management.ansible_tower import AnsibleTowerProvider from cfme.utils.rest import assert_response from cfme.utils.rest import delete_resources_from_detail from cfme.utils.rest import query_resource_attributes @@ -10,32 +11,17 @@ pytestmark = [ test_requirements.rest, - pytest.mark.usefixtures('config_manager_obj_module_scope'), - pytest.mark.uncollectif( - lambda config_manager_obj_module_scope: "ansible" not in config_manager_obj_module_scope - ) + pytest.mark.provider([AnsibleTowerProvider], scope='module'), + pytest.mark.usefixtures('setup_provider') ] -@pytest.fixture(scope='class') -def config_manager(config_manager_obj_class_scope): - """Fixture that provides a random config manager and sets it up.""" - if config_manager_obj_class_scope.type != 'Ansible Tower': - pytest.skip('A non "Ansible Tower" configuration manager provider was selected for test,' - 'Please verify cfme_data.yaml content.') - - config_manager_obj_class_scope.create(validate=True) # actually add it to CFME - yield config_manager_obj_class_scope - - config_manager_obj_class_scope.delete() - - @pytest.fixture -def authentications(appliance, config_manager): +def authentications(appliance, provider): """Creates and returns authentication resources under /api/authentications.""" auth_num = 2 collection = appliance.rest_api.collections.authentications - prov = appliance.rest_api.collections.providers.get(name='{} %'.format(config_manager.name)) + prov = appliance.rest_api.collections.providers.get(name='{} %'.format(provider.name)) data = [] cred_names = [] for __ in range(auth_num): @@ -212,7 +198,7 @@ def test_delete_authentications_from_collection(self, appliance, authentications appliance.rest_api.collections.authentications.action.delete.POST(*authentications) assert_response(appliance, success=False) - def test_authentications_options(self, appliance, config_manager): + def test_authentications_options(self, appliance): """Tests that credential types can be listed through OPTIONS HTTP method. Metadata: @@ -230,11 +216,15 @@ def test_authentications_options(self, appliance, config_manager): @pytest.fixture -def config_manager_rest(config_manager_obj): +def config_manager_rest(provider): """Creates provider using REST API.""" - config_manager_obj.create_rest() - assert_response(config_manager_obj.appliance) - rest_entity = config_manager_obj.rest_api_entity + # first remove the provider + provider.delete() + assert not provider.exists + # now create the provider via rest + provider.create_rest() + assert_response(provider.appliance) + rest_entity = provider.rest_api_entity rest_entity.reload() yield rest_entity diff --git a/cfme/tests/services/test_ansible_workflow_servicecatalogs.py b/cfme/tests/services/test_ansible_workflow_servicecatalogs.py index a2173756e9..c71ca6773e 100644 --- a/cfme/tests/services/test_ansible_workflow_servicecatalogs.py +++ b/cfme/tests/services/test_ansible_workflow_servicecatalogs.py @@ -1,6 +1,7 @@ import pytest from cfme import test_requirements +from cfme.infrastructure.config_management.ansible_tower import AnsibleTowerProvider from cfme.services.myservice import MyService from cfme.services.service_catalogs import ServiceCatalogs from cfme.utils.blockers import BZ @@ -10,10 +11,8 @@ pytestmark = [ test_requirements.service, pytest.mark.tier(2), - pytest.mark.usefixtures("config_manager_obj_module_scope"), - pytest.mark.uncollectif( - lambda config_manager_obj_module_scope: "ansible" not in config_manager_obj_module_scope - ), + pytest.mark.provider([AnsibleTowerProvider], scope='module'), + pytest.mark.usefixtures('setup_provider'), pytest.mark.parametrize('workflow_type', ['multiple_job_workflow', 'inventory_sync_workflow'], ids=['multiple_job_workflow', 'inventory_sync_workflow'], scope='module'), @@ -21,21 +20,12 @@ ] -@pytest.fixture(scope="module") -def tower_manager(config_manager_obj_module_scope): - """ Fixture that sets up Ansible Tower provider""" - if config_manager_obj_module_scope.type == "Ansible Tower": - config_manager_obj_module_scope.create(validate=True) - yield config_manager_obj_module_scope - config_manager_obj_module_scope.delete() - - @pytest.fixture(scope="function") -def ansible_workflow_catitem(appliance, request, tower_manager, dialog, catalog, workflow_type): - config_manager_obj = tower_manager - provider_name = config_manager_obj.yaml_data.get('name') +def ansible_workflow_catitem(appliance, provider, dialog, catalog, workflow_type): + config_manager_obj = provider + provider_name = config_manager_obj.data.get('name') try: - template = config_manager_obj.yaml_data['provisioning_data'][workflow_type] + template = config_manager_obj.data['provisioning_data'][workflow_type] except KeyError: pytest.skip("No such Ansible template: {} found in cfme_data.yaml".format(workflow_type)) catalog_item = appliance.collections.catalog_items.create( @@ -52,8 +42,7 @@ def ansible_workflow_catitem(appliance, request, tower_manager, dialog, catalog, @pytest.mark.meta(automates=[BZ(1719051)]) -def test_tower_workflow_item(appliance, tower_manager, ansible_workflow_catitem, request, - workflow_type): +def test_tower_workflow_item(appliance, ansible_workflow_catitem): """Tests ordering of catalog items for Ansible Workflow templates Metadata: test_flag: provision @@ -79,7 +68,7 @@ def test_tower_workflow_item(appliance, tower_manager, ansible_workflow_catitem, ) -def test_retire_ansible_workflow(appliance, ansible_workflow_catitem, request, workflow_type): +def test_retire_ansible_workflow(appliance, ansible_workflow_catitem): """Tests retiring of catalog items for Ansible Workflow templates Metadata: test_flag: provision diff --git a/cfme/tests/services/test_config_provider_servicecatalogs.py b/cfme/tests/services/test_config_provider_servicecatalogs.py index 479d9f5803..fceefcf6a4 100644 --- a/cfme/tests/services/test_config_provider_servicecatalogs.py +++ b/cfme/tests/services/test_config_provider_servicecatalogs.py @@ -1,6 +1,7 @@ import pytest from cfme import test_requirements +from cfme.infrastructure.config_management.ansible_tower import AnsibleTowerProvider from cfme.services.myservice import MyService from cfme.services.service_catalogs import ServiceCatalogs from cfme.utils.blockers import BZ @@ -10,10 +11,8 @@ pytestmark = [ test_requirements.service, - pytest.mark.usefixtures('config_manager_obj_module_scope'), - pytest.mark.uncollectif( - lambda config_manager_obj_module_scope: "ansible" not in config_manager_obj_module_scope - ), + pytest.mark.provider([AnsibleTowerProvider], scope='module'), + pytest.mark.usefixtures('setup_provider'), pytest.mark.tier(2), pytest.mark.parametrize('job_type', ['template', 'template_limit', 'template_survey', 'textarea_survey'], @@ -23,31 +22,11 @@ ] -@pytest.fixture -def config_manager_obj(appliance, cfg_mgr_key): - collection = "satellite_providers" - if "ansible" in cfg_mgr_key: - collection = "ansible_tower_providers" - - yield getattr(appliance.collections, collection).instantiate(key=cfg_mgr_key) - - -@pytest.fixture(scope="module") -def config_manager(config_manager_obj_module_scope): - """ Fixture that provides a random config manager and sets it up""" - if config_manager_obj_module_scope.type == "Ansible Tower": - config_manager_obj_module_scope.create(validate=True) - else: - config_manager_obj_module_scope.create() - yield config_manager_obj_module_scope - config_manager_obj_module_scope.delete() - - @pytest.fixture(scope="function") -def catalog_item(appliance, request, config_manager, ansible_tower_dialog, catalog, job_type): - config_manager_obj = config_manager - provider_name = config_manager_obj.yaml_data.get('name') - template = config_manager_obj.yaml_data['provisioning_data'][job_type] +def catalog_item(appliance, request, provider, ansible_tower_dialog, catalog, job_type): + config_manager_obj = provider + provider_name = config_manager_obj.data.get('name') + template = config_manager_obj.data['provisioning_data'][job_type] catalog_item = appliance.collections.catalog_items.create( appliance.collections.catalog_items.ANSIBLE_TOWER, name=ansible_tower_dialog.label, @@ -63,7 +42,7 @@ def catalog_item(appliance, request, config_manager, ansible_tower_dialog, catal @pytest.mark.meta(automates=[BZ(1717500)]) # The 'textarea_survey' job type automates BZ 1717500 -def test_order_tower_catalog_item(appliance, config_manager, catalog_item, request, job_type): +def test_order_tower_catalog_item(appliance, provider, catalog_item, request, job_type): """Tests ordering of catalog items for Ansible Template and Workflow jobs Metadata: test_flag: provision @@ -78,7 +57,7 @@ def test_order_tower_catalog_item(appliance, config_manager, catalog_item, reque caseimportance: high """ if job_type == 'template_limit': - host = config_manager.yaml_data['provisioning_data']['inventory_host'] + host = provider.data['provisioning_data']['inventory_host'] dialog_values = {'limit': host} service_catalogs = ServiceCatalogs(appliance, catalog_item.catalog, catalog_item.name, dialog_values=dialog_values) diff --git a/cfme/utils/appliance/__init__.py b/cfme/utils/appliance/__init__.py index e8cc292a32..96db7857b4 100644 --- a/cfme/utils/appliance/__init__.py +++ b/cfme/utils/appliance/__init__.py @@ -60,7 +60,8 @@ RUNNING_UNDER_SPROUT = os.environ.get("RUNNING_UNDER_SPROUT", "false") != "false" # EMS types recognized by IP or credentials RECOGNIZED_BY_IP = [ - "InfraManager", "ContainerManager", "Openstack::CloudManager" + "InfraManager", "ContainerManager", "Openstack::CloudManager", "ConfigurationManager", + "AutomationManager" ] RECOGNIZED_BY_CREDS = ["CloudManager", "Nuage::NetworkManager"] @@ -612,6 +613,9 @@ def managed_known_providers(self): if ems_name == prov.name: found_cruds.add(prov) break + elif prov.name in ems_name: # config managers append e.g. 'Automation Manager' + found_cruds.add(prov) + break else: unrecognized_ems_names.add(ems_name) if unrecognized_ems_names: From 5026fdf78a985af9d8eb861936e7b4c911f7b843 Mon Sep 17 00:00:00 2001 From: john-dupuy Date: Thu, 21 Nov 2019 10:56:16 -0500 Subject: [PATCH 4/5] Fix collection test_advanced_search --- cfme/tests/webui/test_advanced_search.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cfme/tests/webui/test_advanced_search.py b/cfme/tests/webui/test_advanced_search.py index d5b53fde5e..450171a5ef 100644 --- a/cfme/tests/webui/test_advanced_search.py +++ b/cfme/tests/webui/test_advanced_search.py @@ -7,7 +7,7 @@ from cfme import test_requirements from cfme.cloud.provider import CloudProvider from cfme.containers.provider import ContainersProvider -from cfme.infrastructure.config_management import ConfigManager +from cfme.infrastructure.config_management import ConfigManagerProvider from cfme.infrastructure.config_management import ConfigSystem from cfme.infrastructure.provider import InfraProvider from cfme.markers.env_markers.provider import ONE_PER_CATEGORY @@ -23,7 +23,7 @@ pytestmark = [ pytest.mark.uncollectif(lambda param, appliance: - (param.collection in [ConfigManager, 'ansible_tower_providers'] or + (param.collection in [ConfigManagerProvider, 'config_managers'] or param.filter == 'Job Template (Ansible Tower) : Name') or (appliance.version >= '5.11' and param.entity == 'network_load_balancers'), reason='load balancers are no longer supported in 5.11 -> BZ 1672949'), @@ -331,7 +331,7 @@ class TestStorage(object): @inject_tests class TestConfigManagement(object): params_values = [ - SearchParam(ConfigManager, 'All', 'configuration_management', + SearchParam(ConfigManagerProvider, 'All', 'configuration_management', 'Configuration Manager : Name', ('sidebar.providers', "All Configuration Management Providers")), SearchParam(ConfigSystem, 'All', 'configuration_management_systems', From 6ec5f9b727813afc0240516cdc22b48d9cb50ab4 Mon Sep 17 00:00:00 2001 From: john-dupuy Date: Wed, 4 Dec 2019 15:01:20 -0500 Subject: [PATCH 5/5] Add GH blocker and use eq over cmp --- cfme/infrastructure/config_management/__init__.py | 5 +++-- cfme/infrastructure/config_management/ansible_tower.py | 2 +- cfme/infrastructure/config_management/config_profiles.py | 7 +++---- cfme/infrastructure/config_management/satellite.py | 2 +- cfme/tests/infrastructure/test_config_management.py | 6 +++--- cfme/tests/webui/test_advanced_search.py | 4 ++++ conf/supportability.yaml | 5 +++++ 7 files changed, 20 insertions(+), 11 deletions(-) diff --git a/cfme/infrastructure/config_management/__init__.py b/cfme/infrastructure/config_management/__init__.py index 9ae79e2a43..4f8501ea2f 100644 --- a/cfme/infrastructure/config_management/__init__.py +++ b/cfme/infrastructure/config_management/__init__.py @@ -3,6 +3,7 @@ from manageiq_client.api import Entity as RestEntity from navmazing import NavigateToAttribute from navmazing import NavigateToSibling +from navmazing import NavigationDestinationNotFound from widgetastic.widget import Checkbox from widgetastic.widget import Text from widgetastic.widget import TextInput @@ -245,7 +246,7 @@ def is_displayed(self): return self.entities.title.text == title -@attr.s(cmp=False) +@attr.s(eq=False) class ConfigManagerProvider(BaseProvider, Updateable, Pretty): """ This is base class for Configuration manager objects (Red Hat Satellite, Foreman, Ansible Tower) @@ -594,7 +595,7 @@ def step(self, *args, **kwargs): class ConfigManagerAllPage(CFMENavigateStep): def step(self, *args, **kwargs): - raise NotImplementedError( + raise NavigationDestinationNotFound( "There is no page in MIQ that displays all config managers." " Use 'AllOfType' on a config manager provider instance." ) diff --git a/cfme/infrastructure/config_management/ansible_tower.py b/cfme/infrastructure/config_management/ansible_tower.py index 154f126618..ccd7ce0f1f 100644 --- a/cfme/infrastructure/config_management/ansible_tower.py +++ b/cfme/infrastructure/config_management/ansible_tower.py @@ -43,7 +43,7 @@ def is_displayed(self): ) -@attr.s(cmp=False) +@attr.s(eq=False) class AnsibleTowerProvider(ConfigManagerProvider): """ Configuration manager object (Ansible Tower) diff --git a/cfme/infrastructure/config_management/config_profiles.py b/cfme/infrastructure/config_management/config_profiles.py index fccf51eeeb..4a0d57ba81 100644 --- a/cfme/infrastructure/config_management/config_profiles.py +++ b/cfme/infrastructure/config_management/config_profiles.py @@ -26,10 +26,9 @@ class ConfigProfile(BaseEntity, Pretty): @property def type(self): - kind = "Configuration Profile" - if self.manager.type == "ansible_tower": - kind = "Inventory Group" - return kind + return ( + "Inventory Group" if self.manager.type == "ansible_tower" else "Configuration Profile" + ) @property def config_systems(self): diff --git a/cfme/infrastructure/config_management/satellite.py b/cfme/infrastructure/config_management/satellite.py index a65bd6905e..db51507e9f 100644 --- a/cfme/infrastructure/config_management/satellite.py +++ b/cfme/infrastructure/config_management/satellite.py @@ -44,7 +44,7 @@ def is_displayed(self): ) -@attr.s(cmp=False) +@attr.s(eq=False) class SatelliteProvider(ConfigManagerProvider): """ Configuration manager object (Red Hat Satellite, Foreman) diff --git a/cfme/tests/infrastructure/test_config_management.py b/cfme/tests/infrastructure/test_config_management.py index 4af85142f7..3625263ca8 100644 --- a/cfme/tests/infrastructure/test_config_management.py +++ b/cfme/tests/infrastructure/test_config_management.py @@ -12,7 +12,7 @@ pytestmark = [ pytest.mark.provider([AnsibleTowerProvider, SatelliteProvider], scope='module'), - pytest.mark.usefixtures('setup_provider') + pytest.mark.usefixtures('setup_provider_modscope') ] @@ -132,7 +132,7 @@ def test_config_system_tag(config_system, tag): @pytest.mark.tier(3) -@pytest.mark.provider([AnsibleTowerProvider]) +@pytest.mark.provider([AnsibleTowerProvider], scope='module') def test_ansible_tower_job_templates_tag(request, provider, tag): """ Polarion: @@ -159,7 +159,7 @@ def test_ansible_tower_job_templates_tag(request, provider, tag): @pytest.mark.tier(3) -@pytest.mark.provider([AnsibleTowerProvider]) +@pytest.mark.provider([AnsibleTowerProvider], scope='module') @pytest.mark.parametrize('template_type', TEMPLATE_TYPE.values(), ids=list(TEMPLATE_TYPE.keys())) def test_ansible_tower_service_dialog_creation_from_template(provider, template_type): """ diff --git a/cfme/tests/webui/test_advanced_search.py b/cfme/tests/webui/test_advanced_search.py index 450171a5ef..d06f39c552 100644 --- a/cfme/tests/webui/test_advanced_search.py +++ b/cfme/tests/webui/test_advanced_search.py @@ -17,6 +17,7 @@ from cfme.services.workloads import VmsInstances from cfme.utils.appliance.implementations.ui import navigate_to from cfme.utils.blockers import BZ +from cfme.utils.blockers import GH SearchParam = namedtuple("SearchParam", ["collection", "destination", "entity", "filter", "my_filters"]) @@ -296,11 +297,13 @@ class TestContainers(object): @inject_tests +@pytest.mark.meta(blockers=[GH('ManageIQ/integration_tests:9723')]) class TestAnsibleTower(object): params_values = [ SearchParam('ansible_tower_providers', 'All', 'ansible_tower_explorer_provider', 'Automation Manager (Ansible Tower) : Name', ('sidebar.providers', 'All Ansible Tower Providers')), + SearchParam('ansible_tower_systems', 'All', 'ansible_tower_explorer_system', 'Configured System (Ansible Tower) : Hostname', ('sidebar.configured_systems', 'All Ansible Tower Configured Systems')), @@ -329,6 +332,7 @@ class TestStorage(object): @inject_tests +@pytest.mark.meta(blockers=[GH('ManageIQ/integration_tests:9723')]) class TestConfigManagement(object): params_values = [ SearchParam(ConfigManagerProvider, 'All', 'configuration_management', diff --git a/conf/supportability.yaml b/conf/supportability.yaml index ce626c1f4d..89c82ba1bc 100644 --- a/conf/supportability.yaml +++ b/conf/supportability.yaml @@ -31,3 +31,8 @@ - 3.7 - 3.9 - 3.11 + config_manager: + - satellite: + - 6.2 + - ansible_tower: + - 3.10