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

Commit

Permalink
Add Networks and Disks to VM details
Browse files Browse the repository at this point in the history
  • Loading branch information
rawagner committed Nov 8, 2018
1 parent 6533c59 commit 84377c6
Show file tree
Hide file tree
Showing 7 changed files with 326 additions and 11 deletions.
1 change: 1 addition & 0 deletions frontend/public/kubevirt/_style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@

@import 'components/vm';
@import 'components/vmconsoles';
@import 'components/disk';
4 changes: 4 additions & 0 deletions frontend/public/kubevirt/components/_disk.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.disk-loading {
margin-left: 15px;
left: 0%
}
106 changes: 106 additions & 0 deletions frontend/public/kubevirt/components/disk.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import React from 'react';
import * as _ from 'lodash';
import { List, ColHead, ListHeader, ResourceRow } from './factory/okdfactory';
import { PersistentVolumeClaimModel } from '../models';
import { Loading, Firehose, Cog } from './utils/okdutils';
import { getResourceKind, getFlattenForKind } from './utils/resources';
import { DASHES, BUS_VIRTIO, DISK } from './utils/constants';
import { deleteDeviceModal } from './modals/delete-device-modal';

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 DiskHeader = props => <ListHeader>
<ColHead {...props} className={visibleRowStyle} sortField="name">Disk Name</ColHead>
<ColHead {...props} className={visibleRowStyle}>Size</ColHead>
<ColHead {...props} className={visibleRowStyle}>Interface</ColHead>
<ColHead {...props} className={hiddenRowStyle}>Storage Class</ColHead>
</ListHeader>;

const PvcRow = 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" />;
};

const menuActionDelete = (vm, storage) => ({
label: 'Delete',
callback: () => deleteDeviceModal({
type: DISK,
device: storage,
vm: vm
})
});

const getActions = (vm, nic) => {
const actions = [menuActionDelete];
return actions.map(a => a(vm, nic));
};

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

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'} />
</Firehose>;
storageRow = <Firehose resources={[pvcs]} flatten={getFlattenForKind(PersistentVolumeClaimModel.kind)}>
<PvcRow 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);
}
}

