Skip to content

Commit

Permalink
Errata WIP for remaining UI Failures (#15085)
Browse files Browse the repository at this point in the history
* API-factory setup registered host method

* UI::ERRATA
  • Loading branch information
damoore044 authored Jun 21, 2024
1 parent 921df06 commit 0e90a95
Show file tree
Hide file tree
Showing 3 changed files with 866 additions and 526 deletions.
2 changes: 2 additions & 0 deletions robottelo/constants/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,8 @@
REAL_RHEL7_0_ERRATA_ID = 'RHBA-2020:3615' # for REAL_RHEL7_0_0_PACKAGE
REAL_RHEL7_1_ERRATA_ID = 'RHBA-2017:0395' # tcsh bug fix update
REAL_RHEL8_1_ERRATA_ID = 'RHSA-2022:4867' # for REAL_RHEL8_1_PACKAGE
REAL_RHEL8_ERRATA_CVES = ['CVE-2021-27023', 'CVE-2021-27025']
REAL_RHSCLIENT_ERRATA = 'RHSA-2023:5982' # for RH Satellite Client 8
FAKE_1_YUM_REPOS_COUNT = 32
FAKE_3_YUM_REPOS_COUNT = 78
FAKE_9_YUM_SECURITY_ERRATUM = [
Expand Down
252 changes: 252 additions & 0 deletions robottelo/host_helpers/api_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,258 @@ def wait_for_errata_applicability_task(
f'No task was found using query " {search_query} " for host id: {host_id}'
)

def register_host_and_needed_setup(
self,
client,
organization,
activation_key,
environment,
content_view,
enable_repos=False,
rex_key=False,
force=False,
loc=None,
):
"""Helper will setup desired entities to host content. Then, register the
host client to the entities, using associated activation-key.
Attempt to make needed associations between detached entities.
Add desired repos to the content-view prior to calling this helper.
Or, add them to content-view after calling, then publish/promote.
The host will be registered to location: None (visible to all locations).
param client : instance, required
An instance of RHEL content host to register.
param enable_repos : bool, optional
Enable all available repos on the client after registration? Default is False.
Be sure to enable any repo(s) for the client after calling this method.
param rex_key : bool, optional
Add a Remote Execution Key to the client for satellite? Default is False.
param force : bool, optional
Force registration of the client to bypass? Default is False.
A reused fixture content host will fail if already registered.
param loc : instance, optional
Pass a location to limit host visibility. Default is None,
making the client available to all locations.
Required arguments below, can be any of the following type:
int: pass id of the entity to be read
str: pass name of the entity to be searched
entity: pass an entity instance
param organization : int, str, or entity
Pass an Organization instance, name, or id to use.
param activation_key : int, str, or entity
Pass an Activation-Key instance, name, or id.
param environment : int, str, or entity
Pass a Lifecycle-Environment instance, name, or id.
Example: can pass string name 'Library'.
param content_view : int, str, or entity
Pass a Content-View instance, name, or id.
Example: can pass string name 'Default Organization View'.
Note: The Default Organization View cannot be published, promoted, edited etc,
but you can register the client to it. Use the 'Library' environment.
Steps:
1. Get needed entities from arguments (id, name, or instance). Read all as instance.
2. Publish the content-view if no versions exist or needs_publish.
3. Promote the newest content-view-version if not in the environment already. Skip for 'Library'.
4. Assign environment and content-view to the activation-key if not associated.
5. If desired, enable all repositories from content-view for activation-key.
6. Register the host with options, using the activation-key associated with the content.
7. Check host was registered, identity matches passed entities.
Return: dictionary containing the following entries
if succeeded:
result: 'success'
client: registered host client
organization: entities['Organization']
activation_key: entities['ActivationKey']
environment: entities['LifecycleEnvironment']
content_view: entities['ContentView']
if failed:
result: 'error'
client: None, unless registration was successful
message: Details of the failure encountered
"""

method_error = {
'result': 'error',
'client': None,
'message': None,
}
entities = {
'Organization': organization,
'ActivationKey': activation_key,
'LifecycleEnvironment': environment,
'ContentView': content_view,
}
if not hasattr(client, 'hostname'):
method_error['message'] = (
'Argument "client" must be instance, with attribute "hostname".'
)
return method_error
# for entity arguments matched to above params:
# fetch entity instance on satellite,
# from given id or name, else read passed argument as an instance.
for entity, value in entities.items():
param = None
# passed int for entity, try to read by id
if isinstance(value, int):
# equivalent: _satellite_.api.{KEY}(id=VALUE).read()
param = getattr(self._satellite.api, entity)(id=value).read()
# passed str, search for entity by name
elif isinstance(value, str):
search_query = f'name="{value}"'
if entity == 'Organization':
# search for org name itself, will be just scoped to satellite
# equivalent: _satellite_.api.{KEY}().search(...name={VALUE})
result = getattr(self._satellite.api, entity)().search(
query={'search': search_query}
)
else:
# search of non-org entity by name, will be scoped to organization
result = getattr(self._satellite.api, entity)(
organization=entities['Organization']
).search(query={'search': search_query})
if not len(result) > 0:
method_error['message'] = (
f'Could not find {entity} name: {value}, by search query: "{search_query}"'
)
return method_error
param = result[0]
# did not pass int (id) or str (name), must be readable entity instance
else:
if not hasattr(value, 'id'):
method_error['message'] = (
f'Passed entity {entity}, has no attribute id:\n{value}'
)
return method_error
param = value
# updated param, should now be only an entity isntance
if not hasattr(param, 'id'):
method_error['message'] = (
f'Did not get readable instance from parameter on {self._satellite.hostname}:'
f' Param:{entity}:\n{value}'
)
return method_error
# entity found, read updated instance into dictionary
entities[entity] = param.read()

if ( # publish a content-view-version if none exist, or needs_publish is True
len(entities['ContentView'].version) == 0
or entities['ContentView'].needs_publish is True
):
entities['ContentView'].publish()
# read updated entitites after modifying CV
entities = {k: v.read() for k, v in entities.items()}

# promote to non-Library env if not already present:
# skip for 'Library' env selected or passed arg,
# any published version(s) will already be in Library.
if all(
[
environment != 'Library',
entities['LifecycleEnvironment'].name != 'Library',
entities['LifecycleEnvironment'] not in entities['ContentView'].environment,
]
):
# promote newest version by id
entities['ContentView'].version.sort(key=lambda version: version.id)
entities['ContentView'].version[-1].promote(
data={'environment_ids': entities['LifecycleEnvironment'].id}
)
# updated entities after promoting
entities = {k: v.read() for k, v in entities.items()}

if ( # assign env to ak if not present
entities['ActivationKey'].environment is None
or entities['ActivationKey'].environment.id != entities['LifecycleEnvironment'].id
):
entities['ActivationKey'].environment = entities['LifecycleEnvironment']
entities['ActivationKey'].update(['environment'])
entities = {k: v.read() for k, v in entities.items()}
if ( # assign cv to ak if not present
entities['ActivationKey'].content_view is None
or entities['ActivationKey'].content_view.id != entities['ContentView'].id
):
entities['ActivationKey'].content_view = entities['ContentView']
entities['ActivationKey'].update(['content_view'])

entities = {k: v.read() for k, v in entities.items()}
if enable_repos:
repositories = entities['ContentView'].repository
if len(repositories) < 1:
method_error['message'] = (
f' Cannot enable repositories for clients activation-key: {entities["ActivationKey"].name}'
f' There are no repositories added to the content-view: {entities["ContentView"].name}.'
)
return method_error
for repo in repositories:
# fetch content-label for any repo in cv
repo_content_label = self._satellite.cli.Repository.info(
{
'name': repo.read().name,
'organization-id': entities['Organization'].id,
'product': repo.read().product.read().name,
}
)['content-label']
# override the repository to enabled for ak
self._satellite.cli.ActivationKey.content_override(
{
'content-label': repo_content_label,
'id': entities['ActivationKey'].id,
'organization-id': entities['Organization'].id,
'value': int(True),
}
)

# register with now setup entities, using ak
result = client.register(
activation_keys=entities['ActivationKey'].name,
target=self._satellite,
org=entities['Organization'],
setup_remote_execution_pull=rex_key,
force=force,
loc=loc,
)
if result.status != 0:
method_error['message'] = (
f'Failed to register the host: {client.hostname}.\n{result.stderr}'
)
return method_error

# check identity of now registered client, matches expected entities
if not all(
[
client.subscribed,
client.identity['registered_to'] == self._satellite.hostname,
client.identity['org_name'] == entities['Organization'].name,
client.identity['environment_name']
== (f'{entities["LifecycleEnvironment"].name}/{entities["ContentView"].name}'),
]
):
method_error['client'] = client
method_error['message'] = (
f'Registered client identity field(s) do not match expected:\n{client.identity}'
)
return method_error

entities = {k: v.read() for k, v in entities.items()}
return ( # dict containing registered host client, and updated entities
{
'result': 'success',
'client': client,
'organization': entities['Organization'],
'activation_key': entities['ActivationKey'],
'environment': entities['LifecycleEnvironment'],
'content_view': entities['ContentView'],
}
)

def wait_for_syncplan_tasks(self, repo_backend_id=None, timeout=10, repo_name=None):
"""Search the pulp tasks and identify repositories sync tasks with
specified name or backend_identifier
Expand Down
Loading

0 comments on commit 0e90a95

Please sign in to comment.