diff --git a/robottelo/api/utils.py b/robottelo/api/utils.py index 1df1d5abd18..42597a9c6e0 100644 --- a/robottelo/api/utils.py +++ b/robottelo/api/utils.py @@ -455,7 +455,9 @@ def create_role_permissions(role, permissions_types_names, search=None): # prag entities.Filter(permission=permissions_entities, role=role, search=search).create() -def wait_for_tasks(search_query, search_rate=1, max_tries=10, poll_rate=None, poll_timeout=None): +def wait_for_tasks( + search_query, search_rate=1, max_tries=10, poll_rate=None, poll_timeout=None, must_succeed=True +): """Search for tasks by specified search query and poll them to ensure that task has finished. @@ -467,6 +469,7 @@ def wait_for_tasks(search_query, search_rate=1, max_tries=10, poll_rate=None, po ``nailgun.entities.ForemanTask.poll()`` method. :param poll_timeout: Maximum number of seconds to wait until timing out. Parameter for ``nailgun.entities.ForemanTask.poll()`` method. + :param must_succeed: Assert success result on finished task. :return: List of ``nailgun.entities.ForemanTasks`` entities. :raises: ``AssertionError``. If not tasks were found until timeout. """ @@ -474,7 +477,7 @@ def wait_for_tasks(search_query, search_rate=1, max_tries=10, poll_rate=None, po tasks = entities.ForemanTask().search(query={'search': search_query}) if len(tasks) > 0: for task in tasks: - task.poll(poll_rate=poll_rate, timeout=poll_timeout) + task.poll(poll_rate=poll_rate, timeout=poll_timeout, must_succeed=must_succeed) break else: time.sleep(search_rate) diff --git a/tests/foreman/api/test_remoteexecution.py b/tests/foreman/api/test_remoteexecution.py index 0dcd5532e75..cf61ae2749d 100644 --- a/tests/foreman/api/test_remoteexecution.py +++ b/tests/foreman/api/test_remoteexecution.py @@ -17,9 +17,12 @@ :Upstream: No """ import pytest +from nailgun import entities from robottelo.api.utils import wait_for_tasks +from robottelo.config import settings from robottelo.hosts import get_sat_version +from robottelo.utils import ohsnap from robottelo.utils.issue_handlers import is_open CAPSULE_TARGET_VERSION = f'6.{get_sat_version().minor}.z' @@ -72,3 +75,145 @@ def test_positive_run_capsule_upgrade_playbook(module_capsule_configured, target ).refresh() feature_set = {feat['name'] for feat in result['features']} assert {'Ansible', 'Dynflow', 'Script', 'Pulpcore', 'Logs'}.issubset(feature_set) + + +@pytest.mark.tier3 +@pytest.mark.no_containers +@pytest.mark.rhel_ver_match('[^6].*') +def test_negative_time_to_pickup( + module_org, + module_target_sat, + smart_proxy_location, + module_ak_with_cv, + module_capsule_configured_mqtt, + rhel_contenthost, +): + """Time to pickup setting is honored for host registered to mqtt + + :id: a082f599-fbf7-4779-aa18-5139e2bce774 + + :expectedresults: Time to pickup aborts the job if mqtt client doesn't + respond in time + + :CaseImportance: High + + :bz: 2158738, 2118651 + + :parametrized: yes + """ + client_repo = ohsnap.dogfood_repository( + settings.repos.ohsnap_repo_host, + product='client', + repo='client', + release='Client', + os_release=rhel_contenthost.os_version.major, + ) + # Update module_capsule_configured_mqtt to include module_org/smart_proxy_location + module_target_sat.cli.Capsule.update( + { + 'name': module_capsule_configured_mqtt.hostname, + 'organization-ids': module_org.id, + 'location-ids': smart_proxy_location.id, + } + ) + # register host with pull provider rex + result = rhel_contenthost.register( + module_org, + smart_proxy_location, + module_ak_with_cv.name, + target=module_capsule_configured_mqtt, + satellite=module_target_sat, + setup_remote_execution_pull=True, + repo=client_repo.baseurl, + ) + template_id = ( + module_target_sat.api.JobTemplate() + .search(query={'search': 'name="Run Command - Script Default"'})[0] + .id + ) + assert result.status == 0, f'Failed to register host: {result.stderr}' + # check mqtt client is running + result = rhel_contenthost.execute('systemctl status yggdrasild') + assert result.status == 0, f'Failed to start yggdrasil on client: {result.stderr}' + # check that longrunning command is not affected by time_to_pickup BZ#2158738 + job = module_target_sat.api.JobInvocation().run( + synchronous=False, + data={ + 'job_template_id': template_id, + 'organization': module_org.name, + 'location': smart_proxy_location.name, + 'inputs': { + 'command': 'echo start; sleep 10; echo done', + }, + 'targeting_type': 'static_query', + 'search_query': f'name = {rhel_contenthost.hostname}', + 'time_to_pickup': '5', + }, + ) + wait_for_tasks(f'resource_type = JobInvocation and resource_id = {job["id"]}') + result = module_target_sat.api.JobInvocation(id=job['id']).read() + assert result.succeeded == 1 + # stop yggdrasil client on host + result = rhel_contenthost.execute('systemctl stop yggdrasild') + assert result.status == 0, f'Failed to stop yggdrasil on client: {result.stderr}' + + # Make sure the job is executed by the registered-trough capsule + global_ttp = entities.Setting().search( + query={'search': 'name="remote_execution_global_proxy"'} + )[0] + global_ttp.value = False + global_ttp.update(['value']) + + # run script provider rex command with time_to_pickup + job = module_target_sat.api.JobInvocation().run( + synchronous=False, + data={ + 'job_template_id': template_id, + 'organization': module_org.name, + 'location': smart_proxy_location.name, + 'inputs': { + 'command': 'ls -la', + }, + 'targeting_type': 'static_query', + 'search_query': f'name = {rhel_contenthost.hostname}', + 'time_to_pickup': '10', + }, + ) + wait_for_tasks( + f'resource_type = JobInvocation and resource_id = {job["id"]}', must_succeed=False + ) + result = module_target_sat.api.JobInvocation(id=job['id']).read() + assert result.status_label == "failed" + result = entities.ForemanTask().search( + query={'search': f'resource_type = JobInvocation and resource_id = {job["id"]}'} + ) + assert 'The job was not picked up in time' in result[0].humanized['output'] + + # Check that global setting is applied + global_ttp = entities.Setting().search( + query={'search': 'name="remote_execution_time_to_pickup"'} + )[0] + global_ttp.value = '10' + global_ttp.update(['value']) + job = module_target_sat.api.JobInvocation().run( + synchronous=False, + data={ + 'job_template_id': template_id, + 'organization': module_org.name, + 'location': smart_proxy_location.name, + 'inputs': { + 'command': 'ls -la', + }, + 'targeting_type': 'static_query', + 'search_query': f'name = {rhel_contenthost.hostname}', + }, + ) + wait_for_tasks( + f'resource_type = JobInvocation and resource_id = {job["id"]}', must_succeed=False + ) + result = module_target_sat.api.JobInvocation(id=job['id']).read() + assert result.status_label == "failed" + result = entities.ForemanTask().search( + query={'search': f'resource_type = JobInvocation and resource_id = {job["id"]}'} + ) + assert 'The job was not picked up in time' in result[0].humanized['output']