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

Kubevirt: add basic actions to VM list #1761

Merged
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,71 @@
import * as React from 'react';

import { withHandlePromise } from '@console/internal/components/utils';

import {
createModalLauncher,
ModalTitle,
ModalBody,
ModalSubmitFooter,
} from '@console/internal/components/factory';

import { k8sPatch } from '@console/internal/module/k8s';

import { getPxeBootPatch } from 'kubevirt-web-ui-components';
import { getName, getNamespace } from '@console/shared/src';
import { VirtualMachineModel } from '../../models';
import { VMKind } from '../../types/vm';

const StartStopVmModal = withHandlePromise((props: StartStopVmModalProps) => {
const { vm, start, inProgress, errorMessage, handlePromise, close, cancel } = props;

const submit = (event) => {
event.preventDefault();

const patches = [];

// handle PXE boot
if (start) {
const pxePatch = getPxeBootPatch(vm);
patches.push(...pxePatch);
}

patches.push({
op: 'replace',
path: '/spec/running',
value: start,
});

const promise = k8sPatch(VirtualMachineModel, vm, patches);
return handlePromise(promise).then(close);
};

const action = start ? 'Start' : 'Stop';
return (
<form onSubmit={submit} name="form" className="modal-content">
<ModalTitle>{action} Virtual Machine</ModalTitle>
<ModalBody>
Are you sure you want to {action.toLowerCase()} <strong>{getName(vm)}</strong> in namespace{' '}
<strong>{getNamespace(vm)}</strong>?
</ModalBody>
<ModalSubmitFooter
errorMessage={errorMessage}
inProgress={inProgress}
submitText={action}
cancel={cancel}
/>
</form>
);
});

export type StartStopVmModalProps = {
vm: VMKind;
start: boolean;
handlePromise: <T>(promise: Promise<T>) => Promise<T>;
inProgress: boolean;
errorMessage: string;
cancel: () => void;
close: () => void;
};

export const startStopVmModal = createModalLauncher(StartStopVmModal);
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import {
getVmStatus,
VM_STATUS_IMPORTING,
VM_STATUS_V2V_CONVERSION_IN_PROGRESS,
} from 'kubevirt-web-ui-components';

import { asAccessReview, Kebab, KebabOption } from '@console/internal/components/utils';
import { K8sKind, PodKind } from '@console/internal/module/k8s';
import { isVMRunning } from '../../selectors/vm';
import { startStopVmModal } from '../modals/start-stop-vm-modal';
import { VMKind } from '../../types/vm';

type ActionArgs = {
pods: PodKind[];
migrations: any[];
};

const isImporting = (vm: VMKind, { pods, migrations }: ActionArgs): boolean => {
const status = getVmStatus(vm, pods, migrations);
return (
status && [VM_STATUS_IMPORTING, VM_STATUS_V2V_CONVERSION_IN_PROGRESS].includes(status.status)
);
};

const menuActionStart = (kindObj: K8sKind, vm: VMKind, actionArgs: ActionArgs): KebabOption => {
return {
hidden: isImporting(vm, actionArgs) || isVMRunning(vm),
label: 'Start Virtual Machine',
callback: () =>
startStopVmModal({
vm,
start: true,
}),
accessReview: asAccessReview(kindObj, vm, 'patch'),
};
};

const menuActionStop = (kindObj: K8sKind, vm: VMKind): KebabOption => {
return {
hidden: !isVMRunning(vm),
label: 'Stop Virtual Machine',
callback: () =>
startStopVmModal({
vm,
start: false,
}),
accessReview: asAccessReview(kindObj, vm, 'patch'),
};
};

export const menuActions = [
menuActionStart,
menuActionStop,
Kebab.factory.ModifyLabels,
Kebab.factory.ModifyAnnotations,
Kebab.factory.Delete,
];
71 changes: 50 additions & 21 deletions frontend/packages/kubevirt-plugin/src/components/vms/vm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,23 @@ import {
} from 'kubevirt-web-ui-components';
import { getName, getNamespace, getUid } from '@console/shared';

import { NamespaceModel } from '@console/internal/models';
import { NamespaceModel, PodModel } from '@console/internal/models';
import { Table, MultiListPage, TableRow, TableData } from '@console/internal/components/factory';
import { Kebab, ResourceLink } from '@console/internal/components/utils';
import { FirehoseResult, Kebab, ResourceLink } from '@console/internal/components/utils';
// import { actions } from '../../module/okdk8s';

import { sortable } from '@patternfly/react-table';
import { PodKind } from '@console/internal/module/k8s';
import {
VirtualMachineInstanceMigrationModel,
VirtualMachineModel,
// VirtualMachineInstanceModel,
// VirtualMachineInstanceMigrationModel,
} from '../../models';

