katello 4.5.0.rc1 → 4.5.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of katello might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/app/assets/javascripts/katello/hosts/activation_key_edit.js +9 -2
- data/app/controllers/katello/api/registry/registry_proxies_controller.rb +3 -0
- data/app/controllers/katello/api/v2/alternate_content_sources_bulk_actions_controller.rb +44 -0
- data/app/controllers/katello/api/v2/alternate_content_sources_controller.rb +29 -6
- data/app/controllers/katello/api/v2/content_view_components_controller.rb +1 -1
- data/app/controllers/katello/api/v2/content_view_repositories_controller.rb +1 -1
- data/app/controllers/katello/api/v2/repositories_controller.rb +2 -8
- data/app/lib/actions/katello/alternate_content_source/refresh.rb +27 -0
- data/app/lib/actions/katello/cdn_configuration/update.rb +1 -1
- data/app/lib/actions/katello/content_view/publish.rb +1 -1
- data/app/lib/actions/katello/organization/manifest_refresh.rb +1 -1
- data/app/lib/actions/pulp3/alternate_content_source/delete.rb +2 -2
- data/app/lib/actions/pulp3/alternate_content_source/delete_remote.rb +2 -2
- data/app/lib/actions/pulp3/alternate_content_source/refresh.rb +23 -0
- data/app/lib/actions/pulp3/alternate_content_source/update.rb +2 -2
- data/app/lib/actions/pulp3/alternate_content_source/update_remote.rb +2 -2
- data/app/lib/actions/pulp3/orchestration/alternate_content_source/create.rb +0 -2
- data/app/lib/actions/pulp3/orchestration/alternate_content_source/refresh.rb +15 -0
- data/app/lib/actions/pulp3/orchestration/alternate_content_source/update.rb +0 -2
- data/app/lib/actions/pulp3/repository/refresh_distribution.rb +1 -4
- data/app/lib/actions/pulp3/repository/save_artifact.rb +1 -1
- data/app/lib/actions/pulp3/repository/save_distribution_references.rb +0 -2
- data/app/models/katello/alternate_content_source.rb +5 -0
- data/app/services/katello/pulp3/alternate_content_source.rb +6 -0
- data/app/services/katello/pulp3/content_view_version/metadata_map.rb +1 -1
- data/app/services/katello/pulp3/repository.rb +29 -1
- data/app/views/katello/api/v2/alternate_content_sources/base.json.rabl +10 -1
- data/app/views/katello/api/v2/content_facet/show.json.rabl +12 -0
- data/app/views/katello/api/v2/repository_sets/show.json.rabl +4 -0
- data/config/routes/api/v2.rb +16 -4
- data/db/migrate/20220303160220_remove_duplicate_errata.rb +1 -1
- data/db/migrate/20220428203334_add_last_refreshed_to_katello_alternate_content_sources.rb +5 -0
- data/engines/bastion_katello/app/assets/javascripts/bastion_katello/capsule-content/capsule-content.controller.js +1 -1
- data/lib/katello/permission_creator.rb +4 -2
- data/lib/katello/tasks/refresh_alternate_content_sources.rake +10 -0
- data/lib/katello/version.rb +1 -1
- data/webpack/components/Bookmark/index.js +22 -14
- data/webpack/components/Search/Search.js +4 -0
- data/webpack/components/Table/MainTable.scss +5 -1
- data/webpack/components/Table/TableWrapper.js +5 -1
- data/webpack/components/TypeAhead/TypeAhead.js +4 -0
- data/webpack/components/TypeAhead/pf4Search/TypeAheadSearch.js +2 -0
- data/webpack/components/extensions/HostDetails/Cards/ContentViewDetailsCard/ChangeHostCVModal.js +2 -8
- data/webpack/components/extensions/HostDetails/Cards/ContentViewDetailsCard/ContentViewDetailsCard.js +41 -11
- data/webpack/components/extensions/HostDetails/Cards/HostCollectionsCard/HostCollectionsActions.js +2 -2
- data/webpack/components/extensions/HostDetails/Cards/HostCollectionsCard/HostCollectionsCard.js +32 -13
- data/webpack/components/extensions/HostDetails/Cards/HostCollectionsCard/__tests__/hostCollectionsCard.test.js +8 -0
- data/webpack/components/extensions/HostDetails/DetailsTabCards/RecentCommunicationCardExtensions.js +37 -0
- data/webpack/components/extensions/HostDetails/HostDetailsActions.js +11 -0
- data/webpack/components/extensions/HostDetails/Tabs/ContentTab/SecondaryTabsRoutes.js +4 -0
- data/webpack/components/extensions/HostDetails/Tabs/ContentTab/constants.js +2 -0
- data/webpack/components/extensions/HostDetails/Tabs/ContentTab/index.js +6 -1
- data/webpack/components/extensions/HostDetails/Tabs/ErrataTab/ErrataTab.js +120 -51
- data/webpack/components/extensions/HostDetails/Tabs/ModuleStreamsTab/ModuleStreamsTab.js +71 -37
- data/webpack/components/extensions/HostDetails/Tabs/PackagesTab/PackageInstallModal.js +4 -3
- data/webpack/components/extensions/HostDetails/Tabs/PackagesTab/PackagesTab.js +117 -40
- data/webpack/components/extensions/HostDetails/Tabs/RemoteExecutionActions.js +25 -3
- data/webpack/components/extensions/HostDetails/Tabs/RemoteExecutionHooks.js +85 -0
- data/webpack/components/extensions/HostDetails/Tabs/RepositorySetsTab/RepositorySetsTab.js +87 -33
- data/webpack/components/extensions/HostDetails/Tabs/TracesTab/EnableTracerModal.js +14 -7
- data/webpack/components/extensions/HostDetails/Tabs/TracesTab/HostTracesActions.js +2 -1
- data/webpack/components/extensions/HostDetails/Tabs/TracesTab/TracesEnabler.js +104 -0
- data/webpack/components/extensions/HostDetails/Tabs/TracesTab/TracesTab.js +92 -51
- data/webpack/components/extensions/HostDetails/Tabs/__tests__/errataTab.test.js +13 -23
- data/webpack/components/extensions/HostDetails/Tabs/{ModuleStreamsTab/__tests__/modules.fixtures.json → __tests__/moduleStreams.fixtures.json} +0 -0
- data/webpack/components/extensions/HostDetails/Tabs/{ModuleStreamsTab/__tests__ → __tests__}/moduleStreamsTab.test.js +13 -6
- data/webpack/components/extensions/HostDetails/Tabs/__tests__/packageInstallModal.test.js +21 -15
- data/webpack/components/extensions/HostDetails/Tabs/__tests__/packagesTab.test.js +8 -0
- data/webpack/components/extensions/HostDetails/Tabs/__tests__/repositorySets.fixtures.json +4 -1
- data/webpack/components/extensions/HostDetails/Tabs/__tests__/repositorySetsTab.test.js +26 -0
- data/webpack/components/extensions/HostDetails/Tabs/__tests__/tracesTab.test.js +7 -4
- data/webpack/components/extensions/HostDetails/hostDetailsHelpers.js +18 -0
- data/webpack/global_index.js +2 -2
- data/webpack/redux/actions/RedHatRepositories/helpers.js +5 -1
- data/webpack/scenes/AlternateContentSources/ACSActions.js +13 -1
- data/webpack/scenes/AlternateContentSources/ACSConstants.js +14 -0
- data/webpack/scenes/AlternateContentSources/ACSSelectors.js +10 -1
- data/webpack/scenes/AlternateContentSources/Create/ACSCreateContext.js +4 -0
- data/webpack/scenes/AlternateContentSources/Create/ACSCreateWizard.js +160 -0
- data/webpack/scenes/AlternateContentSources/Create/Steps/ACSCreateFinish.js +79 -0
- data/webpack/scenes/AlternateContentSources/Create/Steps/ACSCredentials.js +199 -0
- data/webpack/scenes/AlternateContentSources/Create/Steps/ACSReview.js +104 -0
- data/webpack/scenes/AlternateContentSources/Create/Steps/ACSSmartProxies.js +41 -0
- data/webpack/scenes/AlternateContentSources/Create/Steps/AcsUrlPaths.js +71 -0
- data/webpack/scenes/AlternateContentSources/Create/Steps/NameACS.js +57 -0
- data/webpack/scenes/AlternateContentSources/Create/Steps/SelectSource.js +77 -0
- data/webpack/scenes/AlternateContentSources/Create/__tests__/acsCreate.test.js +149 -0
- data/webpack/scenes/AlternateContentSources/Create/__tests__/acsCreateData.fixtures.json +3 -0
- data/webpack/scenes/AlternateContentSources/Create/__tests__/contentCredentials.fixtures.json +69 -0
- data/webpack/scenes/AlternateContentSources/Create/__tests__/smartProxy.fixtures.json +65 -0
- data/webpack/scenes/AlternateContentSources/MainTable/ACSTable.js +33 -23
- data/webpack/scenes/ContentCredentials/ContentCredentialSelectors.js +4 -1
- data/webpack/scenes/ContentViews/Create/CreateContentViewForm.js +2 -2
- data/webpack/scenes/ContentViews/Create/__tests__/createContentView.test.js +1 -1
- data/webpack/scenes/ContentViews/Details/ComponentContentViews/ComponentContentViewAddModal.js +1 -1
- data/webpack/scenes/ContentViews/Details/ComponentContentViews/ComponentContentViewBulkAddModal.js +2 -2
- data/webpack/scenes/ContentViews/Details/ComponentContentViews/__tests__/contentViewComponents.test.js +4 -4
- data/webpack/scenes/ContentViews/Details/ContentViewInfo.js +1 -1
- data/webpack/scenes/ContentViews/__tests__/contentViewPage.test.js +4 -4
- data/webpack/scenes/ContentViews/components/ContentViewIcon.js +1 -1
- data/webpack/scenes/ContentViews/components/ContentViewsCounter.js +1 -1
- data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPaths.js +1 -1
- data/webpack/scenes/ContentViews/expansions/DetailsExpansion.js +2 -2
- data/webpack/scenes/ContentViews/expansions/RelatedContentViewComponentsModal.js +2 -2
- data/webpack/scenes/ContentViews/expansions/__tests__/contentViewComponentsModal.test.js +1 -1
- data/webpack/scenes/RedHatRepositories/components/Search.js +4 -4
- data/webpack/scenes/SmartProxy/SmartProxyContentActions.js +9 -2
- data/webpack/scenes/SmartProxy/SmartProxyContentConstants.js +1 -1
- data/webpack/scenes/SmartProxy/SmartProxyContentSelectors.js +10 -1
- data/webpack/scenes/Tasks/helpers.js +30 -3
- metadata +34 -14
- data/db/seeds.d/107-enable_dynflow.rb +0 -8
- data/webpack/components/extensions/HostDetails/Tabs/TracesTab/EnableTracerEmptyState.js +0 -42
@@ -1,8 +1,9 @@
|
|
1
1
|
import React, { useCallback, useState } from 'react';
|
2
2
|
import { useSelector, useDispatch } from 'react-redux';
|
3
3
|
import {
|
4
|
-
|
4
|
+
Split, SplitItem, ActionList, ActionListItem, Dropdown,
|
5
5
|
DropdownItem, KebabToggle, Skeleton, Tooltip, ToggleGroup, ToggleGroupItem,
|
6
|
+
DropdownToggle, DropdownToggleAction,
|
6
7
|
} from '@patternfly/react-core';
|
7
8
|
import { TimesIcon, CheckIcon } from '@patternfly/react-icons';
|
8
9
|
import {
|
@@ -33,8 +34,18 @@ import { installErrata } from '../RemoteExecutionActions';
|
|
33
34
|
import { errataInstallUrl } from '../customizedRexUrlHelpers';
|
34
35
|
import './ErrataTab.scss';
|
35
36
|
import hostIdNotReady from '../../HostDetailsActions';
|
36
|
-
import { defaultRemoteActionMethod,
|
37
|
+
import { defaultRemoteActionMethod,
|
38
|
+
hasRequiredPermissions as can,
|
39
|
+
missingRequiredPermissions as cannot,
|
40
|
+
KATELLO_AGENT,
|
41
|
+
userPermissionsFromHostDetails } from '../../hostDetailsHelpers';
|
37
42
|
import SortableColumnHeaders from '../../../../Table/components/SortableColumnHeaders';
|
43
|
+
import { useRexJobPolling } from '../RemoteExecutionHooks';
|
44
|
+
|
45
|
+
const recalculateApplicability = ['edit_hosts'];
|
46
|
+
const invokeRexJobs = ['create_job_invocations'];
|
47
|
+
const doKatelloAgentActions = ['edit_hosts'];
|
48
|
+
const createBookmarks = ['create_bookmarks'];
|
38
49
|
|
39
50
|
export const ErrataTab = () => {
|
40
51
|
const hostDetails = useSelector(state => selectAPIResponse(state, 'HOST_DETAILS'));
|
@@ -44,6 +55,12 @@ export const ErrataTab = () => {
|
|
44
55
|
content_facet_attributes: contentFacetAttributes,
|
45
56
|
errata_status: errataStatus,
|
46
57
|
} = hostDetails;
|
58
|
+
const userPermissions = userPermissionsFromHostDetails({ hostDetails });
|
59
|
+
const showRecalculate =
|
60
|
+
can(
|
61
|
+
recalculateApplicability,
|
62
|
+
userPermissions,
|
63
|
+
);
|
47
64
|
const contentFacet = propsToCamelCase(contentFacetAttributes ?? {});
|
48
65
|
const dispatch = useDispatch();
|
49
66
|
const toggleGroupStates = ['all', 'installable'];
|
@@ -67,6 +84,12 @@ export const ErrataTab = () => {
|
|
67
84
|
= useState(PARAM_TO_FRIENDLY_NAME[initialSeverity] ?? ERRATA_SEVERITY);
|
68
85
|
const activeFilters = [errataTypeSelected, errataSeveritySelected];
|
69
86
|
const defaultFilters = [ERRATA_TYPE, ERRATA_SEVERITY];
|
87
|
+
|
88
|
+
const [isActionOpen, setIsActionOpen] = useState(false);
|
89
|
+
const onActionToggle = () => {
|
90
|
+
setIsActionOpen(prev => !prev);
|
91
|
+
};
|
92
|
+
|
70
93
|
const allUpToDate = errataStatus === 0;
|
71
94
|
const emptyContentTitle = allUpToDate ? __('All errata up-to-date') : __('This host has errata that are applicable, but not installable.');
|
72
95
|
const emptyContentBody = allUpToDate ? __('No action is needed because there are no applicable errata for this host.') : __("You may want to check the host's content view and lifecycle environment.");
|
@@ -136,8 +159,29 @@ export const ErrataTab = () => {
|
|
136
159
|
initialSearchQuery: searchParam || '',
|
137
160
|
});
|
138
161
|
|
139
|
-
const
|
140
|
-
|
162
|
+
const installErrataAction = () => installErrata({
|
163
|
+
hostname, search: fetchBulkParams(),
|
164
|
+
});
|
165
|
+
const {
|
166
|
+
triggerJobStart: triggerBulkApply, lastCompletedJob: lastCompletedBulkApply,
|
167
|
+
isPolling: isBulkApplyInProgress,
|
168
|
+
} = useRexJobPolling(installErrataAction);
|
169
|
+
|
170
|
+
const installErratumAction = id => installErrata({
|
171
|
+
hostname,
|
172
|
+
search: errataSearchQuery(id),
|
173
|
+
});
|
174
|
+
|
175
|
+
const {
|
176
|
+
triggerJobStart: triggerApply, lastCompletedJob: lastCompletedApply,
|
177
|
+
isPolling: isApplyInProgress,
|
178
|
+
} = useRexJobPolling(installErratumAction);
|
179
|
+
|
180
|
+
const actionInProgress = (isApplyInProgress || isBulkApplyInProgress);
|
181
|
+
const disabledReason = __('A remote execution job is in progress');
|
182
|
+
|
183
|
+
const tdSelect = useCallback((errataId, rowIndex, rexJobInProgress) => ({
|
184
|
+
disable: rexJobInProgress || !isSelectable(errataId),
|
141
185
|
isSelected: isSelected(errataId),
|
142
186
|
onSelect: (event, selected) => selectOne(selected, errataId),
|
143
187
|
rowIndex,
|
@@ -146,21 +190,11 @@ export const ErrataTab = () => {
|
|
146
190
|
|
147
191
|
if (!hostId) return <Skeleton />;
|
148
192
|
|
149
|
-
const applyErratumViaRemoteExecution = id =>
|
150
|
-
hostname,
|
151
|
-
search: errataSearchQuery(id),
|
152
|
-
}));
|
193
|
+
const applyErratumViaRemoteExecution = id => triggerApply(id);
|
153
194
|
|
154
195
|
const applyViaRemoteExecution = () => {
|
155
|
-
|
156
|
-
|
157
|
-
}));
|
158
|
-
|
159
|
-
const params = { page: metadata.page, per_page: metadata.per_page, search: metadata.search };
|
160
|
-
dispatch(getInstallableErrata(
|
161
|
-
hostId,
|
162
|
-
{ ...params, include_applicable: toggleGroupState === ALL },
|
163
|
-
));
|
196
|
+
triggerBulkApply();
|
197
|
+
selectNone();
|
164
198
|
};
|
165
199
|
|
166
200
|
const bulkCustomizedRexUrl = () => errataInstallUrl({
|
@@ -185,6 +219,10 @@ export const ErrataTab = () => {
|
|
185
219
|
));
|
186
220
|
|
187
221
|
const defaultRemoteAction = defaultRemoteActionMethod({ hostDetails });
|
222
|
+
const showActions = defaultRemoteAction === KATELLO_AGENT ?
|
223
|
+
can(doKatelloAgentActions, userPermissions) :
|
224
|
+
can(invokeRexJobs, userPermissions);
|
225
|
+
|
188
226
|
const apply = () => {
|
189
227
|
if (defaultRemoteAction === KATELLO_AGENT) {
|
190
228
|
applyByKatelloAgent();
|
@@ -193,7 +231,10 @@ export const ErrataTab = () => {
|
|
193
231
|
}
|
194
232
|
};
|
195
233
|
|
196
|
-
const
|
234
|
+
const readOnlyBookmarks =
|
235
|
+
cannot(createBookmarks, userPermissionsFromHostDetails({ hostDetails }));
|
236
|
+
|
237
|
+
const dropdownKebabItems = [
|
197
238
|
<DropdownItem
|
198
239
|
aria-label="bulk_add"
|
199
240
|
key="bulk_add"
|
@@ -204,20 +245,7 @@ export const ErrataTab = () => {
|
|
204
245
|
</DropdownItem>,
|
205
246
|
];
|
206
247
|
|
207
|
-
|
208
|
-
dropdownItems.push((
|
209
|
-
<DropdownItem
|
210
|
-
aria-label="apply_via_katello_agent"
|
211
|
-
key="apply_via_katello_agent"
|
212
|
-
component="button"
|
213
|
-
onClick={applyByKatelloAgent}
|
214
|
-
isDisabled={selectedCount === 0}
|
215
|
-
>
|
216
|
-
{__('Apply via Katello agent')}
|
217
|
-
</DropdownItem>));
|
218
|
-
}
|
219
|
-
|
220
|
-
dropdownItems.push((
|
248
|
+
const dropdownItems = [
|
221
249
|
<DropdownItem
|
222
250
|
aria-label="apply_via_remote_execution"
|
223
251
|
key="apply_via_remote_execution"
|
@@ -226,9 +254,7 @@ export const ErrataTab = () => {
|
|
226
254
|
isDisabled={selectedCount === 0}
|
227
255
|
>
|
228
256
|
{__('Apply via remote execution')}
|
229
|
-
</DropdownItem
|
230
|
-
|
231
|
-
dropdownItems.push((
|
257
|
+
</DropdownItem>,
|
232
258
|
<DropdownItem
|
233
259
|
aria-label="apply_via_customized_remote_execution"
|
234
260
|
key="apply_via_customized_remote_execution"
|
@@ -237,7 +263,21 @@ export const ErrataTab = () => {
|
|
237
263
|
isDisabled={selectedCount === 0}
|
238
264
|
>
|
239
265
|
{__('Apply via customized remote execution')}
|
240
|
-
</DropdownItem
|
266
|
+
</DropdownItem>,
|
267
|
+
];
|
268
|
+
|
269
|
+
if (defaultRemoteAction === KATELLO_AGENT) {
|
270
|
+
dropdownItems.unshift((
|
271
|
+
<DropdownItem
|
272
|
+
aria-label="apply_via_katello_agent"
|
273
|
+
key="apply_via_katello_agent"
|
274
|
+
component="button"
|
275
|
+
onClick={applyByKatelloAgent}
|
276
|
+
isDisabled={selectedCount === 0}
|
277
|
+
>
|
278
|
+
{__('Apply via Katello agent')}
|
279
|
+
</DropdownItem>));
|
280
|
+
}
|
241
281
|
|
242
282
|
const handleErrataTypeSelected = newType => setErrataTypeSelected((prevType) => {
|
243
283
|
if (prevType === newType) {
|
@@ -253,27 +293,47 @@ export const ErrataTab = () => {
|
|
253
293
|
return newSeverity;
|
254
294
|
});
|
255
295
|
|
256
|
-
const actionButtons = (
|
296
|
+
const actionButtons = showActions ? (
|
257
297
|
<>
|
258
298
|
<Split hasGutter>
|
259
299
|
<SplitItem>
|
260
300
|
<ActionList isIconList>
|
261
301
|
<ActionListItem>
|
262
|
-
<
|
302
|
+
<Dropdown
|
303
|
+
aria-label="errata_dropdown"
|
304
|
+
toggle={
|
305
|
+
<DropdownToggle
|
306
|
+
aria-label="expand_errata_toggle"
|
307
|
+
splitButtonItems={[
|
308
|
+
<DropdownToggleAction key="action" aria-label="bulk_actions" onClick={apply}>
|
309
|
+
{__('Apply')}
|
310
|
+
</DropdownToggleAction>,
|
311
|
+
]}
|
312
|
+
splitButtonVariant="action"
|
313
|
+
toggleVariant="primary"
|
314
|
+
onToggle={onActionToggle}
|
315
|
+
isDisabled={selectedCount === 0}
|
316
|
+
/>
|
317
|
+
}
|
318
|
+
isOpen={isActionOpen}
|
319
|
+
dropdownItems={dropdownItems}
|
320
|
+
/>
|
263
321
|
</ActionListItem>
|
322
|
+
{showRecalculate &&
|
264
323
|
<ActionListItem>
|
265
324
|
<Dropdown
|
266
|
-
toggle={<KebabToggle aria-label="
|
325
|
+
toggle={<KebabToggle aria-label="bulk_actions_kebab" onToggle={toggleBulkAction} />}
|
267
326
|
isOpen={isBulkActionOpen}
|
268
327
|
isPlain
|
269
|
-
dropdownItems={
|
328
|
+
dropdownItems={dropdownKebabItems}
|
270
329
|
/>
|
271
330
|
</ActionListItem>
|
331
|
+
}
|
272
332
|
</ActionList>
|
273
333
|
</SplitItem>
|
274
334
|
</Split>
|
275
335
|
</>
|
276
|
-
);
|
336
|
+
) : null;
|
277
337
|
|
278
338
|
const hostIsNonLibrary = (
|
279
339
|
contentFacet?.contentViewDefault === false && contentFacet.lifecycleEnvironmentLibrary === false
|
@@ -352,15 +412,17 @@ export const ErrataTab = () => {
|
|
352
412
|
ouiaId="host-errata-table"
|
353
413
|
additionalListeners={[
|
354
414
|
hostId, toggleGroupState, errataTypeSelected,
|
355
|
-
errataSeveritySelected, activeSortColumn, activeSortDirection
|
415
|
+
errataSeveritySelected, activeSortColumn, activeSortDirection,
|
416
|
+
lastCompletedApply, lastCompletedBulkApply]}
|
356
417
|
fetchItems={fetchItems}
|
357
418
|
bookmarkController="katello_errata"
|
419
|
+
readOnlyBookmarks={readOnlyBookmarks}
|
358
420
|
autocompleteEndpoint={`/hosts/${hostId}/errata/auto_complete_search`}
|
359
421
|
foremanApiAutoComplete
|
360
422
|
rowsCount={results?.length}
|
361
423
|
variant={TableVariant.compact}
|
362
424
|
{...selectAll}
|
363
|
-
displaySelectAllCheckbox
|
425
|
+
displaySelectAllCheckbox={showActions}
|
364
426
|
>
|
365
427
|
<Thead>
|
366
428
|
<Tr>
|
@@ -391,6 +453,7 @@ export const ErrataTab = () => {
|
|
391
453
|
{
|
392
454
|
title: __('Apply via remote execution'),
|
393
455
|
onClick: () => applyErratumViaRemoteExecution(errataId),
|
456
|
+
isDisabled: actionInProgress,
|
394
457
|
},
|
395
458
|
{
|
396
459
|
title: __('Apply via customized remote execution'),
|
@@ -425,7 +488,12 @@ export const ErrataTab = () => {
|
|
425
488
|
onToggle: (_event, _rInx, isOpen) => expandedErrata.onToggle(isOpen, id),
|
426
489
|
}}
|
427
490
|
/>
|
428
|
-
|
491
|
+
{showActions ? (
|
492
|
+
<Td
|
493
|
+
select={tdSelect(errataId, rowIndex, actionInProgress)}
|
494
|
+
title={actionInProgress && disabledReason}
|
495
|
+
/>
|
496
|
+
) : null}
|
429
497
|
<Td>
|
430
498
|
<a href={urlBuilder(`errata/${id}`, '')}>{errataId}</a>
|
431
499
|
</Td>
|
@@ -449,13 +517,14 @@ export const ErrataTab = () => {
|
|
449
517
|
</Td>
|
450
518
|
<Td><TableText wrapModifier="truncate">{title}</TableText></Td>
|
451
519
|
<Td key={publishedAt}><IsoDate date={publishedAt} /></Td>
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
520
|
+
{showActions ? (
|
521
|
+
<Td
|
522
|
+
key={`rowActions-${id}`}
|
523
|
+
actions={{
|
524
|
+
items: rowActions,
|
525
|
+
}}
|
526
|
+
/>
|
527
|
+
) : null}
|
459
528
|
</Tr>
|
460
529
|
<Tr key="child_row" isExpanded={isExpanded}>
|
461
530
|
{isExpanded && (
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import React, { useCallback, useState } from 'react';
|
2
|
-
import { useSelector
|
2
|
+
import { useSelector } from 'react-redux';
|
3
3
|
import { FormattedMessage } from 'react-intl';
|
4
4
|
import { translate as __ } from 'foremanReact/common/I18n';
|
5
5
|
import { Skeleton,
|
@@ -7,6 +7,7 @@ import { Skeleton,
|
|
7
7
|
Button,
|
8
8
|
Split,
|
9
9
|
SplitItem,
|
10
|
+
Spinner,
|
10
11
|
Checkbox,
|
11
12
|
Dropdown,
|
12
13
|
Text,
|
@@ -39,6 +40,13 @@ import {
|
|
39
40
|
} from './ModuleStreamsConstants';
|
40
41
|
import { moduleStreamAction } from '../RemoteExecutionActions';
|
41
42
|
import { katelloModuleStreamActionUrl } from '../customizedRexUrlHelpers';
|
43
|
+
import { useRexJobPolling } from '../RemoteExecutionHooks';
|
44
|
+
import {
|
45
|
+
hasRequiredPermissions as can,
|
46
|
+
missingRequiredPermissions as cannot,
|
47
|
+
userPermissionsFromHostDetails,
|
48
|
+
} from '../../hostDetailsHelpers';
|
49
|
+
|
42
50
|
|
43
51
|
const EnabledIcon = ({ streamText, streamInstallStatus, upgradable }) => {
|
44
52
|
switch (true) {
|
@@ -99,14 +107,9 @@ HostInstalledProfiles.propTypes = {
|
|
99
107
|
installedProfiles: PropTypes.arrayOf(PropTypes.string).isRequired,
|
100
108
|
};
|
101
109
|
|
102
|
-
const performModuleStreamAction = (hostName, action, moduleSpec, dispatch) => {
|
103
|
-
dispatch(moduleStreamAction({ hostname: hostName, action, moduleSpec }));
|
104
|
-
};
|
105
|
-
|
106
110
|
const ModuleActionConfirmationModal = ({
|
107
|
-
|
111
|
+
hostname, action, moduleSpec, actionModalOpen, setActionModalOpen, triggerModuleStreamAction,
|
108
112
|
}) => {
|
109
|
-
const dispatch = useDispatch();
|
110
113
|
let title;
|
111
114
|
let body;
|
112
115
|
let confirmText;
|
@@ -163,7 +166,7 @@ const ModuleActionConfirmationModal = ({
|
|
163
166
|
aria-label="confirm-module-action"
|
164
167
|
key="confirm-module-action"
|
165
168
|
onClick={() => {
|
166
|
-
|
169
|
+
triggerModuleStreamAction({ hostname, action, moduleSpec });
|
167
170
|
setActionModalOpen(false);
|
168
171
|
}}
|
169
172
|
>
|
@@ -188,21 +191,26 @@ const ModuleActionConfirmationModal = ({
|
|
188
191
|
};
|
189
192
|
|
190
193
|
ModuleActionConfirmationModal.propTypes = {
|
191
|
-
|
194
|
+
hostname: PropTypes.string.isRequired,
|
192
195
|
action: PropTypes.string.isRequired,
|
193
196
|
moduleSpec: PropTypes.string.isRequired,
|
194
197
|
actionModalOpen: PropTypes.bool.isRequired,
|
195
198
|
setActionModalOpen: PropTypes.func.isRequired,
|
199
|
+
triggerModuleStreamAction: PropTypes.func.isRequired,
|
196
200
|
};
|
197
201
|
|
202
|
+
const invokeRexJobs = ['create_job_invocations'];
|
203
|
+
const createBookmarks = ['create_bookmarks'];
|
204
|
+
|
198
205
|
export const ModuleStreamsTab = () => {
|
199
|
-
const
|
206
|
+
const hostDetails = useSelector(selectHostDetails);
|
207
|
+
const { id: hostId, name: hostname } = hostDetails;
|
208
|
+
const showActions = can(invokeRexJobs, userPermissionsFromHostDetails({ hostDetails }));
|
200
209
|
const [useCustomizedRex, setUseCustomizedRex] = useState('');
|
201
210
|
const [dropdownOpen, setDropdownOpen] = useState('');
|
202
211
|
const [actionModalOpen, setActionModalOpen] = useState(false);
|
203
212
|
const [actionableModuleSpec, setActionableModuleSpec] = useState(null);
|
204
213
|
const [hostModuleStreamAction, setHostModuleStreamAction] = useState(null);
|
205
|
-
const dispatch = useDispatch();
|
206
214
|
|
207
215
|
const emptyContentTitle = __('This host does not have any Module streams.');
|
208
216
|
const emptyContentBody = __('Module streams will appear here when available.');
|
@@ -244,6 +252,18 @@ export const ModuleStreamsTab = () => {
|
|
244
252
|
initialSortColumnName: 'Name',
|
245
253
|
});
|
246
254
|
|
255
|
+
const {
|
256
|
+
triggerJobStart: triggerModuleStreamAction, lastCompletedJob: tableJobCompleted,
|
257
|
+
isPolling: isModuleStreamActionInProgress,
|
258
|
+
} = useRexJobPolling(moduleStreamAction);
|
259
|
+
|
260
|
+
const {
|
261
|
+
triggerJobStart: triggerConfirmModalAction, lastCompletedJob: confirmModalJobCompleted,
|
262
|
+
isPolling: isConfirmModalActionInProgress,
|
263
|
+
} = useRexJobPolling(moduleStreamAction);
|
264
|
+
|
265
|
+
const actionInProgress = (isModuleStreamActionInProgress || isConfirmModalActionInProgress);
|
266
|
+
|
247
267
|
const fetchItems = useCallback(
|
248
268
|
(params) => {
|
249
269
|
let extraParams = params;
|
@@ -279,7 +299,7 @@ export const ModuleStreamsTab = () => {
|
|
279
299
|
});
|
280
300
|
|
281
301
|
const customizedActionURL = (action, moduleSpec) =>
|
282
|
-
katelloModuleStreamActionUrl({ hostname
|
302
|
+
katelloModuleStreamActionUrl({ hostname, action, moduleSpec });
|
283
303
|
|
284
304
|
const response = useSelector(selectModuleStream);
|
285
305
|
const { results, ...metadata } = response;
|
@@ -297,11 +317,13 @@ export const ModuleStreamsTab = () => {
|
|
297
317
|
});
|
298
318
|
/* eslint-enable no-unused-vars */
|
299
319
|
|
320
|
+
const hideBookmarkActions =
|
321
|
+
cannot(createBookmarks, userPermissionsFromHostDetails({ hostDetails }));
|
322
|
+
|
300
323
|
if (!hostId) return <Skeleton />;
|
301
324
|
|
302
325
|
const activeFilters = [statusSelected, installStatusSelected];
|
303
326
|
const defaultFilters = [MODULE_STREAM_STATUS, MODULE_STREAM_INSTALLATION_STATUS];
|
304
|
-
|
305
327
|
return (
|
306
328
|
<div>
|
307
329
|
<div id="modulestreams-tab">
|
@@ -323,9 +345,11 @@ export const ModuleStreamsTab = () => {
|
|
323
345
|
}}
|
324
346
|
ouiaId="host-module-stream-table"
|
325
347
|
additionalListeners={[hostId, activeSortColumn, activeSortDirection,
|
326
|
-
statusSelected, installStatusSelected
|
348
|
+
statusSelected, installStatusSelected, confirmModalJobCompleted,
|
349
|
+
tableJobCompleted]}
|
327
350
|
fetchItems={fetchItems}
|
328
351
|
bookmarkController="katello_host_available_module_streams"
|
352
|
+
readOnlyBookmarks={hideBookmarkActions}
|
329
353
|
autocompleteEndpoint={`/hosts/${hostId}/module_streams/auto_complete_search`}
|
330
354
|
foremanApiAutoComplete
|
331
355
|
rowsCount={results?.length}
|
@@ -352,6 +376,11 @@ export const ModuleStreamsTab = () => {
|
|
352
376
|
setSelected={handleModuleStreamInstallationStatusSelected}
|
353
377
|
/>
|
354
378
|
</SplitItem>
|
379
|
+
{actionInProgress && (
|
380
|
+
<SplitItem style={{ alignSelf: 'center' }}>
|
381
|
+
<Spinner size="lg" style={{ marginTop: '2px' }} />
|
382
|
+
</SplitItem>
|
383
|
+
)}
|
355
384
|
</Split>
|
356
385
|
}
|
357
386
|
>
|
@@ -460,11 +489,11 @@ export const ModuleStreamsTab = () => {
|
|
460
489
|
key={`dropdownItem-enable-${id}`}
|
461
490
|
component="button"
|
462
491
|
onClick={() => {
|
463
|
-
|
492
|
+
triggerModuleStreamAction({ hostname, action: 'enable', moduleSpec });
|
464
493
|
setUseCustomizedRex('');
|
465
494
|
setDropdownOpen('');
|
466
495
|
}}
|
467
|
-
isDisabled={stateText(moduleStreamStatus) ===
|
496
|
+
isDisabled={actionInProgress || stateText(moduleStreamStatus) ===
|
468
497
|
HOST_MODULE_STREAM_STATUSES.ENABLED}
|
469
498
|
>
|
470
499
|
{__('Enable')}
|
@@ -480,7 +509,7 @@ export const ModuleStreamsTab = () => {
|
|
480
509
|
setUseCustomizedRex('');
|
481
510
|
setDropdownOpen('');
|
482
511
|
}}
|
483
|
-
isDisabled={stateText(moduleStreamStatus) !==
|
512
|
+
isDisabled={actionInProgress || stateText(moduleStreamStatus) !==
|
484
513
|
HOST_MODULE_STREAM_STATUSES.ENABLED}
|
485
514
|
>
|
486
515
|
{__('Disable')}
|
@@ -491,11 +520,11 @@ export const ModuleStreamsTab = () => {
|
|
491
520
|
key={`dropdownItem-install-${id}`}
|
492
521
|
component="button"
|
493
522
|
onClick={() => {
|
494
|
-
|
523
|
+
triggerModuleStreamAction({ hostname, action: 'install', moduleSpec });
|
495
524
|
setUseCustomizedRex('');
|
496
525
|
setDropdownOpen('');
|
497
526
|
}}
|
498
|
-
isDisabled={(upgradable ||
|
527
|
+
isDisabled={(actionInProgress || upgradable ||
|
499
528
|
(installedStatus !== INSTALLED_STATE.NOTINSTALLED) ||
|
500
529
|
!(stateText(moduleStreamStatus) === HOST_MODULE_STREAM_STATUSES.ENABLED ||
|
501
530
|
stateText(moduleStreamStatus) === HOST_MODULE_STREAM_STATUSES.DISABLED)
|
@@ -508,11 +537,11 @@ export const ModuleStreamsTab = () => {
|
|
508
537
|
key={`dropdownItem-update-${id}`}
|
509
538
|
component="button"
|
510
539
|
onClick={() => {
|
511
|
-
|
540
|
+
triggerModuleStreamAction({ hostname, action: 'update', moduleSpec });
|
512
541
|
setUseCustomizedRex('');
|
513
542
|
setDropdownOpen('');
|
514
543
|
}}
|
515
|
-
isDisabled={!upgradable}
|
544
|
+
isDisabled={actionInProgress || !upgradable}
|
516
545
|
>
|
517
546
|
{__('Update')}
|
518
547
|
</DropdownItem>,
|
@@ -527,6 +556,7 @@ export const ModuleStreamsTab = () => {
|
|
527
556
|
setUseCustomizedRex('');
|
528
557
|
setDropdownOpen('');
|
529
558
|
}}
|
559
|
+
isDisabled={actionInProgress}
|
530
560
|
>
|
531
561
|
{__('Reset')}
|
532
562
|
<InactiveText style={{ marginBottom: '1px' }} text={__('Reset to the default state')} />
|
@@ -542,6 +572,7 @@ export const ModuleStreamsTab = () => {
|
|
542
572
|
setUseCustomizedRex('');
|
543
573
|
setDropdownOpen('');
|
544
574
|
}}
|
575
|
+
isDisabled={actionInProgress}
|
545
576
|
>
|
546
577
|
{__('Remove')}
|
547
578
|
<InactiveText style={{ marginBottom: '1px' }} text={__('Uninstall and reset')} />
|
@@ -552,7 +583,7 @@ export const ModuleStreamsTab = () => {
|
|
552
583
|
<Tr key={`${id} ${index}`}>
|
553
584
|
<Td>
|
554
585
|
<a
|
555
|
-
href={`/module_streams?search=module_spec%3D${moduleSpec}+and+host%3D${
|
586
|
+
href={`/module_streams?search=module_spec%3D${moduleSpec}+and+host%3D${hostname}`}
|
556
587
|
>
|
557
588
|
{name}
|
558
589
|
</a>
|
@@ -574,20 +605,22 @@ export const ModuleStreamsTab = () => {
|
|
574
605
|
installedProfiles={installedProfiles}
|
575
606
|
/>
|
576
607
|
</Td>
|
577
|
-
|
578
|
-
<
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
608
|
+
{showActions && (
|
609
|
+
<Td key={`actions-td-${id}-${dropdownOpen}`}>
|
610
|
+
<Dropdown
|
611
|
+
aria-label={`actions-dropdown-${id}`}
|
612
|
+
key={`actions-dropdown-${id}-${dropdownOpen}`}
|
613
|
+
isPlain
|
614
|
+
style={{ width: 'inherit' }}
|
615
|
+
position={DropdownPosition.right}
|
616
|
+
toggle={
|
617
|
+
<KebabToggle aria-label={`kebab-dropdown-${id}`} onToggle={() => ((dropdownOpen === id) ? setDropdownOpen('') : setDropdownOpen(id))} id={`toggle-dropdown-${id}`} />
|
618
|
+
}
|
619
|
+
isOpen={id === dropdownOpen}
|
620
|
+
dropdownItems={dropdownItems}
|
621
|
+
/>
|
622
|
+
</Td>
|
623
|
+
)}
|
591
624
|
</Tr>
|
592
625
|
);
|
593
626
|
})
|
@@ -596,11 +629,12 @@ export const ModuleStreamsTab = () => {
|
|
596
629
|
</TableWrapper>
|
597
630
|
{actionModalOpen &&
|
598
631
|
<ModuleActionConfirmationModal
|
599
|
-
|
632
|
+
hostname={hostname}
|
600
633
|
action={hostModuleStreamAction}
|
601
634
|
moduleSpec={actionableModuleSpec}
|
602
635
|
actionModalOpen={actionModalOpen}
|
603
636
|
setActionModalOpen={setActionModalOpen}
|
637
|
+
triggerModuleStreamAction={triggerConfirmModalAction}
|
604
638
|
/>
|
605
639
|
}
|
606
640
|
</div>
|
@@ -15,7 +15,6 @@ import { HOST_YUM_INSTALLABLE_PACKAGES_KEY } from './YumInstallablePackagesConst
|
|
15
15
|
import { selectHostYumInstallablePackagesStatus } from './YumInstallablePackagesSelectors';
|
16
16
|
import { getHostYumInstallablePackages } from './YumInstallablePackagesActions';
|
17
17
|
import './PackageInstallModal.scss';
|
18
|
-
import { installPackageBySearch } from '../RemoteExecutionActions';
|
19
18
|
import { katelloPackageInstallBySearchUrl, katelloPackageInstallUrl } from '../customizedRexUrlHelpers';
|
20
19
|
import hostIdNotReady from '../../HostDetailsActions';
|
21
20
|
import { installPackageViaKatelloAgent } from './HostPackagesActions';
|
@@ -101,7 +100,7 @@ InstallDropdown.defaultProps = {
|
|
101
100
|
};
|
102
101
|
|
103
102
|
const PackageInstallModal = ({
|
104
|
-
isOpen, closeModal, hostId, hostName, showKatelloAgent,
|
103
|
+
isOpen, closeModal, hostId, hostName, showKatelloAgent, triggerPackageInstall,
|
105
104
|
}) => {
|
106
105
|
const emptyContentTitle = __('No packages available to install');
|
107
106
|
const emptyContentBody = __('No packages available to install on this host. Please check the host\'s content view and lifecycle environment.');
|
@@ -127,6 +126,7 @@ const PackageInstallModal = ({
|
|
127
126
|
selectedResults,
|
128
127
|
...selectAll
|
129
128
|
} = useBulkSelect({ results, metadata });
|
129
|
+
|
130
130
|
const fetchItems = (params) => {
|
131
131
|
if (!hostId) return hostIdNotReady;
|
132
132
|
|
@@ -141,7 +141,7 @@ const PackageInstallModal = ({
|
|
141
141
|
const selectedPackageNames = () => selectedResults.map(({ name }) => name);
|
142
142
|
|
143
143
|
const installViaRex = () => {
|
144
|
-
|
144
|
+
triggerPackageInstall(fetchBulkParams());
|
145
145
|
selectNone();
|
146
146
|
closeModal();
|
147
147
|
};
|
@@ -271,6 +271,7 @@ PackageInstallModal.propTypes = {
|
|
271
271
|
hostId: PropTypes.number.isRequired,
|
272
272
|
hostName: PropTypes.string.isRequired,
|
273
273
|
showKatelloAgent: PropTypes.bool,
|
274
|
+
triggerPackageInstall: PropTypes.func.isRequired,
|
274
275
|
};
|
275
276
|
|
276
277
|
PackageInstallModal.defaultProps = {
|