Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BareMetalHost: Add Restart action #4862

Merged
merged 3 commits into from
Apr 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as React from 'react';
import * as _ from 'lodash';
import { RebootingIcon } from '@patternfly/react-icons';
import {
referenceForModel,
K8sResourceKind,
Expand Down Expand Up @@ -39,12 +40,14 @@ import {
getHostBios,
getHostProvisioningState,
getHostBootMACAddress,
isHostScheduledForRestart,
} from '../../selectors';
import { BareMetalHostKind } from '../../types';
import { HOST_REGISTERING_STATES } from '../../constants/bare-metal-host';
import MachineLink from './MachineLink';
import BareMetalHostPowerStatusIcon from './BareMetalHostPowerStatusIcon';
import BareMetalHostStatus from './BareMetalHostStatus';
import { HOST_SCHEDULED_FOR_RESTART } from './BareMetalHostSecondaryStatus';

type BareMetalHostDetailsProps = {
obj: BareMetalHostKind;
Expand Down Expand Up @@ -148,6 +151,12 @@ const BareMetalHostDetails: React.FC<BareMetalHostDetailsProps> = ({
title={powerStatus}
icon={<BareMetalHostPowerStatusIcon powerStatus={powerStatus} />}
/>
{isHostScheduledForRestart(host) && (
<StatusIconAndText
title={HOST_SCHEDULED_FOR_RESTART}
icon={<RebootingIcon />}
/>
)}
</dd>
</>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,36 @@
import * as React from 'react';
import { SecondaryStatus } from '@console/shared';
import { BareMetalHostKind } from '../../types';
import { getHostPowerStatus, getHostProvisioningState } from '../../selectors';
import {
getHostPowerStatus,
getHostProvisioningState,
isHostScheduledForRestart,
} from '../../selectors';
import { HOST_POWER_STATUS_POWERED_ON, HOST_REGISTERING_STATES } from '../../constants';

type BareMetalHostSecondaryStatusProps = {
host: BareMetalHostKind;
};

export const HOST_SCHEDULED_FOR_RESTART = 'Restart pending';

const BareMetalHostSecondaryStatus: React.FC<BareMetalHostSecondaryStatusProps> = ({ host }) => {
const powerStatus = getHostPowerStatus(host);
const provisioningState = getHostProvisioningState(host);
const status = [];
// don't show power status when host is powered on

// don't show power status when host registration/inspection hasn't finished
if (
!(powerStatus === HOST_POWER_STATUS_POWERED_ON) &&
!HOST_REGISTERING_STATES.includes(provisioningState)
) {
status.push(powerStatus);
if (!HOST_REGISTERING_STATES.includes(provisioningState)) {
if (isHostScheduledForRestart(host)) {
status.push(HOST_SCHEDULED_FOR_RESTART);
}

// don't show power status when host is powered on
if (powerStatus !== HOST_POWER_STATUS_POWERED_ON) {
status.push(powerStatus);
}
}

return <SecondaryStatus status={status} />;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ import {
} from '@console/shared';
import { confirmModal, deleteModal } from '@console/internal/components/modals';
import { MachineModel, MachineSetModel } from '@console/internal/models';
import { findNodeMaintenance, getHostMachine, getHostPowerStatus } from '../../selectors';
import {
findNodeMaintenance,
getHostMachine,
getHostPowerStatus,
isHostScheduledForRestart,
} from '../../selectors';
import { BareMetalHostModel, NodeMaintenanceModel } from '../../models';
import { getHostStatus } from '../../status/host-status';
import {
Expand All @@ -39,6 +44,7 @@ import { DELETE_MACHINE_ANNOTATION } from '../../constants/machine';
import { deprovision } from '../../k8s/requests/bare-metal-host';
import { getMachineMachineSetOwner } from '../../selectors/machine';
import { findMachineSet } from '../../selectors/machine-set';
import { restartHostModal } from '../modals/RestartHostModal';

type ActionArgs = {
machine?: MachineKind;
Expand Down Expand Up @@ -129,6 +135,16 @@ export const PowerOff = (
accessReview: host && asAccessReview(BareMetalHostModel, host, 'update'),
});

export const Restart = (kindObj: K8sKind, host: BareMetalHostKind, { nodeName }: ActionArgs) => ({
hidden:
[HOST_POWER_STATUS_POWERED_OFF, HOST_POWER_STATUS_POWERING_OFF].includes(
getHostPowerStatus(host),
) || isHostScheduledForRestart(host),
label: 'Restart',
callback: () => restartHostModal({ host, nodeName }),
accessReview: host && asAccessReview(BareMetalHostModel, host, 'update'),
});

export const Delete = (
kindObj: K8sKind,
host: BareMetalHostKind,
Expand Down Expand Up @@ -159,6 +175,7 @@ export const menuActions = [
PowerOn,
Deprovision,
PowerOff,
Restart,
Kebab.factory.ModifyLabels,
Kebab.factory.ModifyAnnotations,
Edit,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import * as React from 'react';
import { getName } from '@console/shared';
import { withHandlePromise } from '@console/internal/components/utils';
import {
createModalLauncher,
ModalTitle,
ModalBody,
ModalSubmitFooter,
} from '@console/internal/components/factory';
import { BareMetalHostKind } from '../../types';
import { restartHost } from '../../k8s/requests/bare-metal-host';

export type RestartHostModalProps = {
host: BareMetalHostKind;
nodeName: string;
handlePromise: <T>(promise: Promise<T>) => Promise<T>;
inProgress: boolean;
errorMessage: string;
cancel?: () => void;
close?: () => void;
};

const RestartHostModal = ({
host,
nodeName,
inProgress,
errorMessage,
handlePromise,
close = undefined,
cancel = undefined,
}: RestartHostModalProps) => {
const onSubmit = React.useCallback(
async (event) => {
event.preventDefault();
const promise = restartHost(host);
await handlePromise(promise);
return close();
},
[host, close, handlePromise],
);

const text = nodeName
? `The bare metal host ${getName(
host,
)} will be restarted gracefully after all managed workloads are moved.`
: `The bare metal host ${getName(host)} will be restarted gracefully.`;

return (
<form onSubmit={onSubmit} name="form" className="modal-content">
<ModalTitle>Restart Bare Metal Host</ModalTitle>
<ModalBody>{text}</ModalBody>
<ModalSubmitFooter
cancel={cancel}
errorMessage={errorMessage}
inProgress={inProgress}
submitDisabled={false}
submitText="Restart"
/>
</form>
);
};

export const restartHostModal = createModalLauncher(withHandlePromise(RestartHostModal));
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,18 @@ export const powerOffHost = (host: BareMetalHostKind) =>
export const powerOnHost = (host: BareMetalHostKind) =>
k8sPatch(BareMetalHostModel, host, [{ op: 'replace', path: '/spec/online', value: true }]);

export const restartHost = (host: BareMetalHostKind) =>
k8sPatch(BareMetalHostModel, host, [
{
op: 'replace',
path: '/metadata/annotations',
value: {
...host.metadata.annotations,
'reboot.metal3.io': 'UI', // value is irrelevant
},
},
]);

export const deprovision = async (machine: MachineKind, machineSet?: MachineSetKind) => {
await k8sPatch(MachineModel, machine, [
new PatchBuilder('/metadata/annotations')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as _ from 'lodash';
import { getName } from '@console/shared/src/selectors/common';
import { getName, getAnnotations } from '@console/shared/src/selectors/common';
import { MachineKind } from '@console/internal/module/k8s';
import {
BareMetalHostDisk,
Expand All @@ -16,6 +16,8 @@ import {
HOST_POWER_STATUS_POWERED_OFF,
} from '../constants';

const ANNOTATION_HOST_RESTART = 'reboot.metal3.io';

export const getHostOperationalStatus = (host: BareMetalHostKind): string =>
_.get(host, 'status.operationalStatus');
export const getHostErrorType = (host: BareMetalHostKind): string => host.status?.errorType;
Expand All @@ -42,6 +44,12 @@ export const getHostDescription = (host: BareMetalHostKind): string =>
_.get(host, 'spec.description', '');
export const isHostPoweredOn = (host: BareMetalHostKind): boolean =>
_.get(host, 'status.poweredOn', false);
export const isHostScheduledForRestart = (host: BareMetalHostKind) =>
!!Object.getOwnPropertyNames(getAnnotations(host, {})).find(
(annotation) =>
annotation === ANNOTATION_HOST_RESTART ||
annotation.startsWith(`${ANNOTATION_HOST_RESTART}/`),
);
export const getHostPowerStatus = (host: BareMetalHostKind): string => {
const isOnline = isHostOnline(host);
const isPoweredOn = isHostPoweredOn(host);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export const baremetalNodeSecondaryStatus = ({
states.push('Scheduling disabled');
}
// show host power status only if there is actual host associated to node
if (host && !isHostPoweredOn(host)) states.push('Host is powered off');
if (host && !isHostPoweredOn(host)) {
states.push('Host is powered off');
}
return states;
};