2017-09-20 12:30:53 +03:00
|
|
|
import React from 'react';
|
2017-10-04 20:27:55 +03:00
|
|
|
import { Row, Col } from 'react-styled-flexboxgrid';
|
2017-09-20 12:30:53 +03:00
|
|
|
import forceArray from 'force-array';
|
2017-10-13 22:51:18 +03:00
|
|
|
import find from 'lodash.find';
|
2017-10-31 12:29:15 +02:00
|
|
|
import remcalc from 'remcalc';
|
|
|
|
import titleCase from 'title-case';
|
2017-09-20 12:30:53 +03:00
|
|
|
|
2017-09-27 17:44:57 +03:00
|
|
|
import {
|
|
|
|
FormGroup,
|
|
|
|
Input,
|
|
|
|
FormLabel,
|
|
|
|
ViewContainer,
|
2017-10-04 20:27:55 +03:00
|
|
|
StatusLoader,
|
2017-10-13 22:51:18 +03:00
|
|
|
Select,
|
|
|
|
Message,
|
|
|
|
MessageTitle,
|
|
|
|
MessageDescription,
|
|
|
|
Button,
|
2017-10-31 12:29:15 +02:00
|
|
|
QueryBreakpoints,
|
|
|
|
Table,
|
|
|
|
TableThead,
|
|
|
|
TableTr,
|
|
|
|
TableTh,
|
|
|
|
TableTbody,
|
|
|
|
TableTd,
|
|
|
|
Checkbox,
|
|
|
|
P,
|
|
|
|
DotIcon,
|
|
|
|
IconActions,
|
|
|
|
PopoverContainer,
|
|
|
|
PopoverTarget,
|
|
|
|
Popover,
|
|
|
|
PopoverItem,
|
|
|
|
PopoverDivider,
|
|
|
|
Anchor
|
2017-09-27 17:44:57 +03:00
|
|
|
} from 'joyent-ui-toolkit';
|
|
|
|
|
2017-10-31 12:29:15 +02:00
|
|
|
const { SmallOnly, Small, Medium } = QueryBreakpoints;
|
2017-09-20 12:30:53 +03:00
|
|
|
|
2017-10-31 12:29:15 +02:00
|
|
|
const stateColor = {
|
|
|
|
PROVISIONING: 'primary',
|
|
|
|
RUNNING: 'green',
|
|
|
|
STOPPING: 'grey',
|
|
|
|
STOPPED: 'grey',
|
|
|
|
DELETED: 'secondaryActive',
|
|
|
|
FAILED: 'red'
|
|
|
|
};
|
|
|
|
|
|
|
|
const Item = ({
|
|
|
|
id,
|
|
|
|
name,
|
|
|
|
state,
|
|
|
|
allowedActions,
|
|
|
|
onStop,
|
|
|
|
onStart,
|
|
|
|
onReboot,
|
|
|
|
onResize,
|
|
|
|
onEnableFw,
|
|
|
|
onDisableFw,
|
|
|
|
onCreateSnap,
|
|
|
|
onStartSnap
|
|
|
|
}) => (
|
|
|
|
<TableTr>
|
|
|
|
<TableTd center middle>
|
|
|
|
<FormGroup name={name} reduxForm>
|
|
|
|
<Checkbox />
|
|
|
|
</FormGroup>
|
|
|
|
</TableTd>
|
|
|
|
<TableTd>
|
|
|
|
<code>{id.substring(0, 7)}</code>
|
|
|
|
</TableTd>
|
|
|
|
<TableTd>
|
|
|
|
<Anchor to={`/instances/${name}`}>{name}</Anchor>
|
|
|
|
</TableTd>
|
|
|
|
<TableTd middle>
|
|
|
|
<DotIcon color={stateColor[state]} /> {titleCase(state)}
|
|
|
|
</TableTd>
|
|
|
|
<TableTd border="left" middle center actionable>
|
|
|
|
<PopoverContainer clickable>
|
|
|
|
<PopoverTarget>
|
|
|
|
<IconActions />
|
|
|
|
</PopoverTarget>
|
|
|
|
<Popover placement="right-start">
|
|
|
|
{!allowedActions.stop ? null : (
|
|
|
|
<PopoverItem onClick={onStop}>Stop</PopoverItem>
|
|
|
|
)}
|
|
|
|
{!allowedActions.start ? null : (
|
|
|
|
<PopoverItem onClick={onStart}>Start</PopoverItem>
|
|
|
|
)}
|
|
|
|
{!allowedActions.reboot ? null : (
|
|
|
|
<PopoverItem onClick={onReboot}>Reboot</PopoverItem>
|
|
|
|
)}
|
|
|
|
{!allowedActions.enableFw ? null : (
|
|
|
|
<PopoverItem onClick={onEnableFw}>Enable Firewall</PopoverItem>
|
|
|
|
)}
|
|
|
|
{!allowedActions.disableFw ? null : (
|
|
|
|
<PopoverItem onClick={onDisableFw}>Disable Firewall</PopoverItem>
|
|
|
|
)}
|
|
|
|
{!allowedActions.disableFw ? null : (
|
|
|
|
<PopoverItem onClick={onDisableFw}>Disable Firewall</PopoverItem>
|
|
|
|
)}
|
|
|
|
<PopoverDivider />
|
|
|
|
{!allowedActions.resize ? null : (
|
|
|
|
<PopoverItem onClick={onResize}>Resize</PopoverItem>
|
|
|
|
)}
|
|
|
|
{!allowedActions.createSnap ? null : (
|
|
|
|
<PopoverItem onClick={onCreateSnap}>Create Snapshot</PopoverItem>
|
|
|
|
)}
|
|
|
|
{!allowedActions.startSnap ? null : (
|
|
|
|
<PopoverItem onClick={onStartSnap}>Start from Snapshot</PopoverItem>
|
|
|
|
)}
|
|
|
|
<PopoverItem>Delete</PopoverItem>
|
|
|
|
</Popover>
|
|
|
|
</PopoverContainer>
|
|
|
|
</TableTd>
|
|
|
|
</TableTr>
|
|
|
|
);
|
2017-10-13 22:51:18 +03:00
|
|
|
|
2017-09-27 17:44:57 +03:00
|
|
|
export default ({
|
2017-10-11 19:59:59 +03:00
|
|
|
instances = [],
|
|
|
|
selected = [],
|
2017-09-27 17:44:57 +03:00
|
|
|
loading,
|
2017-10-13 22:51:18 +03:00
|
|
|
error,
|
2017-09-27 17:44:57 +03:00
|
|
|
handleChange = () => null,
|
2017-10-04 20:27:55 +03:00
|
|
|
onAction = () => null,
|
|
|
|
handleSubmit,
|
2017-10-13 22:51:18 +03:00
|
|
|
submitting = false,
|
|
|
|
pristine = true,
|
2017-10-04 20:27:55 +03:00
|
|
|
...rest
|
2017-09-27 17:44:57 +03:00
|
|
|
}) => {
|
2017-10-11 19:59:59 +03:00
|
|
|
const allowedActions = {
|
|
|
|
stop: selected.some(({ state }) => state === 'RUNNING'),
|
|
|
|
start: selected.some(({ state }) => state !== 'RUNNING'),
|
|
|
|
reboot: true,
|
|
|
|
resize:
|
|
|
|
selected.length === 1 && selected.every(({ brand }) => brand === 'KVM'),
|
2017-10-13 22:51:18 +03:00
|
|
|
// eslint-disable-next-line camelcase
|
2017-10-11 19:59:59 +03:00
|
|
|
enableFw: selected.some(({ firewall_enabled }) => !firewall_enabled),
|
2017-10-13 22:51:18 +03:00
|
|
|
// eslint-disable-next-line camelcase
|
2017-10-11 19:59:59 +03:00
|
|
|
disableFw: selected.some(({ firewall_enabled }) => firewall_enabled),
|
2017-10-13 22:51:18 +03:00
|
|
|
createSnap: selected.length === 1,
|
2017-10-11 19:59:59 +03:00
|
|
|
startSnap:
|
|
|
|
selected.length === 1 &&
|
|
|
|
selected.every(({ snapshots = [] }) => snapshots.length)
|
|
|
|
};
|
|
|
|
|
2017-10-13 22:51:18 +03:00
|
|
|
const handleActions = ev => {
|
|
|
|
ev.stopPropagation();
|
|
|
|
ev.preventDefault();
|
|
|
|
|
2017-10-11 19:59:59 +03:00
|
|
|
onAction({
|
2017-10-13 22:51:18 +03:00
|
|
|
name: ev.target.value,
|
2017-10-11 19:59:59 +03:00
|
|
|
items: selected
|
|
|
|
});
|
2017-10-13 22:51:18 +03:00
|
|
|
};
|
2017-10-11 19:59:59 +03:00
|
|
|
|
2017-10-31 12:29:15 +02:00
|
|
|
const items = forceArray(instances).map(instance => {
|
|
|
|
// eslint-disable-next-line camelcase
|
|
|
|
const { id, name, state, firewall_enabled, snapshots, brand } = instance;
|
2017-10-13 22:51:18 +03:00
|
|
|
const isSelected = Boolean(find(selected, ['id', id]));
|
|
|
|
const isSubmitting = isSelected && submitting;
|
|
|
|
|
2017-10-31 12:29:15 +02:00
|
|
|
const allowedActions = {
|
|
|
|
stop: state === 'RUNNING',
|
|
|
|
start: state !== 'RUNNING',
|
|
|
|
reboot: true,
|
|
|
|
resize: brand === 'KVM',
|
|
|
|
// eslint-disable-next-line camelcase
|
|
|
|
enableFw: !firewall_enabled,
|
|
|
|
// eslint-disable-next-line camelcase
|
|
|
|
disableFw: firewall_enabled,
|
|
|
|
createSnap: true,
|
|
|
|
startSnap: Boolean(snapshots.length)
|
|
|
|
};
|
|
|
|
|
|
|
|
return {
|
|
|
|
...instance,
|
|
|
|
isSubmitting,
|
|
|
|
isSelected,
|
|
|
|
allowedActions,
|
|
|
|
onStop: () => onAction({ name: 'stop', items: [instance] }),
|
|
|
|
onStart: () => onAction({ name: 'start', items: [instance] }),
|
|
|
|
onReboot: () => onAction({ name: 'reboot', items: [instance] }),
|
|
|
|
onResize: () => onAction({ name: 'resize', items: [instance] }),
|
|
|
|
onEnableFw: () => onAction({ name: 'enableFw', items: [instance] }),
|
|
|
|
onDisableFw: () => onAction({ name: 'disableFw', items: [instance] }),
|
|
|
|
onCreateSnap: () => onAction({ name: 'createSnap', items: [instance] }),
|
|
|
|
onStartSnap: () => onAction({ name: 'startSnap', items: [instance] })
|
|
|
|
};
|
2017-10-13 22:51:18 +03:00
|
|
|
});
|
2017-09-20 12:30:53 +03:00
|
|
|
|
2017-09-27 17:44:57 +03:00
|
|
|
const _loading =
|
|
|
|
!items.length && loading ? (
|
|
|
|
<ViewContainer center>
|
|
|
|
<StatusLoader />
|
|
|
|
</ViewContainer>
|
|
|
|
) : null;
|
2017-09-27 17:23:49 +03:00
|
|
|
|
2017-10-13 22:51:18 +03:00
|
|
|
const _error = error &&
|
2017-10-31 12:03:44 +02:00
|
|
|
!submitting && (
|
|
|
|
<Message error>
|
|
|
|
<MessageTitle>Ooops!</MessageTitle>
|
|
|
|
<MessageDescription>{error}</MessageDescription>
|
|
|
|
</Message>
|
|
|
|
);
|
2017-10-13 22:51:18 +03:00
|
|
|
|
2017-10-31 12:29:15 +02:00
|
|
|
const _table = !items.length ? null : (
|
|
|
|
<Table>
|
|
|
|
<TableThead>
|
|
|
|
<TableTr>
|
|
|
|
<TableTh xs="48" />
|
|
|
|
<TableTh xs="80" left bottom>
|
|
|
|
<P>Id</P>
|
|
|
|
</TableTh>
|
|
|
|
<TableTh left bottom>
|
|
|
|
<P>Name</P>
|
|
|
|
</TableTh>
|
|
|
|
<TableTh xs="105" left bottom>
|
|
|
|
<P>Status</P>
|
|
|
|
</TableTh>
|
|
|
|
<TableTh xs="48" />
|
|
|
|
</TableTr>
|
|
|
|
</TableThead>
|
|
|
|
<TableTbody>{items.map(instance => <Item key={instance.id} {...instance} />)}</TableTbody>
|
|
|
|
</Table>
|
|
|
|
);
|
|
|
|
|
2017-09-20 12:30:53 +03:00
|
|
|
return (
|
2017-10-13 22:51:18 +03:00
|
|
|
<form>
|
2017-10-04 20:27:55 +03:00
|
|
|
<Row between="xs">
|
2017-10-13 22:51:18 +03:00
|
|
|
<Col xs={8} sm={8} lg={6}>
|
2017-10-04 20:27:55 +03:00
|
|
|
<Row>
|
|
|
|
<Col xs={7} sm={7} md={6} lg={6}>
|
|
|
|
<FormGroup name="filter" reduxForm>
|
|
|
|
<FormLabel>Filter instances</FormLabel>
|
2017-10-09 16:44:26 +03:00
|
|
|
<Input
|
|
|
|
placeholder="Search for name, state, tags, etc..."
|
2017-10-13 22:51:18 +03:00
|
|
|
disabled={pristine && !items.length}
|
2017-10-09 16:44:26 +03:00
|
|
|
fluid
|
|
|
|
/>
|
2017-10-04 20:27:55 +03:00
|
|
|
</FormGroup>
|
|
|
|
</Col>
|
|
|
|
<Col xs={5} sm={3} lg={3}>
|
|
|
|
<FormGroup name="sort" reduxForm>
|
|
|
|
<FormLabel>Sort</FormLabel>
|
2017-10-09 16:55:22 +03:00
|
|
|
<Select disabled={!items.length} fluid>
|
2017-10-04 20:27:55 +03:00
|
|
|
<option value="name">Name</option>
|
|
|
|
<option value="state">State</option>
|
|
|
|
<option value="primary_ip">IP</option>
|
|
|
|
<option value="image.name">Image</option>
|
|
|
|
<option value="firewall_enabled">Firewall</option>
|
|
|
|
<option value="created">Created</option>
|
|
|
|
<option value="updated">Updated</option>
|
|
|
|
<option value="brand">Brand</option>
|
|
|
|
<option value="memory">Memory</option>
|
|
|
|
<option value="disk">Disk</option>
|
|
|
|
<option value="package.name">Package</option>
|
|
|
|
</Select>
|
|
|
|
</FormGroup>
|
|
|
|
</Col>
|
|
|
|
</Row>
|
|
|
|
</Col>
|
2017-10-13 22:51:18 +03:00
|
|
|
<Col xs={4} sm={4} lg={6}>
|
2017-10-04 20:27:55 +03:00
|
|
|
<Row end="xs">
|
2017-10-13 22:51:18 +03:00
|
|
|
<Col xs={6} sm={4} md={3} lg={2}>
|
2017-10-04 20:27:55 +03:00
|
|
|
<FormGroup>
|
|
|
|
<FormLabel>⁣</FormLabel>
|
|
|
|
<Select
|
|
|
|
value="actions"
|
2017-10-11 19:59:59 +03:00
|
|
|
disabled={!items.length || !selected.length}
|
|
|
|
onChange={handleActions}
|
2017-10-09 16:55:22 +03:00
|
|
|
fluid
|
2017-10-04 20:27:55 +03:00
|
|
|
>
|
|
|
|
<option value="actions" selected disabled>
|
|
|
|
≡
|
|
|
|
</option>
|
2017-10-11 19:59:59 +03:00
|
|
|
<option value="stop" disabled={!allowedActions.stop}>
|
|
|
|
Stop
|
|
|
|
</option>
|
|
|
|
<option value="start" disabled={!allowedActions.start}>
|
|
|
|
Start
|
|
|
|
</option>
|
|
|
|
<option value="reboot" disabled={!allowedActions.reboot}>
|
|
|
|
Reboot
|
|
|
|
</option>
|
|
|
|
<option value="resize" disabled={!allowedActions.resize}>
|
|
|
|
Resize
|
|
|
|
</option>
|
|
|
|
<option value="enableFw" disabled={!allowedActions.enableFw}>
|
|
|
|
Enable Firewall
|
|
|
|
</option>
|
|
|
|
<option
|
|
|
|
value="disableFw"
|
|
|
|
disabled={!allowedActions.disableFw}
|
|
|
|
>
|
|
|
|
Disable Firewall
|
|
|
|
</option>
|
|
|
|
<option
|
|
|
|
value="createSnap"
|
|
|
|
disabled={!allowedActions.createSnap}
|
|
|
|
>
|
|
|
|
Create Snapshot
|
|
|
|
</option>
|
|
|
|
<option
|
|
|
|
value="startSnap"
|
|
|
|
disabled={!allowedActions.startSnap}
|
|
|
|
>
|
|
|
|
Start from Snapshot
|
|
|
|
</option>
|
2017-10-04 20:27:55 +03:00
|
|
|
</Select>
|
|
|
|
</FormGroup>
|
|
|
|
</Col>
|
2017-10-13 22:51:18 +03:00
|
|
|
<Col xs={6} sm={6} md={5} lg={2}>
|
|
|
|
<FormGroup>
|
|
|
|
<FormLabel>⁣</FormLabel>
|
|
|
|
<Button
|
|
|
|
type="button"
|
|
|
|
small
|
|
|
|
icon
|
|
|
|
fluid
|
|
|
|
onClick={() => onAction({ name: 'create' })}
|
|
|
|
>
|
|
|
|
<SmallOnly>+</SmallOnly>
|
|
|
|
<Medium>Create</Medium>
|
|
|
|
</Button>
|
|
|
|
</FormGroup>
|
|
|
|
</Col>
|
2017-10-04 20:27:55 +03:00
|
|
|
</Row>
|
|
|
|
</Col>
|
|
|
|
</Row>
|
2017-10-13 22:51:18 +03:00
|
|
|
{_error}
|
2017-10-31 12:29:15 +02:00
|
|
|
{_loading}
|
|
|
|
{_table}
|
2017-09-20 12:30:53 +03:00
|
|
|
</form>
|
|
|
|
);
|
|
|
|
};
|