Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './vm-status-modal';
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import * as React from 'react';
import { Modal } from '@patternfly/react-core';
import { HandlePromiseProps, withHandlePromise } from '@console/internal/components/utils';
import { ModalComponentProps } from '@console/internal/components/factory';
import { ModalFooter } from '../modal/modal-footer';
import { PAUSED_VM_MODAL_MESSAGE } from '../../../constants/vm';
import { VMIKind } from '../../../types';
import { unpauseVMI } from '../../../k8s/requests/vmi/actions';

const modalTitle = 'Edit pause state';

const VMStatusModal = withHandlePromise<VMStatusModalProps>(
({ vmi, isOpen, setOpen, title = modalTitle, handlePromise, inProgress, errorMessage }) => {
const [showPatchError, setPatchError] = React.useState<boolean>(false);

const onSubmit = async (event) => {
event.preventDefault();

const promise = unpauseVMI(vmi);
handlePromise(promise)
.then(() => setOpen(false))
.catch(() => setPatchError(true));
};

const footer = (
<ModalFooter
errorMessage={showPatchError && errorMessage}
inProgress={inProgress}
onSubmit={onSubmit}
onCancel={() => setOpen(false)}
submitButtonText="Unpause"
/>
);

return (
<Modal
title={title}
isOpen={isOpen}
isSmall
onClose={() => setOpen(false)}
footer={footer}
isFooterLeftAligned
>
<div>{PAUSED_VM_MODAL_MESSAGE}</div>
</Modal>
);
},
);

export type VMStatusModalProps = HandlePromiseProps &
ModalComponentProps & {
vmi: VMIKind;
title?: string;
isOpen: boolean;
setOpen: (isOpen: boolean) => void;
};

