This repository has been archived by the owner on Mar 1, 2024. It is now read-only.
forked from openshift/console
-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Networks and Disks to VM details
- Loading branch information
Showing
7 changed files
with
326 additions
and
11 deletions.
There are no files selected for viewing
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 |
---|---|---|
|
@@ -4,3 +4,4 @@ | |
|
||
@import 'components/vm'; | ||
@import 'components/vmconsoles'; | ||
@import 'components/disk'; |
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,4 @@ | ||
.disk-loading { | ||
margin-left: 15px; | ||
left: 0% | ||
} |
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,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
94
frontend/public/kubevirt/components/modals/delete-device-modal.jsx
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,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); |
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,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>; | ||
|
||
}; |
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 @@ | ||
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'; |
Oops, something went wrong.