mirror of
https://github.com/ansible/awx.git
synced 2026-03-11 14:39:30 -02:30
resolves advanced search button
This commit is contained in:
62
awx/ui/src/components/HealthCheckButton/HealthCheckButton.js
Normal file
62
awx/ui/src/components/HealthCheckButton/HealthCheckButton.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Plural, t } from '@lingui/macro';
|
||||||
|
import { Button, DropdownItem, Tooltip } from '@patternfly/react-core';
|
||||||
|
import { useKebabifiedMenu } from 'contexts/Kebabified';
|
||||||
|
|
||||||
|
function HealthCheckButton({ isDisabled, onClick, selectedItems }) {
|
||||||
|
const { isKebabified } = useKebabifiedMenu();
|
||||||
|
const hopNodeSelected =
|
||||||
|
selectedItems.filter((instance) => instance.node_type === 'hop').length > 0;
|
||||||
|
const hasSelectedItems = selectedItems.length > 0;
|
||||||
|
|
||||||
|
const buildTooltip = () => {
|
||||||
|
if (hopNodeSelected) {
|
||||||
|
return (
|
||||||
|
<Plural
|
||||||
|
value={hopNodeSelected}
|
||||||
|
one="Cannot run health check on a hop node. Deselect the hop node to run a health check."
|
||||||
|
other="Cannot run health check on hop nodes. Deselect the hop nodes to run health checks."
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return selectedItems.length ? (
|
||||||
|
<Plural
|
||||||
|
value={selectedItems.length}
|
||||||
|
one="Click to run a health check on the selected instance."
|
||||||
|
other="Click to run a health check on the selected instances."
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
t`Select an instance to run a health check.`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isKebabified) {
|
||||||
|
return (
|
||||||
|
<Tooltip data-cy="healthCheckTooltip" content={buildTooltip()}>
|
||||||
|
<DropdownItem
|
||||||
|
key="approve"
|
||||||
|
isDisabled={hopNodeSelected || isDisabled || !hasSelectedItems}
|
||||||
|
component="button"
|
||||||
|
onClick={onClick}
|
||||||
|
ouiaId="health-check"
|
||||||
|
>
|
||||||
|
{t`Health Check`}
|
||||||
|
</DropdownItem>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Tooltip data-cy="healthCheckTooltip" content={buildTooltip()}>
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
isDisabled={hopNodeSelected || isDisabled || !hasSelectedItems}
|
||||||
|
variant="secondary"
|
||||||
|
ouiaId="health-check"
|
||||||
|
onClick={onClick}
|
||||||
|
>{t`Health Check`}</Button>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HealthCheckButton;
|
||||||
1
awx/ui/src/components/HealthCheckButton/index.js
Normal file
1
awx/ui/src/components/HealthCheckButton/index.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { default } from './HealthCheckButton';
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,5 @@
|
|||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
|
import { t } from '@lingui/macro';
|
||||||
import { Plural, t } from '@lingui/macro';
|
|
||||||
import { useLocation, useParams } from 'react-router-dom';
|
import { useLocation, useParams } from 'react-router-dom';
|
||||||
import 'styled-components/macro';
|
import 'styled-components/macro';
|
||||||
|
|
||||||
@@ -16,7 +15,6 @@ import DisassociateButton from 'components/DisassociateButton';
|
|||||||
import AssociateModal from 'components/AssociateModal';
|
import AssociateModal from 'components/AssociateModal';
|
||||||
import AlertModal from 'components/AlertModal';
|
import AlertModal from 'components/AlertModal';
|
||||||
import ErrorDetail from 'components/ErrorDetail';
|
import ErrorDetail from 'components/ErrorDetail';
|
||||||
|
|
||||||
import useRequest, {
|
import useRequest, {
|
||||||
useDeleteItems,
|
useDeleteItems,
|
||||||
useDismissableError,
|
useDismissableError,
|
||||||
@@ -24,8 +22,7 @@ import useRequest, {
|
|||||||
import useSelected from 'hooks/useSelected';
|
import useSelected from 'hooks/useSelected';
|
||||||
import { InstanceGroupsAPI, InstancesAPI } from 'api';
|
import { InstanceGroupsAPI, InstancesAPI } from 'api';
|
||||||
import { getQSConfig, parseQueryString, mergeParams } from 'util/qs';
|
import { getQSConfig, parseQueryString, mergeParams } from 'util/qs';
|
||||||
|
import HealthCheckButton from 'components/HealthCheckButton/HealthCheckButton';
|
||||||
import { Button, Tooltip } from '@patternfly/react-core';
|
|
||||||
import InstanceListItem from './InstanceListItem';
|
import InstanceListItem from './InstanceListItem';
|
||||||
|
|
||||||
const QS_CONFIG = getQSConfig('instance', {
|
const QS_CONFIG = getQSConfig('instance', {
|
||||||
@@ -83,11 +80,16 @@ function InstanceList() {
|
|||||||
fetchInstances();
|
fetchInstances();
|
||||||
}, [fetchInstances]);
|
}, [fetchInstances]);
|
||||||
|
|
||||||
const { error: healthCheckError, request: fetchHealthCheck } = useRequest(
|
const {
|
||||||
|
error: healthCheckError,
|
||||||
|
request: fetchHealthCheck,
|
||||||
|
isLoading: isHealthCheckLoading,
|
||||||
|
} = useRequest(
|
||||||
useCallback(async () => {
|
useCallback(async () => {
|
||||||
await Promise.all(selected.map(({ id }) => InstancesAPI.healthCheck(id)));
|
await Promise.all(selected.map(({ id }) => InstancesAPI.healthCheck(id)));
|
||||||
fetchInstances();
|
fetchInstances();
|
||||||
}, [selected, fetchInstances])
|
clearSelected();
|
||||||
|
}, [selected, clearSelected, fetchInstances])
|
||||||
);
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -168,7 +170,9 @@ function InstanceList() {
|
|||||||
<>
|
<>
|
||||||
<PaginatedTable
|
<PaginatedTable
|
||||||
contentError={contentError}
|
contentError={contentError}
|
||||||
hasContentLoading={isLoading || isDisassociateLoading}
|
hasContentLoading={
|
||||||
|
isLoading || isDisassociateLoading || isHealthCheckLoading
|
||||||
|
}
|
||||||
items={instances}
|
items={instances}
|
||||||
itemCount={count}
|
itemCount={count}
|
||||||
pluralizedItemName={t`Instances`}
|
pluralizedItemName={t`Instances`}
|
||||||
@@ -182,6 +186,15 @@ function InstanceList() {
|
|||||||
key: 'hostname__icontains',
|
key: 'hostname__icontains',
|
||||||
isDefault: true,
|
isDefault: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: t`Node Type`,
|
||||||
|
key: `or__node_type`,
|
||||||
|
options: [
|
||||||
|
[`control`, t`Control`],
|
||||||
|
[`execution`, t`Execution`],
|
||||||
|
[`hybrid`, t`Hybrid`],
|
||||||
|
],
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
toolbarSortColumns={[
|
toolbarSortColumns={[
|
||||||
{
|
{
|
||||||
@@ -216,28 +229,11 @@ function InstanceList() {
|
|||||||
itemsToDisassociate={selected}
|
itemsToDisassociate={selected}
|
||||||
modalTitle={t`Disassociate instance from instance group?`}
|
modalTitle={t`Disassociate instance from instance group?`}
|
||||||
/>,
|
/>,
|
||||||
<Tooltip
|
<HealthCheckButton
|
||||||
content={
|
isDisabled={!canAdd}
|
||||||
selected.length ? (
|
onClick={fetchHealthCheck}
|
||||||
<Plural
|
selectedItems={selected}
|
||||||
value={selected.length}
|
/>,
|
||||||
one="Click to run a health check on the selected instance."
|
|
||||||
other="Click to run a health check on the selected instances."
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
t`Select an instance to run a health check.`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<Button
|
|
||||||
isDisabled={!canAdd || !selected.length}
|
|
||||||
variant="secondary"
|
|
||||||
ouiaId="health-check"
|
|
||||||
onClick={fetchHealthCheck}
|
|
||||||
>{t`Health Check`}</Button>
|
|
||||||
</div>
|
|
||||||
</Tooltip>,
|
|
||||||
]}
|
]}
|
||||||
emptyStateControls={
|
emptyStateControls={
|
||||||
canAdd ? (
|
canAdd ? (
|
||||||
@@ -253,9 +249,8 @@ function InstanceList() {
|
|||||||
<HeaderRow qsConfig={QS_CONFIG} isExpandable>
|
<HeaderRow qsConfig={QS_CONFIG} isExpandable>
|
||||||
<HeaderCell sortKey="hostname">{t`Name`}</HeaderCell>
|
<HeaderCell sortKey="hostname">{t`Name`}</HeaderCell>
|
||||||
<HeaderCell sortKey="errors">{t`Status`}</HeaderCell>
|
<HeaderCell sortKey="errors">{t`Status`}</HeaderCell>
|
||||||
<HeaderCell>{t`Running Jobs`}</HeaderCell>
|
<HeaderCell sortKey="node_type">{t`Node Type`}</HeaderCell>
|
||||||
<HeaderCell>{t`Total Jobs`}</HeaderCell>
|
<HeaderCell sortKey="capacity_adjustment">{t`Capacity Adjustment`}</HeaderCell>
|
||||||
<HeaderCell>{t`Capacity Adjustment`}</HeaderCell>
|
|
||||||
<HeaderCell>{t`Used Capacity`}</HeaderCell>
|
<HeaderCell>{t`Used Capacity`}</HeaderCell>
|
||||||
<HeaderCell>{t`Actions`}</HeaderCell>
|
<HeaderCell>{t`Actions`}</HeaderCell>
|
||||||
</HeaderRow>
|
</HeaderRow>
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ function InstanceListItem({
|
|||||||
rowIndex,
|
rowIndex,
|
||||||
}) {
|
}) {
|
||||||
const { me = {} } = useConfig();
|
const { me = {} } = useConfig();
|
||||||
|
const { id } = useParams();
|
||||||
const [forks, setForks] = useState(
|
const [forks, setForks] = useState(
|
||||||
computeForks(
|
computeForks(
|
||||||
instance.mem_capacity,
|
instance.mem_capacity,
|
||||||
@@ -68,7 +69,6 @@ function InstanceListItem({
|
|||||||
instance.capacity_adjustment
|
instance.capacity_adjustment
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
const { id } = useParams();
|
|
||||||
|
|
||||||
const labelId = `check-action-${instance.id}`;
|
const labelId = `check-action-${instance.id}`;
|
||||||
|
|
||||||
@@ -147,8 +147,7 @@ function InstanceListItem({
|
|||||||
<StatusLabel status={instance.errors ? 'error' : 'healthy'} />
|
<StatusLabel status={instance.errors ? 'error' : 'healthy'} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Td>
|
</Td>
|
||||||
<Td dataLabel={t`Running Jobs`}>{instance.jobs_running}</Td>
|
<Td dataLabel={t`Node Type`}>{instance.node_type}</Td>
|
||||||
<Td dataLabel={t`Total Jobs`}>{instance.jobs_total}</Td>
|
|
||||||
<Td dataLabel={t`Capacity Adjustment`}>
|
<Td dataLabel={t`Capacity Adjustment`}>
|
||||||
<SliderHolder data-cy="slider-holder">
|
<SliderHolder data-cy="slider-holder">
|
||||||
<div data-cy="cpu-capacity">{t`CPU ${instance.cpu_capacity}`}</div>
|
<div data-cy="cpu-capacity">{t`CPU ${instance.cpu_capacity}`}</div>
|
||||||
@@ -197,7 +196,8 @@ function InstanceListItem({
|
|||||||
<Td colSpan={7}>
|
<Td colSpan={7}>
|
||||||
<ExpandableRowContent>
|
<ExpandableRowContent>
|
||||||
<DetailList>
|
<DetailList>
|
||||||
<Detail label={t`Node Type`} value={instance.node_type} />
|
<Detail value={instance.jobs_running} label={t`Running Jobs`} />
|
||||||
|
<Detail value={instance.jobs_total} label={t`Total Jobs`} />
|
||||||
<Detail
|
<Detail
|
||||||
label={t`Policy Type`}
|
label={t`Policy Type`}
|
||||||
value={instance.managed_by_policy ? t`Auto` : t`Manual`}
|
value={instance.managed_by_policy ? t`Auto` : t`Manual`}
|
||||||
|
|||||||
@@ -274,9 +274,8 @@ describe('<InstanceListItem/>', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
expect(wrapper.find('InstanceListItem').prop('isExpanded')).toBe(true);
|
expect(wrapper.find('InstanceListItem').prop('isExpanded')).toBe(true);
|
||||||
expect(wrapper.find('Detail[label="Node Type"]').prop('value')).toBe(
|
expect(wrapper.find('Detail[label="Running Jobs"]').prop('value')).toBe(0);
|
||||||
'hybrid'
|
expect(wrapper.find('Detail[label="Total Jobs"]').prop('value')).toBe(68);
|
||||||
);
|
|
||||||
expect(wrapper.find('Detail[label="Policy Type"]').prop('value')).toBe(
|
expect(wrapper.find('Detail[label="Policy Type"]').prop('value')).toBe(
|
||||||
'Auto'
|
'Auto'
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -149,19 +149,18 @@ function InstanceDetail({ setBreadcrumb }) {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Detail label={t`Node Type`} value={instance.node_type} />
|
<Detail label={t`Node Type`} value={instance.node_type} />
|
||||||
<Detail
|
|
||||||
label={t`Policy Type`}
|
|
||||||
value={instance.managed_by_policy ? t`Auto` : t`Manual`}
|
|
||||||
/>
|
|
||||||
<Detail label={t`Running Jobs`} value={instance.jobs_running} />
|
|
||||||
<Detail label={t`Total Jobs`} value={instance.jobs_total} />
|
|
||||||
{!isHopNode && (
|
{!isHopNode && (
|
||||||
<>
|
<>
|
||||||
|
<Detail
|
||||||
|
label={t`Policy Type`}
|
||||||
|
value={instance.managed_by_policy ? t`Auto` : t`Manual`}
|
||||||
|
/>
|
||||||
|
<Detail label={t`Running Jobs`} value={instance.jobs_running} />
|
||||||
|
<Detail label={t`Total Jobs`} value={instance.jobs_total} />
|
||||||
<Detail
|
<Detail
|
||||||
label={t`Last Health Check`}
|
label={t`Last Health Check`}
|
||||||
value={formatDateString(healthCheck?.last_health_check)}
|
value={formatDateString(healthCheck?.last_health_check)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Detail
|
<Detail
|
||||||
label={t`Capacity Adjustment`}
|
label={t`Capacity Adjustment`}
|
||||||
value={
|
value={
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React, { useCallback, useEffect } from 'react';
|
import React, { useCallback, useEffect } from 'react';
|
||||||
|
import { t } from '@lingui/macro';
|
||||||
import { Plural, t } from '@lingui/macro';
|
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import 'styled-components/macro';
|
import 'styled-components/macro';
|
||||||
|
import { PageSection, Card } from '@patternfly/react-core';
|
||||||
|
|
||||||
import useExpanded from 'hooks/useExpanded';
|
import useExpanded from 'hooks/useExpanded';
|
||||||
import DataListToolbar from 'components/DataListToolbar';
|
import DataListToolbar from 'components/DataListToolbar';
|
||||||
@@ -13,13 +13,11 @@ import PaginatedTable, {
|
|||||||
} from 'components/PaginatedTable';
|
} from 'components/PaginatedTable';
|
||||||
import AlertModal from 'components/AlertModal';
|
import AlertModal from 'components/AlertModal';
|
||||||
import ErrorDetail from 'components/ErrorDetail';
|
import ErrorDetail from 'components/ErrorDetail';
|
||||||
|
|
||||||
import useRequest, { useDismissableError } from 'hooks/useRequest';
|
import useRequest, { useDismissableError } from 'hooks/useRequest';
|
||||||
import useSelected from 'hooks/useSelected';
|
import useSelected from 'hooks/useSelected';
|
||||||
import { InstancesAPI } from 'api';
|
import { InstancesAPI } from 'api';
|
||||||
import { getQSConfig, parseQueryString } from 'util/qs';
|
import { getQSConfig, parseQueryString } from 'util/qs';
|
||||||
|
import HealthCheckButton from 'components/HealthCheckButton';
|
||||||
import { Button, Tooltip, PageSection, Card } from '@patternfly/react-core';
|
|
||||||
import InstanceListItem from './InstanceListItem';
|
import InstanceListItem from './InstanceListItem';
|
||||||
|
|
||||||
const QS_CONFIG = getQSConfig('instance', {
|
const QS_CONFIG = getQSConfig('instance', {
|
||||||
@@ -69,7 +67,11 @@ function InstanceList() {
|
|||||||
fetchInstances();
|
fetchInstances();
|
||||||
}, [fetchInstances]);
|
}, [fetchInstances]);
|
||||||
|
|
||||||
const { error: healthCheckError, request: fetchHealthCheck } = useRequest(
|
const {
|
||||||
|
error: healthCheckError,
|
||||||
|
request: fetchHealthCheck,
|
||||||
|
isLoading: isHealthCheckLoading,
|
||||||
|
} = useRequest(
|
||||||
useCallback(async () => {
|
useCallback(async () => {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
selected
|
selected
|
||||||
@@ -86,38 +88,13 @@ function InstanceList() {
|
|||||||
const { expanded, isAllExpanded, handleExpand, expandAll } =
|
const { expanded, isAllExpanded, handleExpand, expandAll } =
|
||||||
useExpanded(instances);
|
useExpanded(instances);
|
||||||
|
|
||||||
const hopNodeSelected = selected.filter(
|
|
||||||
(instance) => instance.node_type === 'hop'
|
|
||||||
).length;
|
|
||||||
|
|
||||||
const buildTooltip = () => {
|
|
||||||
if (hopNodeSelected) {
|
|
||||||
return (
|
|
||||||
<Plural
|
|
||||||
value={hopNodeSelected}
|
|
||||||
one="Cannot run health check on a hop node. Deselect the hop node to run a health check."
|
|
||||||
other="Cannot run health check on hop nodes. Deselect the hop nodes to run health checks."
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return selected.length ? (
|
|
||||||
<Plural
|
|
||||||
value={selected.length}
|
|
||||||
one="Click to run a health check on the selected instance."
|
|
||||||
other="Click to run a health check on the selected instances."
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
t`Select an instance to run a health check.`
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageSection>
|
<PageSection>
|
||||||
<Card>
|
<Card>
|
||||||
<PaginatedTable
|
<PaginatedTable
|
||||||
contentError={contentError}
|
contentError={contentError}
|
||||||
hasContentLoading={isLoading}
|
hasContentLoading={isLoading || isHealthCheckLoading}
|
||||||
items={instances}
|
items={instances}
|
||||||
itemCount={count}
|
itemCount={count}
|
||||||
pluralizedItemName={t`Instances`}
|
pluralizedItemName={t`Instances`}
|
||||||
@@ -157,18 +134,10 @@ function InstanceList() {
|
|||||||
onExpandAll={expandAll}
|
onExpandAll={expandAll}
|
||||||
qsConfig={QS_CONFIG}
|
qsConfig={QS_CONFIG}
|
||||||
additionalControls={[
|
additionalControls={[
|
||||||
<Tooltip ouiaId="healthCheckTooltip" content={buildTooltip()}>
|
<HealthCheckButton
|
||||||
<div>
|
onClick={fetchHealthCheck}
|
||||||
<Button
|
selectedItems={selected}
|
||||||
isDisabled={
|
/>,
|
||||||
!selected.length || Boolean(hopNodeSelected)
|
|
||||||
}
|
|
||||||
variant="secondary"
|
|
||||||
ouiaId="health-check"
|
|
||||||
onClick={fetchHealthCheck}
|
|
||||||
>{t`Health Check`}</Button>
|
|
||||||
</div>
|
|
||||||
</Tooltip>,
|
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -179,7 +148,7 @@ function InstanceList() {
|
|||||||
<HeaderCell sortKey="node_type">{t`Node Type`}</HeaderCell>
|
<HeaderCell sortKey="node_type">{t`Node Type`}</HeaderCell>
|
||||||
<HeaderCell sortKey="capacity_adjustment">{t`Capacity Adjustment`}</HeaderCell>
|
<HeaderCell sortKey="capacity_adjustment">{t`Capacity Adjustment`}</HeaderCell>
|
||||||
<HeaderCell>{t`Used Capacity`}</HeaderCell>
|
<HeaderCell>{t`Used Capacity`}</HeaderCell>
|
||||||
<HeaderCell sortKey="enabled">{t`Actions`}</HeaderCell>
|
<HeaderCell>{t`Actions`}</HeaderCell>
|
||||||
</HeaderRow>
|
</HeaderRow>
|
||||||
}
|
}
|
||||||
renderRow={(instance, index) => (
|
renderRow={(instance, index) => (
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ describe('<InstanceList/>', () => {
|
|||||||
expect(wrapper.find('InstanceListItem').length).toBe(4);
|
expect(wrapper.find('InstanceListItem').length).toBe(4);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should run health check', async () => {
|
test('Should run health check', async () => {
|
||||||
// Ensures health check button is disabled on mount
|
// Ensures health check button is disabled on mount
|
||||||
expect(
|
expect(
|
||||||
wrapper.find('Button[ouiaId="health-check"]').prop('isDisabled')
|
wrapper.find('Button[ouiaId="health-check"]').prop('isDisabled')
|
||||||
@@ -168,7 +168,7 @@ describe('<InstanceList/>', () => {
|
|||||||
);
|
);
|
||||||
expect(InstancesAPI.healthCheck).toBeCalledTimes(3);
|
expect(InstancesAPI.healthCheck).toBeCalledTimes(3);
|
||||||
});
|
});
|
||||||
test('should render health check error', async () => {
|
test('Should render health check error', async () => {
|
||||||
InstancesAPI.healthCheck.mockRejectedValue(
|
InstancesAPI.healthCheck.mockRejectedValue(
|
||||||
new Error({
|
new Error({
|
||||||
response: {
|
response: {
|
||||||
@@ -206,6 +206,8 @@ describe('<InstanceList/>', () => {
|
|||||||
expect(
|
expect(
|
||||||
wrapper.find('Button[ouiaId="health-check"]').prop('isDisabled')
|
wrapper.find('Button[ouiaId="health-check"]').prop('isDisabled')
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
expect(wrapper.find('Tooltip[ouiaId="healthCheckTooltip"]').length).toBe(1);
|
expect(wrapper.find('Tooltip[data-cy="healthCheckTooltip"]').length).toBe(
|
||||||
|
1
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -106,20 +106,25 @@ function InstanceListItem({
|
|||||||
);
|
);
|
||||||
debounceUpdateInstance({ capacity_adjustment: roundedValue });
|
debounceUpdateInstance({ capacity_adjustment: roundedValue });
|
||||||
};
|
};
|
||||||
|
const isHopNode = instance.node_type === 'hop';
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Tr
|
<Tr
|
||||||
id={`instance-row-${instance.id}`}
|
id={`instance-row-${instance.id}`}
|
||||||
ouiaId={`instance-row-${instance.id}`}
|
ouiaId={`instance-row-${instance.id}`}
|
||||||
>
|
>
|
||||||
<Td
|
{isHopNode ? (
|
||||||
expand={{
|
<Td />
|
||||||
rowIndex,
|
) : (
|
||||||
isExpanded,
|
<Td
|
||||||
onToggle: onExpand,
|
expand={{
|
||||||
}}
|
rowIndex,
|
||||||
/>
|
isExpanded,
|
||||||
|
disabled: isHopNode,
|
||||||
|
onToggle: onExpand,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<Td
|
<Td
|
||||||
select={{
|
select={{
|
||||||
rowIndex,
|
rowIndex,
|
||||||
@@ -133,88 +138,92 @@ function InstanceListItem({
|
|||||||
<b>{instance.hostname}</b>
|
<b>{instance.hostname}</b>
|
||||||
</Link>
|
</Link>
|
||||||
</Td>
|
</Td>
|
||||||
<Td dataLabel={t`Status`}>
|
{!instance.node_type !== 'hop' && (
|
||||||
<Tooltip
|
<Td dataLabel={t`Status`}>
|
||||||
content={
|
<Tooltip
|
||||||
<div>
|
content={
|
||||||
{t`Last Health Check`}
|
<div>
|
||||||
|
{t`Last Health Check`}
|
||||||
{formatDateString(instance.last_health_check)}
|
|
||||||
</div>
|
{formatDateString(instance.last_health_check)}
|
||||||
}
|
|
||||||
>
|
|
||||||
<StatusLabel status={instance.errors ? 'error' : 'healthy'} />
|
|
||||||
</Tooltip>
|
|
||||||
</Td>
|
|
||||||
<Td dataLabel={t`Node Type`}>{instance.node_type}</Td>
|
|
||||||
{instance.node_type !== 'hop' && (
|
|
||||||
<Td dataLabel={t`Capacity Adjustment`}>
|
|
||||||
<SliderHolder data-cy="slider-holder">
|
|
||||||
<div data-cy="cpu-capacity">{t`CPU ${instance.cpu_capacity}`}</div>
|
|
||||||
<SliderForks data-cy="slider-forks">
|
|
||||||
<div data-cy="number-forks">
|
|
||||||
<Plural value={forks} one="# fork" other="# forks" />
|
|
||||||
</div>
|
</div>
|
||||||
<Slider
|
}
|
||||||
areCustomStepsContinuous
|
>
|
||||||
max={1}
|
<StatusLabel status={instance.errors ? 'error' : 'healthy'} />
|
||||||
min={0}
|
</Tooltip>
|
||||||
step={0.1}
|
</Td>
|
||||||
value={instance.capacity_adjustment}
|
)}
|
||||||
onChange={handleChangeValue}
|
<Td dataLabel={t`Node Type`}>{instance.node_type}</Td>
|
||||||
isDisabled={!me?.is_superuser || !instance.enabled}
|
{!isHopNode && (
|
||||||
data-cy="slider"
|
<>
|
||||||
|
<Td dataLabel={t`Capacity Adjustment`}>
|
||||||
|
<SliderHolder data-cy="slider-holder">
|
||||||
|
<div data-cy="cpu-capacity">{t`CPU ${instance.cpu_capacity}`}</div>
|
||||||
|
<SliderForks data-cy="slider-forks">
|
||||||
|
<div data-cy="number-forks">
|
||||||
|
<Plural value={forks} one="# fork" other="# forks" />
|
||||||
|
</div>
|
||||||
|
<Slider
|
||||||
|
areCustomStepsContinuous
|
||||||
|
max={1}
|
||||||
|
min={0}
|
||||||
|
step={0.1}
|
||||||
|
value={instance.capacity_adjustment}
|
||||||
|
onChange={handleChangeValue}
|
||||||
|
isDisabled={!me?.is_superuser || !instance.enabled}
|
||||||
|
data-cy="slider"
|
||||||
|
/>
|
||||||
|
</SliderForks>
|
||||||
|
<div data-cy="mem-capacity">{t`RAM ${instance.mem_capacity}`}</div>
|
||||||
|
</SliderHolder>
|
||||||
|
</Td>
|
||||||
|
|
||||||
|
<Td
|
||||||
|
dataLabel={t`Instance group used capacity`}
|
||||||
|
css="--pf-c-table--cell--MinWidth: 175px;"
|
||||||
|
>
|
||||||
|
{usedCapacity(instance)}
|
||||||
|
</Td>
|
||||||
|
|
||||||
|
<ActionsTd
|
||||||
|
dataLabel={t`Actions`}
|
||||||
|
css="--pf-c-table--cell--Width: 125px"
|
||||||
|
>
|
||||||
|
<ActionItem visible>
|
||||||
|
<InstanceToggle
|
||||||
|
css="display: inline-flex;"
|
||||||
|
fetchInstances={fetchInstances}
|
||||||
|
instance={instance}
|
||||||
/>
|
/>
|
||||||
</SliderForks>
|
</ActionItem>
|
||||||
<div data-cy="mem-capacity">{t`RAM ${instance.mem_capacity}`}</div>
|
</ActionsTd>
|
||||||
</SliderHolder>
|
</>
|
||||||
</Td>
|
|
||||||
)}
|
|
||||||
{instance.node_type !== 'hop' && (
|
|
||||||
<Td
|
|
||||||
dataLabel={t`Instance group used capacity`}
|
|
||||||
css="--pf-c-table--cell--MinWidth: 175px;"
|
|
||||||
>
|
|
||||||
{usedCapacity(instance)}
|
|
||||||
</Td>
|
|
||||||
)}
|
|
||||||
{instance.node_type !== 'hop' && (
|
|
||||||
<ActionsTd
|
|
||||||
dataLabel={t`Actions`}
|
|
||||||
css="--pf-c-table--cell--Width: 125px"
|
|
||||||
>
|
|
||||||
<ActionItem visible>
|
|
||||||
<InstanceToggle
|
|
||||||
css="display: inline-flex;"
|
|
||||||
fetchInstances={fetchInstances}
|
|
||||||
instance={instance}
|
|
||||||
/>
|
|
||||||
</ActionItem>
|
|
||||||
</ActionsTd>
|
|
||||||
)}
|
)}
|
||||||
</Tr>
|
</Tr>
|
||||||
<Tr
|
{!isHopNode && (
|
||||||
ouiaId={`instance-row-${instance.id}-expanded`}
|
<Tr
|
||||||
isExpanded={isExpanded}
|
ouiaId={`instance-row-${instance.id}-expanded`}
|
||||||
>
|
isExpanded={isExpanded}
|
||||||
<Td colSpan={2} />
|
>
|
||||||
<Td colSpan={7}>
|
<Td colSpan={2} />
|
||||||
<ExpandableRowContent>
|
<Td colSpan={7}>
|
||||||
<DetailList>
|
<ExpandableRowContent>
|
||||||
<Detail value={instance.jobs_running} label={t`Running Jobs`} />
|
<DetailList>
|
||||||
<Detail value={instance.jobs_total} label={t`Total Jobs`} />
|
<Detail value={instance.jobs_running} label={t`Running Jobs`} />
|
||||||
<Detail
|
<Detail value={instance.jobs_total} label={t`Total Jobs`} />
|
||||||
label={t`Policy Type`}
|
<Detail
|
||||||
value={instance.managed_by_policy ? t`Auto` : t`Manual`}
|
label={t`Policy Type`}
|
||||||
/>
|
value={instance.managed_by_policy ? t`Auto` : t`Manual`}
|
||||||
<Detail
|
/>
|
||||||
label={t`Last Health Check`}
|
<Detail
|
||||||
value={formatDateString(instance.last_health_check)}
|
label={t`Last Health Check`}
|
||||||
/>
|
value={formatDateString(instance.last_health_check)}
|
||||||
</DetailList>
|
/>
|
||||||
</ExpandableRowContent>
|
</DetailList>
|
||||||
</Td>
|
</ExpandableRowContent>
|
||||||
</Tr>
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
)}
|
||||||
{updateError && (
|
{updateError && (
|
||||||
<AlertModal
|
<AlertModal
|
||||||
variant="error"
|
variant="error"
|
||||||
|
|||||||
@@ -17,17 +17,14 @@ function Instances() {
|
|||||||
}
|
}
|
||||||
setBreadcrumbConfig({
|
setBreadcrumbConfig({
|
||||||
'/instances': t`Instances`,
|
'/instances': t`Instances`,
|
||||||
[`/instances/${instance.id}`]: t`${instance.hostname}`,
|
[`/instances/${instance.id}`]: `${instance.hostname}`,
|
||||||
[`/instances/${instance.id}/details`]: t`Details`,
|
[`/instances/${instance.id}/details`]: t`Details`,
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ScreenHeader
|
<ScreenHeader streamType="instance" breadcrumbConfig={breadcrumbConfig} />
|
||||||
streamType="instances"
|
|
||||||
breadcrumbConfig={breadcrumbConfig}
|
|
||||||
/>
|
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path="/instances/:id">
|
<Route path="/instances/:id">
|
||||||
<Instance setBreadcrumb={buildBreadcrumbConfig} />
|
<Instance setBreadcrumb={buildBreadcrumbConfig} />
|
||||||
|
|||||||
Reference in New Issue
Block a user