-
Notifications
You must be signed in to change notification settings - Fork 593
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Gilad Lekner
authored and
Gilad Lekner
committed
Jul 16, 2020
1 parent
5970c5d
commit 46ec70d
Showing
12 changed files
with
440 additions
and
0 deletions.
There are no files selected for viewing
91 changes: 91 additions & 0 deletions
91
frontend/packages/kubevirt-plugin/src/components/modals/snapshots-modal/snapshots-modal.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import * as React from 'react'; | ||
import { Alert, AlertVariant, Form, TextInput } from '@patternfly/react-core'; | ||
import { prefixedID } from '../../../utils'; | ||
import { HandlePromiseProps, withHandlePromise } from '@console/internal/components/utils'; | ||
import { getName, getNamespace } from '@console/shared'; | ||
import { | ||
createModalLauncher, | ||
ModalBody, | ||
ModalComponentProps, | ||
ModalTitle, | ||
} from '@console/internal/components/factory'; | ||
import { k8sCreate } from '@console/internal/module/k8s'; | ||
import { VMLikeEntityKind } from '../../../types/vmLike'; | ||
import { FormRow } from '../../form/form-row'; | ||
import { ADD_SNAPSHOT, SAVE } from '../../../utils/strings'; | ||
import { ModalFooter } from '../modal/modal-footer'; | ||
import { VMSnapshotWrapper } from '../../../k8s/wrapper/vm/vm-snapshot-wrapper'; | ||
|
||
const getSnapshotName = (vmName: string) => { | ||
const date = new Date(); | ||
const initial = `${vmName}-`; | ||
return initial.concat( | ||
date.getFullYear().toString(), | ||
'-', | ||
(date.getUTCMonth() + 1).toString(), | ||
'-', | ||
date.getDate().toString(), | ||
); | ||
}; | ||
|
||
const SnapshotsModal = withHandlePromise((props: SnapshotsModalProps) => { | ||
const { vmLikeEntity, inProgress, errorMessage, handlePromise, close, cancel } = props; | ||
const vmName = getName(vmLikeEntity); | ||
const [name, setName] = React.useState<string>(getSnapshotName(vmName)); | ||
const asId = prefixedID.bind(null, 'snapshot'); | ||
|
||
const submit = async (e) => { | ||
e.preventDefault(); | ||
const snapshotWrapper = new VMSnapshotWrapper().init({ | ||
name, | ||
namespace: getNamespace(vmLikeEntity), | ||
vmName, | ||
}); | ||
|
||
// eslint-disable-next-line promise/catch-or-return | ||
handlePromise(k8sCreate(snapshotWrapper.getModel(), snapshotWrapper.asResource())).then(close); | ||
}; | ||
|
||
return ( | ||
<div className="modal-content"> | ||
<ModalTitle>{ADD_SNAPSHOT}</ModalTitle> | ||
<ModalBody> | ||
<Alert | ||
title="Snapshot only includes disks backed by a snapshot supported storage class" | ||
isInline | ||
variant={AlertVariant.info} | ||
className="co-m-form-row" | ||
/> | ||
<Form onSubmit={submit}> | ||
<FormRow title="Snapshot Name" fieldId={asId('name')} isRequired> | ||
<TextInput | ||
autoFocus | ||
isRequired | ||
id={asId('name')} | ||
value={name} | ||
onChange={(v) => setName(v)} | ||
/> | ||
</FormRow> | ||
</Form> | ||
</ModalBody> | ||
<ModalFooter | ||
id="snapshot" | ||
submitButtonText={SAVE} | ||
errorMessage={errorMessage} | ||
isDisabled={inProgress} | ||
onSubmit={submit} | ||
onCancel={(e) => { | ||
e.stopPropagation(); | ||
cancel(); | ||
}} | ||
/> | ||
</div> | ||
); | ||
}); | ||
|
||
export default createModalLauncher(SnapshotsModal); | ||
|
||
export type SnapshotsModalProps = { | ||
vmLikeEntity: VMLikeEntityKind; | ||
} & ModalComponentProps & | ||
HandlePromiseProps; |
119 changes: 119 additions & 0 deletions
119
frontend/packages/kubevirt-plugin/src/components/vm-snapshots/snapshot-row.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import * as React from 'react'; | ||
import { TableData, TableRow, RowFunction } from '@console/internal/components/factory'; | ||
import { | ||
asAccessReview, | ||
Kebab, | ||
KebabOption, | ||
ResourceLink, | ||
Timestamp, | ||
} from '@console/internal/components/utils'; | ||
import { deleteModal } from '@console/internal/components/modals'; | ||
import { | ||
getName, | ||
getNamespace, | ||
dimensifyRow, | ||
getCreationTimestamp, | ||
Status, | ||
ErrorStatus, | ||
} from '@console/shared'; | ||
import { referenceFor } from '@console/internal/module/k8s'; | ||
import { VirtualMachineSnapshotModel } from '../../models'; | ||
import { isVMI } from '../../selectors/check-type'; | ||
import { VMLikeEntityKind } from '../../types/vmLike'; | ||
import { VMSnapshotRowActionOpts, VMSnapshotRowCustomData } from './types'; | ||
import { VMSnapshot } from '../../types'; | ||
import { getVMSnapshotError, isVMSnapshotReady } from '../../selectors/snapshot/snapshot'; | ||
|
||
// TODO: add revertMenuAction once implemented | ||
|
||
const menuActionDelete = ( | ||
snapshot: VMSnapshot, | ||
{ withProgress }: { withProgress: (promise: Promise<any>) => void }, | ||
): KebabOption => ({ | ||
label: 'Delete', | ||
callback: () => | ||
withProgress( | ||
deleteModal({ | ||
kind: VirtualMachineSnapshotModel, | ||
resource: snapshot, | ||
}), | ||
), | ||
accessReview: asAccessReview(VirtualMachineSnapshotModel, snapshot, 'delete'), | ||
}); | ||
|
||
const getActions = ( | ||
snapshot: VMSnapshot, | ||
vmLikeEntity: VMLikeEntityKind, | ||
opts: VMSnapshotRowActionOpts, | ||
) => { | ||
if (isVMI(vmLikeEntity)) { | ||
return []; | ||
} | ||
const actions = [menuActionDelete]; | ||
return actions.map((a) => a(snapshot, opts)); | ||
}; | ||
|
||
export type VMSnapshotSimpleRowProps = { | ||
data: VMSnapshot; | ||
columnClasses: string[]; | ||
actionsComponent: React.ReactNode; | ||
index: number; | ||
style: object; | ||
}; | ||
|
||
export const SnapshotSimpleRow: React.FC<VMSnapshotSimpleRowProps> = ({ | ||
data: snapshot, | ||
columnClasses, | ||
actionsComponent, | ||
index, | ||
style, | ||
}) => { | ||
const dimensify = dimensifyRow(columnClasses); | ||
const name = getName(snapshot); | ||
const namespace = getNamespace(snapshot); | ||
const error = getVMSnapshotError(snapshot); | ||
const readyToUse = isVMSnapshotReady(snapshot); | ||
|
||
return ( | ||
<TableRow id={snapshot?.metadata?.uid} index={index} trKey={name} style={style}> | ||
<TableData className={dimensify()}> | ||
<ResourceLink | ||
kind={referenceFor(VirtualMachineSnapshotModel)} | ||
namespace={namespace} | ||
name={name} | ||
/> | ||
</TableData> | ||
<TableData className={dimensify()}> | ||
<Timestamp timestamp={getCreationTimestamp(snapshot)} /> | ||
</TableData> | ||
<TableData className={dimensify()}> | ||
{error ? ( | ||
<ErrorStatus>{error?.message}</ErrorStatus> | ||
) : ( | ||
<Status status={readyToUse ? 'Ready' : 'Not Ready'} /> | ||
)} | ||
</TableData> | ||
<TableData className={dimensify(true)}>{actionsComponent}</TableData> | ||
</TableRow> | ||
); | ||
}; | ||
|
||
export const SnapshotRow: RowFunction<VMSnapshot, VMSnapshotRowCustomData> = ({ | ||
obj: snapshot, | ||
customData: { withProgress, vmLikeEntity, columnClasses }, | ||
index, | ||
style, | ||
}) => ( | ||
<SnapshotSimpleRow | ||
data={snapshot} | ||
columnClasses={columnClasses} | ||
index={index} | ||
style={style} | ||
actionsComponent={ | ||
<Kebab | ||
options={getActions(snapshot, vmLikeEntity, { withProgress })} | ||
id={`kebab-for-${getName(snapshot)}`} | ||
/> | ||
} | ||
/> | ||
); |
9 changes: 9 additions & 0 deletions
9
frontend/packages/kubevirt-plugin/src/components/vm-snapshots/types.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { VMLikeEntityKind } from '../../types/vmLike'; | ||
|
||
export type VMSnapshotRowActionOpts = { withProgress: (promise: Promise<any>) => void }; | ||
|
||
export type VMSnapshotRowCustomData = { | ||
vmLikeEntity: VMLikeEntityKind; | ||
columnClasses: string[]; | ||
isDisabled: boolean; | ||
} & VMSnapshotRowActionOpts; |
9 changes: 9 additions & 0 deletions
9
frontend/packages/kubevirt-plugin/src/components/vm-snapshots/utils.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import * as classNames from 'classnames'; | ||
import { Kebab } from '@console/internal/components/utils'; | ||
|
||
export const snapshotsTableColumnClasses = [ | ||
classNames('col-lg-2'), | ||
classNames('col-lg-2'), | ||
classNames('col-lg-2'), | ||
Kebab.columnClass, | ||
]; |
139 changes: 139 additions & 0 deletions
139
frontend/packages/kubevirt-plugin/src/components/vm-snapshots/vm-snapshots.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
import * as React from 'react'; | ||
import { Table, RowFunction } from '@console/internal/components/factory'; | ||
import { sortable } from '@patternfly/react-table'; | ||
import { getName, getNamespace, dimensifyHeader } from '@console/shared'; | ||
import { useSafetyFirst } from '@console/internal/components/safety-first'; | ||
import { Button } from '@patternfly/react-core'; | ||
import { EmptyBox } from '@console/internal/components/utils'; | ||
import { | ||
WatchK8sResource, | ||
useK8sWatchResource, | ||
} from '@console/internal/components/utils/k8s-watch-hook'; | ||
import { getVmSnapshotVmName } from '../../selectors/snapshot/snapshot'; | ||
import { VMSnapshot } from '../../types'; | ||
import { isVMI } from '../../selectors/check-type'; | ||
import { wrapWithProgress } from '../../utils/utils'; | ||
import { VMLikeEntityTabProps } from '../vms/types'; | ||
import { snapshotsTableColumnClasses } from './utils'; | ||
import { ADD_SNAPSHOT } from '../../utils/strings'; | ||
import { VirtualMachineSnapshotModel } from '../../models'; | ||
import { SnapshotRow } from './snapshot-row'; | ||
import SnapshotModal from '../modals/snapshots-modal/snapshots-modal'; | ||
import { asVM, isVMRunningOrExpectedRunning } from '../../selectors/vm'; | ||
|
||
export type VMSnapshotsTableProps = { | ||
data?: any[]; | ||
customData?: object; | ||
row: RowFunction; | ||
columnClasses: string[]; | ||
loadError: any; | ||
loaded: boolean; | ||
}; | ||
|
||
const NoDataEmptyMsg = () => <EmptyBox label="Snapshots" />; | ||
|
||
export const VMSnapshotsTable: React.FC<VMSnapshotsTableProps> = ({ | ||
data, | ||
customData, | ||
row: Row, | ||
columnClasses, | ||
loaded, | ||
loadError, | ||
}) => ( | ||
<Table | ||
aria-label="VM Snapshots List" | ||
loaded={loaded} | ||
loadError={loadError} | ||
data={data} | ||
NoDataEmptyMsg={NoDataEmptyMsg} | ||
Header={() => | ||
dimensifyHeader( | ||
[ | ||
{ | ||
title: 'Name', | ||
sortField: 'metadata.name', | ||
transforms: [sortable], | ||
}, | ||
{ | ||
title: 'Created', | ||
sortField: 'metadata.creationTimestamp', | ||
transforms: [sortable], | ||
}, | ||
{ | ||
title: 'Status', | ||
sortField: 'status.readyToUse', | ||
transforms: [sortable], | ||
}, | ||
{ | ||
title: '', | ||
}, | ||
], | ||
columnClasses, | ||
) | ||
} | ||
Row={Row} | ||
customData={{ ...customData, columnClasses }} | ||
virtualize | ||
/> | ||
); | ||
|
||
export const VMSnapshotsPage: React.FC<VMLikeEntityTabProps> = ({ obj: vmLikeEntity }) => { | ||
const vmName = getName(vmLikeEntity); | ||
const namespace = getNamespace(vmLikeEntity); | ||
|
||
const resource: WatchK8sResource = React.useMemo( | ||
() => ({ | ||
isList: true, | ||
kind: VirtualMachineSnapshotModel.kind, | ||
namespaced: true, | ||
namespace, | ||
}), | ||
[namespace], | ||
); | ||
|
||
const [snapshots, snapshotsLoaded, snapshotsError] = useK8sWatchResource<VMSnapshot[]>(resource); | ||
const [isLocked, setIsLocked] = useSafetyFirst(false); | ||
const withProgress = wrapWithProgress(setIsLocked); | ||
const filteredSnapshots = snapshots.filter((snap) => getVmSnapshotVmName(snap) === vmName); | ||
const isDisabled = isLocked || isVMRunningOrExpectedRunning(asVM(vmLikeEntity)); | ||
|
||
return ( | ||
<div className="co-m-list"> | ||
{!isVMI(vmLikeEntity) && ( | ||
<div className="co-m-pane__filter-bar"> | ||
<div className="co-m-pane__filter-bar-group"> | ||
<Button | ||
variant="primary" | ||
id="add-snapshot" | ||
onClick={() => | ||
withProgress( | ||
SnapshotModal({ | ||
blocking: true, | ||
vmLikeEntity, | ||
}).result, | ||
) | ||
} | ||
isDisabled={isDisabled} | ||
> | ||
{ADD_SNAPSHOT} | ||
</Button> | ||
</div> | ||
</div> | ||
)} | ||
<div className="co-m-pane__body"> | ||
<VMSnapshotsTable | ||
loaded={snapshotsLoaded} | ||
loadError={snapshotsError} | ||
data={filteredSnapshots} | ||
customData={{ | ||
vmLikeEntity, | ||
withProgress, | ||
isDisabled, | ||
}} | ||
row={SnapshotRow} | ||
columnClasses={snapshotsTableColumnClasses} | ||
/> | ||
</div> | ||
</div> | ||
); | ||
}; |
Oops, something went wrong.