Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 56 additions & 3 deletions airgun/entities/host_new.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@
from navmazing import NavigateToSibling
from wait_for import wait_for

from airgun.entities.all_hosts import AllHostsEntity
from airgun.entities.host import HostEntity
from airgun.navigation import NavigateStepWithWait as NavigateStep, navigator
from airgun.views.all_hosts import AllHostsTableView
from airgun.views.cloud_insights import RemediateSummary
from airgun.views.fact import HostFactView
from airgun.views.host import HostsView as LegacyHostsView
from airgun.views.host import HostsJobInvocationStatusView, HostsView as LegacyHostsView
from airgun.views.host_new import (
AllAssignedRolesView,
ContainerfileInstallCommandView,
EditAnsibleRolesView,
EditSystemPurposeView,
EnableTracerView,
HostsView,
InstallPackagesView,
ManageHostCollectionModal,
ManageHostStatusesView,
Expand Down Expand Up @@ -349,6 +350,57 @@ def schedule_job(self, entity_name, values):
view.fill(values)
view.submit.click()

def schedule_remote_job(self, entities_list, values, timeout=60, wait_for_results=True):

@LadislavVasina1 LadislavVasina1 Jul 9, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rmynar Shouldn't this be placed in entities/all_hosts.py as it does action through all hosts page?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a tricky question. Actually this is shared for both - host_new and all_hosts. I would prefer to keep it here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I now see that, the entity has a part where it does action through host details, my bad, but as the header is shared across all hosts page and host details, can't we just use the all host approach, and keep this entity (and moved to all_hosts.py) minimal with just the else branch of this entity?
AllHostsEntity.all_hosts_navigate_and_select_hosts_helper( handles one or more hosts well...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rmynar ^^^ what do you think about this comment?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously this entity was only in host_new. This PR reflects new state when the entity can be used also in all_hosts. Although current design is not perfect I would like to keep it as is in order to increase pass rate. However I agree with need to redesign, which has to be done separately.

"""Apply Schedule Remote Job action to the hosts names in entities_list using PF5 UI.

For multiple hosts, uses AllHostsEntity helper to select checkboxes, then clicks
"Schedule a job" button which opens the wizard with hosts pre-selected.

:param entities_list: The host names to apply the remote job.
:param values: the values to fill The Job invocation view.
:param timeout: The time to wait for the job to finish.
:param wait_for_results: Whether to wait for the job to finish execution.

:returns: The job invocation status view values
"""
if len(entities_list) == 1:
entity_name = entities_list[0]
details_view = self.navigate_to(self, 'NewDetails', entity_name=entity_name)
self.browser.plugin.ensure_page_safe()
details_view.wait_displayed()
self.browser.wait_for_element(details_view.schedule_job, exception=False)
details_view.schedule_job.fill('Schedule a job')
view = JobInvocationCreateView(self.browser)
self.browser.plugin.ensure_page_safe()
else:
hosts_view = AllHostsEntity.all_hosts_navigate_and_select_hosts_helper(
self, host_names=entities_list
)
schedule_button_locator = ".//button[contains(., 'Schedule a job') or @data-ouia-component-id='schedule-a-job-dropdown']"
wait_for(
lambda: hosts_view.browser.element(schedule_button_locator, check_visibility=True),
timeout=10,
delay=0.5,
handle_exception=True,
)
schedule_button = hosts_view.browser.element(schedule_button_locator)
hosts_view.browser.click(schedule_button)
view = JobInvocationCreateView(self.browser)
self.browser.plugin.ensure_page_safe()
view.wait_displayed()

view.fill(values)
self.browser.plugin.ensure_page_safe()
view.submit.click()
view.flash.assert_no_error()
view.flash.dismiss()
status_view = HostsJobInvocationStatusView(self.browser)
self.browser.plugin.ensure_page_safe()
status_view.wait_displayed()
if wait_for_results:
status_view.wait_for_result(timeout=timeout)
return status_view.read()

def run_job(self, entity_name):
"""Run a job on selected host"""
view = self.navigate_to(self, 'NewDetails', entity_name=entity_name)
Expand Down Expand Up @@ -1231,7 +1283,7 @@ def get_content_view_envs(self, entity_name):
class ShowAllHosts(NavigateStep):
"""Navigate to new UI All Hosts page"""

VIEW = HostsView
VIEW = AllHostsTableView

def step(self, *args, **kwargs):
self.view.menu.select('Hosts', 'All Hosts')
Expand All @@ -1252,6 +1304,7 @@ def prerequisite(self, *args, **kwargs):

def step(self, *args, **kwargs):
entity_name = kwargs.get('entity_name')

self.parent.search(entity_name)
self.parent.table.row(name=entity_name)['Name'].widget.click()

Expand Down
5 changes: 2 additions & 3 deletions airgun/entities/job_invocation.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ def run(self, values):
"""Run specific job"""
view = self.navigate_to(self, 'Run')
view.fill(values)
view.submit.expander.click()
self.browser.wait_for_element(view.submit.submit, exception=False)
view.submit.click()
view.submit.click() # PF5 wizard: submit.click() handles everything

def search(self, value):
"""Search for specific job invocation"""
Expand All @@ -40,6 +38,7 @@ def wait_job_invocation_state(self, entity_name, host_name, expected_state='succ
"""Check job invocation state from table view"""
view = self.navigate_to(self, 'All')
view.search(f'host = {host_name}')

wait_for(
lambda: view.table.row(description=entity_name)['Status'].read() == expected_state,
timeout=300,
Expand Down
3 changes: 3 additions & 0 deletions airgun/views/all_hosts.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ class AllHostsTableView(BaseLoggedInView, SearchableViewMixinPF4):
locator='//li[contains(@class, "pf-v5-c-menu__list-item")]//button[span/span[text()="Change associations"]]/following-sibling::div[contains(@class, "pf-v5-c-menu")]'
)

# Additional action buttons
schedule_a_job = PF5OUIADropdown('schedule-a-job-dropdown')

table_loading = Text('//h5[normalize-space(.)="Loading"]')
no_results = Text('//h5[normalize-space(.)="No Results"]')
manage_columns = PF5Button('Manage columns')
Expand Down
15 changes: 15 additions & 0 deletions airgun/views/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -772,3 +772,18 @@ def __init__(self, parent, logger=None):
"""Expand the selected wizard step"""
View.__init__(self, parent, logger=logger)
self.expander.click()


class PF5WizardStepView(View):
"""PF5 Wizard step - doesn't use expanders, uses wizard navigation instead.

The PF5 wizard pattern navigates between steps using Next/Back buttons
in the footer, not by clicking expander buttons. Steps are already visible
when you're on that step.
"""

def __init__(self, parent, logger=None):
"""Initialize PF5 wizard step without clicking expander."""
View.__init__(self, parent, logger=logger)
# PF5 wizards don't have expanders - steps are navigated via Next/Back buttons
# The step is already visible when active
2 changes: 1 addition & 1 deletion airgun/views/host_new.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ class HostsView(BaseLoggedInView, SearchableViewMixinPF4):

@property
def is_displayed(self):
return self.title.is_displayed
return self.browser.wait_for_element(self.title, exception=False) is not None


class BreadcrumbSwitcher(Widget):
Expand Down
Loading
Loading