Skip to content

Commit

Permalink
Feature/slice builder viewer updates (#236)
Browse files Browse the repository at this point in the history
* #217: added suuport for adding Storage to VM in slice builder

* #217: updated slice builder to add local name to Stprage component

* #217: updated Storage details field

* #210: updated the download JSON function to be slice setup instead of Cytoscape format

* #210: updated the download JSON func in Slice Viewer

* #217: added boot script field to slice builder

* #210: added file select/ drop component to slice builder

* #210: completed slice topology slice upload function

* #210: added error catch for slice json upload

* #217: completed boot script support with modification after adding a VM

* #217: fixed boot script name

* #217: added helper text to Boot Script

* #210: parsed CapacityHints and show actual allocated capacities for VM in slice viewer

* #210: fixed capacity parsing issue for configuring slice
  • Loading branch information
yaxue1123 authored Dec 9, 2022
1 parent b58a54a commit dbeaefb
Show file tree
Hide file tree
Showing 17 changed files with 297 additions and 57 deletions.
30 changes: 30 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"react-cytoscapejs": "^1.2.1",
"react-datetime-picker": "^3.5.0",
"react-dom": "^16.14.0",
"react-dropzone": "^14.2.3",
"react-helmet": "^6.1.0",
"react-router-dom": "^5.2.0",
"react-scripts": "^5.0.0",
Expand Down
22 changes: 18 additions & 4 deletions src/components/SliceViewer/DetailForm.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { Component } from 'react'
import { CopyToClipboard } from 'react-copy-to-clipboard';
import CopyButton from "../common/CopyButton";
import { default as portalData } from "../../services/portalData.json";

Expand Down Expand Up @@ -82,15 +81,30 @@ export default class DetailForm extends Component {
}
<div className="row mb-2">
<label>Cores</label>
<input type="number" className="form-control" defaultValue={data.capacities.core} disabled/>
<input
type="text"
className="form-control"
defaultValue={data.capacities.core > 0? data.capacities.core : "configuring"}
disabled
/>
</div>
<div className="row mb-2">
<label>RAM(GB)</label>
<input type="number" className="form-control" defaultValue={data.capacities.ram} disabled/>
<input
type="text"
className="form-control"
defaultValue={data.capacities.ram > 0? data.capacities.ram : "configuring"}
disabled
/>
</div>
<div className="row mb-2">
<label>Disk(GB)</label>
<input type="number" className="form-control" defaultValue={data.capacities.disk} disabled />
<input
type="text"
className="form-control"
defaultValue={data.capacities.disk > 0? data.capacities.disk : "configuring"}
disabled
/>
</div>
</div>
}
Expand Down
37 changes: 23 additions & 14 deletions src/components/SliceViewer/Graph.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import IconLink from '../../imgs/SliceComponentIcons/Link.png';
import IconSharedNIC from '../../imgs/SliceComponentIcons/SharedNIC.png';
import IconSmartNIC from '../../imgs/SliceComponentIcons/SmartNIC.png';
import IconNVME from '../../imgs/SliceComponentIcons/NVME.png';
import IconStorage from '../../imgs/SliceComponentIcons/RotatingStorage.png';
import IconNS from '../../imgs/SliceComponentIcons/NetworkService.png';
import _ from "lodash";
import { OverlayTrigger, Tooltip } from 'react-bootstrap';
Expand Down Expand Up @@ -76,11 +77,6 @@ export default class Graph extends Component {

}

saveJSON = () => {
var jsonBlob = new Blob([ JSON.stringify( this.cy.json() ) ], { type: 'application/javascript;charset=utf-8' });
saveAs( jsonBlob, `${this.props.sliceName}.json` );
}