export default VMStatusModal;
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react';
import { PodKind, K8sResourceKind } from '@console/internal/module/k8s';
import { OffIcon, UnknownIcon, SyncAltIcon } from '@patternfly/react-icons';
import { OffIcon, PausedIcon, SyncAltIcon, UnknownIcon } from '@patternfly/react-icons';
import {
PopoverStatus,
StatusIconAndText,
Expand All @@ -10,12 +10,24 @@ import {
ProgressStatus,
PendingStatus,
} from '@console/shared';
import { Progress, ProgressVariant, ProgressSize } from '@patternfly/react-core';
import {
Progress,
ProgressVariant,
ProgressSize,
Button,
ButtonVariant,
} from '@patternfly/react-core';
import { Link } from 'react-router-dom';
import { resourcePath } from '@console/internal/components/utils';
import { PodModel } from '@console/internal/models';
import { unpauseVMI } from '../../k8s/requests/vmi/actions';
import { VirtualMachineModel } from '../../models';
import { VM_DETAIL_EVENTS_HREF, CDI_KUBEVIRT_IO, STORAGE_IMPORT_PVC_NAME } from '../../constants';
import {
VM_DETAIL_EVENTS_HREF,
CDI_KUBEVIRT_IO,
STORAGE_IMPORT_PVC_NAME,
PAUSED_VM_MODAL_MESSAGE,
} from '../../constants';
import { getLabels } from '../../selectors/selectors';
import { getVMStatus } from '../../statuses/vm/vm';
import {
Expand All @@ -33,6 +45,7 @@ import {
VM_STATUS_OFF,
VM_STATUS_ERROR,
VM_STATUS_IMPORT_PENDING,
VM_STATUS_PAUSED,
} from '../../statuses/vm/constants';
import { VMKind, VMIKind } from '../../types';

Expand Down Expand Up @@ -102,6 +115,11 @@ export const VMStatus: React.FC<VMStatusProps> = ({
)}`; // to default tab
const additionalText = verbose ? getAdditionalImportText(statusDetail.pod) : null;

const unpauseVM = async (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
event.preventDefault();
await unpauseVMI(vmi);
};

switch (statusDetail.status) {
case VM_STATUS_V2V_CONVERSION_PENDING:
return (
Expand Down Expand Up @@ -248,6 +266,16 @@ export const VMStatus: React.FC<VMStatusProps> = ({
/>
</ProgressStatus>
);
case VM_STATUS_PAUSED:
return (
<PopoverStatus title="Paused" icon={<PausedIcon />}>
<VMStatusPopoverContent message={PAUSED_VM_MODAL_MESSAGE}>
<Button variant={ButtonVariant.primary} onClick={unpauseVM} id="paused-popover-submit">
Unpause
</Button>
</VMStatusPopoverContent>
</PopoverStatus>
);
case VM_STATUS_RUNNING:
return <StatusIconAndText title="Running" icon={<SyncAltIcon />} />;
case VM_STATUS_OFF:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as _ from 'lodash';
import * as React from 'react';
import { asAccessReview, Kebab, KebabOption } from '@console/internal/components/utils';
import { K8sKind, K8sResourceKind, PodKind } from '@console/internal/module/k8s';
import { K8sKind, K8sResourceCommon, K8sResourceKind, PodKind } from '@console/internal/module/k8s';
import { getName, getNamespace } from '@console/shared';
import { confirmModal } from '@console/internal/components/modals';
import { VMIKind, VMKind } from '../../types/vm';
Expand All @@ -15,17 +15,19 @@ import { cancelMigration } from '../../k8s/requests/vmim';
import { cloneVMModal } from '../modals/clone-vm-modal';
import { VMCDRomModal } from '../modals/cdrom-vm-modal';
import { getVMStatus } from '../../statuses/vm/vm';
import { isVMIPaused } from '../../selectors/vmi';
import { unpauseVMI, VMIActionType } from '../../k8s/requests/vmi/actions';

type ActionArgs = {
migration?: K8sResourceKind;
vmi?: VMIKind;
vmStatus?: VMMultiStatus;
};

const getVMActionMessage = (vm, action: VMActionType) => (
const getActionMessage = (obj: K8sResourceCommon, action: VMActionType | VMIActionType) => (
<>
Are you sure you want to {action} <strong>{getName(vm)}</strong> in namespace{' '}
<strong>{getNamespace(vm)}</strong>?
Are you sure you want to {action} <strong>{getName(obj)}</strong> in namespace{' '}
<strong>{getNamespace(obj)}</strong>?
</>
);

Expand All @@ -41,7 +43,7 @@ export const menuActionStart = (
callback: () =>
confirmModal({
title,
message: getVMActionMessage(vm, VMActionType.Start),
message: getActionMessage(vm, VMActionType.Start),
btnText: _.capitalize(VMActionType.Start),
executeFn: () => startVM(vm),
}),
Expand All @@ -57,7 +59,7 @@ const menuActionStop = (kindObj: K8sKind, vm: VMKind): KebabOption => {
callback: () =>
confirmModal({
title,
message: getVMActionMessage(vm, VMActionType.Stop),
message: getActionMessage(vm, VMActionType.Stop),
btnText: _.capitalize(VMActionType.Stop),
executeFn: () => stopVM(vm),
}),
Expand All @@ -77,14 +79,29 @@ const menuActionRestart = (
callback: () =>
confirmModal({
title,
message: getVMActionMessage(vm, VMActionType.Restart),
message: getActionMessage(vm, VMActionType.Restart),
btnText: _.capitalize(VMActionType.Restart),
executeFn: () => restartVM(vm),
}),
accessReview: asAccessReview(kindObj, vm, 'patch'),
};
};

const menuActionUnpause = (kindObj: K8sKind, vm: VMKind, { vmi }: ActionArgs): KebabOption => {
const title = 'Unpause Virtual Machine';
return {
hidden: !isVMIPaused(vmi),
label: title,
callback: () =>
confirmModal({
title,
message: getActionMessage(vmi, VMIActionType.Unpause),
btnText: _.capitalize(VMIActionType.Unpause),
executeFn: () => unpauseVMI(vmi),
}),
};
};

const menuActionMigrate = (
kindObj: K8sKind,
vm: VMKind,
Expand Down Expand Up @@ -156,6 +173,7 @@ export const vmMenuActions = [
menuActionStart,
menuActionStop,
menuActionRestart,
menuActionUnpause,
menuActionMigrate,
menuActionCancelMigration,
menuActionClone,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { vmDescriptionModal, vmFlavorModal } from '../modals';
import { VMCDRomModal } from '../modals/cdrom-vm-modal';
import { DedicatedResourcesModal } from '../modals/dedicated-resources-modal/dedicated-resources-modal';
import { BootOrderModal } from '../modals/boot-order-modal/boot-order-modal';
import VMStatusModal from '../modals/vm-status-modal/vm-status-modal';
import { getDescription } from '../../selectors/selectors';
import { getCDRoms, isDedicatedCPUPlacement } from '../../selectors/vm/selectors';
import { getVMTemplateNamespacedName } from '../../selectors/vm-template/selectors';
Expand All @@ -32,8 +33,8 @@ import {
getWorkloadProfile,
getDevices,
} from '../../selectors/vm';

import './vm-resource.scss';
import { isVMIPaused } from '../../selectors/vmi';

export const VMDetailsItem: React.FC<VMDetailsItemProps> = ({
title,
Expand Down Expand Up @@ -103,6 +104,7 @@ export const VMDetailsList: React.FC<VMResourceListProps> = ({
const [isDedicatedResourcesModalOpen, setDedicatedResourcesModalOpen] = React.useState<boolean>(
false,
);
const [isStatusModalOpen, setStatusModalOpen] = React.useState<boolean>(false);

const id = getBasicID(vm);
const vmStatus = getVMStatus({ vm, vmi, pods, migrations });
Expand All @@ -117,7 +119,14 @@ export const VMDetailsList: React.FC<VMResourceListProps> = ({

return (
<dl className="co-m-pane__details">
<VMDetailsItem title="Status" idValue={prefixedID(id, 'vm-statuses')}>
<VMDetailsItem
title="Status"
canEdit={isVMIPaused(vmi)}
editButtonId={prefixedID(id, 'status-edit')}
onEditClick={() => setStatusModalOpen(true)}
idValue={prefixedID(id, 'vm-statuses')}
>
<VMStatusModal isOpen={isStatusModalOpen} setOpen={setStatusModalOpen} vmi={vmi} />
<VMStatuses vm={vm} vmi={vmi} pods={pods} migrations={migrations} />
</VMDetailsItem>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,6 @@ export enum DeviceType {
}

export const VM_DETAIL_EVENTS_HREF = 'events';

export const PAUSED_VM_MODAL_MESSAGE =
'This VM has been paused. If you wish to unpause it, please click the Unpause button below. For further details, please check with your system administrator.';
32 changes: 32 additions & 0 deletions frontend/packages/kubevirt-plugin/src/k8s/requests/vmi/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { coFetch } from '@console/internal/co-fetch';
import { resourceURL } from '@console/internal/module/k8s';
import { getName, getNamespace } from '@console/shared';
import { VirtualMachineInstanceModel } from '../../../models';
import { VMIKind } from '../../../types/vm';

export enum VMIActionType {
Unpause = 'unpause',
}

const VMIActionRequest = async (vmi: VMIKind, action: VMIActionType) => {
const method = 'PUT';
let url = resourceURL(
{
...VirtualMachineInstanceModel,
apiGroup: `subresources.${VirtualMachineInstanceModel.apiGroup}`,
},
{
ns: getNamespace(vmi),
name: getName(vmi),
},
);

url = `${url}/${action}`;

const response = await coFetch(url, { method });
const text = await response.text();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's just return the text - please update the VMActionRequest as well

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could also parametrize this method by the model, but I am not sure we want to do that


return text;
};

export const unpauseVMI = async (vmi: VMIKind) => VMIActionRequest(vmi, VMIActionType.Unpause);
3 changes: 3 additions & 0 deletions frontend/packages/kubevirt-plugin/src/selectors/vmi/basic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,6 @@ export const getVMIInterfaces = (vmi: VMIKind) =>
(vmi && vmi.status && vmi.status.interfaces) || [];

export const getVMINodeName = (vmi: VMIKind) => vmi && vmi.status && vmi.status.nodeName;

export const isVMIPaused = (vmi: VMIKind): boolean =>
getVMIConditionsByType(vmi, 'Paused').length > 0;
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export const VM_STATUS_STARTING = 'VM_STATUS_STARTING';
export const VM_STATUS_VMI_WAITING = 'VM_STATUS_VMI_WAITING';
export const VM_STATUS_IMPORTING = 'VM_STATUS_IMPORTING';
export const VM_STATUS_STOPPING = 'VM_STATUS_STOPPING';
export const VM_STATUS_PAUSED = 'VM_STATUS_PAUSED';

export const VM_STATUS_V2V_CONVERSION_IN_PROGRESS = 'VM_STATUS_CONVERSION_IN_PROGRESS';
export const VM_STATUS_V2V_CONVERSION_PENDING = 'VM_STATUS_CONVERSION_PENDING';
Expand Down
6 changes: 6 additions & 0 deletions frontend/packages/kubevirt-plugin/src/statuses/vm/vm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,10 @@ import {
VM_STATUS_V2V_CONVERSION_PENDING,
CONVERSION_PROGRESS_ANNOTATION,
VM_STATUS_IMPORT_PENDING,
VM_STATUS_PAUSED,
} from './constants';
import { Status } from '..';
import { isVMIPaused } from '../../selectors/vmi/basic';

const isBeingMigrated = (vm: VMKind, migrations?: K8sResourceKind[]): VMStatus => {
const migration = findVMIMigration(vm, migrations);
Expand Down Expand Up @@ -96,6 +98,9 @@ const isReady = (vmi: VMIKind, launcherPod: PodKind): VMStatus => {
return NOT_HANDLED;
};

const isPaused = (vmi: VMIKind): VMStatus =>
isVMIPaused(vmi) ? { status: VM_STATUS_PAUSED } : NOT_HANDLED;

const isVMError = (vm: VMKind): VMStatus => {
// is an issue with the VM definition?
const condition = getVMStatusConditions(vm)[0];
Expand Down Expand Up @@ -237,6 +242,7 @@ export const getVMStatus = ({
}): VMStatus => {
const launcherPod = findVMPod(vm, pods);
return (
isPaused(vmi) ||
Copy link
Member

@atiratree atiratree Jan 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO importing should be first

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok let's keep the priority for that one

isV2VConversion(vm, pods) || // these statuses must precede isRunning() because they do not rely on ready vms
isBeingMigrated(vm, migrations) || // -||-
isBeingImported(vm, pods) || // -||-
Expand Down