Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #37630 - Display basic list of hosts on the new job_invocations page #913

Merged
merged 1 commit into from
Nov 6, 2024
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
51 changes: 34 additions & 17 deletions app/controllers/api/v2/job_invocations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ module V2
class JobInvocationsController < ::Api::V2::BaseController
include ::Api::Version2
include ::Foreman::Renderer
include RemoteExecutionHelper

before_action :find_optional_nested_object, :only => %w{output raw_output}
before_action :find_host, :only => %w{output raw_output}
before_action :find_resource, :only => %w{show update destroy clone cancel rerun outputs}
before_action :find_resource, :only => %w{show update destroy clone cancel rerun outputs hosts}

wrap_parameters JobInvocation, :include => (JobInvocation.attribute_names + [:ssh])

Expand All @@ -20,14 +21,9 @@ def index
param :id, :identifier, :required => true
param :host_status, :bool, required: false, desc: N_('Show Job status for the hosts')
def show
@hosts = @job_invocation.targeting.hosts.authorized(:view_hosts, Host)
@template_invocations = @job_invocation.template_invocations
.where(host: @hosts)
.includes(:input_values)

set_hosts_and_template_invocations
if params[:host_status] == 'true'
template_invocations = @template_invocations.includes(:run_host_job_task).to_a
@host_statuses = Hash[template_invocations.map { |ti| [ti.host_id, template_invocation_status(ti)] }]
set_statuses_and_smart_proxies
end
end

Expand Down Expand Up @@ -111,6 +107,19 @@ def output
render :json => host_output(@nested_obj, @host, :default => [], :since => params[:since])
end

api :GET, '/job_invocations/:id/hosts', N_('List hosts belonging to job invocation')
param_group :search_and_pagination, ::Api::V2::BaseController
add_scoped_search_description_for(JobInvocation)
param :id, :identifier, :required => true
def hosts
set_hosts_and_template_invocations
set_statuses_and_smart_proxies
@total = @job_invocation.targeting.hosts.size
@hosts = @hosts.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page], :per_page => params[:per_page])
@subtotal = @hosts.respond_to?(:total_entries) ? @hosts.total_entries : @hosts.sizes
render :hosts, :layout => 'api/v2/layouts/index_layout'
end

api :GET, '/job_invocations/:id/hosts/:host_id/raw', N_('Get raw output for a host')
param :id, :identifier, :required => true
param :host_id, :identifier, :required => true
Expand Down Expand Up @@ -187,7 +196,7 @@ def allowed_nested_id

def action_permission
case params[:action]
when 'output', 'raw_output', 'outputs'
when 'output', 'raw_output', 'outputs', 'hosts'
:view
when 'cancel'
:cancel
Expand Down Expand Up @@ -256,15 +265,23 @@ def parent_scope
resource_class.where(nil)
end

def template_invocation_status(template_invocation)
task = template_invocation.try(:run_host_job_task)
parent_task = @job_invocation.task

return(parent_task.result == 'cancelled' ? 'cancelled' : 'N/A') if task.nil?
return task.state if task.state == 'running' || task.state == 'planned'
return 'error' if task.result == 'warning'
def set_hosts_and_template_invocations
@hosts = @job_invocation.targeting.hosts.authorized(:view_hosts, Host)
@template_invocations = @job_invocation.template_invocations
.where(host: @hosts)
.includes(:input_values)
end

task.result
def set_statuses_and_smart_proxies
template_invocations = @template_invocations.includes(:run_host_job_task).to_a
hosts = @hosts.to_a
@host_statuses = Hash[hosts.map do |host|
template_invocation = template_invocations.find { |ti| ti.host_id == host.id }
task = template_invocation.try(:run_host_job_task)
[host.id, template_invocation_status(task, @job_invocation.task)]
end]
@smart_proxy_id = Hash[template_invocations.map { |ti| [ti.host_id, ti.smart_proxy_id] }]
@smart_proxy_name = Hash[template_invocations.map { |ti| [ti.host_id, ti.smart_proxy_name] }]
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions app/helpers/remote_execution_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ def host_counter(label, count)
end

def template_invocation_status(task, parent_task)
return(parent_task.result == 'cancelled' ? _('cancelled') : _('Awaiting start')) if task.nil?
return(parent_task.result == 'cancelled' ? 'cancelled' : 'N/A') if task.nil?
return task.state if task.state == 'running' || task.state == 'planned'
return _('error') if task.result == 'warning'
return 'error' if task.result == 'warning'

task.result
end
Expand Down
15 changes: 15 additions & 0 deletions app/views/api/v2/job_invocations/hosts.json.rabl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
collection @hosts

attribute :name, :operatingsystem_id, :operatingsystem_name, :hostgroup_id, :hostgroup_name

node :job_status do |host|
@host_statuses[host.id]
end

node :smart_proxy_id do |host|
@smart_proxy_id[host.id]
end

