Skip to content
This repository has been archived by the owner on Mar 1, 2024. It is now read-only.

Commit

Permalink
Add Disk in VM details
Browse files Browse the repository at this point in the history
  • Loading branch information
rawagner authored and mareklibra committed Dec 18, 2018
1 parent 4412857 commit 8424ecb
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 51 deletions.
4 changes: 2 additions & 2 deletions frontend/public/kubevirt/components/_disk.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.disk-loading {
.kubevirt-disk__loading {
margin-left: 15px;
left: 0%
}
}
224 changes: 183 additions & 41 deletions frontend/public/kubevirt/components/disk.jsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,34 @@
import React from 'react';
import * as _ from 'lodash-es';
import { Button, Alert } from 'patternfly-react';
import { List, ColHead, ListHeader, ResourceRow } from './factory/okdfactory';
import { PersistentVolumeClaimModel } from '../models';
import { PersistentVolumeClaimModel, StorageClassModel, VirtualMachineModel } from '../models';
import { Loading, Firehose, Kebab } from './utils/okdutils';
import { getResourceKind, getFlattenForKind } from './utils/resources';
import { DASHES, BUS_VIRTIO, DISK } from './utils/constants';
import { deleteDeviceModal } from './modals/delete-device-modal';
import { CreateDiskRow, getAddDiskPatch, getDisks } from 'kubevirt-web-ui-components';
import { k8sPatch } from '../module/okdk8s';
import { LoadingInline } from './okdcomponents';
import { WithResources } from './utils/withResources';

const visibleRowStyle = 'col-lg-3 col-md-3 col-sm-3 col-xs-4';
const hiddenRowStyle = 'col-lg-3 col-md-3 col-sm-3 hidden-xs';
const columnStyle = 'col-lg-3 col-md-3 col-sm-3 col-xs-3';

const DiskHeader = props => <ListHeader>
<ColHead {...props} className={visibleRowStyle} sortField="name">Name</ColHead>
<ColHead {...props} className={visibleRowStyle}>Size</ColHead>
<ColHead {...props} className={visibleRowStyle}>Interface</ColHead>
<ColHead {...props} className={hiddenRowStyle}>Storage Class</ColHead>
<ColHead {...props} className={columnStyle} sortField="name">Name</ColHead>
<ColHead {...props} className={columnStyle}>Size</ColHead>
<ColHead {...props} className={columnStyle}>Interface</ColHead>
<ColHead {...props} className={columnStyle}>Storage Class</ColHead>
</ListHeader>;

