feat(xo-web/host): ability to enable/disable multipathing (#3865)

See #3659
This commit is contained in:
badrAZ 2019-01-25 15:46:44 +01:00 committed by Pierre Donias
parent 810c976d37
commit e9fb37325d
8 changed files with 147 additions and 16 deletions

View File

@ -12,6 +12,7 @@
- [Backup NG] Support zstd compression for full backups [#3773](https://github.com/vatesfr/xen-orchestra/issues/3773) (PR [#3883](https://github.com/vatesfr/xen-orchestra/pull/3883))
- [VM] Ability to copy a VM with zstd compression [#3773](https://github.com/vatesfr/xen-orchestra/issues/3773) (PR [#3889](https://github.com/vatesfr/xen-orchestra/pull/3889))
- [VM & Host] "Pool > Host" breadcrumb at the top of the page (PR [#3898](https://github.com/vatesfr/xen-orchestra/pull/3898))
- [Hosts] Ability to enable/disable host multipathing [#3659](https://github.com/vatesfr/xen-orchestra/issues/3659) (PR [#3865](https://github.com/vatesfr/xen-orchestra/pull/3865))
### Bug fixes

View File

@ -173,6 +173,7 @@ const TRANSFORMS = {
total: 0,
}
})(),
multipathing: obj.multipathing,
patches: patches || link(obj, 'patches'),
powerOnMode: obj.power_on_mode,
power_state: metrics ? (isRunning ? 'Running' : 'Halted') : 'Unknown',

View File

@ -67,7 +67,7 @@ export default class ActionButton extends Component {
const { children, handler, tooltip } = props
let handlerParam
if ('handlerParam' in props) {
if (props.handlerParam !== undefined) {
handlerParam = props.handlerParam
} else {
let empty = true

View File

@ -21,6 +21,11 @@ const messages = {
showLogs: 'Show logs',
noValue: 'None',
compression: 'Compression',
multipathing: 'Multipathing',
enableMultipathing: 'Enable multipathing',
disableMultipathing: 'Disable multipathing',
enableAllHostsMultipathing: 'Enable all hosts multipathing',
disableAllHostsMultipathing: 'Disable all hosts multipathing',
// ----- Modals -----
alertOk: 'OK',
@ -757,6 +762,9 @@ const messages = {
hostStatus: 'Status',
hostBuildNumber: 'Build number',
hostIscsiName: 'iSCSI name',
hostMultipathingSrs: 'Click to see concerned SRs',
hostMultipathingWarning:
'The host{nHosts, plural, one {} other {s}} will lose the connection to the SRs. Do you want to continue?',
hostXenServerVersion: 'Version',
hostStatusEnabled: 'Enabled',
hostStatusDisabled: 'Disabled',

View File

@ -589,6 +589,22 @@ export const setPoolMaster = host =>
export const editHost = (host, props) =>
_call('host.set', { ...props, id: resolveId(host) })
import MultipathingModalBody from './multipathing-modal' // eslint-disable-line import/first
export const setHostsMultipathing = ({
host,
hosts = [host],
multipathing,
}) => {
const ids = resolveIds(hosts)
return confirm({
title: _(multipathing ? 'enableMultipathing' : 'disableMultipathing'),
body: <MultipathingModalBody hostIds={ids} />,
}).then(
() => Promise.all(map(ids, id => editHost(id, { multipathing }))),
noop
)
}
export const fetchHostStats = (host, granularity) =>
_call('host.stats', { host: resolveId(host), granularity })

View File

@ -0,0 +1,53 @@
import PropTypes from 'prop-types'
import React, { Component } from 'react'
import _ from '../../intl'
import Collapse from '../../collapse'
import { connectStore } from '../../utils'
import { createGetObjectsOfType, createSelector } from '../../selectors'
import { Sr } from '../../render-xo-item'
@connectStore(
{
srIds: createSelector(
createGetObjectsOfType('PBD').filter((_, { hostIds }) => pbd =>
hostIds.includes(pbd.host)
),
pbds => {
const srIds = new Set([])
for (const id in pbds) {
srIds.add(pbds[id].SR)
}
return [...srIds]
}
),
},
{ withRef: true }
)
export default class MultipathingModal extends Component {
static propTypes = {
hostIds: PropTypes.arrayOf(PropTypes.string).isRequired,
}
render() {
const { hostIds, srIds } = this.props
return (
<div>
{_('hostMultipathingWarning', {
nHosts: hostIds.length,
})}
<Collapse
buttonText={_('hostMultipathingSrs')}
size='small'
className='mt-1'
>
{srIds.map(srId => (
<div key={srId}>
<Sr id={srId} link newTab />
</div>
))}
</Collapse>
</div>
)
}
}

View File

@ -2,8 +2,9 @@ import _ from 'intl'
import Component from 'base-component'
import Copiable from 'copiable'
import React from 'react'
import TabButton from 'tab-button'
import SelectFiles from 'select-files'
import StateButton from 'state-button'
import TabButton from 'tab-button'
import Upgrade from 'xoa-upgrade'
import { Text } from 'editable'
import { Toggle } from 'form'
@ -13,13 +14,14 @@ import { Container, Row, Col } from 'grid'
import { forEach, map, noop } from 'lodash'
import { createGetObjectsOfType, createSelector } from 'selectors'
import {
enableHost,
detachHost,
disableHost,
enableHost,
forgetHost,
setRemoteSyslogHost,
restartHost,
installSupplementalPack,
restartHost,
setHostsMultipathing,
setRemoteSyslogHost,
} from 'xo'
const ALLOW_INSTALL_SUPP_PACK = process.env.XOA_PLAN > 1
@ -183,6 +185,22 @@ export default class extends Component {
<th>{_('hostIscsiName')}</th>
<Copiable tagName='td'>{host.iSCSI_name}</Copiable>
</tr>
<tr>
<th>{_('multipathing')}</th>
<td>
<StateButton
className='mb-1'
data-host={host}
data-multipathing={!host.multipathing}
disabledLabel={_('stateDisabled')}
disabledTooltip={_('enableMultipathing')}
enabledLabel={_('stateEnabled')}
enabledTooltip={_('disableMultipathing')}
handler={setHostsMultipathing}
state={host.multipathing}
/>
</td>
</tr>
<tr>
<th>{_('hostRemoteSyslog')}</th>
<td>

View File

@ -7,14 +7,15 @@ import Component from 'base-component'
import renderXoItem from 'render-xo-item'
import SelectFiles from 'select-files'
import Upgrade from 'xoa-upgrade'
import { map } from 'lodash'
import { connectStore } from 'utils'
import { injectIntl } from 'react-intl'
import { createGetObjectsOfType } from 'selectors'
import { Text, XoSelect } from 'editable'
import { Container, Row, Col } from 'grid'
import { createGetObjectsOfType, createGroupBy } from 'selectors'
import { injectIntl } from 'react-intl'
import { map } from 'lodash'
import { Text, XoSelect } from 'editable'
import {
installSupplementalPackOnAllHosts,
setHostsMultipathing,
setPoolMaster,
setRemoteSyslogHost,
setRemoteSyslogHosts,
@ -47,13 +48,19 @@ class PoolMaster extends Component {
}
@injectIntl
@connectStore({
hosts: createGetObjectsOfType('host')
@connectStore(() => {
const getHosts = createGetObjectsOfType('host')
.filter((_, { pool }) => ({ $pool: pool.id }))
.sort(),
gpuGroups: createGetObjectsOfType('gpuGroup')
.filter((_, { pool }) => ({ $pool: pool.id }))
.sort(),
.sort()
return {
hosts: getHosts,
hostsByMultipathing: createGroupBy(getHosts, () => ({ multipathing }) =>
multipathing ? 'enabled' : 'disabled'
),
gpuGroups: createGetObjectsOfType('gpuGroup')
.filter((_, { pool }) => ({ $pool: pool.id }))
.sort(),
}
})
export default class TabAdvanced extends Component {
_setRemoteSyslogHosts = () =>
@ -62,9 +69,13 @@ export default class TabAdvanced extends Component {
)
render() {
const { hosts, gpuGroups, pool } = this.props
const { hosts, gpuGroups, pool, hostsByMultipathing } = this.props
const { state } = this
const { editRemoteSyslog } = state
const {
enabled: hostsEnabledMultipathing,
disabled: hostsDisabledMultipathing,
} = hostsByMultipathing
return (
<div>
<Container>
@ -159,6 +170,29 @@ export default class TabAdvanced extends Component {
</Col>
</Row>
</Container>
<h3 className='mt-1 mb-1'>{_('multipathing')}</h3>
<div>
<ActionButton
btnStyle='success'
data-hosts={hostsDisabledMultipathing}
data-multipathing
disabled={hostsDisabledMultipathing === undefined}
handler={setHostsMultipathing}
icon='host'
>
{_('enableAllHostsMultipathing')}
</ActionButton>{' '}
<ActionButton
btnStyle='danger'
data-hosts={hostsEnabledMultipathing}
data-multipathing={false}
disabled={hostsEnabledMultipathing === undefined}
handler={setHostsMultipathing}
icon='host'
>
{_('disableAllHostsMultipathing')}
</ActionButton>
</div>
<h3 className='mt-1 mb-1'>{_('supplementalPackPoolNew')}</h3>
<Upgrade place='poolSupplementalPacks' required={2}>
<SelectFiles