foreman_rh_cloud 13.0.9 → 13.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/concerns/inventory_upload/report_actions.rb +8 -1
- data/app/controllers/foreman_inventory_upload/accounts_controller.rb +82 -7
- data/app/controllers/foreman_inventory_upload/api/tasks_controller.rb +110 -0
- data/app/controllers/foreman_inventory_upload/reports_controller.rb +41 -17
- data/app/controllers/foreman_inventory_upload/uploads_controller.rb +0 -9
- data/config/routes.rb +4 -2
- data/db/migrate/20251209163012_drop_task_output_tables.foreman_rh_cloud.rb +24 -0
- data/lib/foreman_inventory_upload/async/generate_all_reports_job.rb +1 -1
- data/lib/foreman_inventory_upload/async/host_inventory_report_job.rb +39 -0
- data/lib/foreman_inventory_upload/async/upload_report_direct_job.rb +28 -57
- data/lib/foreman_rh_cloud/plugin.rb +1 -0
- data/lib/foreman_rh_cloud/version.rb +1 -1
- data/lib/tasks/rh_cloud_inventory.rake +4 -2
- data/package.json +1 -1
- data/test/controllers/accounts_controller_test.rb +10 -11
- data/test/controllers/insights_cloud/api/cloud_request_controller_test.rb +1 -2
- data/test/jobs/host_inventory_report_job_test.rb +161 -97
- data/test/jobs/single_host_report_job_test.rb +36 -54
- data/test/jobs/upload_report_direct_job_test.rb +132 -132
- data/test/unit/rh_cloud_permissions_test.rb +2 -0
- data/webpack/ForemanInventoryUpload/Components/AccountList/AccountList.fixtures.js +6 -6
- data/webpack/ForemanInventoryUpload/Components/AccountList/AccountList.js +49 -34
- data/webpack/ForemanInventoryUpload/Components/AccountList/AccountListActions.js +2 -2
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/ListItem.fixtures.js +2 -2
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/ListItem.js +15 -9
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/__tests__/__snapshots__/ListItem.test.js.snap +6 -5
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItemStatus/ListItemStatus.fixtures.js +2 -2
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItemStatus/ListItemStatus.js +10 -14
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItemStatus/ListItemStatusHelper.js +9 -4
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItemStatus/__tests__/__snapshots__/ListItemStatus.test.js.snap +4 -4
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountList.test.js.snap +15 -9
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountListActions.test.js.snap +7 -7
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountListReducer.test.js.snap +6 -6
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountListSelectors.test.js.snap +12 -12
- data/webpack/ForemanInventoryUpload/Components/Dashboard/Dashboard.js +36 -132
- data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/Dashboard.test.js +60 -17
- data/webpack/ForemanInventoryUpload/Components/Dashboard/index.js +1 -34
- data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/__snapshots__/integration.test.js.snap +0 -1
- data/webpack/ForemanInventoryUpload/Components/NavContainer/NavContainer.js +1 -26
- data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/__snapshots__/PageTitle.test.js.snap +2 -2
- data/webpack/ForemanInventoryUpload/Components/TabHeader/TabHeader.js +22 -9
- data/webpack/ForemanInventoryUpload/Components/TabHeader/__tests__/TabHeader.test.js +67 -4
- data/webpack/ForemanInventoryUpload/Components/TaskHistory/TaskHistory.js +140 -0
- data/webpack/ForemanInventoryUpload/Components/TaskHistory/index.js +1 -0
- data/webpack/ForemanInventoryUpload/Components/TaskHistory/taskHistory.scss +40 -0
- data/webpack/ForemanInventoryUpload/Components/TaskProgress/TaskProgress.js +340 -0
- data/webpack/ForemanInventoryUpload/Components/TaskProgress/index.js +1 -0
- data/webpack/ForemanInventoryUpload/Components/TaskProgress/taskProgress.scss +8 -0
- data/webpack/ForemanInventoryUpload/ForemanInventoryHelpers.js +2 -2
- data/webpack/ForemanInventoryUpload/ForemanInventoryUploadReducers.js +0 -2
- data/webpack/ForemanInventoryUpload/__tests__/__snapshots__/ForemanInventoryHelpers.test.js.snap +1 -1
- data/webpack/ForemanRhCloudPages.js +0 -1
- data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationHelpers.js +1 -2
- data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationModal.js +2 -4
- data/webpack/__mocks__/foremanReact/components/common/dates/RelativeDateTime.js +14 -0
- data/webpack/__tests__/ForemanRhCloudHelpers.test.js +5 -1
- metadata +10 -61
- data/app/models/task_output_line.rb +0 -2
- data/app/models/task_output_status.rb +0 -2
- data/lib/foreman_inventory_upload/async/generate_report_job.rb +0 -61
- data/lib/foreman_inventory_upload/async/progress_output.rb +0 -38
- data/lib/foreman_inventory_upload/async/shell_process.rb +0 -77
- data/test/controllers/reports_controller_test.rb +0 -21
- data/test/controllers/uploads_controller_test.rb +0 -21
- data/test/jobs/generate_report_job_test.rb +0 -146
- data/test/unit/shell_process_job_test.rb +0 -29
- data/webpack/ForemanInventoryUpload/Components/Dashboard/DashboardActions.js +0 -88
- data/webpack/ForemanInventoryUpload/Components/Dashboard/DashboardConstants.js +0 -9
- data/webpack/ForemanInventoryUpload/Components/Dashboard/DashboardReducer.js +0 -68
- data/webpack/ForemanInventoryUpload/Components/Dashboard/DashboardSelectors.js +0 -17
- data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/DashboardActions.test.js +0 -51
- data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/DashboardIntegration.test.js +0 -17
- data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/DashboardReducer.test.js +0 -64
- data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/DashboardSelectors.test.js +0 -46
- data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/__snapshots__/Dashboard.test.js.snap +0 -36
- data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/__snapshots__/DashboardActions.test.js.snap +0 -76
- data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/__snapshots__/DashboardReducer.test.js.snap +0 -44
- data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/__snapshots__/DashboardSelectors.test.js.snap +0 -42
- data/webpack/ForemanInventoryUpload/Components/FullScreenModal/FullScreenModal.fixtures.js +0 -0
- data/webpack/ForemanInventoryUpload/Components/FullScreenModal/FullScreenModal.js +0 -55
- data/webpack/ForemanInventoryUpload/Components/FullScreenModal/FullScreenModalHelper.js +0 -0
- data/webpack/ForemanInventoryUpload/Components/FullScreenModal/__tests__/FullScreenModal.test.js +0 -13
- data/webpack/ForemanInventoryUpload/Components/FullScreenModal/__tests__/__snapshots__/FullScreenModal.test.js.snap +0 -65
- data/webpack/ForemanInventoryUpload/Components/FullScreenModal/fullScreenModal.scss +0 -20
- data/webpack/ForemanInventoryUpload/Components/FullScreenModal/index.js +0 -1
- data/webpack/ForemanInventoryUpload/Components/ReportGenerate/ReportGenerate.fixtures.js +0 -18
- data/webpack/ForemanInventoryUpload/Components/ReportGenerate/ReportGenerate.js +0 -65
- data/webpack/ForemanInventoryUpload/Components/ReportGenerate/ReportGenerateHelper.js +0 -0
- data/webpack/ForemanInventoryUpload/Components/ReportGenerate/__tests__/ReportGenerate.test.js +0 -14
- data/webpack/ForemanInventoryUpload/Components/ReportGenerate/__tests__/__snapshots__/ReportGenerate.test.js.snap +0 -47
- data/webpack/ForemanInventoryUpload/Components/ReportGenerate/index.js +0 -1
- data/webpack/ForemanInventoryUpload/Components/ReportGenerate/reportGenerate.scss +0 -0
- data/webpack/ForemanInventoryUpload/Components/ReportUpload/ReportUpload.fixtures.js +0 -18
- data/webpack/ForemanInventoryUpload/Components/ReportUpload/ReportUpload.js +0 -46
- data/webpack/ForemanInventoryUpload/Components/ReportUpload/ReportUploadHelper.js +0 -0
- data/webpack/ForemanInventoryUpload/Components/ReportUpload/__tests__/ReportUpload.test.js +0 -14
- data/webpack/ForemanInventoryUpload/Components/ReportUpload/__tests__/__snapshots__/ReportUpload.test.js.snap +0 -47
- data/webpack/ForemanInventoryUpload/Components/ReportUpload/index.js +0 -1
- data/webpack/ForemanInventoryUpload/Components/ReportUpload/reportUpload.scss +0 -0
- data/webpack/ForemanInventoryUpload/Components/TabBody/TabBody.fixtures.js +0 -0
- data/webpack/ForemanInventoryUpload/Components/TabBody/TabBody.js +0 -31
- data/webpack/ForemanInventoryUpload/Components/TabBody/TabBodyHelper.js +0 -0
- data/webpack/ForemanInventoryUpload/Components/TabBody/__tests__/TabBody.test.js +0 -13
- data/webpack/ForemanInventoryUpload/Components/TabBody/__tests__/__snapshots__/TabBody.test.js.snap +0 -19
- data/webpack/ForemanInventoryUpload/Components/TabBody/index.js +0 -1
- data/webpack/ForemanInventoryUpload/Components/TabBody/tabBody.scss +0 -5
- data/webpack/ForemanInventoryUpload/Components/Terminal/Terminal.fixtures.js +0 -10
- data/webpack/ForemanInventoryUpload/Components/Terminal/Terminal.js +0 -110
- data/webpack/ForemanInventoryUpload/Components/Terminal/TerminalHelper.js +0 -6
- data/webpack/ForemanInventoryUpload/Components/Terminal/__tests__/Terminal.test.js +0 -34
- data/webpack/ForemanInventoryUpload/Components/Terminal/__tests__/__snapshots__/Terminal.test.js.snap +0 -98
- data/webpack/ForemanInventoryUpload/Components/Terminal/index.js +0 -1
- data/webpack/ForemanInventoryUpload/Components/Terminal/terminal.scss +0 -32
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ae4a5664771227e232b4a38812dbf04fe704f1be85f10105873543cb2cc4b1c2
|
|
4
|
+
data.tar.gz: 69720759a2e2365af0b9c7e94c82e0f3adb15202d65ddf454e7beefa694cb30d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f8c2edc1f86b1db29db4f8209d26fb3466795d1f31b7854cfd7dc826ae35f29b7557dc7388ebf0ab2a4217c9ae4399f765e75afe1c7278676ab6a25e28091d3b
|
|
7
|
+
data.tar.gz: 725d9f97783c10cbe2d69345ce8647c94f5fbf12d1cffd5e087f7a5cacc528423c253047ae3e97e0b3af307db1e995ee660848bbe839d44d989f4d5d746d5085
|
|
@@ -11,7 +11,14 @@ module InventoryUpload
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def start_report_generation(organization_id, disconnected)
|
|
14
|
-
|
|
14
|
+
upload = !disconnected
|
|
15
|
+
ForemanTasks.async_task(
|
|
16
|
+
ForemanInventoryUpload::Async::HostInventoryReportJob,
|
|
17
|
+
ForemanInventoryUpload.generated_reports_folder,
|
|
18
|
+
organization_id,
|
|
19
|
+
"", # hosts_filter
|
|
20
|
+
upload
|
|
21
|
+
)
|
|
15
22
|
end
|
|
16
23
|
|
|
17
24
|
def report_file(organization_id)
|
|
@@ -6,15 +6,20 @@ module ForemanInventoryUpload
|
|
|
6
6
|
|
|
7
7
|
accounts = Hash[
|
|
8
8
|
labels.map do |id, label|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
generate_task = latest_task_for(id, ForemanInventoryUpload::Async::HostInventoryReportJob)
|
|
10
|
+
|
|
11
|
+
# Check sub-action completion status
|
|
12
|
+
generated_status = sub_action_status(generate_task, 'GenerateHostReport')
|
|
13
|
+
uploaded_status = sub_action_status(generate_task, 'UploadReportDirectJob')
|
|
14
|
+
|
|
11
15
|
report_file_paths = ForemanInventoryUpload.report_file_paths(id)
|
|
12
16
|
|
|
13
17
|
[
|
|
14
18
|
label,
|
|
15
19
|
{
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
generated_status: generated_status,
|
|
21
|
+
uploaded_status: uploaded_status,
|
|
22
|
+
generate_task: task_json(generate_task),
|
|
18
23
|
report_file_paths: report_file_paths,
|
|
19
24
|
id: id,
|
|
20
25
|
},
|
|
@@ -30,9 +35,79 @@ module ForemanInventoryUpload
|
|
|
30
35
|
|
|
31
36
|
private
|
|
32
37
|
|
|
33
|
-
def
|
|
34
|
-
|
|
35
|
-
|
|
38
|
+
def controller_permission
|
|
39
|
+
'foreman_rh_cloud'
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def latest_task_for(org_id, job_class)
|
|
43
|
+
ForemanTasks::Task
|
|
44
|
+
.for_action_types([job_class.name])
|
|
45
|
+
.joins(:links)
|
|
46
|
+
.where(foreman_tasks_links: {
|
|
47
|
+
resource_type: 'Organization',
|
|
48
|
+
resource_id: org_id,
|
|
49
|
+
})
|
|
50
|
+
.with_duration
|
|
51
|
+
.order('started_at DESC')
|
|
52
|
+
.first
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def sub_action_status(task, action_class_name)
|
|
56
|
+
return nil unless task
|
|
57
|
+
|
|
58
|
+
# If task is still running, return the state
|
|
59
|
+
return task.state unless task.state == 'stopped'
|
|
60
|
+
|
|
61
|
+
# For GenerateHostReport: always show status if task completed (generation always runs)
|
|
62
|
+
if action_class_name == 'GenerateHostReport'
|
|
63
|
+
return task.result
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# For UploadReportDirectJob: only show status if task had upload enabled
|
|
67
|
+
if action_class_name == 'UploadReportDirectJob'
|
|
68
|
+
main_action = task.main_action
|
|
69
|
+
return nil unless main_action.respond_to?(:input)
|
|
70
|
+
|
|
71
|
+
# Check if upload was enabled for this task
|
|
72
|
+
return nil unless main_action.input[:upload]
|
|
73
|
+
|
|
74
|
+
# Return the task result
|
|
75
|
+
return task.result
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
nil
|
|
79
|
+
rescue StandardError => e
|
|
80
|
+
Rails.logger.warn("Failed to get sub-action status for #{action_class_name} in task #{task.id}: #{e.message}")
|
|
81
|
+
nil
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def task_json(task)
|
|
85
|
+
return nil unless task
|
|
86
|
+
|
|
87
|
+
{
|
|
88
|
+
id: task.id,
|
|
89
|
+
label: task.label,
|
|
90
|
+
state: task.state,
|
|
91
|
+
result: task.result,
|
|
92
|
+
progress: task.progress,
|
|
93
|
+
started_at: task.started_at,
|
|
94
|
+
ended_at: task.ended_at,
|
|
95
|
+
duration: task.try(:duration)&.to_f,
|
|
96
|
+
report_file_path: task_report_file_path(task),
|
|
97
|
+
}
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def task_report_file_path(task)
|
|
101
|
+
return nil unless task&.state == 'stopped'
|
|
102
|
+
|
|
103
|
+
# Get the main action from the task
|
|
104
|
+
main_action = task.main_action
|
|
105
|
+
return nil unless main_action.respond_to?(:report_file_path)
|
|
106
|
+
|
|
107
|
+
main_action.report_file_path
|
|
108
|
+
rescue StandardError => e
|
|
109
|
+
Rails.logger.warn("Failed to get report file path for task #{task.id}: #{e.message}")
|
|
110
|
+
nil
|
|
36
111
|
end
|
|
37
112
|
end
|
|
38
113
|
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
module ForemanInventoryUpload
|
|
2
|
+
module Api
|
|
3
|
+
class TasksController < ::Api::V2::BaseController
|
|
4
|
+
ACTION_TYPES = [
|
|
5
|
+
'ForemanInventoryUpload::Async::HostInventoryReportJob',
|
|
6
|
+
'ForemanInventoryUpload::Async::UploadReportDirectJob',
|
|
7
|
+
].freeze
|
|
8
|
+
|
|
9
|
+
# GET /foreman_inventory_upload/api/tasks/current
|
|
10
|
+
# Returns current running/active tasks for inventory operations
|
|
11
|
+
def current
|
|
12
|
+
organization_id = validate_organization_id if params[:organization_id].present?
|
|
13
|
+
return if performed?
|
|
14
|
+
|
|
15
|
+
tasks = ForemanTasks::Task
|
|
16
|
+
.active
|
|
17
|
+
.for_action_types(ACTION_TYPES)
|
|
18
|
+
.with_duration
|
|
19
|
+
|
|
20
|
+
if organization_id.present?
|
|
21
|
+
tasks = tasks.joins(:links)
|
|
22
|
+
.where(foreman_tasks_links: {
|
|
23
|
+
resource_type: 'Organization',
|
|
24
|
+
resource_id: organization_id,
|
|
25
|
+
})
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
render json: {
|
|
29
|
+
tasks: tasks.map { |task| task_json(task) },
|
|
30
|
+
}
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# GET /foreman_inventory_upload/api/tasks/history
|
|
34
|
+
# Returns recent task history for inventory operations
|
|
35
|
+
def history
|
|
36
|
+
organization_id = validate_organization_id if params[:organization_id].present?
|
|
37
|
+
return if performed?
|
|
38
|
+
|
|
39
|
+
limit = validated_limit
|
|
40
|
+
|
|
41
|
+
tasks = ForemanTasks::Task
|
|
42
|
+
.for_action_types(ACTION_TYPES)
|
|
43
|
+
.with_duration
|
|
44
|
+
.order('started_at DESC')
|
|
45
|
+
.limit(limit)
|
|
46
|
+
|
|
47
|
+
if organization_id.present?
|
|
48
|
+
tasks = tasks.joins(:links)
|
|
49
|
+
.where(foreman_tasks_links: {
|
|
50
|
+
resource_type: 'Organization',
|
|
51
|
+
resource_id: organization_id,
|
|
52
|
+
})
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
render json: {
|
|
56
|
+
tasks: tasks.map { |task| task_json(task) },
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
def controller_permission
|
|
63
|
+
'foreman_rh_cloud'
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def task_json(task)
|
|
67
|
+
{
|
|
68
|
+
id: task.id,
|
|
69
|
+
label: task.label,
|
|
70
|
+
action: task.action,
|
|
71
|
+
state: task.state,
|
|
72
|
+
result: task.result,
|
|
73
|
+
progress: task.progress,
|
|
74
|
+
started_at: task.started_at,
|
|
75
|
+
ended_at: task.ended_at,
|
|
76
|
+
duration: task.duration&.to_f,
|
|
77
|
+
humanized: task.humanized,
|
|
78
|
+
report_file_path: task_report_file_path(task),
|
|
79
|
+
}
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def task_report_file_path(task)
|
|
83
|
+
return nil unless task.state == 'stopped' && task.result == 'success'
|
|
84
|
+
return nil unless task.action == 'ForemanInventoryUpload::Async::HostInventoryReportJob'
|
|
85
|
+
|
|
86
|
+
task.main_action&.output&.[]('report_file')
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def validate_organization_id
|
|
90
|
+
org_id = params[:organization_id].to_i
|
|
91
|
+
if org_id.zero?
|
|
92
|
+
render json: { message: 'Invalid organization_id parameter' }, status: :bad_request
|
|
93
|
+
return nil
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
begin
|
|
97
|
+
Organization.find(org_id).id
|
|
98
|
+
rescue ActiveRecord::RecordNotFound
|
|
99
|
+
render json: { message: "Organization with id #{org_id} not found" }, status: :not_found
|
|
100
|
+
nil
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def validated_limit
|
|
105
|
+
limit = params[:limit]&.to_i || 10
|
|
106
|
+
limit.clamp(1, 100)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -4,30 +4,54 @@ module ForemanInventoryUpload
|
|
|
4
4
|
class ReportsController < ::ApplicationController
|
|
5
5
|
include InventoryUpload::ReportActions
|
|
6
6
|
|
|
7
|
-
def
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
).first&.start_at || nil
|
|
7
|
+
def generate
|
|
8
|
+
organization_id = validate_organization_id
|
|
9
|
+
return if performed?
|
|
10
|
+
|
|
11
|
+
disconnected = boolean_param(:disconnected)
|
|
12
|
+
|
|
13
|
+
task = start_report_generation(organization_id, disconnected)
|
|
15
14
|
|
|
16
15
|
render json: {
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
id: task.id,
|
|
17
|
+
humanized: {
|
|
18
|
+
action: task.action,
|
|
19
|
+
},
|
|
19
20
|
}, status: :ok
|
|
20
21
|
end
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
organization_id = params[:organization_id]
|
|
24
|
-
disconnected = params[:disconnected]
|
|
23
|
+
private
|
|
25
24
|
|
|
26
|
-
|
|
25
|
+
def controller_permission
|
|
26
|
+
'foreman_rh_cloud'
|
|
27
|
+
end
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
def action_permission
|
|
30
|
+
case params[:action]
|
|
31
|
+
when 'generate'
|
|
32
|
+
'generate'
|
|
33
|
+
else
|
|
34
|
+
super
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def validate_organization_id
|
|
39
|
+
org_id = params[:organization_id].to_i
|
|
40
|
+
if org_id.zero?
|
|
41
|
+
render json: { message: 'Invalid organization_id parameter' }, status: :bad_request
|
|
42
|
+
return nil
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
begin
|
|
46
|
+
Organization.find(org_id).id
|
|
47
|
+
rescue ActiveRecord::RecordNotFound
|
|
48
|
+
render json: { message: "Organization with id #{org_id} not found" }, status: :not_found
|
|
49
|
+
nil
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def boolean_param(key)
|
|
54
|
+
Foreman::Cast.to_bool(params[key]) || false
|
|
31
55
|
end
|
|
32
56
|
end
|
|
33
57
|
end
|
|
@@ -5,15 +5,6 @@ module ForemanInventoryUpload
|
|
|
5
5
|
|
|
6
6
|
before_action :require_non_iop_smart_proxy, only: [:enable_cloud_connector]
|
|
7
7
|
|
|
8
|
-
def last
|
|
9
|
-
label = ForemanInventoryUpload::Async::UploadReportDirectJob.output_label(params[:organization_id])
|
|
10
|
-
output = ForemanInventoryUpload::Async::ProgressOutput.get(label)&.full_output
|
|
11
|
-
|
|
12
|
-
render json: {
|
|
13
|
-
output: output,
|
|
14
|
-
}, status: :ok
|
|
15
|
-
end
|
|
16
|
-
|
|
17
8
|
def download_file
|
|
18
9
|
filename, file = report_file(params[:organization_id])
|
|
19
10
|
|
data/config/routes.rb
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
Rails.application.routes.draw do
|
|
2
2
|
namespace :foreman_inventory_upload do
|
|
3
|
-
get ':organization_id/reports/last', to: 'reports#last', constraints: { organization_id: %r{[^\/]+} }
|
|
4
3
|
post ':organization_id/reports', to: 'reports#generate', constraints: { organization_id: %r{[^\/]+} }
|
|
5
|
-
get ':organization_id/uploads/last', to: 'uploads#last', constraints: { organization_id: %r{[^\/]+} }
|
|
6
4
|
get ':organization_id/uploads/file', to: 'uploads#download_file', constraints: { organization_id: %r{[^\/]+} }
|
|
7
5
|
get 'missing_hosts', to: 'missing_hosts#index'
|
|
8
6
|
get 'accounts', to: 'accounts#index'
|
|
@@ -76,6 +74,10 @@ Rails.application.routes.draw do
|
|
|
76
74
|
post 'enable_connector', to: 'inventory#enable_cloud_connector'
|
|
77
75
|
post 'cloud_request', to: 'cloud_request#update'
|
|
78
76
|
get 'advisor_engine_config', to: 'advisor_engine_config#show'
|
|
77
|
+
|
|
78
|
+
# Inventory upload task endpoints
|
|
79
|
+
get 'inventory_upload/tasks/current', to: 'foreman_inventory_upload/api/tasks#current'
|
|
80
|
+
get 'inventory_upload/tasks/history', to: 'foreman_inventory_upload/api/tasks#history'
|
|
79
81
|
end
|
|
80
82
|
|
|
81
83
|
namespace 'advisor_engine' do
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
class DropTaskOutputTables < ActiveRecord::Migration[6.0]
|
|
2
|
+
def up
|
|
3
|
+
drop_table :task_output_lines
|
|
4
|
+
drop_table :task_output_statuses
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def down
|
|
8
|
+
create_table :task_output_lines do |t|
|
|
9
|
+
t.string :label
|
|
10
|
+
t.string :line
|
|
11
|
+
t.timestamps
|
|
12
|
+
|
|
13
|
+
t.index :label
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
create_table :task_output_statuses do |t|
|
|
17
|
+
t.string :label
|
|
18
|
+
t.string :status
|
|
19
|
+
t.timestamps
|
|
20
|
+
|
|
21
|
+
t.index :label, unique: true
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -42,7 +42,7 @@ module ForemanInventoryUpload
|
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
def plan_generate_report(folder, organization, disconnected)
|
|
45
|
-
plan_action(ForemanInventoryUpload::Async::
|
|
45
|
+
plan_action(ForemanInventoryUpload::Async::HostInventoryReportJob, folder, organization.id, '', !disconnected)
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def logger
|
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
module ForemanInventoryUpload
|
|
2
2
|
module Async
|
|
3
3
|
class HostInventoryReportJob < ::Actions::EntryAction
|
|
4
|
+
def resource_locks
|
|
5
|
+
:link
|
|
6
|
+
end
|
|
7
|
+
|
|
4
8
|
def plan(base_folder, organization_id, hosts_filter = "", upload = true)
|
|
9
|
+
organization = Organization.find(organization_id)
|
|
10
|
+
action_subject(organization)
|
|
11
|
+
|
|
12
|
+
plan_self(
|
|
13
|
+
base_folder: base_folder,
|
|
14
|
+
organization_id: organization_id,
|
|
15
|
+
hosts_filter: hosts_filter,
|
|
16
|
+
upload: upload
|
|
17
|
+
)
|
|
18
|
+
|
|
5
19
|
sequence do
|
|
6
20
|
plan_action(
|
|
7
21
|
GenerateHostReport,
|
|
@@ -31,9 +45,34 @@ module ForemanInventoryUpload
|
|
|
31
45
|
_("Host inventory report job")
|
|
32
46
|
end
|
|
33
47
|
|
|
48
|
+
def organization
|
|
49
|
+
Organization.find(input[:organization_id])
|
|
50
|
+
end
|
|
51
|
+
|
|
34
52
|
def organization_id
|
|
35
53
|
input[:organization_id]
|
|
36
54
|
end
|
|
55
|
+
|
|
56
|
+
def report_file_path
|
|
57
|
+
filename = ForemanInventoryUpload.facts_archive_name(input[:organization_id], input[:hosts_filter])
|
|
58
|
+
|
|
59
|
+
if input[:upload]
|
|
60
|
+
# For upload tasks, check: done folder (uploaded), uploads folder (queued), generated_reports folder (failed upload)
|
|
61
|
+
[
|
|
62
|
+
ForemanInventoryUpload.done_file_path(filename),
|
|
63
|
+
ForemanInventoryUpload.uploads_file_path(filename),
|
|
64
|
+
File.join(input[:base_folder], filename),
|
|
65
|
+
].find { |path| File.exist?(path) }
|
|
66
|
+
else
|
|
67
|
+
# For generate-only tasks, only check generated_reports folder
|
|
68
|
+
generated_path = File.join(input[:base_folder], filename)
|
|
69
|
+
File.exist?(generated_path) ? generated_path : nil
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def rescue_strategy_for_self
|
|
74
|
+
Dynflow::Action::Rescue::Fail
|
|
75
|
+
end
|
|
37
76
|
end
|
|
38
77
|
end
|
|
39
78
|
end
|
|
@@ -42,16 +42,15 @@ module ForemanInventoryUpload
|
|
|
42
42
|
"upload_for_#{label}"
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
+
def resource_locks
|
|
46
|
+
:link
|
|
47
|
+
end
|
|
48
|
+
|
|
45
49
|
def plan(filename, organization_id)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
# This matches the pattern in GenerateReportJob. A full fix for thread-safety
|
|
50
|
-
# requires UI changes to display multiple concurrent tasks per org (tracked for PR #2).
|
|
51
|
-
label = UploadReportDirectJob.output_label(organization_id)
|
|
52
|
-
clear_task_output(label)
|
|
50
|
+
organization = Organization.find(organization_id)
|
|
51
|
+
action_subject(organization)
|
|
52
|
+
|
|
53
53
|
plan_self(
|
|
54
|
-
instance_label: label,
|
|
55
54
|
filename: filename,
|
|
56
55
|
organization_id: organization_id
|
|
57
56
|
)
|
|
@@ -59,21 +58,12 @@ module ForemanInventoryUpload
|
|
|
59
58
|
|
|
60
59
|
def try_execute
|
|
61
60
|
if content_disconnected?
|
|
62
|
-
|
|
63
|
-
progress_output.write_line("Report was not moved and upload was canceled because connection to Insights is not enabled. Report location: #{filename}.")
|
|
64
|
-
progress_output.status = "Task aborted, exit 1"
|
|
65
|
-
done!
|
|
66
|
-
end
|
|
61
|
+
logger.info("Upload canceled: connection to Insights is not enabled. Report location: #{filename}")
|
|
67
62
|
return
|
|
68
63
|
end
|
|
69
64
|
|
|
70
65
|
unless organization.owner_details&.dig('upstreamConsumer', 'idCert')
|
|
71
66
|
logger.info("Skipping organization '#{organization}', no candlepin certificate defined.")
|
|
72
|
-
progress_output do |progress_output|
|
|
73
|
-
progress_output.write_line("Skipping organization #{organization}, no candlepin certificate defined.")
|
|
74
|
-
progress_output.status = "Task aborted, exit 1"
|
|
75
|
-
done!
|
|
76
|
-
end
|
|
77
67
|
return
|
|
78
68
|
end
|
|
79
69
|
|
|
@@ -81,31 +71,13 @@ module ForemanInventoryUpload
|
|
|
81
71
|
cer_file.write(certificate[:cert])
|
|
82
72
|
cer_file.write(certificate[:key])
|
|
83
73
|
cer_file.flush
|
|
84
|
-
|
|
74
|
+
upload_file(cer_file.path)
|
|
85
75
|
end
|
|
86
76
|
|
|
77
|
+
move_to_done_folder
|
|
87
78
|
done!
|
|
88
79
|
end
|
|
89
80
|
|
|
90
|
-
def upload_report(cer_path)
|
|
91
|
-
progress_output do |progress_output|
|
|
92
|
-
progress_output.write_line("Uploading report for organization #{organization.label}...")
|
|
93
|
-
progress_output.status = "Running upload"
|
|
94
|
-
|
|
95
|
-
begin
|
|
96
|
-
upload_file(cer_path)
|
|
97
|
-
progress_output.write_line("Upload completed successfully")
|
|
98
|
-
move_to_done_folder
|
|
99
|
-
progress_output.write_line("Uploaded file moved to done/ folder")
|
|
100
|
-
progress_output.status = "pid #{Process.pid} exit 0"
|
|
101
|
-
rescue StandardError => e
|
|
102
|
-
progress_output.write_line("Upload failed: #{e.message}")
|
|
103
|
-
progress_output.status = "pid #{Process.pid} exit 1"
|
|
104
|
-
raise
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
|
|
109
81
|
def upload_file(cer_path)
|
|
110
82
|
cert_content = File.read(cer_path)
|
|
111
83
|
|
|
@@ -137,7 +109,10 @@ module ForemanInventoryUpload
|
|
|
137
109
|
def move_to_done_folder
|
|
138
110
|
FileUtils.mkdir_p(ForemanInventoryUpload.done_folder)
|
|
139
111
|
done_file = ForemanInventoryUpload.done_file_path(File.basename(filename))
|
|
140
|
-
|
|
112
|
+
if File.exist?(done_file)
|
|
113
|
+
logger.warn("Destination file #{done_file} already exists. Overwriting with new report.")
|
|
114
|
+
end
|
|
115
|
+
FileUtils.mv(filename, done_file, force: true)
|
|
141
116
|
logger.debug("Moved #{filename} to #{done_file}")
|
|
142
117
|
end
|
|
143
118
|
|
|
@@ -154,9 +129,20 @@ module ForemanInventoryUpload
|
|
|
154
129
|
end
|
|
155
130
|
|
|
156
131
|
def foreman_certificate
|
|
132
|
+
cert_path = Setting[:ssl_certificate]
|
|
133
|
+
key_path = Setting[:ssl_priv_key]
|
|
134
|
+
|
|
135
|
+
unless cert_path && File.readable?(cert_path)
|
|
136
|
+
raise "SSL certificate file not found or not readable: #{cert_path}"
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
unless key_path && File.readable?(key_path)
|
|
140
|
+
raise "SSL private key file not found or not readable: #{key_path}"
|
|
141
|
+
end
|
|
142
|
+
|
|
157
143
|
{
|
|
158
|
-
cert: File.read(
|
|
159
|
-
key: File.read(
|
|
144
|
+
cert: File.read(cert_path),
|
|
145
|
+
key: File.read(key_path),
|
|
160
146
|
}
|
|
161
147
|
end
|
|
162
148
|
|
|
@@ -169,20 +155,10 @@ module ForemanInventoryUpload
|
|
|
169
155
|
end
|
|
170
156
|
|
|
171
157
|
def content_disconnected?
|
|
158
|
+
return false if ForemanRhCloud.with_iop_smart_proxy?
|
|
172
159
|
!Setting[:subscription_connection_enabled]
|
|
173
160
|
end
|
|
174
161
|
|
|
175
|
-
def progress_output
|
|
176
|
-
progress_output = ProgressOutput.register(instance_label)
|
|
177
|
-
yield(progress_output)
|
|
178
|
-
ensure
|
|
179
|
-
progress_output.close
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
def instance_label
|
|
183
|
-
input[:instance_label]
|
|
184
|
-
end
|
|
185
|
-
|
|
186
162
|
def logger
|
|
187
163
|
Foreman::Logging.logger('background')
|
|
188
164
|
end
|
|
@@ -190,11 +166,6 @@ module ForemanInventoryUpload
|
|
|
190
166
|
def rescue_strategy_for_self
|
|
191
167
|
Dynflow::Action::Rescue::Fail
|
|
192
168
|
end
|
|
193
|
-
|
|
194
|
-
def clear_task_output(label)
|
|
195
|
-
TaskOutputLine.where(label: label).delete_all
|
|
196
|
-
TaskOutputStatus.where(label: label).delete_all
|
|
197
|
-
end
|
|
198
169
|
end
|
|
199
170
|
end
|
|
200
171
|
end
|
|
@@ -44,6 +44,7 @@ module ForemanRhCloud
|
|
|
44
44
|
'foreman_inventory_upload/reports': [:last],
|
|
45
45
|
'foreman_inventory_upload/uploads': [:auto_upload, :show_auto_upload, :download_file, :last],
|
|
46
46
|
'foreman_inventory_upload/tasks': [:show],
|
|
47
|
+
'foreman_inventory_upload/api/tasks': [:current, :history],
|
|
47
48
|
'foreman_inventory_upload/cloud_status': [:index],
|
|
48
49
|
'foreman_inventory_upload/uploads_settings': [:index],
|
|
49
50
|
'foreman_inventory_upload/missing_hosts': [:index],
|
|
@@ -12,9 +12,11 @@ namespace :rh_cloud_inventory do
|
|
|
12
12
|
User.as_anonymous_admin do
|
|
13
13
|
organizations.each do |organization|
|
|
14
14
|
ForemanTasks.async_task(
|
|
15
|
-
ForemanInventoryUpload::Async::
|
|
15
|
+
ForemanInventoryUpload::Async::HostInventoryReportJob,
|
|
16
16
|
ForemanInventoryUpload.generated_reports_folder,
|
|
17
|
-
organization.id
|
|
17
|
+
organization.id,
|
|
18
|
+
'', # hosts_filter
|
|
19
|
+
true # upload
|
|
18
20
|
)
|
|
19
21
|
puts "Generated and uploaded inventory report for organization '#{organization.name}'"
|
|
20
22
|
end
|
data/package.json
CHANGED
|
@@ -5,22 +5,21 @@ class AccountsControllerTest < ActionController::TestCase
|
|
|
5
5
|
|
|
6
6
|
include FolderIsolation
|
|
7
7
|
|
|
8
|
-
test 'Returns statuses for each
|
|
8
|
+
test 'Returns statuses for each organization' do
|
|
9
9
|
test_org = FactoryBot.create(:organization)
|
|
10
10
|
|
|
11
|
-
generate_label = ForemanInventoryUpload::Async::GenerateReportJob.output_label(test_org.id)
|
|
12
|
-
generate_output = ForemanInventoryUpload::Async::ProgressOutput.register(generate_label)
|
|
13
|
-
generate_output.status = 'generate_status_test'
|
|
14
|
-
upload_label = ForemanInventoryUpload::Async::UploadReportDirectJob.output_label(test_org.id)
|
|
15
|
-
upload_output = ForemanInventoryUpload::Async::ProgressOutput.register(upload_label)
|
|
16
|
-
upload_output.status = 'upload_status_test'
|
|
17
|
-
|
|
18
11
|
get :index, session: set_session_user
|
|
19
12
|
|
|
20
13
|
assert_response :success
|
|
21
14
|
actual = JSON.parse(response.body)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
15
|
+
assert actual['accounts'].key?(test_org.name)
|
|
16
|
+
actual_account = actual['accounts'][test_org.name]
|
|
17
|
+
|
|
18
|
+
# Verify the response structure
|
|
19
|
+
assert_includes actual_account.keys, 'generated_status'
|
|
20
|
+
assert_includes actual_account.keys, 'uploaded_status'
|
|
21
|
+
assert_includes actual_account.keys, 'generate_task'
|
|
22
|
+
assert_includes actual_account.keys, 'report_file_paths'
|
|
23
|
+
assert_equal test_org.id, actual_account['id']
|
|
25
24
|
end
|
|
26
25
|
end
|