Skip to content

Commit

Permalink
Merge pull request #549 from Kitware/volumes-view
Browse files Browse the repository at this point in the history
Volumes view
  • Loading branch information
TristanWright authored Aug 4, 2017
2 parents 920a366 + 83875da commit c06f09d
Show file tree
Hide file tree
Showing 43 changed files with 1,259 additions and 105 deletions.
19 changes: 19 additions & 0 deletions docs/content/docs/usage__ebs-volumes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# EBS Volumes

Complementing the ability to create and run simulations on AWS EC2 instances, HPCCloud also supports mounting of AWS Elastic Block Store (EBS) volumes to provide input and to store output for these simulations. Volumes can also be reused between EC2 instances. For example, you could run a resource intensive simulation on a large expensive cluster and map its output to a EBS volume. You could then use that same volume as input for a visualization job on a smaller cluster which is not as expensive.

## Creating New Volumes

There are two methods to create new volumes. Through the preference panel and a taskflow run:

### Preferences Panel

Volumes created here are not instantiated on AWS but are available to select when starting a new taskflow. To create one you need a valid AWS profile already created and available.

### Taskflow Run

On a run panel there are three fields for volumes, these fields are only available on the with EC2 server type is selected. You can either select an existing volume or you can create a new one by specifying the name and the size. Existing volumes have either been used in previous taskflows or were created from the Preference panel, they must be in the detached or available state. If you provide a volume name and size a volume will be created and attached to the cluster.

## Removing Volumes

To remove a volume the volume needs to be in either the detached or available state. You do so from the volume preference page with the delete button.
1 change: 1 addition & 0 deletions docs/tpl/__en__
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ sidebar:
usage__create-account: Signing up
usage__trad-cluster: Adding traditional clusters
usage__aws-profiles: Adding AWS profiles
usage__ebs-volumes: Adding EBS volumes
usage__creating: Projects and simulations
usage__simput: Simput
usage__running: Running workflows
Expand Down
1 change: 1 addition & 0 deletions docs/tpl/__sidebar__
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ docs:
usage__create-account: usage__create-account.html
usage__trad-cluster: usage__trad-cluster.html
usage__aws-profiles: usage__aws-profiles.html
usage__ebs-volumes: usage__ebs-volumes.html
usage__creating: usage__creating.html
usage__simput: usage__simput.html
usage__running: usage__running.html
Expand Down
2 changes: 1 addition & 1 deletion src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import * as ProjectActions from './redux/actions/projects';
import * as TaskflowActions from './redux/actions/taskflows';
import * as NetworkActions from './redux/actions/network';
import * as Behavior from './StateTransitionBehavior';
import Toaster from './widgets/Toaster';
import Toaster from './panels/Toaster';

import { updateVisualizerStateAccessor } from 'pvw-visualizer/src/redux/selectors/stateAccessor';

Expand Down
8 changes: 4 additions & 4 deletions src/config/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Logout from '../pages/Logout';
import Preferences from '../pages/Preferences';
import PreferencesAWS from '../pages/Preferences/AWS';
import PreferencesCluster from '../pages/Preferences/Cluster';
import PreferencesOpenStack from '../pages/Preferences/OpenStack';
import PreferencesVolumes from '../pages/Preferences/Volumes';
import PreferencesUser from '../pages/Preferences/User';
import PreferencesGroups from '../pages/Preferences/Groups';
import PreferencesStatus from '../pages/Preferences/ServerStatus';
Expand Down Expand Up @@ -181,15 +181,15 @@ export default {
}, {
path: 'Cluster',
component: PreferencesCluster,
}, {
path: 'Volumes',
component: PreferencesVolumes,
}, {
path: 'Status',
component: PreferencesStatus,
}, {
path: 'Network',
component: PreferencesNetwork,
}, {
path: 'OpenStack',
component: PreferencesOpenStack,
},
],
},
Expand Down
2 changes: 2 additions & 0 deletions src/network/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import system from './remote/system';
import taskflows from './remote/taskflows';
import tasks from './remote/tasks';
import user from './remote/user';
import volumes from './remote/volumes';

import ClientBuilder from './remote/GirderClient';