const PvcRow = props => {
const PvcColumn = props => {
if (props.loadError) {
return DASHES;
} else if (props.loaded){
const pvc = props.flatten(props.resources);
return _.get(pvc, props.pvcPath, DASHES);
}
return <Loading className="disk-loading" />;
return <Loading className="kubevirt-disk__loading" />;
};

const menuActionDelete = (vm, storage) => ({
Expand All @@ -41,66 +45,204 @@ const getActions = (vm, nic) => {
return actions.map(a => a(vm, nic));
};

export const DiskRow = ({obj: storage}) => {
const VmDiskRow = ({ storage }) => {
const pvcName = _.get(storage.volume, 'persistentVolumeClaim.claimName');
let sizeRow = DASHES;
let storageRow = DASHES;
let sizeColumn;
let storageColumn;

if (pvcName) {
const pvcs = getResourceKind(PersistentVolumeClaimModel, pvcName, true, storage.vm.metadata.namespace, false);
sizeRow = <Firehose resources={[pvcs]} flatten={getFlattenForKind(PersistentVolumeClaimModel.kind)}>
<PvcRow pvcPath={'spec.resources.requests.storage'} />
sizeColumn = <Firehose resources={[pvcs]} flatten={getFlattenForKind(PersistentVolumeClaimModel.kind)}>
<PvcColumn pvcPath={'spec.resources.requests.storage'} />
</Firehose>;
storageRow = <Firehose resources={[pvcs]} flatten={getFlattenForKind(PersistentVolumeClaimModel.kind)}>
<PvcRow pvcPath={'spec.storageClassName'} />
storageColumn = <Firehose resources={[pvcs]} flatten={getFlattenForKind(PersistentVolumeClaimModel.kind)}>
<PvcColumn pvcPath={'spec.storageClassName'} />
</Firehose>;
} else {
const dataVolumeName = _.get(storage.volume, 'dataVolume.name');
const dataVolume = _.get(storage.vm, 'spec.dataVolumeTemplates', []).find(dv => _.get(dv,'metadata.name') === dataVolumeName);
if (dataVolume) {
sizeRow = _.get(dataVolume,'spec.pvc.resources.requests.storage', DASHES);
storageRow = _.get(dataVolume,'spec.pvc.storageClassName', DASHES);
sizeColumn = _.get(dataVolume,'spec.pvc.resources.requests.storage');
storageColumn = _.get(dataVolume,'spec.pvc.storageClassName');
}
}

return <ResourceRow obj={storage}>
<div className={visibleRowStyle}>
<div className={columnStyle}>
{storage.name}
</div>
<div className={visibleRowStyle}>
{sizeRow}
<div className={columnStyle}>
{sizeColumn || DASHES}
</div>
<div className={visibleRowStyle}>
<div className={columnStyle}>
{_.get(storage, 'disk.bus') || BUS_VIRTIO}
</div>
<div className={hiddenRowStyle}>
{storageRow}
<div className={columnStyle}>
{storageColumn || DASHES}
</div>
<div className="dropdown-kebab-pf">
<Kebab
options={getActions(storage.vm, storage)}
key={`delete-disk-${storage.name}`}
key={`kebab-for--${storage.name}`}
isDisabled={_.get(storage.vm.metadata, 'deletionTimestamp')}
id={`kebab-for-${storage.name}`}
/>
</div>
</ResourceRow>;
};

export const Disk = ({obj: vm}) => {
const disks = _.get(vm, 'spec.template.spec.domain.devices.disks',[]);
const volumes = _.get(vm,'spec.template.spec.volumes',[]);
const storages = disks.map(disk => {
const volume = volumes.find(v => v.name === disk.volumeName);
return {
...disk,
vm,
volume,
};
});
return <div className="co-m-list">
<div className="co-m-pane__body">
<List data={storages} Header={DiskHeader} Row={DiskRow} loaded={true} />
</div>
</div>;

const STORAGE_TYPE_VM = 'storage-type-vm';
const STORAGE_TYPE_CREATE = 'storage-type-create';

export const DiskRow = (onChange, onAccept, onCancel) => ({obj: storage}) => {
const storageClasses = {
resource: getResourceKind(StorageClassModel, undefined, false, undefined, true),
};
switch (storage.storageType) {
case STORAGE_TYPE_VM:
return <VmDiskRow storage={storage} />;
case STORAGE_TYPE_CREATE:
return <div className="row co-resource-list__item">
<Firehose resources={[storageClasses.resource]}>
<WithResources resourceMap={{storageClasses}}>
<CreateDiskRow
storage={storage}
onAccept={onAccept}
onCancel={onCancel}
onChange={onChange}
LoadingComponent={LoadingInline}
/>
</WithResources>
</Firehose>
</div>;
default:
// eslint-disable-next-line
console.warn(`Unknown storage type ${storage.storageType}`);
break;
}
};

const getVmDiskBus = vm => {
const disks = getDisks(vm);
return disks.length > 0 ? _.get(disks[0], 'disk.bus', BUS_VIRTIO) : BUS_VIRTIO;
};

export class Disk extends React.Component {

constructor(props) {
super(props);
this.state = {
newStorage: null,
};
this._getStorages = this.getStorages.bind(this);
this._createStorageHandler = this.createStorageHandler.bind(this);
this._onChange = this.onChange.bind(this);
this._onAccept = this.onAccept.bind(this);
this._onCancel = this.onCancel.bind(this);
this._errorDismissHandler = this.errorDismissHandler.bind(this);
this.DiskRow = DiskRow(this._onChange, this._onAccept, this._onCancel);
}

getStorages(vm) {
const storages = this.state.newStorage ? [{...this.state.newStorage}] : [];
const disks = _.get(vm, 'spec.template.spec.domain.devices.disks',[]);
const volumes = _.get(vm,'spec.template.spec.volumes',[]);
storages.push(...disks.map(disk => {
const volume = volumes.find(v => v.name === disk.volumeName);
return {
...disk,
vm,
volume,
storageType: STORAGE_TYPE_VM,
};
}));
return storages;
}

createStorageHandler() {
this.setState({
newStorage: {
storageType: STORAGE_TYPE_CREATE,
bus: {
value: getVmDiskBus(this.props.obj),
},
},
});
}

onChange(value, key) {
const newStorage = {
...this.state.newStorage,
[key]: value,
};
this.setState({
newStorage,
});
}

onAccept() {
const newStorage = {
...this.state.newStorage,
error: null,
creating: true,
};
const storage = {
name: _.get(newStorage, 'name.value'),
size: _.get(newStorage, 'size.value'),
bus: _.get(newStorage, 'bus.value'),
storageClass: _.get(newStorage, 'storageClass.value'),
};

const addDiskPatch = getAddDiskPatch(this.props.obj, storage);
const patch = k8sPatch(VirtualMachineModel, this.props.obj, addDiskPatch);
patch.then(() => {
this.setState({newStorage: null});
}).catch(error => {
this.setState({
newStorage: {
...this.state.newStorage,
error: error.message || 'Error occured, please try again',
creating: false,
},
});
});
this.setState({
newStorage,
});
}

onCancel() {
this.setState({
newStorage: null,
});
}

errorDismissHandler() {
this.setState({
newStorage: {
...this.state.newStorage,
error: null,
},
});
}

render() {
const vm = this.props.obj;
const storages = this.getStorages(vm);
const alert = _.get(this.state.newStorage, 'error') && <Alert onDismiss={this._errorDismissHandler}>{this.state.newStorage.error}</Alert>;
return <React.Fragment>
<div className="co-m-list">
<div className="co-m-pane__filter-bar">
<div className="co-m-pane__filter-bar-group">
<Button bsStyle="primary" id="create-disk-btn" onClick={this._createStorageHandler} disabled={!!this.state.newStorage}>Create Disk</Button>
</div>
</div>
<div className="co-m-pane__body">
{alert}
<List data={storages} Header={DiskHeader} Row={this.DiskRow} loaded={true} />
</div>
</div>
</React.Fragment>;
}
}
4 changes: 2 additions & 2 deletions frontend/public/kubevirt/components/modals/create-vm-modal.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { resourceLauncher } from '../utils/resourceLauncher';
import { modalResourceLauncher } from '../utils/modalResourceLauncher';
import { CreateVmWizard, TEMPLATE_TYPE_LABEL } from 'kubevirt-web-ui-components';
import { k8sCreate } from '../../module/okdk8s';
import {
Expand All @@ -12,7 +12,7 @@ import { getResourceKind } from '../utils/resources';
import { units } from '../utils/okdutils';

export const openCreateVmWizard = ( activeNamespace, createTemplate = false ) => {
const launcher = resourceLauncher(CreateVmWizard, {
const launcher = modalResourceLauncher(CreateVmWizard, {
namespaces: {
resource: getResourceKind(NamespaceModel, undefined, true, undefined, true),
},
Expand Down
2 changes: 1 addition & 1 deletion frontend/public/kubevirt/components/utils/constants.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export const DASHES = '---';

export const BUS_VIRTIO = 'VirtIO';
export const BUS_VIRTIO = 'virtio';

export const NETWORK_TYPE_MULTUS = 'Multus';
export const NETWORK_TYPE_POD = 'Pod Networking';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { WithResources } from './withResources';
const EMPTY_LIST = [];
const EMPTY_OBJECT = {};

export const resourceLauncher = (Component, resourceMap, resourceToProps) => (props) => {
export const modalResourceLauncher = (Component, resourceMap, resourceToProps) => (props) => {
const modalContainer = document.getElementById('modal-container');

const result = new Promise(resolve => {
Expand Down
12 changes: 10 additions & 2 deletions frontend/public/kubevirt/components/utils/withResources.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export class WithResources extends React.Component {
render() {
const { dispose, children } = this.props;

if (!this.state.loaded) {
if (!this.state.loaded && this.props.showLoader) {
return <Loader onExit={dispose} />;
}

Expand All @@ -96,6 +96,14 @@ WithResources.defaultProps = {
WithResources.propTypes = {
resources: PropTypes.object,
resourceMap: PropTypes.object.isRequired,
dispose: PropTypes.func.isRequired,
dispose: PropTypes.func,
resourceToProps: PropTypes.func,
showLoader: PropTypes.bool,
};

WithResources.defaultProps = {
showLoader: true,
resourceToProps: null,
dispose: null,
resource: undefined,
};
4 changes: 2 additions & 2 deletions frontend/public/kubevirt/components/vm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {
VM_STATUS_TO_TEXT,
} from 'kubevirt-web-ui-components';
import { DASHES, IMPORTER_DV_POD_PREFIX, VIRT_LAUNCHER_POD_PREFIX } from './utils/constants';
import { resourceLauncher } from './utils/resourceLauncher';
import { modalResourceLauncher } from './utils/modalResourceLauncher';
import { showError } from './utils/showErrors';
import VmConsolesConnected from './vmconsoles';
import { Nic } from './nic';
Expand Down Expand Up @@ -75,7 +75,7 @@ const menuActionMigrate = (kind, vm) => ({
hidden: !_.get(vm, 'spec.running', false),
label: 'Migrate Virtual Machine',
callback: () => {
return resourceLauncher(BasicMigrationDialog, {
return modalResourceLauncher(BasicMigrationDialog, {
virtualMachineInstance: {
resource: getResourceKind(VirtualMachineInstanceModel, vm.metadata.name, true, vm.metadata.namespace, false, getLabelMatcher(vm)),
required: true,
Expand Down

0 comments on commit 8424ecb

Please sign in to comment.