node :smart_proxy_name do |host|
@smart_proxy_name[host.id]
end
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
get '/raw', :to => 'job_invocations#raw_output'
end
member do
get 'hosts'
post 'cancel'
post 'rerun'
get 'template_invocations', :to => 'template_invocations#template_invocations'
Expand Down
2 changes: 1 addition & 1 deletion webpack/JobInvocationDetail/JobInvocationActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
UPDATE_JOB,
} from './JobInvocationConstants';

export const getData = url => dispatch => {
export const getJobInvocation = url => dispatch => {
const fetchData = withInterval(
get({
key: JOB_INVOCATION_KEY,
Expand Down
84 changes: 84 additions & 0 deletions webpack/JobInvocationDetail/JobInvocationConstants.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
/* eslint-disable camelcase */
import React from 'react';
import { foremanUrl } from 'foremanReact/common/helpers';
import { translate as __ } from 'foremanReact/common/I18n';
import { useForemanHostDetailsPageUrl } from 'foremanReact/Root/Context/ForemanContext';
import JobStatusIcon from '../react_app/components/RecentJobsCard/JobStatusIcon';

export const JOB_INVOCATION_KEY = 'JOB_INVOCATION_KEY';
export const CURRENT_PERMISSIONS = 'CURRENT_PERMISSIONS';
export const UPDATE_JOB = 'UPDATE_JOB';
export const CANCEL_JOB = 'CANCEL_JOB';
export const GET_TASK = 'GET_TASK';
export const GET_TEMPLATE_INVOCATIONS = 'GET_TEMPLATE_INVOCATIONS';
export const CHANGE_ENABLED_RECURRING_LOGIC = 'CHANGE_ENABLED_RECURRING_LOGIC';
export const CANCEL_RECURRING_LOGIC = 'CANCEL_RECURRING_LOGIC';
export const GET_REPORT_TEMPLATES = 'GET_REPORT_TEMPLATES';
export const GET_REPORT_TEMPLATE_INPUTS = 'GET_REPORT_TEMPLATE_INPUTS';
export const JOB_INVOCATION_HOSTS = 'JOB_INVOCATION_HOSTS';
export const currentPermissionsUrl = foremanUrl(
'/api/v2/permissions/current_permissions'
);
Expand All @@ -20,6 +27,12 @@ export const STATUS = {
CANCELLED: 'cancelled',
};

export const STATUS_UPPERCASE = {
RESOLVED: 'RESOLVED',
ERROR: 'ERROR',
PENDING: 'PENDING',
};

export const DATE_OPTIONS = {
day: 'numeric',
month: 'short',
Expand All @@ -29,3 +42,74 @@ export const DATE_OPTIONS = {
hour12: false,
timeZoneName: 'short',
};

const Columns = () => {
const getColumnsStatus = ({ hostJobStatus }) => {
switch (hostJobStatus) {
case 'success':
return { title: __('Succeeded'), status: 0 };
case 'error':
return { title: __('Failed'), status: 1 };
case 'planned':
return { title: __('Scheduled'), status: 2 };
case 'running':
return { title: __('Pending'), status: 3 };
case 'cancelled':
return { title: __('Cancelled'), status: 4 };
case 'N/A':
return { title: __('Awaiting start'), status: 5 };
default:
return { title: hostJobStatus, status: 6 };
}
};
const hostDetailsPageUrl = useForemanHostDetailsPageUrl();

return {
name: {
title: __('Name'),
wrapper: ({ name }) => (
<a href={`${hostDetailsPageUrl}${name}`}>{name}</a>
),
weight: 1,
},
groups: {
title: __('Host group'),
wrapper: ({ hostgroup_id, hostgroup_name }) => (
<a href={`/hostgroups/${hostgroup_id}/edit`}>{hostgroup_name}</a>
),
weight: 2,
},
os: {
title: __('OS'),
wrapper: ({ operatingsystem_id, operatingsystem_name }) => (
<a href={`/operatingsystems/${operatingsystem_id}/edit`}>
{operatingsystem_name}
</a>
),
weight: 3,
},
smart_proxy: {
title: __('Smart proxy'),
wrapper: ({ smart_proxy_name, smart_proxy_id }) => (
<a href={`/smart_proxies/${smart_proxy_id}`}>{smart_proxy_name}</a>
),
weight: 4,
},
status: {
title: __('Status'),
wrapper: ({ job_status }) => {
const { title, status } = getColumnsStatus({
hostJobStatus: job_status,
});
return (
<JobStatusIcon status={status}>
{title || __('Unknown')}
</JobStatusIcon>
);
},
weight: 5,
},
};
};

export default Columns;
1 change: 0 additions & 1 deletion webpack/JobInvocationDetail/JobInvocationDetail.scss
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,3 @@
height: $chart_size;
}
}

Loading
Loading