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

Add Networks and Disks to VM details #91

Merged
merged 1 commit into from
Nov 8, 2018
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
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