import { VMKind } from '../../types';
import { menuActions } from './menu-actions';

// import { openCreateVmWizard } from '../modals/create-vm-modal';
// import { menuActions } from './menu-actions';

const tableColumnClasses = [
classNames('col-lg-4', 'col-md-4', 'col-sm-6', 'col-xs-6'),
Expand All @@ -51,39 +53,52 @@ const VMHeader = () => [
// title: 'Status',
// props: { className: tableColumnClasses[2] },
// },
// {
// title: '',
// props: { className: Kebab.columnClass, props: { className: tableColumnClasses[3] } },
// },
{
title: '',
props: { className: Kebab.columnClass, props: { className: tableColumnClasses[3] } },
},
];

const VMRow: React.FC<VMRowProps> = ({ obj: vm, index, key, style }) => {
const VMRow: React.FC<VMRowProps> = ({ obj: vm, customData, index, key, style }) => {
const name = getName(vm);
const namespace = getNamespace(vm);
const uid = getUid(vm);

return (
<TableRow id={getUid(vm)} index={index} trKey={key} style={style}>
<TableRow id={uid} index={index} trKey={key} style={style}>
<TableData className={tableColumnClasses[0]}>
<ResourceLink kind={VirtualMachineModel.kind} name={name} namespace={namespace} />
</TableData>
<TableData className={tableColumnClasses[1]}>
<ResourceLink kind={NamespaceModel.kind} name={namespace} title={namespace} />
</TableData>
{/* TODO(mlibra): migrate VM status in a follow-up */}
{/* TODO(mlibra): migrate actions in a follow-up */}
<TableData className={Kebab.columnClass}>
<Kebab
options={menuActions.map((action) => action(VirtualMachineModel, vm, customData))}
key={`kebab-for-${uid}`}
id={`kebab-for-${uid}`}
/>
</TableData>
</TableRow>
);
};

const VMList: React.FC<React.ComponentProps<typeof Table> & VMListProps> = (props) => (
<Table
{...props}
aria-label={VirtualMachineModel.labelPlural}
Header={VMHeader}
Row={VMRow}
virtualize
/>
);
const VMList: React.FC<React.ComponentProps<typeof Table> & VMListProps> = (props) => {
const { resources } = props;
return (
<Table
{...props}
aria-label={VirtualMachineModel.labelPlural}
Header={VMHeader}
Row={VMRow}
customData={{
pods: resources.pods.data || [],
migrations: resources.migrations.data || [],
}}
/>
);
};

VMList.displayName = 'VMList';

Expand Down Expand Up @@ -122,6 +137,12 @@ const getCreateProps = (namespace: string) => ({

export const VirtualMachinesPage: React.FC<VirtualMachinesPageProps> = (props) => {
const { namespace } = props;
const resources = [
getResource(VirtualMachineModel, { namespace, prop: 'vms' }),
getResource(PodModel, { namespace, prop: 'pods' }),
getResource(VirtualMachineInstanceMigrationModel, { namespace, prop: 'migrations' }),
];

const flatten = ({ vms: { data: vmsData, loaded, loadError } }) =>
loaded && !loadError ? vmsData : [];

Expand All @@ -133,7 +154,7 @@ export const VirtualMachinesPage: React.FC<VirtualMachinesPageProps> = (props) =
rowFilters={filters}
ListComponent={VMList}
createProps={getCreateProps(props.namespace)}
resources={[getResource(VirtualMachineModel, { namespace, prop: 'vms' })]}
resources={resources}
flatten={flatten}
/>
);
Expand All @@ -144,10 +165,18 @@ type VMRowProps = {
index: number;
key: string;
style: object;
customData: {
pods: PodKind[];
migrations: any[];
};
};

type VMListProps = {
data: VMKind[];
resources: {
pods: FirehoseResult<PodKind[]>;
migrations: FirehoseResult<any[]>;
};
};

type VirtualMachinesPageProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ export const getDisks = (volume) => _.get(volume, 'spec.template.spec.domain.dev
export const getVolumes = (vm: VMKind) => _.get(vm, 'spec.template.spec.volumes', []);
export const getDataVolumeTemplates = (vm: VMKind) => _.get(vm, 'spec.dataVolumeTemplates', []);

export const isVMRunning = (value: VMKind) =>
_.get(value, 'spec.running', false) as VMKind['spec']['running'];

export const isVMReady = (value: VMKind) =>
_.get(value, 'status.ready', false) as VMKind['status']['ready'];

export const isVMCreated = (value: VMKind) =>
_.get(value, 'status.created', false) as VMKind['status']['created'];

export const getVmPreferableDiskBus = (vm: VMKind) =>
getDisks(vm)
.map((disk) => getDiskBus(disk))
Expand Down