Expand All @@ -34,6 +35,7 @@ const endpoints = [
taskflows,
tasks,
user,
volumes,
];

var url;
Expand Down
2 changes: 1 addition & 1 deletion src/network/remote/tasks.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export default function ({ client, filterQuery, mustContain, busy }) {
export default function ({ client, busy }) {
return {
getTask(id) {
return busy(client._.get(`/tasks/${id}`));
Expand Down
66 changes: 66 additions & 0 deletions src/network/remote/volumes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { transformRequest } from './utils';

const headers = {
'Content-Type': 'application/json',
};

export default function ({ client, filterQuery, mustContain, encodeQueryAsString, busy }) {
return {

// get /volumes
// List available volumes.
listVolumes(limit = null) {
if (limit) {
return busy(client._.get(`/volumes?limit=${limit}`));
}
return busy(client._.get('/volumes'));
},

// post /volumes
// Create a volume
createVolume(volume) {
return busy(client._.post('/volumes', volume, {
transformRequest, headers,
}));
},

// get /volumes/{id}
// Get a volume
getVolume(id) {
return busy(client._.get(`/volumes/${id}`));
},

// delete /volumes/{id}
// Delete a volume
deleteVolume(id) {
return busy(client._.delete(`/volumes/${id}`));
},

// get /volumes/{id}/log
// Get log entries for volume
getVolumeLog(volumeId, offset = 0) {
if (offset) {
return busy(client._.get(`/volumes/${volumeId}/log?offset=${offset}`));
}
return busy(client._.get(`/volumes/${volumeId}/log`));
},

// get /volumes/{id}/status
// Get the volume's current state
getVolumeStatus(id) {
return busy(client._.get(`/volumes/${id}/status`));
},

// put /volumes/{id}/attach/{cluster}
// Attach a volume to a cluster
attachVolume(id, cluster) {
return busy(client._.put(`/volumes/${id}/attach/${cluster}`));
},

// put /volumes/{id}/attach/{cluster}
// Detach a volume to a cluster
detachVolume(id) {
return busy(client._.put(`/volumes/${id}/detach`));
},
};
}
2 changes: 1 addition & 1 deletion src/pages/Preferences/Cluster/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// import client from '../../../network';
import React from 'react';
import ClusterForm from './ClusterForm';
import ActiveList from '../../../panels/ActiveList';
import Toolbar from '../../../panels/Toolbar';
import ButtonBar from '../../../panels/ButtonBar';
import EmptyPlaceholder from '../../../panels/EmptyPlaceholder';
import PresetSelector from '../PresetSelector';
import React from 'react';
import { breadcrumb } from '..';
import getNetworkError from '../../../utils/getNetworkError';

Expand Down
10 changes: 0 additions & 10 deletions src/pages/Preferences/OpenStack/index.js

This file was deleted.

4 changes: 4 additions & 0 deletions src/pages/Preferences/ServerStatus/ClusterStatus.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,15 @@ export default React.createClass({
subtitle={ this.props.status }
onChange={this.props.logToggle}
>
{ this.props.simulation ?
<div>
<p>Active Simulation: <em>{this.props.simulation.name}</em></p>
{ this.props.simulation.step ?
<p>Step: <em>{this.props.simulation.step}</em></p>
: null
}
</div>
: null }
<ExecutionUnit inline logOnly
unit={{ name: 'Log', log: (this.props.log || []), status: '' }}
open
Expand Down
66 changes: 64 additions & 2 deletions src/pages/Preferences/ServerStatus/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,27 @@ import style from 'HPCCloudStyle/JobMonitor.mcss';
import { connect } from 'react-redux';
import { dispatch } from '../../../redux';
import * as ClusterActions from '../../../redux/actions/clusters';
import * as VolumeActions from '../../../redux/actions/volumes';
import { fetchServers } from '../../../redux/actions/statuses';

const noSimulation = { name: 'no simulation on this cluster.', step: '' };

function matchIdInArray(_id, arr, key = 'name') {
for (let i = 0; i < arr.length; i++) {
if (arr[i]._id === _id) {
return arr[i][key];
}
}
return '';
}

const StatusPage = React.createClass({
displayName: 'Preferences/Status',

propTypes: {
ec2: React.PropTypes.array,
volumes: React.PropTypes.array,
volumeLogs: React.PropTypes.object,
ec2Clusters: React.PropTypes.array,
tradClusters: React.PropTypes.array,
network: React.PropTypes.object,
Expand All @@ -31,18 +43,22 @@ const StatusPage = React.createClass({
deleteCluster: React.PropTypes.func,
fetchClusters: React.PropTypes.func,
fetchServers: React.PropTypes.func,
fetchVolumes: React.PropTypes.func,
getVolumeLog: React.PropTypes.func,
},

getDefaultProps() {
return {
ec2: [],
clusters: [],
volumeLogs: {},
};
},

componentDidMount() {
this.props.fetchClusters();
this.props.fetchServers();
this.props.fetchVolumes();
},

logToggle(id, offset) {
Expand All @@ -53,8 +69,27 @@ const StatusPage = React.createClass({
};
},

volumeLogToggle(id, offset) {
return (open) => {
if (open) {
this.props.getVolumeLog(id, offset);
}
};
},

profileMapper(el, index) {
return { name: el.name, value: el.status };
return { _id: el._id, Name: el.name, Region: el.regionName, Status: el.status };
},

volumesMapper(el, index) {
return {
_id: el._id,
Name: el.name,
Size: el.size,
Status: el.status,
Cluster: matchIdInArray(el.clusterId, this.props.ec2Clusters),
Profile: matchIdInArray(el.profileId, this.props.ec2),
};
},

ec2Mapper(el, index) {
Expand Down Expand Up @@ -83,6 +118,15 @@ const StatusPage = React.createClass({
/>);
},

volumeMapper(el, index) {
const log = this.props.volumeLogs[el._id] || [];
const offset = el.log ? el.log.length : 0;
return (<ClusterStatus key={el._id} title={el.name} status={el.status}
log={log} logToggle={this.volumeLogToggle(el._id, offset)}
noControls
/>);
},

render() {
const clusterBreadCrumb = breadcrumb(this.props.user, 'Status');
return (
Expand All @@ -91,8 +135,12 @@ const StatusPage = React.createClass({
onAction={this.addItem} hasTabs
/>
<div className={ style.container }>
<OutputPanel items={ this.props.ec2.map(this.profileMapper) } title="EC2 Profiles" />
{/* AWS Profiles */}
<OutputPanel table title="AWS Profiles"
headers={['Name', 'Region', 'Status']}
items={ this.props.ec2.map(this.profileMapper) } />

{/* EC2 Clusters */}
<div className={ style.toolbar }>
<div className={ style.title }> EC2 Clusters </div>
<div className={ style.buttons } />
Expand All @@ -101,6 +149,16 @@ const StatusPage = React.createClass({
{ this.props.ec2Clusters.map(this.ec2Mapper) }
</div>

{/* Volumes */}
<div className={ style.toolbar }>
<div className={ style.title }> EBS Volumes </div>
<div className={ style.buttons } />
</div>
<div className={ style.taskflowContent }>
{ this.props.volumes.map(this.volumeMapper) }
</div>

{/* Trad Clusters */}
<div className={ style.toolbar }>
<div className={ style.title }> Traditional Clusters </div>
<div className={ style.buttons } />
Expand All @@ -127,6 +185,8 @@ export default connect(
simulations: state.simulations.mapById,
network: state.network,
ec2: localState.statuses.ec2,
volumes: localState.volumes.list,
volumeLogs: localState.volumes.logById,
ec2Clusters,
tradClusters,
user: state.auth.user,
Expand All @@ -138,5 +198,7 @@ export default connect(
deleteCluster: (id) => dispatch(ClusterActions.deleteCluster(id)),
fetchClusters: () => dispatch(ClusterActions.fetchClusters()),
fetchServers: () => dispatch(fetchServers()),
fetchVolumes: () => dispatch(VolumeActions.fetchVolumes()),
getVolumeLog: (id, offset) => dispatch(VolumeActions.getVolumeLog(id, offset)),
})
)(StatusPage);
Loading

0 comments on commit c06f09d

Please sign in to comment.