Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
9 changes: 8 additions & 1 deletion app/controllers/concerns/inventory_upload/report_actions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,14 @@ def initialize(**params)
end

def start_report_generation(organization_id, disconnected)
ForemanTasks.async_task(ForemanInventoryUpload::Async::GenerateReportJob, ForemanInventoryUpload.generated_reports_folder, organization_id, disconnected)
upload = !disconnected
ForemanTasks.async_task(
ForemanInventoryUpload::Async::HostInventoryReportJob,
ForemanInventoryUpload.generated_reports_folder,
organization_id,
"", # hosts_filter
upload
)
end

def report_file(organization_id)
Expand Down
99 changes: 92 additions & 7 deletions app/controllers/foreman_inventory_upload/accounts_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,20 @@ def index

accounts = Hash[
labels.map do |id, label|
generate_report_status = status_for(id, ForemanInventoryUpload::Async::GenerateReportJob)
upload_report_status = status_for(id, ForemanInventoryUpload::Async::UploadReportDirectJob)
generate_task = latest_task_for(id, ForemanInventoryUpload::Async::HostInventoryReportJob)

# Check sub-action completion status
generated_status = sub_action_status(generate_task, 'GenerateHostReport')
uploaded_status = sub_action_status(generate_task, 'UploadReportDirectJob')

report_file_paths = ForemanInventoryUpload.report_file_paths(id)

[
label,
{
generate_report_status: generate_report_status,
upload_report_status: upload_report_status,
generated_status: generated_status,
uploaded_status: uploaded_status,
generate_task: task_json(generate_task),
report_file_paths: report_file_paths,
id: id,
},
Expand All @@ -30,9 +35,89 @@ def index

private

def status_for(label, job_class)
label = job_class.output_label(label)
ForemanInventoryUpload::Async::ProgressOutput.get(label)&.status
def latest_task_for(org_id, job_class)
ForemanTasks::Task
.for_action_types([job_class.name])
.joins(:links)
.where(foreman_tasks_links: {
resource_type: 'Organization',
resource_id: org_id,
})
.with_duration
.order('started_at DESC')
.first
end

def task_status_string(task)
return nil unless task

if task.state == 'stopped'
# Mimic old ProgressOutput format: "pid 12345 exit 0"
exit_code = task.result == 'success' ? 0 : 1
"pid #{Process.pid} exit #{exit_code}"
else
task.state
end
end

def sub_action_status(task, action_class_name)
return nil unless task

# If task is still running, return the state
return task.state unless task.state == 'stopped'

# For GenerateHostReport: always show status if task completed (generation always runs)
if action_class_name == 'GenerateHostReport'
exit_code = task.result == 'success' ? 0 : 1
return "pid #{Process.pid} exit #{exit_code}"
end

# For UploadReportDirectJob: only show status if task had upload enabled
if action_class_name == 'UploadReportDirectJob'
main_action = task.main_action
return nil unless main_action.respond_to?(:input)

# Check if upload was enabled for this task
return nil unless main_action.input[:upload]

# Show same status as overall task (upload runs as part of the task)
exit_code = task.result == 'success' ? 0 : 1
return "pid #{Process.pid} exit #{exit_code}"
end

nil
rescue StandardError => e
Rails.logger.warn("Failed to get sub-action status for #{action_class_name} in task #{task.id}: #{e.message}")
nil
end

def task_json(task)
return nil unless task

{
id: task.id,
label: task.label,
state: task.state,
result: task.result,
progress: task.progress,
started_at: task.started_at,
ended_at: task.ended_at,
duration: task.try(:duration)&.to_f,
report_file_path: task_report_file_path(task),
}
end

def task_report_file_path(task)
return nil unless task&.state == 'stopped'

# Get the main action from the task
main_action = task.main_action
return nil unless main_action.respond_to?(:report_file_path)

main_action.report_file_path
rescue StandardError => e
Rails.logger.warn("Failed to get report file path for task #{task.id}: #{e.message}")
nil
end
end
end
106 changes: 106 additions & 0 deletions app/controllers/foreman_inventory_upload/api/tasks_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
module ForemanInventoryUpload
module Api
class TasksController < ::Api::V2::BaseController
ACTION_TYPES = [
'ForemanInventoryUpload::Async::HostInventoryReportJob',
'ForemanInventoryUpload::Async::UploadReportDirectJob',
].freeze

# GET /foreman_inventory_upload/api/tasks/current
# Returns current running/active tasks for inventory operations
def current
organization_id = validate_organization_id if params[:organization_id].present?
return if performed?

tasks = ForemanTasks::Task
.active
.for_action_types(ACTION_TYPES)
.with_duration

if organization_id.present?
tasks = tasks.joins(:links)
.where(foreman_tasks_links: {
resource_type: 'Organization',
resource_id: organization_id,
})
end

render json: {
tasks: tasks.map { |task| task_json(task) },
}
end

# GET /foreman_inventory_upload/api/tasks/history
# Returns recent task history for inventory operations
def history
organization_id = validate_organization_id if params[:organization_id].present?
return if performed?

limit = validated_limit

tasks = ForemanTasks::Task
.for_action_types(ACTION_TYPES)
.with_duration
.order('started_at DESC')
.limit(limit)

if organization_id.present?
tasks = tasks.joins(:links)
.where(foreman_tasks_links: {
resource_type: 'Organization',
resource_id: organization_id,
})
end

render json: {
tasks: tasks.map { |task| task_json(task) },
}
end

private

def task_json(task)
{
id: task.id,
label: task.label,
action: task.action,
state: task.state,
result: task.result,
progress: task.progress,
started_at: task.started_at,
ended_at: task.ended_at,
duration: task.duration&.to_f,
humanized: task.humanized,
report_file_path: task_report_file_path(task),
}
end

def task_report_file_path(task)
return nil unless task.state == 'stopped' && task.result == 'success'
return nil unless task.action == 'ForemanInventoryUpload::Async::HostInventoryReportJob'

task.main_action&.output&.[]('report_file')
end

def validate_organization_id
org_id = params[:organization_id].to_i
if org_id.zero?
render json: { message: 'Invalid organization_id parameter' }, status: :bad_request
return nil
end

begin
Organization.find(org_id).id
rescue ActiveRecord::RecordNotFound
render json: { message: "Organization with id #{org_id} not found" }, status: :not_found
nil
end
end

def validated_limit
limit = params[:limit]&.to_i || 10
limit.clamp(1, 100)
end
end
end
end
45 changes: 28 additions & 17 deletions app/controllers/foreman_inventory_upload/reports_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,41 @@ module ForemanInventoryUpload
class ReportsController < ::ApplicationController
include InventoryUpload::ReportActions

def last
label = ForemanInventoryUpload::Async::GenerateReportJob.output_label(params[:organization_id])
output = ForemanInventoryUpload::Async::ProgressOutput.get(label)&.full_output
task_label = ForemanInventoryUpload::Async::GenerateAllReportsJob.name
scheduled = ForemanTasks::Task.where(
:label => task_label,
:state => 'scheduled'
).first&.start_at || nil
def generate
organization_id = validate_organization_id
return if performed?

disconnected = boolean_param(:disconnected)

task = start_report_generation(organization_id, disconnected)

render json: {
output: output,
scheduled: scheduled,
id: task.id,
humanized: {
action: task.action,
},
}, status: :ok
end

def generate
organization_id = params[:organization_id]
disconnected = params[:disconnected]
private

start_report_generation(organization_id, disconnected)
def validate_organization_id
org_id = params[:organization_id].to_i
if org_id.zero?
render json: { message: 'Invalid organization_id parameter' }, status: :bad_request
return nil
end

render json: {
action_status: 'success',
}, status: :ok
begin
Organization.find(org_id).id
rescue ActiveRecord::RecordNotFound
render json: { message: "Organization with id #{org_id} not found" }, status: :not_found
nil
end
end

def boolean_param(key)
Foreman::Cast.to_bool(params[key]) || false
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,6 @@ class UploadsController < ::ApplicationController

before_action :require_non_iop_smart_proxy, only: [:enable_cloud_connector]

def last
label = ForemanInventoryUpload::Async::UploadReportDirectJob.output_label(params[:organization_id])
output = ForemanInventoryUpload::Async::ProgressOutput.get(label)&.full_output

render json: {
output: output,
}, status: :ok
end

def download_file
filename, file = report_file(params[:organization_id])

Expand Down
6 changes: 4 additions & 2 deletions config/routes.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
Rails.application.routes.draw do
namespace :foreman_inventory_upload do
get ':organization_id/reports/last', to: 'reports#last', constraints: { organization_id: %r{[^\/]+} }
post ':organization_id/reports', to: 'reports#generate', constraints: { organization_id: %r{[^\/]+} }
get ':organization_id/uploads/last', to: 'uploads#last', constraints: { organization_id: %r{[^\/]+} }
get ':organization_id/uploads/file', to: 'uploads#download_file', constraints: { organization_id: %r{[^\/]+} }
get 'missing_hosts', to: 'missing_hosts#index'
get 'accounts', to: 'accounts#index'
Expand Down Expand Up @@ -76,6 +74,10 @@
post 'enable_connector', to: 'inventory#enable_cloud_connector'
post 'cloud_request', to: 'cloud_request#update'
get 'advisor_engine_config', to: 'advisor_engine_config#show'

# Inventory upload task endpoints
get 'inventory_upload/tasks/current', to: 'foreman_inventory_upload/api/tasks#current'
get 'inventory_upload/tasks/history', to: 'foreman_inventory_upload/api/tasks#history'
end

namespace 'advisor_engine' do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def rescue_strategy_for_self
end

def plan_generate_report(folder, organization, disconnected)
plan_action(ForemanInventoryUpload::Async::GenerateReportJob, folder, organization.id, disconnected)
plan_action(ForemanInventoryUpload::Async::HostInventoryReportJob, folder, organization.id, '', !disconnected)
end

def logger
Expand Down
Loading
Loading