savePNG = () => {
var png64 = this.cy.png({
'bg': 'white',
Expand All @@ -105,15 +101,18 @@ export default class Graph extends Component {
Reset Layout
</button>
<div className="d-flex flex-row-reverse">
<OverlayTrigger
placement="top"
delay={{ show: 100, hide: 300 }}
overlay={renderTooltip("slice-download-tooltip", "Export the topology in the Cytoscape JSON format used at initialisation.")}
>
<button onClick={this.saveJSON} className="btn btn-sm btn-outline-primary ml-2">
Download JSON
</button>
</OverlayTrigger>
{
this.props.isNewSlice &&
<OverlayTrigger
placement="top"
delay={{ show: 100, hide: 300 }}
overlay={renderTooltip("slice-download-tooltip", "Export the topology setup as JSON file.")}
>
<button onClick={this.props.onSaveJSON} className="btn btn-sm btn-outline-primary ml-2">
Download JSON
</button>
</OverlayTrigger>
}
<button onClick={this.savePNG} className="btn btn-sm btn-outline-primary ml-2">Download PNG</button>
{
this.props.isNewSlice &&
Expand Down Expand Up @@ -219,6 +218,16 @@ export default class Graph extends Component {
"width": 100,
}
},
{
"selector": ".graphStorage",
"style": {
"background-image": `${IconStorage}`,
"background-fit": "contain",
"background-color": "#fff",
"height": 70,
"width": 100,
}
},
{
"selector": ".graphLink",
"style": {
Expand Down
44 changes: 41 additions & 3 deletions src/components/SliceViewer/NewSliceDetailForm.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { Component } from 'react'
import SingleComponent from './SingleComponent';
import validator from "../../utils/sliceValidator";
import Dropfile from "../common/Dropfile";

export default class NewSliceDetailForm extends Component {
state = {
Expand All @@ -12,6 +13,8 @@ export default class NewSliceDetailForm extends Component {
isRamChanged: false,
disk: 0,
isDiskChanged: false,
BootScript: "",
isBootScriptChanged: false,
showVMComponent: false,
validationResult: {
isValid: true,
Expand Down Expand Up @@ -62,6 +65,11 @@ export default class NewSliceDetailForm extends Component {
this.setState({ disk, validationResult, isDiskChanged: true });
}

handleBootScriptChange = (e) => {
const BootScript = e.target.value;
this.setState({ BootScript, isBootScriptChanged: true });
}

handleShowVMComponent = () => {
const showOrNot = this.state.showVMComponent;
this.setState({ showVMComponent: !showOrNot });
Expand All @@ -70,13 +78,15 @@ export default class NewSliceDetailForm extends Component {
handleVMUpdate = (e) => {
e.preventDefault();
const data = this.props.data;
const { nodeName, core, ram, disk, isNameChanged, isCoreChanged, isRamChanged, isDiskChanged } = this.state;
const { nodeName, core, ram, disk, BootScript, isNameChanged, isCoreChanged,
isRamChanged, isDiskChanged, isBootScriptChanged } = this.state;
const newName = isNameChanged ? nodeName : data.properties.name;
const newCore = isCoreChanged ? core : JSON.parse(data.capacities).core;
const newRam = isRamChanged ? ram : JSON.parse(data.capacities).ram;
const newDisk = isDiskChanged ? disk : JSON.parse(data.capacities).disk;
const newBootScript = isBootScriptChanged ? BootScript : data.BootScript;
const capacities = JSON.stringify({"core": newCore, "ram": newRam, "disk": newDisk});
this.props.onVMUpdate({ vm_id: this.props.data.id, new_name: newName, new_capacities: capacities });
this.props.onVMUpdate({ vm_id: this.props.data.id, new_name: newName, new_capacities: capacities, new_boot_script: newBootScript });
}

isCPAvailable = () => {
Expand Down Expand Up @@ -114,6 +124,10 @@ export default class NewSliceDetailForm extends Component {
this.props.onSingleComponentAdd(data);
}

handleFileDrop = (textStr) => {
this.props.onJsonUpload(textStr);
}

getVMComponents = () => {
const vm_component_ids = [];

Expand Down Expand Up @@ -154,7 +168,21 @@ export default class NewSliceDetailForm extends Component {
<form>
{
(!data || !data.properties) && (
<div className="my-3"><i className="fa fa-info-circle mx-2" />Click an element on the topology to view details or make changes. </div>
<div className="my-3">
{
this.props.nodes.length > 0 &&
<span>
<i className="fa fa-info-circle mx-2" />
Click an element on the topology to view details or make changes.
</span>
}
{
this.props.nodes.length === 0 &&
<Dropfile
onFileDrop={this.handleFileDrop}
/>
}
</div>
)
}

Expand Down Expand Up @@ -225,6 +253,16 @@ export default class NewSliceDetailForm extends Component {
{
this.state.showVMComponent &&
<div className="form-row px-3">
<div className="col-12">
<label for="BootScript" className="slice-builder-label">Boot Script (optional)</label>
<textarea
className="form-control"
id="BootScript"
rows="1"
defaultValue={data.BootScript}
onChange={this.handleBootScriptChange}
/>
</div>
<div className="col-12">
<SingleComponent
addedComponents={this.getVMComponents()}
Expand Down
39 changes: 34 additions & 5 deletions src/components/SliceViewer/SideNodes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class SideNodes extends React.Component {
nodeComponents: [],
imageType: "qcow2",
selectedImageRef: "default_rocky_8",
BootScript: ""
}

osImageToAbbr = {
Expand All @@ -36,9 +37,11 @@ class SideNodes extends React.Component {

handleAddNode = () => {
// type: currently only support 'VM'
const { selectedSite, nodeName, nodeType, core, ram, disk, imageType, selectedImageRef, nodeComponents } = this.state;
const { selectedSite, nodeName, nodeType, core, ram, disk,
imageType, selectedImageRef, nodeComponents, BootScript } = this.state;
const image = `${selectedImageRef},${imageType}`;
this.props.onNodeAdd(nodeType, selectedSite, nodeName, Number(core), Number(ram), Number(disk), image, nodeComponents);
this.props.onNodeAdd(nodeType, selectedSite, nodeName, Number(core),
Number(ram), Number(disk), image, nodeComponents, BootScript);
this.setState({
selectedSite: "",
nodeName: "",
Expand All @@ -49,6 +52,7 @@ class SideNodes extends React.Component {
nodeComponents: [],
imageType: "qcow2",
selectedImageRef: "default_rocky_8",
BootScript: "",
})
}

Expand Down Expand Up @@ -103,7 +107,11 @@ class SideNodes extends React.Component {
}

handleImageRefChange = (e) => {
this.setState({ selectedImageRef: e.target.value })
this.setState({ selectedImageRef: e.target.value });
}

handleBootScriptChange = (e) => {
this.setState({ BootScript: e.target.value });
}

getSiteResource = () => {
Expand Down Expand Up @@ -138,14 +146,14 @@ class SideNodes extends React.Component {
}

render() {
const { selectedSite, nodeName, imageType, selectedImageRef, core, ram, disk, nodeComponents } = this.state;
const { selectedSite, nodeName, imageType, selectedImageRef, core, ram,
disk, BootScript, nodeComponents } = this.state;
const validationResult = validator.validateNodeComponents(selectedSite, nodeName, this.props.nodes, core, ram, disk, nodeComponents);
const renderTooltip = (id, content) => (
<Tooltip id={id}>
{content}
</Tooltip>
);

return(
<div>
{this.props.resources !== null &&
Expand Down Expand Up @@ -279,6 +287,27 @@ class SideNodes extends React.Component {
{validationResult.message}
</div>
}
<div className="form-row">
<div className="form-group slice-builder-form-group col-md-12">
<label for="BootScript" className="slice-builder-label">
Boot Script (optional)
<OverlayTrigger
placement="right"
delay={{ show: 100, hide: 300 }}
overlay={renderTooltip("boot-script-tooltip", portalData.helperText.bootScriptDescription)}
>
<i className="fa fa-question-circle text-secondary ml-2"></i>
</OverlayTrigger>
</label>
<textarea
className="form-control"
id="BootScript"
rows="1"
value={BootScript}
onChange={this.handleBootScriptChange}
/>
</div>
</div>
</div>
<div className="mt-2 bg-light node-components-panel">
<SingleComponent
Expand Down
5 changes: 4 additions & 1 deletion src/components/SliceViewer/SingleComponent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@ export default class SingleComponent extends Component {
"GPU": ["RTX6000", "Tesla T4"],
"SmartNIC": ["ConnectX-6", "ConnectX-5"],
"SharedNIC": ["ConnectX-6"],
"NVME": ["P4510"]
"NVME": ["P4510"],
"Storage": ["NAS"]
},
modelDetails: {
"RTX6000": "NVIDIA Corporation TU102GL [Quadro RTX 6000/8000] (rev a1)",
"Tesla T4": "NVIDIA Corporation TU104GL [Tesla T4] (rev a1)",
"ConnectX-6": "Mellanox ConnectX-6 VPI MCX653 dual port 100Gbps",
"ConnectX-5": "Mellanox ConnectX-5 Dual Port 10/25GbE",
"P4510": "Dell Express Flash NVMe P4510 1TB SFF",
"NAS": "Site-local NAS share"
},
componentType: "",
componentName: "",
Expand Down Expand Up @@ -88,6 +90,7 @@ export default class SingleComponent extends Component {
<option value="SmartNIC">SmartNIC</option>
<option value="SharedNIC">SharedNIC</option>
<option value="NVME">NVME</option>
<option value="Storage">Storage</option>
</select>
</div>
<div className="form-group slice-builder-form-group col-md-3">
Expand Down
2 changes: 1 addition & 1 deletion src/components/SshKey/GenerateKey.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class GenerateKey extends Form {
data: {
name: "",
description: "",
keyType: "",
keyType: "sliver",
},
errors: {},
nameTooltip: {
Expand Down
2 changes: 1 addition & 1 deletion src/components/SshKey/UploadKey.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class UploadKey extends Form {
data: {
publickey: "",
description: "",
keyType: "",
keyType: "sliver",
},
errors: {},
publickeyTooltip: {
Expand Down
Loading

0 comments on commit dbeaefb

Please sign in to comment.