return <ResourceRow obj={storage}>
<div className={visibleRowStyle}>
{storage.name}
</div>
<div className={visibleRowStyle}>
{sizeRow}
</div>
<div className={visibleRowStyle}>
{_.get(storage, 'disk.bus') || BUS_VIRTIO}
</div>
<div className={hiddenRowStyle}>
{storageRow}
</div>
<div className="co-resource-kebab">
<Cog
options={getActions(storage.vm, storage)}
key={`delete-disk-${storage.name}`}
isDisabled={_.get(storage.vm.metadata, 'deletionTimestamp')}
id={`cog-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>;
};
94 changes: 94 additions & 0 deletions frontend/public/kubevirt/components/modals/delete-device-modal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import React from 'react';
import PropTypes from 'prop-types';
import * as _ from 'lodash';
import { Form } from 'patternfly-react';

import { PromiseComponent } from '../utils/okdutils';
import { createModalLauncher, ModalTitle, ModalBody, ModalSubmitFooter } from '../factory/okdfactory';
import { k8sPatch } from '../../module/okdk8s';
import { VirtualMachineModel } from '../../models';
import { NETWORK, DISK } from '../utils/constants';

class DeleteDeviceModal extends PromiseComponent {
constructor(props) {
super(props);
this._cancel = this.props.cancel.bind(this);
this._submit = this.submit.bind(this);
}

submit(event) {
event.preventDefault();
const { vm, type, device } = this.props;

const deviceType = type === NETWORK ? {type: 'interfaces', spec: 'networks'} : { type: 'disks', spec: 'volumes'};
const devices = _.get(vm, `spec.template.spec.domain.devices.${deviceType.type}`, []);

const deviceIndex = devices.findIndex(d => d.name === device.name);
const specIndex = _.get(vm, `spec.template.spec.${deviceType.spec}`,[]).findIndex(spec => spec.name === device.name || spec.name === device.volumeName);

const patch = [];

if (deviceIndex !== -1) {
patch.push({
op: 'remove',
path: `/spec/template/spec/domain/devices/${deviceType.type}/${deviceIndex}`,
});
}

if (specIndex !== -1) {
patch.push({
op: 'remove',
path: `/spec/template/spec/${deviceType.spec}/${specIndex}`,
});
}

// disk may have dataVolumeTemplate defined that should be deleted too
if (type === DISK && _.get(device, 'volume.dataVolume') && _.get(vm, 'spec.dataVolumeTemplates')) {
const dataVolumeIndex = vm.spec.dataVolumeTemplates.findIndex(dataVolume => _.get(dataVolume, 'metadata.name') === device.volume.dataVolume.name);
if (dataVolumeIndex !== -1) {
patch.push({
op: 'remove',
path: `/spec/dataVolumeTemplates/${dataVolumeIndex}`,
});
}
}

// if pod network is deleted, we need to set autoAttachPodInterface to false
if (type === NETWORK && _.get(device, 'network.pod')) {
const op = _.has(vm, 'spec.domain.devices.autoAttachPodInterface') ? 'replace' : 'add';
patch.push({
op,
path: '/spec/template/spec/domain/devices/autoAttachPodInterface',
value: false
});
}

if (patch.length === 0) {
this.props.close();
} else {
const promise = k8sPatch(VirtualMachineModel, vm, patch);
this.handlePromise(promise).then(this.props.close);
}
}

render () {
const {vm, device} = this.props;
return <Form onSubmit={this._submit}>
<ModalTitle>Delete {device.name} from {vm.metadata.name}</ModalTitle>
<ModalBody>
Are you sure you want to delete <strong>{device.name}</strong>
<span> from <strong>{vm.metadata.name} </strong>?</span>
</ModalBody>
<ModalSubmitFooter errorMessage="" inProgress={false} submitText={'Delete'} cancel={this._cancel} />
</Form>;
}
}

DeleteDeviceModal.propTypes = {
device: PropTypes.object.isRequired,
vm: PropTypes.object.isRequired,
type: PropTypes.string.isRequired,
close: PropTypes.func.isRequired
};

export const deleteDeviceModal = createModalLauncher(DeleteDeviceModal);
83 changes: 83 additions & 0 deletions frontend/public/kubevirt/components/nic.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React from 'react';
import * as _ from 'lodash';
import { Cog } from './utils/okdutils';
import { List, ColHead, ListHeader, ResourceRow } from './factory/okdfactory';
import { DASHES, BUS_VIRTIO, NETWORK_TYPE_MULTUS, NETWORK_TYPE_POD, NETWORK } from './utils/constants';
import { deleteDeviceModal } from './modals/delete-device-modal';

const getNetworkType = network => {
if (network) {
if (network.hasOwnProperty('pod')){
return NETWORK_TYPE_POD;
} else if (network.hasOwnProperty('multus')){
return NETWORK_TYPE_MULTUS;
}
}
return DASHES;
};

const menuActionDelete = (vm, nic) => ({
label: 'Delete',
callback: () => deleteDeviceModal({
type: NETWORK,
device: nic,
vm: vm
})
});

const getActions = (vm, nic) => {
const actions = [menuActionDelete];
return actions.map(a => a(vm, nic));
};

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 NicHeader = props => <ListHeader>
<ColHead {...props} className={visibleRowStyle} sortField="name">Nic Name</ColHead>
<ColHead {...props} className={visibleRowStyle} sortField="model">Model</ColHead>
<ColHead {...props} className={visibleRowStyle} sortField="network">Network</ColHead>
<ColHead {...props} className={hiddenRowStyle} sortField="macAddress">Mac Address</ColHead>
</ListHeader>;

export const NicRow = ({obj: nic}) => <ResourceRow obj={nic}>
<div className={visibleRowStyle} co-resource-link-wrapper>
{nic.name}
</div>
<div className={visibleRowStyle}>
{nic.model || BUS_VIRTIO}
</div>
<div className={visibleRowStyle}>
{getNetworkType(nic.network)}
</div>
<div className={hiddenRowStyle}>
{nic.macAddress || DASHES}
</div>
<div className="co-resource-kebab">
<Cog
options={getActions(nic.vm, nic)}
key={`delete-nic-${nic.name}`}
isDisabled={_.get(nic.vm.metadata, 'deletionTimestamp')}
id={`cog-for-${nic.name}`}
/>
</div>
</ResourceRow>;

export const Nic = ({obj: vm}) => {
const interfaces = _.get(vm,'spec.template.spec.domain.devices.interfaces',[]);
const networks = _.get(vm,'spec.template.spec.networks',[]);
const nics = interfaces.map(i => {
const network = networks.find(n => n.name === i.name);
return {
...i,
network,
vm: vm
};
});
return <div className="co-m-list">
<div className="co-m-pane__body">
<List data={nics} Header={NicHeader} Row={NicRow} loaded={true} />
</div>
</div>;

};
9 changes: 9 additions & 0 deletions frontend/public/kubevirt/components/utils/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const DASHES = '---';

export const BUS_VIRTIO = 'VirtIO';

export const NETWORK_TYPE_MULTUS = 'Multus';
export const NETWORK_TYPE_POD = 'Pod Networking';

export const NETWORK = 'Network';
export const DISK = 'Disk';
Loading

0 comments on commit 84377c6

Please sign in to comment.