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
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
103 changes: 96 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,93 @@ def index

private

def status_for(label, job_class)
label = job_class.output_label(label)
ForemanInventoryUpload::Async::ProgressOutput.get(label)&.status
def controller_permission
'foreman_rh_cloud'
end

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
110 changes: 110 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,110 @@
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 controller_permission
'foreman_rh_cloud'
end

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
58 changes: 41 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,54 @@ 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 controller_permission
'foreman_rh_cloud'
end

render json: {
action_status: 'success',
}, status: :ok
def action_permission
case params[:action]
when 'generate'
'generate'
else
super
end
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 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