foreman_rh_cloud 13.0.8 → 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/queue_for_upload_job.rb +1 -23
- data/lib/foreman_inventory_upload/async/upload_report_direct_job.rb +171 -0
- data/lib/foreman_inventory_upload.rb +0 -4
- data/lib/foreman_rh_cloud/plugin.rb +1 -0
- data/lib/foreman_rh_cloud/version.rb +1 -1
- data/lib/inventory_sync/async/inventory_hosts_sync.rb +0 -2
- 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/queue_for_upload_job_test.rb +1 -12
- data/test/jobs/single_host_report_job_test.rb +36 -54
- data/test/jobs/upload_report_direct_job_test.rb +399 -0
- 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 +4 -5
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/ListItem.js +15 -7
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/__tests__/__snapshots__/ListItem.test.js.snap +11 -11
- 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 +37 -130
- 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/PageHeader.js +24 -17
- data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/PageHeader.test.js +178 -8
- data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/__snapshots__/PageTitle.test.js.snap +2 -2
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/ToolbarButtons/ToolbarButtons.js +3 -1
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/ToolbarButtons/__tests__/ToolbarButtons.test.js +69 -51
- 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/InsightsTable/__tests__/InsightsTable.test.js +11 -19
- data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationHelpers.js +1 -2
- data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationModal.js +2 -4
- data/webpack/InsightsVulnerabilityHostIndexExtensions/__tests__/CVECountCell.test.js +77 -22
- data/webpack/__mocks__/foremanReact/components/common/dates/RelativeDateTime.js +14 -0
- data/webpack/__tests__/ForemanRhCloudHelpers.test.js +5 -1
- metadata +13 -68
- 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/lib/foreman_inventory_upload/async/upload_report_job.rb +0 -97
- data/lib/foreman_inventory_upload/scripts/uploader.sh.erb +0 -55
- 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/jobs/upload_report_job_test.rb +0 -38
- 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/PageHeader/__tests__/__snapshots__/PageHeader.test.js.snap +0 -36
- 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
- data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/__snapshots__/InsightsTable.test.js.snap +0 -112
- data/webpack/__mocks__/foremanReact/common/hooks/API/APIHooks.js +0 -3
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::UploadReportJob.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
|
|
@@ -9,7 +9,6 @@ module ForemanInventoryUpload
|
|
|
9
9
|
def run
|
|
10
10
|
logger.debug('Ensuring objects')
|
|
11
11
|
ensure_ouput_folder
|
|
12
|
-
ensure_output_script
|
|
13
12
|
logger.debug("Copying #{report_file} to #{uploads_folder}")
|
|
14
13
|
enqueued_file_name = File.join(uploads_folder, report_file)
|
|
15
14
|
FileUtils.mv(File.join(base_folder, report_file), enqueued_file_name)
|
|
@@ -22,31 +21,10 @@ module ForemanInventoryUpload
|
|
|
22
21
|
@uploads_folder ||= ForemanInventoryUpload.uploads_folder
|
|
23
22
|
end
|
|
24
23
|
|
|
25
|
-
def script_file
|
|
26
|
-
@script_file ||= File.join(uploads_folder, ForemanInventoryUpload.upload_script_file)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
24
|
def ensure_ouput_folder
|
|
30
25
|
FileUtils.mkdir_p(uploads_folder)
|
|
31
26
|
end
|
|
32
27
|
|
|
33
|
-
def ensure_output_script
|
|
34
|
-
return if File.exist?(script_file)
|
|
35
|
-
|
|
36
|
-
script_source = File.join(ForemanRhCloud::Engine.root, 'lib/foreman_inventory_upload/scripts/uploader.sh.erb')
|
|
37
|
-
|
|
38
|
-
template_src = Foreman::Renderer::Source::String.new(content: File.read(script_source))
|
|
39
|
-
scope = Foreman::Renderer::Scope::Base.new(
|
|
40
|
-
source: template_src,
|
|
41
|
-
variables: {
|
|
42
|
-
upload_url: ForemanInventoryUpload.upload_url,
|
|
43
|
-
}
|
|
44
|
-
)
|
|
45
|
-
script_source = Foreman::Renderer.render(template_src, scope)
|
|
46
|
-
File.write(script_file, script_source)
|
|
47
|
-
FileUtils.chmod('+x', script_file)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
28
|
def logger
|
|
51
29
|
Foreman::Logging.logger('background')
|
|
52
30
|
end
|
|
@@ -60,7 +38,7 @@ module ForemanInventoryUpload
|
|
|
60
38
|
end
|
|
61
39
|
|
|
62
40
|
def plan_upload_report(enqueued_file_name, organization_id)
|
|
63
|
-
plan_action(
|
|
41
|
+
plan_action(UploadReportDirectJob, enqueued_file_name, organization_id)
|
|
64
42
|
end
|
|
65
43
|
end
|
|
66
44
|
end
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
require 'tempfile'
|
|
2
|
+
require 'rest-client'
|
|
3
|
+
|
|
4
|
+
module ForemanInventoryUpload
|
|
5
|
+
module Async
|
|
6
|
+
class UploadReportDirectJob < ::Actions::EntryAction
|
|
7
|
+
include AsyncHelpers
|
|
8
|
+
include ::ForemanRhCloud::Async::ExponentialBackoff
|
|
9
|
+
include ::ForemanRhCloud::CloudRequest
|
|
10
|
+
|
|
11
|
+
# Wrapper class to avoid monkey-patching File for multipart uploads
|
|
12
|
+
class FileUpload
|
|
13
|
+
attr_reader :file, :content_type
|
|
14
|
+
|
|
15
|
+
def initialize(file, content_type:)
|
|
16
|
+
@file = file
|
|
17
|
+
@content_type = content_type
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def read(*args)
|
|
21
|
+
@file.read(*args)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def path
|
|
25
|
+
@file.path
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def respond_to_missing?(method_name, include_private = false)
|
|
29
|
+
@file.respond_to?(method_name, include_private) || super
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def method_missing(method_name, *args, &block)
|
|
33
|
+
if @file.respond_to?(method_name)
|
|
34
|
+
@file.send(method_name, *args, &block)
|
|
35
|
+
else
|
|
36
|
+
super
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.output_label(label)
|
|
42
|
+
"upload_for_#{label}"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def resource_locks
|
|
46
|
+
:link
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def plan(filename, organization_id)
|
|
50
|
+
organization = Organization.find(organization_id)
|
|
51
|
+
action_subject(organization)
|
|
52
|
+
|
|
53
|
+
plan_self(
|
|
54
|
+
filename: filename,
|
|
55
|
+
organization_id: organization_id
|
|
56
|
+
)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def try_execute
|
|
60
|
+
if content_disconnected?
|
|
61
|
+
logger.info("Upload canceled: connection to Insights is not enabled. Report location: #{filename}")
|
|
62
|
+
return
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
unless organization.owner_details&.dig('upstreamConsumer', 'idCert')
|
|
66
|
+
logger.info("Skipping organization '#{organization}', no candlepin certificate defined.")
|
|
67
|
+
return
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
Tempfile.create([organization.name, '.pem']) do |cer_file|
|
|
71
|
+
cer_file.write(certificate[:cert])
|
|
72
|
+
cer_file.write(certificate[:key])
|
|
73
|
+
cer_file.flush
|
|
74
|
+
upload_file(cer_file.path)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
move_to_done_folder
|
|
78
|
+
done!
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def upload_file(cer_path)
|
|
82
|
+
cert_content = File.read(cer_path)
|
|
83
|
+
|
|
84
|
+
File.open(filename, 'rb') do |file|
|
|
85
|
+
# Wrap file with FileUpload class for RestClient multipart handling
|
|
86
|
+
# RestClient requires objects with :read, :path, and :content_type methods
|
|
87
|
+
wrapped_file = FileUpload.new(file, content_type: 'application/vnd.redhat.qpc.tar+tgz')
|
|
88
|
+
|
|
89
|
+
response = execute_cloud_request(
|
|
90
|
+
method: :post,
|
|
91
|
+
url: ForemanInventoryUpload.upload_url,
|
|
92
|
+
payload: {
|
|
93
|
+
multipart: true,
|
|
94
|
+
file: wrapped_file,
|
|
95
|
+
},
|
|
96
|
+
headers: {
|
|
97
|
+
'X-Org-Id' => organization.label,
|
|
98
|
+
},
|
|
99
|
+
ssl_client_cert: OpenSSL::X509::Certificate.new(cert_content),
|
|
100
|
+
ssl_client_key: OpenSSL::PKey::RSA.new(cert_content),
|
|
101
|
+
timeout: 600,
|
|
102
|
+
open_timeout: 60
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
logger.debug("Upload response code: #{response.code}")
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def move_to_done_folder
|
|
110
|
+
FileUtils.mkdir_p(ForemanInventoryUpload.done_folder)
|
|
111
|
+
done_file = ForemanInventoryUpload.done_file_path(File.basename(filename))
|
|
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)
|
|
116
|
+
logger.debug("Moved #{filename} to #{done_file}")
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def certificate
|
|
120
|
+
ForemanRhCloud.with_iop_smart_proxy? ? foreman_certificate : manifest_certificate
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def manifest_certificate
|
|
124
|
+
candlepin_id_certificate = organization.owner_details['upstreamConsumer']['idCert']
|
|
125
|
+
{
|
|
126
|
+
cert: candlepin_id_certificate['cert'],
|
|
127
|
+
key: candlepin_id_certificate['key'],
|
|
128
|
+
}
|
|
129
|
+
end
|
|
130
|
+
|
|
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
|
+
|
|
143
|
+
{
|
|
144
|
+
cert: File.read(cert_path),
|
|
145
|
+
key: File.read(key_path),
|
|
146
|
+
}
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def filename
|
|
150
|
+
input[:filename]
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def organization
|
|
154
|
+
Organization.find(input[:organization_id])
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def content_disconnected?
|
|
158
|
+
return false if ForemanRhCloud.with_iop_smart_proxy?
|
|
159
|
+
!Setting[:subscription_connection_enabled]
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def logger
|
|
163
|
+
Foreman::Logging.logger('background')
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def rescue_strategy_for_self
|
|
167
|
+
Dynflow::Action::Rescue::Fail
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
@@ -48,10 +48,6 @@ module ForemanInventoryUpload
|
|
|
48
48
|
@outputs_folder ||= ensure_folder(File.join(ForemanInventoryUpload.base_folder, 'outputs/'))
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
-
def self.upload_script_file
|
|
52
|
-
'uploader.sh'
|
|
53
|
-
end
|
|
54
|
-
|
|
55
51
|
def self.facts_archive_name(organization, filter = nil)
|
|
56
52
|
"report_for_#{organization}#{filter.empty? ? nil : "[#{filter.to_s.parameterize}]"}.tar.xz"
|
|
57
53
|
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],
|