foreman_inventory_upload 1.0.0.beta7 → 1.0.0
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/foreman_inventory_upload/accounts_controller.rb +39 -0
- data/app/controllers/foreman_inventory_upload/reports_controller.rb +3 -4
- data/app/controllers/foreman_inventory_upload/uploads_controller.rb +3 -3
- data/app/views/scripts/uploader.sh.erb +21 -9
- data/config/routes.rb +5 -5
- data/lib/foreman_inventory_upload.rb +14 -10
- data/lib/foreman_inventory_upload/async/generate_all_reports_job.rb +4 -7
- data/lib/foreman_inventory_upload/async/generate_report_job.rb +13 -9
- data/lib/foreman_inventory_upload/async/queue_for_upload_job.rb +7 -7
- data/lib/foreman_inventory_upload/async/upload_report_job.rb +22 -17
- data/lib/foreman_inventory_upload/generators/archived_report.rb +2 -2
- data/lib/foreman_inventory_upload/generators/queries.rb +18 -9
- data/lib/foreman_inventory_upload/generators/slice.rb +16 -9
- data/lib/foreman_inventory_upload/version.rb +1 -1
- data/lib/tasks/generator.rake +19 -5
- data/test/controllers/{statuses_controller_test.rb → accounts_controller_test.rb} +6 -7
- data/test/controllers/reports_controller_test.rb +3 -3
- data/test/controllers/uploads_controller_test.rb +3 -3
- data/test/unit/archived_report_generator_test.rb +3 -3
- data/test/unit/slice_generator_test.rb +22 -0
- data/webpack/ForemanInventoryUpload/Components/AccountList/AccountList.fixtures.js +5 -2
- data/webpack/ForemanInventoryUpload/Components/AccountList/AccountList.js +11 -9
- data/webpack/ForemanInventoryUpload/Components/AccountList/AccountListActions.js +3 -3
- data/webpack/ForemanInventoryUpload/Components/AccountList/AccountListReducer.js +7 -7
- data/webpack/ForemanInventoryUpload/Components/AccountList/AccountListSelectors.js +1 -1
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/ListItem.fixtures.js +1 -1
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/ListItem.js +9 -7
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/__tests__/__snapshots__/ListItem.test.js.snap +6 -4
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItemStatus/ListItemStatus.fixtures.js +2 -1
- data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItemStatus/ListItemStatus.js +5 -5
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/AccountListReducer.test.js +2 -2
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/AccountListSelectors.test.js +4 -4
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountList.test.js.snap +15 -12
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountListReducer.test.js.snap +13 -10
- data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountListSelectors.test.js.snap +9 -3
- data/webpack/ForemanInventoryUpload/Components/AccountList/index.js +3 -6
- data/webpack/ForemanInventoryUpload/Components/Dashboard/Dashboard.js +5 -5
- data/webpack/ForemanInventoryUpload/Components/Dashboard/index.js +1 -4
- data/webpack/ForemanInventoryUpload/Components/NavContainer/__tests__/__snapshots__/NavContainer.test.js.snap +4 -4
- data/webpack/ForemanInventoryUpload/Components/StatusChart/StatusChart.js +4 -1
- metadata +7 -7
- data/app/controllers/foreman_inventory_upload/statuses_controller.rb +0 -41
data/lib/tasks/generator.rake
CHANGED
@@ -4,12 +4,26 @@ namespace :foreman_inventory_upload do
|
|
4
4
|
namespace :report do
|
5
5
|
desc 'Generate inventory report to be sent to Red Hat cloud'
|
6
6
|
task generate: :environment do
|
7
|
-
|
8
|
-
|
7
|
+
portal_user = ENV['portal_user']
|
8
|
+
organizations = [ENV['organization_id']]
|
9
|
+
base_folder = ENV['target'] || Dir.pwd
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
unless portal_user || organizations.empty?
|
12
|
+
puts "Must specify either portal_user or organization_id"
|
13
|
+
end
|
14
|
+
|
15
|
+
if portal_user
|
16
|
+
puts "Generating report for all organizations associated with #{portal_user}"
|
17
|
+
base_folder = File.join(base_folder, portal_user)
|
18
|
+
organizations = ForemanInventoryUpload::Generators::Queries.organizations_for_user(portal_user).pluck(:id)
|
19
|
+
end
|
20
|
+
|
21
|
+
organizations.each do |organization|
|
22
|
+
target = File.join(base_folder, ForemanInventoryUpload.facts_archive_name(organization))
|
23
|
+
archived_report_generator = ForemanInventoryUpload::Generators::ArchivedReport.new(target, Logger.new(STDOUT))
|
24
|
+
archived_report_generator.render(organization: organization)
|
25
|
+
puts "Successfully generated #{target} for organization id #{organization}"
|
26
|
+
end
|
13
27
|
end
|
14
28
|
end
|
15
29
|
end
|
@@ -1,25 +1,24 @@
|
|
1
1
|
require 'test_plugin_helper'
|
2
2
|
|
3
|
-
class
|
4
|
-
tests ForemanInventoryUpload::
|
3
|
+
class AccountsControllerTest < ActionController::TestCase
|
4
|
+
tests ForemanInventoryUpload::AccountsController
|
5
5
|
|
6
6
|
include FolderIsolation
|
7
7
|
|
8
8
|
test 'Returns statuses for each process type' do
|
9
|
-
|
10
|
-
configuration.save!
|
9
|
+
test_org = FactoryBot.create(:organization)
|
11
10
|
|
12
|
-
generate_label = ForemanInventoryUpload::Async::GenerateReportJob.output_label(
|
11
|
+
generate_label = ForemanInventoryUpload::Async::GenerateReportJob.output_label(test_org.id)
|
13
12
|
generate_output = ForemanInventoryUpload::Async::ProgressOutput.register(generate_label)
|
14
13
|
generate_output.status = 'generate_status_test'
|
15
|
-
upload_label = ForemanInventoryUpload::Async::UploadReportJob.output_label(
|
14
|
+
upload_label = ForemanInventoryUpload::Async::UploadReportJob.output_label(test_org.id)
|
16
15
|
upload_output = ForemanInventoryUpload::Async::ProgressOutput.register(upload_label)
|
17
16
|
upload_output.status = 'upload_status_test'
|
18
17
|
|
19
18
|
get :index, session: set_session_user
|
20
19
|
|
21
20
|
assert_response :success
|
22
|
-
actual = JSON.parse(response.body)['
|
21
|
+
actual = JSON.parse(response.body)['accounts'][test_org.id.to_s]
|
23
22
|
assert_equal 'generate_status_test', actual['generate_report_status']
|
24
23
|
assert_equal 'upload_status_test', actual['upload_report_status']
|
25
24
|
end
|
@@ -5,14 +5,14 @@ class ReportsControllerTest < ActionController::TestCase
|
|
5
5
|
|
6
6
|
test 'Returns latest report generation status' do
|
7
7
|
progress_output = mock('progress_output')
|
8
|
-
|
8
|
+
test_org = FactoryBot.create(:organization)
|
9
9
|
ForemanInventoryUpload::Async::ProgressOutput
|
10
10
|
.expects(:get)
|
11
|
-
.with(ForemanInventoryUpload::Async::GenerateReportJob.output_label(
|
11
|
+
.with(ForemanInventoryUpload::Async::GenerateReportJob.output_label(test_org.id))
|
12
12
|
.returns(progress_output)
|
13
13
|
progress_output.expects(:full_output).returns('test output')
|
14
14
|
|
15
|
-
get :last, params: {
|
15
|
+
get :last, params: { organization_id: test_org.id }, session: set_session_user
|
16
16
|
|
17
17
|
assert_response :success
|
18
18
|
actual = JSON.parse(response.body)
|
@@ -5,14 +5,14 @@ class UploadsControllerTest < ActionController::TestCase
|
|
5
5
|
|
6
6
|
test 'Returns latest upload status' do
|
7
7
|
progress_output = mock('progress_output')
|
8
|
-
|
8
|
+
test_org = FactoryBot.create(:organization)
|
9
9
|
ForemanInventoryUpload::Async::ProgressOutput
|
10
10
|
.expects(:get)
|
11
|
-
.with(ForemanInventoryUpload::Async::UploadReportJob.output_label(
|
11
|
+
.with(ForemanInventoryUpload::Async::UploadReportJob.output_label(test_org.id))
|
12
12
|
.returns(progress_output)
|
13
13
|
progress_output.expects(:full_output).returns('test output')
|
14
14
|
|
15
|
-
get :last, params: {
|
15
|
+
get :last, params: { organization_id: test_org.id }, session: set_session_user
|
16
16
|
|
17
17
|
assert_response :success
|
18
18
|
actual = JSON.parse(response.body)
|
@@ -46,13 +46,13 @@ class ArchivedReportGeneratorTest < ActiveSupport::TestCase
|
|
46
46
|
|
47
47
|
test 'generates a report for a single host' do
|
48
48
|
batches = Host.where(id: @host.id).in_batches
|
49
|
-
|
49
|
+
test_org = FactoryBot.create(:organization)
|
50
50
|
|
51
|
-
ForemanInventoryUpload::Generators::Queries.expects(:
|
51
|
+
ForemanInventoryUpload::Generators::Queries.expects(:for_org).with(test_org.id).returns(batches)
|
52
52
|
Dir.mktmpdir do |tmpdir|
|
53
53
|
target = File.join(tmpdir, 'test.tar.gz')
|
54
54
|
generator = ForemanInventoryUpload::Generators::ArchivedReport.new(target, Logger.new(STDOUT))
|
55
|
-
generator.render(
|
55
|
+
generator.render(organization: test_org.id)
|
56
56
|
|
57
57
|
files = Dir["#{tmpdir}/*"]
|
58
58
|
assert_equal "#{tmpdir}/test.tar.gz", files.first
|
@@ -109,4 +109,26 @@ class ReportGeneratorTest < ActiveSupport::TestCase
|
|
109
109
|
assert_equal hypervisor_host.name, fact_values['virtual_host_name']
|
110
110
|
assert_equal hypervisor_host.subscription_facet.uuid, fact_values['virtual_host_uuid']
|
111
111
|
end
|
112
|
+
|
113
|
+
test 'generates a report with system purpose' do
|
114
|
+
@host.subscription_facet.purpose_usage = 'test_usage'
|
115
|
+
@host.subscription_facet.purpose_role = 'test_role'
|
116
|
+
@host.subscription_facet.save!
|
117
|
+
|
118
|
+
batch = Host.where(id: @host.id).in_batches.first
|
119
|
+
generator = ForemanInventoryUpload::Generators::Slice.new(batch, [], 'slice_123')
|
120
|
+
|
121
|
+
json_str = generator.render
|
122
|
+
actual = JSON.parse(json_str.join("\n"))
|
123
|
+
|
124
|
+
assert_equal 'slice_123', actual['report_slice_id']
|
125
|
+
assert_not_nil(actual_host = actual['hosts'].first)
|
126
|
+
assert_equal @host.name, actual_host['display_name']
|
127
|
+
assert_equal @host.fqdn, actual_host['fqdn']
|
128
|
+
assert_not_nil(host_facts = actual_host['facts']&.first)
|
129
|
+
assert_equal 'satellite', host_facts['namespace']
|
130
|
+
assert_not_nil(fact_values = host_facts['facts'])
|
131
|
+
assert_equal 'test_usage', fact_values['system_purpose_usage']
|
132
|
+
assert_equal 'test_role', fact_values['system_purpose_role']
|
133
|
+
end
|
112
134
|
end
|
@@ -2,20 +2,23 @@ import { noop } from 'patternfly-react';
|
|
2
2
|
|
3
3
|
export const API_SUCCESS_RESPONSE = {
|
4
4
|
Account1: {
|
5
|
+
label: 'test_org1',
|
5
6
|
upload_report_status: 'running',
|
6
7
|
generate_report_status: 'running',
|
7
8
|
},
|
8
9
|
Account2: {
|
10
|
+
label: 'test_org2',
|
9
11
|
upload_report_status: 'unknown',
|
10
12
|
generate_report_status: 'failure',
|
11
13
|
},
|
12
14
|
Account3: {
|
15
|
+
label: 'test_org3',
|
13
16
|
upload_report_status: 'success',
|
14
17
|
generate_report_status: 'running',
|
15
18
|
},
|
16
19
|
};
|
17
20
|
|
18
|
-
export const
|
21
|
+
export const accounts = API_SUCCESS_RESPONSE;
|
19
22
|
|
20
23
|
export const pollingProcessID = 0;
|
21
24
|
|
@@ -26,7 +29,7 @@ export const accountID = 'user@redhat.com';
|
|
26
29
|
export const processStatusName = 'upload_report_status';
|
27
30
|
|
28
31
|
export const props = {
|
29
|
-
|
32
|
+
accounts,
|
30
33
|
fetchAccountsStatus: noop,
|
31
34
|
startAccountStatusPolling: noop,
|
32
35
|
stopAccountStatusPolling: noop,
|
@@ -20,23 +20,23 @@ class AccountList extends Component {
|
|
20
20
|
}
|
21
21
|
|
22
22
|
render() {
|
23
|
-
const {
|
24
|
-
const
|
23
|
+
const { accounts, error } = this.props;
|
24
|
+
const accountIds = Object.keys(accounts);
|
25
25
|
|
26
26
|
if (error) {
|
27
27
|
return <ErrorState error={error} />;
|
28
28
|
}
|
29
29
|
|
30
|
-
if (
|
30
|
+
if (accountIds.length === 0) {
|
31
31
|
return <EmptyState />;
|
32
32
|
}
|
33
|
-
const items =
|
34
|
-
const
|
33
|
+
const items = accountIds.map((accountID, index) => {
|
34
|
+
const account = accounts[accountID];
|
35
35
|
return (
|
36
36
|
<ListItem
|
37
37
|
key={index}
|
38
|
-
|
39
|
-
|
38
|
+
accountID={accountID}
|
39
|
+
account={account}
|
40
40
|
initExpanded={index === 0}
|
41
41
|
/>
|
42
42
|
);
|
@@ -50,10 +50,11 @@ AccountList.propTypes = {
|
|
50
50
|
startAccountStatusPolling: PropTypes.func,
|
51
51
|
stopAccountStatusPolling: PropTypes.func,
|
52
52
|
pollingProcessID: PropTypes.number,
|
53
|
-
|
53
|
+
account: PropTypes.shape({
|
54
54
|
generate_report_status: PropTypes.string,
|
55
55
|
upload_report_status: PropTypes.string,
|
56
56
|
}),
|
57
|
+
accounts: PropTypes.object,
|
57
58
|
error: PropTypes.string,
|
58
59
|
};
|
59
60
|
|
@@ -62,10 +63,11 @@ AccountList.defaultProps = {
|
|
62
63
|
startAccountStatusPolling: noop,
|
63
64
|
stopAccountStatusPolling: noop,
|
64
65
|
pollingProcessID: 0,
|
65
|
-
|
66
|
+
account: {
|
66
67
|
generate_report_status: 'unknown',
|
67
68
|
upload_report_status: 'unknown',
|
68
69
|
},
|
70
|
+
accounts: {},
|
69
71
|
error: '',
|
70
72
|
};
|
71
73
|
|
@@ -10,12 +10,12 @@ import {
|
|
10
10
|
export const fetchAccountsStatus = () => async dispatch => {
|
11
11
|
try {
|
12
12
|
const {
|
13
|
-
data: {
|
14
|
-
} = await API.get('
|
13
|
+
data: { accounts },
|
14
|
+
} = await API.get('accounts');
|
15
15
|
dispatch({
|
16
16
|
type: INVENTORY_ACCOUNT_STATUS_POLLING,
|
17
17
|
payload: {
|
18
|
-
|
18
|
+
accounts,
|
19
19
|
},
|
20
20
|
});
|
21
21
|
} catch (error) {
|
@@ -7,7 +7,7 @@ import {
|
|
7
7
|
} from './AccountListConstants';
|
8
8
|
|
9
9
|
const initialState = Immutable({
|
10
|
-
|
10
|
+
accounts: {},
|
11
11
|
pollingProcessID: 0,
|
12
12
|
error: null,
|
13
13
|
});
|
@@ -17,7 +17,7 @@ export default (state = initialState, action) => {
|
|
17
17
|
payload: {
|
18
18
|
pollingProcessID,
|
19
19
|
error,
|
20
|
-
|
20
|
+
accounts,
|
21
21
|
accountID,
|
22
22
|
processStatusName,
|
23
23
|
} = {},
|
@@ -27,13 +27,13 @@ export default (state = initialState, action) => {
|
|
27
27
|
case INVENTORY_ACCOUNT_STATUS_POLLING:
|
28
28
|
return state.merge({
|
29
29
|
...state,
|
30
|
-
|
30
|
+
accounts,
|
31
31
|
error: null,
|
32
32
|
});
|
33
33
|
case INVENTORY_ACCOUNT_STATUS_POLLING_ERROR:
|
34
34
|
return state.merge({
|
35
35
|
...state,
|
36
|
-
|
36
|
+
accounts: {},
|
37
37
|
error,
|
38
38
|
});
|
39
39
|
case INVENTORY_ACCOUNT_STATUS_POLLING_START:
|
@@ -42,10 +42,10 @@ export default (state = initialState, action) => {
|
|
42
42
|
pollingProcessID,
|
43
43
|
});
|
44
44
|
case INVENTORY_PROCESS_RESTART:
|
45
|
-
return state.setIn(['
|
46
|
-
...state.
|
45
|
+
return state.setIn(['accounts'], {
|
46
|
+
...state.accounts,
|
47
47
|
[accountID]: {
|
48
|
-
...state.
|
48
|
+
...state.accounts[accountID],
|
49
49
|
[processStatusName]: 'Restarting...',
|
50
50
|
},
|
51
51
|
});
|
@@ -2,7 +2,7 @@ import { selectForemanInventoryUpload } from '../../ForemanInventoryUploadSelect
|
|
2
2
|
|
3
3
|
export const selectAccountsList = state =>
|
4
4
|
selectForemanInventoryUpload(state).accountsList;
|
5
|
-
export const
|
5
|
+
export const selectAccounts = state => selectAccountsList(state).accounts;
|
6
6
|
export const selectPollingProcessID = state =>
|
7
7
|
selectAccountsList(state).pollingProcessID;
|
8
8
|
export const selectError = state => selectAccountsList(state).error;
|
data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/ListItem.fixtures.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
export const props = {
|
1
|
+
export const props = { accountID: '1' };
|
@@ -4,34 +4,36 @@ import PropTypes from 'prop-types';
|
|
4
4
|
import ListItemStatus from '../ListItemStatus';
|
5
5
|
import Dashboard from '../../../Dashboard';
|
6
6
|
|
7
|
-
const ListItem = ({
|
7
|
+
const ListItem = ({ accountID, account, initExpanded }) => (
|
8
8
|
<ListView.Item
|
9
9
|
leftContent={<ListView.Icon name="user" />}
|
10
|
-
heading={
|
10
|
+
heading={account.label}
|
11
11
|
additionalInfo={[
|
12
|
-
<ListItemStatus key={`${
|
12
|
+
<ListItemStatus key={`${accountID}_status`} account={account} />,
|
13
13
|
]}
|
14
14
|
stacked
|
15
15
|
hideCloseIcon
|
16
16
|
initExpanded={initExpanded}
|
17
17
|
>
|
18
|
-
<Dashboard accountID={
|
18
|
+
<Dashboard accountID={accountID} account={account} />
|
19
19
|
</ListView.Item>
|
20
20
|
);
|
21
21
|
|
22
22
|
ListItem.propTypes = {
|
23
|
-
|
24
|
-
|
23
|
+
accountID: PropTypes.string.isRequired,
|
24
|
+
account: PropTypes.shape({
|
25
25
|
generate_report_status: PropTypes.string,
|
26
26
|
upload_report_status: PropTypes.string,
|
27
|
+
label: PropTypes.string,
|
27
28
|
}),
|
28
29
|
initExpanded: PropTypes.bool,
|
29
30
|
};
|
30
31
|
|
31
32
|
ListItem.defaultProps = {
|
32
|
-
|
33
|
+
account: {
|
33
34
|
generate_report_status: 'unknown',
|
34
35
|
upload_report_status: 'unknown',
|
36
|
+
label: 'default_org_name',
|
35
37
|
},
|
36
38
|
initExpanded: false,
|
37
39
|
};
|
@@ -6,9 +6,10 @@ exports[`ListItem rendering render with Props 1`] = `
|
|
6
6
|
additionalInfo={
|
7
7
|
Array [
|
8
8
|
<ListItemStatus
|
9
|
-
|
9
|
+
account={
|
10
10
|
Object {
|
11
11
|
"generate_report_status": "unknown",
|
12
|
+
"label": "default_org_name",
|
12
13
|
"upload_report_status": "unknown",
|
13
14
|
}
|
14
15
|
}
|
@@ -19,7 +20,7 @@ exports[`ListItem rendering render with Props 1`] = `
|
|
19
20
|
compoundExpand={false}
|
20
21
|
compoundExpanded={false}
|
21
22
|
description={null}
|
22
|
-
heading="
|
23
|
+
heading="default_org_name"
|
23
24
|
hideCloseIcon={true}
|
24
25
|
initExpanded={false}
|
25
26
|
leftContent={
|
@@ -36,13 +37,14 @@ exports[`ListItem rendering render with Props 1`] = `
|
|
36
37
|
stacked={true}
|
37
38
|
>
|
38
39
|
<Connect(Dashboard)
|
39
|
-
|
40
|
-
statuses={
|
40
|
+
account={
|
41
41
|
Object {
|
42
42
|
"generate_report_status": "unknown",
|
43
|
+
"label": "default_org_name",
|
43
44
|
"upload_report_status": "unknown",
|
44
45
|
}
|
45
46
|
}
|
47
|
+
accountID="1"
|
46
48
|
/>
|
47
49
|
</ListViewItem>
|
48
50
|
`;
|
@@ -5,12 +5,12 @@ import { translate as __ } from 'foremanReact/common/I18n';
|
|
5
5
|
import './listItemStatus.scss';
|
6
6
|
import { getStatusIconByRegex } from './ListItemStatusHelper';
|
7
7
|
|
8
|
-
const ListItemStatus = ({
|
8
|
+
const ListItemStatus = ({ account }) => {
|
9
9
|
const generatingStatusIcon = getStatusIconByRegex(
|
10
|
-
|
10
|
+
account.generate_report_status
|
11
11
|
);
|
12
12
|
const uploadingStatusIcon = getStatusIconByRegex(
|
13
|
-
|
13
|
+
account.upload_report_status
|
14
14
|
);
|
15
15
|
return (
|
16
16
|
<Grid className="status">
|
@@ -27,14 +27,14 @@ const ListItemStatus = ({ statuses }) => {
|
|
27
27
|
};
|
28
28
|
|
29
29
|
ListItemStatus.propTypes = {
|
30
|
-
|
30
|
+
account: PropTypes.shape({
|
31
31
|
generate_report_status: PropTypes.string,
|
32
32
|
upload_report_status: PropTypes.string,
|
33
33
|
}),
|
34
34
|
};
|
35
35
|
|
36
36
|
ListItemStatus.defaultProps = {
|
37
|
-
|
37
|
+
account: {
|
38
38
|
generate_report_status: 'unknown',
|
39
39
|
uploupload_report_statusading: 'unknown',
|
40
40
|
},
|