From 7a2a88b7ad93e95d890c6e2d97d131cc564fa2e3 Mon Sep 17 00:00:00 2001 From: "Rajaa.BARHTAOUI" Date: Thu, 21 Feb 2019 11:43:40 +0100 Subject: [PATCH] feat(xo-web/new-network): dedicated view (#3906) Fixes #3895 --- CHANGELOG.unreleased.md | 1 + packages/xo-web/src/common/intl/messages.js | 11 +- .../xo/create-bonded-network-modal/index.js | 113 -------- .../common/xo/create-network-modal/index.js | 84 ------ packages/xo-web/src/common/xo/index.js | 35 +-- packages/xo-web/src/icons.scss | 11 + packages/xo-web/src/xo-app/menu/index.js | 5 + packages/xo-web/src/xo-app/new/index.js | 2 + .../xo-web/src/xo-app/new/network/index.css | 5 + .../xo-web/src/xo-app/new/network/index.js | 242 ++++++++++++++++++ .../xo-web/src/xo-app/pool/tab-network.js | 17 +- 11 files changed, 280 insertions(+), 246 deletions(-) delete mode 100644 packages/xo-web/src/common/xo/create-bonded-network-modal/index.js delete mode 100644 packages/xo-web/src/common/xo/create-network-modal/index.js create mode 100644 packages/xo-web/src/xo-app/new/network/index.css create mode 100644 packages/xo-web/src/xo-app/new/network/index.js diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index 9f90de839..c8209c8cc 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -5,6 +5,7 @@ - [VM migration] Display same-pool hosts first in the selector [#3262](https://github.com/vatesfr/xen-orchestra/issues/3262) (PR [#3890](https://github.com/vatesfr/xen-orchestra/pull/3890)) - [Home/VM] Sort VM by start time [#3955](https://github.com/vatesfr/xen-orchestra/issues/3955) (PR [#3970](https://github.com/vatesfr/xen-orchestra/pull/3970)) - [Editable fields] Unfocusing (clicking outside) submits the change instead of canceling (PR [#3980](https://github.com/vatesfr/xen-orchestra/pull/3980)) +- [Network] Dedicated page for network creation [#3895](https://github.com/vatesfr/xen-orchestra/issues/3895) (PR [#3906](https://github.com/vatesfr/xen-orchestra/pull/3906)) ### Bug fixes diff --git a/packages/xo-web/src/common/intl/messages.js b/packages/xo-web/src/common/intl/messages.js index 9a3024fec..db5cea6ba 100644 --- a/packages/xo-web/src/common/intl/messages.js +++ b/packages/xo-web/src/common/intl/messages.js @@ -101,6 +101,7 @@ const messages = { newMenu: 'New', taskMenu: 'Tasks', taskPage: 'Tasks', + newNetworkPage: 'Network', newVmPage: 'VM', newSrPage: 'Storage', newServerPage: 'Server', @@ -563,6 +564,10 @@ const messages = { newSrNfsOptions: 'Comma delimited NFS options', reattachNewSrTooltip: 'Reattach SR', + // ------ New Newtork ----- + createNewNetworkNoPermission: 'You have no permission to create a network', + createNewNetworkOn: 'Create a new network on {select}', + // ----- Acls, Users, Groups ------ subjectName: 'Users/Groups', objectName: 'Object', @@ -1690,7 +1695,6 @@ const messages = { // ----- Network ----- newNetworkCreate: 'Create network', - newBondedNetworkCreate: 'Create bonded network', newNetworkInterface: 'Interface', newNetworkName: 'Name', newNetworkDescription: 'Description', @@ -1698,13 +1702,14 @@ const messages = { newNetworkDefaultVlan: 'No VLAN if empty', newNetworkMtu: 'MTU', newNetworkDefaultMtu: 'Default: 1500', - newNetworkNoNameErrorTitle: 'Name required', - newNetworkNoNameErrorMessage: 'A name is required to create a network', newNetworkBondMode: 'Bond mode', + newNetworkInfo: 'Info', + newNetworkType: 'Type', deleteNetwork: 'Delete network', deleteNetworkConfirm: 'Are you sure you want to delete this network?', networkInUse: 'This network is currently in use', pillBonded: 'Bonded', + bondedNetwork: 'Bonded network', // ----- Add host ----- addHostSelectHost: 'Host', diff --git a/packages/xo-web/src/common/xo/create-bonded-network-modal/index.js b/packages/xo-web/src/common/xo/create-bonded-network-modal/index.js deleted file mode 100644 index e701caefb..000000000 --- a/packages/xo-web/src/common/xo/create-bonded-network-modal/index.js +++ /dev/null @@ -1,113 +0,0 @@ -import Component from 'base-component' -import map from 'lodash/map' -import React from 'react' -import { createGetObject, createSelector } from 'selectors' -import { getBondModes } from 'xo' -import { injectIntl } from 'react-intl' - -import _, { messages } from '../../intl' -import { Col } from '../../grid' -import { connectStore } from '../../utils' -import { SelectPif } from '../../select-objects' -import SingleLineRow from '../../single-line-row' - -@connectStore( - () => ({ - poolMaster: createSelector( - createGetObject((_, props) => props.pool), - pool => pool.master - ), - }), - { withRef: true } -) -class CreateBondedNetworkModalBody extends Component { - componentWillMount() { - getBondModes().then(bondModes => - this.setState({ bondModes, bondMode: bondModes[0] }) - ) - } - - _getPifPredicate = createSelector( - () => this.props.poolMaster, - hostId => pif => pif.$host === hostId && pif.vlan === -1 - ) - - get value() { - const { name, description, pifs, mtu, bondMode } = this.state - return { - pool: this.props.pool, - name, - description, - pifs: map(pifs, pif => pif.id), - mtu, - bondMode, - } - } - - render() { - const { formatMessage } = this.props.intl - return ( -
- - {_('newNetworkInterface')} - - - - -   - - {_('newNetworkName')} - - - - -   - - {_('newNetworkDescription')} - - - - -   - - {_('newNetworkMtu')} - - - - -   - - {_('newNetworkBondMode')} - - - - -
- ) - } -} -export default injectIntl(CreateBondedNetworkModalBody, { withRef: true }) diff --git a/packages/xo-web/src/common/xo/create-network-modal/index.js b/packages/xo-web/src/common/xo/create-network-modal/index.js deleted file mode 100644 index 1a8e75fa1..000000000 --- a/packages/xo-web/src/common/xo/create-network-modal/index.js +++ /dev/null @@ -1,84 +0,0 @@ -import React, { Component } from 'react' -import { injectIntl } from 'react-intl' -import { createSelector } from 'selectors' - -import SingleLineRow from '../../single-line-row' -import _, { messages } from '../../intl' -import { SelectPif } from '../../select-objects' -import { Col } from '../../grid' - -class CreateNetworkModalBody extends Component { - _getPifPredicate = createSelector( - () => { - const { container } = this.props - return container.type === 'pool' ? container.master : container.id - }, - hostId => pif => pif.$host === hostId && pif.vlan === -1 - ) - - get value() { - const { refs } = this - const { container } = this.props - return { - pool: container.$pool, - name: refs.name.value, - description: refs.description.value, - pif: refs.pif.value && refs.pif.value.id, - mtu: refs.mtu.value, - vlan: refs.vlan.value, - } - } - - render() { - const { formatMessage } = this.props.intl - return ( -
- - {_('newNetworkInterface')} - - - - -   - - {_('newNetworkName')} - - - - -   - - {_('newNetworkDescription')} - - - - -   - - {_('newNetworkVlan')} - - - - -   - - {_('newNetworkMtu')} - - - - -
- ) - } -} -export default injectIntl(CreateNetworkModalBody, { withRef: true }) diff --git a/packages/xo-web/src/common/xo/index.js b/packages/xo-web/src/common/xo/index.js index 3efbd934b..5464407da 100644 --- a/packages/xo-web/src/common/xo/index.js +++ b/packages/xo-web/src/common/xo/index.js @@ -1627,39 +1627,10 @@ export const setVif = ( export const editNetwork = (network, props) => _call('network.set', { ...props, id: resolveId(network) }) -import CreateNetworkModalBody from './create-network-modal' // eslint-disable-line import/first -export const createNetwork = container => - confirm({ - icon: 'network', - title: _('newNetworkCreate'), - body: , - }).then(params => { - if (!params.name) { - return error( - _('newNetworkNoNameErrorTitle'), - _('newNetworkNoNameErrorMessage') - ) - } - return _call('network.create', params) - }, noop) - export const getBondModes = () => _call('network.getBondModes') - -import CreateBondedNetworkModalBody from './create-bonded-network-modal' // eslint-disable-line import/first -export const createBondedNetwork = container => - confirm({ - icon: 'network', - title: _('newBondedNetworkCreate'), - body: , - }).then(params => { - if (!params.name) { - return error( - _('newNetworkNoNameErrorTitle'), - _('newNetworkNoNameErrorMessage') - ) - } - return _call('network.createBonded', params) - }, noop) +export const createNetwork = params => _call('network.create', params) +export const createBondedNetwork = params => + _call('network.createBonded', params) export const deleteNetwork = network => confirm({ diff --git a/packages/xo-web/src/icons.scss b/packages/xo-web/src/icons.scss index d564512a5..a74270840 100644 --- a/packages/xo-web/src/icons.scss +++ b/packages/xo-web/src/icons.scss @@ -854,6 +854,10 @@ @extend .fa; @extend .fa-database; } + &-network { + @extend .fa; + @extend .fa-sitemap; + } &-import { @extend .fa; @extend .fa-file-archive-o; @@ -906,6 +910,13 @@ @extend .fa-times; } } + // New network + &-new-network { + &-create { + @extend .fa; + @extend .fa-play; + } + } // OS Icons &-centos { @extend .fa; diff --git a/packages/xo-web/src/xo-app/menu/index.js b/packages/xo-web/src/xo-app/menu/index.js index fa6e28b84..e468153b4 100644 --- a/packages/xo-web/src/xo-app/menu/index.js +++ b/packages/xo-web/src/xo-app/menu/index.js @@ -369,6 +369,11 @@ export default class Menu extends Component { label: 'newVmPage', }, isAdmin && { to: '/new/sr', icon: 'menu-new-sr', label: 'newSrPage' }, + isPoolAdmin && { + to: '/new/network', + icon: 'menu-new-network', + label: 'newNetworkPage', + }, isAdmin && { to: '/settings/servers', icon: 'menu-settings-servers', diff --git a/packages/xo-web/src/xo-app/new/index.js b/packages/xo-web/src/xo-app/new/index.js index 3c5568831..588cb2b59 100644 --- a/packages/xo-web/src/xo-app/new/index.js +++ b/packages/xo-web/src/xo-app/new/index.js @@ -1,8 +1,10 @@ import { routes } from 'utils' +import Network from './network' import Sr from './sr' const New = routes('vm', { + network: Network, sr: Sr, })(({ children }) => children) diff --git a/packages/xo-web/src/xo-app/new/network/index.css b/packages/xo-web/src/xo-app/new/network/index.css new file mode 100644 index 000000000..560e7fe93 --- /dev/null +++ b/packages/xo-web/src/xo-app/new/network/index.css @@ -0,0 +1,5 @@ +.inlineSelect { + display: inline-block; + font-size: 1rem; + width: 20em; +} diff --git a/packages/xo-web/src/xo-app/new/network/index.js b/packages/xo-web/src/xo-app/new/network/index.js new file mode 100644 index 000000000..aa3acb67b --- /dev/null +++ b/packages/xo-web/src/xo-app/new/network/index.js @@ -0,0 +1,242 @@ +import _, { messages } from 'intl' +import ActionButton from 'action-button' +import decorate from 'apply-decorators' +import PropTypes from 'prop-types' +import React, { Component } from 'react' +import Wizard, { Section } from 'wizard' +import { connectStore } from 'utils' +import { createBondedNetwork, createNetwork, getBondModes } from 'xo' +import { createGetObject, getIsPoolAdmin } from 'selectors' +import { injectIntl } from 'react-intl' +import { injectState, provideState } from 'reaclette' +import { linkState } from 'reaclette-utils' +import { map } from 'lodash' +import { Select, Toggle } from 'form' +import { SelectPif, SelectPool } from 'select-objects' + +import Page from '../../page' +import styles from './index.css' + +const EMPTY = { + bonded: false, + bondMode: undefined, + description: '', + mtu: '', + name: '', + pif: undefined, + pifs: [], + vlan: '', +} + +const NewNetwork = decorate([ + connectStore(() => ({ + isPoolAdmin: getIsPoolAdmin, + pool: createGetObject((_, props) => props.location.query.pool), + })), + injectIntl, + provideState({ + initialState: () => ({ ...EMPTY, bondModes: undefined }), + effects: { + initialize: async () => ({ bondModes: await getBondModes() }), + linkState, + onChangeMode: (_, bondMode) => ({ bondMode }), + onChangePif: (_, value) => ({ bonded }) => + bonded ? { pifs: value } : { pif: value }, + reset: () => EMPTY, + toggleBonded: () => ({ bonded }) => ({ + ...EMPTY, + bonded: !bonded, + }), + }, + computed: { + modeOptions: ({ bondModes }) => + bondModes !== undefined + ? bondModes.map(mode => ({ + label: mode, + value: mode, + })) + : [], + pifPredicate: (_, { pool }) => pif => + pif.vlan === -1 && pif.$host === (pool && pool.master), + }, + }), + injectState, + class extends Component { + static contextTypes = { + router: PropTypes.object, + } + + _create = () => { + const { pool, state } = this.props + const { + bonded, + bondMode, + description, + mtu, + name, + pif, + pifs, + vlan, + } = state + return bonded + ? createBondedNetwork({ + bondMode: bondMode.value, + description, + mtu, + name, + pifs: map(pifs, 'id'), + pool: pool.id, + vlan, + }) + : createNetwork({ + description, + mtu, + name, + pif: pif.id, + pool: pool.id, + vlan, + }) + } + + _selectPool = pool => { + const { + effects, + location: { pathname }, + } = this.props + effects.reset() + this.context.router.push({ + pathname, + query: pool !== null && { pool: pool.id }, + }) + } + + _renderHeader = () => { + const { isPoolAdmin, pool } = this.props + return ( +

+ {isPoolAdmin + ? _('createNewNetworkOn', { + select: ( + + + + ), + }) + : _('createNewNetworkNoPermission')} +

+ ) + } + + render() { + const { state, effects, intl, pool } = this.props + const { + bonded, + bondMode, + description, + modeOptions, + mtu, + name, + pif, + pifPredicate, + pifs, + vlan, + } = state + const { formatMessage } = intl + return ( + + {pool !== undefined && ( +
+ +
+
+ {' '} + +
+
+
+
+ + + + + + + + + {bonded ? ( +
+ + +
+ )} +
+
+
+
+ + {_('newNetworkCreate')} + + + {_('formReset')} + +
+
+ )} +
+ ) + } + }, +]) +export { NewNetwork as default } diff --git a/packages/xo-web/src/xo-app/pool/tab-network.js b/packages/xo-web/src/xo-app/pool/tab-network.js index daf6f1541..856842883 100644 --- a/packages/xo-web/src/xo-app/pool/tab-network.js +++ b/packages/xo-web/src/xo-app/pool/tab-network.js @@ -10,10 +10,10 @@ import map from 'lodash/map' import React, { Component } from 'react' import some from 'lodash/some' import SortedTable from 'sorted-table' -import TabButton from 'tab-button' import Tooltip from 'tooltip' import { connectStore } from 'utils' import { Container, Row, Col } from 'grid' +import { TabButtonLink } from 'tab-button' import { Text, Number } from 'editable' import { Toggle } from 'form' import { @@ -24,8 +24,6 @@ import { } from 'selectors' import { connectPif, - createBondedNetwork, - createNetwork, deleteNetwork, disconnectPif, editNetwork, @@ -362,19 +360,10 @@ export default class TabNetworks extends Component { - -