feat(xo-web/host): ability to enable/disable multipathing (#3865)
See #3659
This commit is contained in:
parent
810c976d37
commit
e9fb37325d
@ -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))
|
- [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] 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))
|
- [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
|
### Bug fixes
|
||||||
|
|
||||||
|
@ -173,6 +173,7 @@ const TRANSFORMS = {
|
|||||||
total: 0,
|
total: 0,
|
||||||
}
|
}
|
||||||
})(),
|
})(),
|
||||||
|
multipathing: obj.multipathing,
|
||||||
patches: patches || link(obj, 'patches'),
|
patches: patches || link(obj, 'patches'),
|
||||||
powerOnMode: obj.power_on_mode,
|
powerOnMode: obj.power_on_mode,
|
||||||
power_state: metrics ? (isRunning ? 'Running' : 'Halted') : 'Unknown',
|
power_state: metrics ? (isRunning ? 'Running' : 'Halted') : 'Unknown',
|
||||||
|
@ -67,7 +67,7 @@ export default class ActionButton extends Component {
|
|||||||
const { children, handler, tooltip } = props
|
const { children, handler, tooltip } = props
|
||||||
|
|
||||||
let handlerParam
|
let handlerParam
|
||||||
if ('handlerParam' in props) {
|
if (props.handlerParam !== undefined) {
|
||||||
handlerParam = props.handlerParam
|
handlerParam = props.handlerParam
|
||||||
} else {
|
} else {
|
||||||
let empty = true
|
let empty = true
|
||||||
|
@ -21,6 +21,11 @@ const messages = {
|
|||||||
showLogs: 'Show logs',
|
showLogs: 'Show logs',
|
||||||
noValue: 'None',
|
noValue: 'None',
|
||||||
compression: 'Compression',
|
compression: 'Compression',
|
||||||
|
multipathing: 'Multipathing',
|
||||||
|
enableMultipathing: 'Enable multipathing',
|
||||||
|
disableMultipathing: 'Disable multipathing',
|
||||||
|
enableAllHostsMultipathing: 'Enable all hosts multipathing',
|
||||||
|
disableAllHostsMultipathing: 'Disable all hosts multipathing',
|
||||||
|
|
||||||
// ----- Modals -----
|
// ----- Modals -----
|
||||||
alertOk: 'OK',
|
alertOk: 'OK',
|
||||||
@ -757,6 +762,9 @@ const messages = {
|
|||||||
hostStatus: 'Status',
|
hostStatus: 'Status',
|
||||||
hostBuildNumber: 'Build number',
|
hostBuildNumber: 'Build number',
|
||||||
hostIscsiName: 'iSCSI name',
|
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',
|
hostXenServerVersion: 'Version',
|
||||||
hostStatusEnabled: 'Enabled',
|
hostStatusEnabled: 'Enabled',
|
||||||
hostStatusDisabled: 'Disabled',
|
hostStatusDisabled: 'Disabled',
|
||||||
|
@ -589,6 +589,22 @@ export const setPoolMaster = host =>
|
|||||||
export const editHost = (host, props) =>
|
export const editHost = (host, props) =>
|
||||||
_call('host.set', { ...props, id: resolveId(host) })
|
_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) =>
|
export const fetchHostStats = (host, granularity) =>
|
||||||
_call('host.stats', { host: resolveId(host), granularity })
|
_call('host.stats', { host: resolveId(host), granularity })
|
||||||
|
|
||||||
|
53
packages/xo-web/src/common/xo/multipathing-modal/index.js
Normal file
53
packages/xo-web/src/common/xo/multipathing-modal/index.js
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -2,8 +2,9 @@ import _ from 'intl'
|
|||||||
import Component from 'base-component'
|
import Component from 'base-component'
|
||||||
import Copiable from 'copiable'
|
import Copiable from 'copiable'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import TabButton from 'tab-button'
|
|
||||||
import SelectFiles from 'select-files'
|
import SelectFiles from 'select-files'
|
||||||
|
import StateButton from 'state-button'
|
||||||
|
import TabButton from 'tab-button'
|
||||||
import Upgrade from 'xoa-upgrade'
|
import Upgrade from 'xoa-upgrade'
|
||||||
import { Text } from 'editable'
|
import { Text } from 'editable'
|
||||||
import { Toggle } from 'form'
|
import { Toggle } from 'form'
|
||||||
@ -13,13 +14,14 @@ import { Container, Row, Col } from 'grid'
|
|||||||
import { forEach, map, noop } from 'lodash'
|
import { forEach, map, noop } from 'lodash'
|
||||||
import { createGetObjectsOfType, createSelector } from 'selectors'
|
import { createGetObjectsOfType, createSelector } from 'selectors'
|
||||||
import {
|
import {
|
||||||
enableHost,
|
|
||||||
detachHost,
|
detachHost,
|
||||||
disableHost,
|
disableHost,
|
||||||
|
enableHost,
|
||||||
forgetHost,
|
forgetHost,
|
||||||
setRemoteSyslogHost,
|
|
||||||
restartHost,
|
|
||||||
installSupplementalPack,
|
installSupplementalPack,
|
||||||
|
restartHost,
|
||||||
|
setHostsMultipathing,
|
||||||
|
setRemoteSyslogHost,
|
||||||
} from 'xo'
|
} from 'xo'
|
||||||
|
|
||||||
const ALLOW_INSTALL_SUPP_PACK = process.env.XOA_PLAN > 1
|
const ALLOW_INSTALL_SUPP_PACK = process.env.XOA_PLAN > 1
|
||||||
@ -183,6 +185,22 @@ export default class extends Component {
|
|||||||
<th>{_('hostIscsiName')}</th>
|
<th>{_('hostIscsiName')}</th>
|
||||||
<Copiable tagName='td'>{host.iSCSI_name}</Copiable>
|
<Copiable tagName='td'>{host.iSCSI_name}</Copiable>
|
||||||
</tr>
|
</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>
|
<tr>
|
||||||
<th>{_('hostRemoteSyslog')}</th>
|
<th>{_('hostRemoteSyslog')}</th>
|
||||||
<td>
|
<td>
|
||||||
|
@ -7,14 +7,15 @@ import Component from 'base-component'
|
|||||||
import renderXoItem from 'render-xo-item'
|
import renderXoItem from 'render-xo-item'
|
||||||
import SelectFiles from 'select-files'
|
import SelectFiles from 'select-files'
|
||||||
import Upgrade from 'xoa-upgrade'
|
import Upgrade from 'xoa-upgrade'
|
||||||
import { map } from 'lodash'
|
|
||||||
import { connectStore } from 'utils'
|
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 { 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 {
|
import {
|
||||||
installSupplementalPackOnAllHosts,
|
installSupplementalPackOnAllHosts,
|
||||||
|
setHostsMultipathing,
|
||||||
setPoolMaster,
|
setPoolMaster,
|
||||||
setRemoteSyslogHost,
|
setRemoteSyslogHost,
|
||||||
setRemoteSyslogHosts,
|
setRemoteSyslogHosts,
|
||||||
@ -47,13 +48,19 @@ class PoolMaster extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@injectIntl
|
@injectIntl
|
||||||
@connectStore({
|
@connectStore(() => {
|
||||||
hosts: createGetObjectsOfType('host')
|
const getHosts = createGetObjectsOfType('host')
|
||||||
.filter((_, { pool }) => ({ $pool: pool.id }))
|
.filter((_, { pool }) => ({ $pool: pool.id }))
|
||||||
.sort(),
|
.sort()
|
||||||
|
return {
|
||||||
|
hosts: getHosts,
|
||||||
|
hostsByMultipathing: createGroupBy(getHosts, () => ({ multipathing }) =>
|
||||||
|
multipathing ? 'enabled' : 'disabled'
|
||||||
|
),
|
||||||
gpuGroups: createGetObjectsOfType('gpuGroup')
|
gpuGroups: createGetObjectsOfType('gpuGroup')
|
||||||
.filter((_, { pool }) => ({ $pool: pool.id }))
|
.filter((_, { pool }) => ({ $pool: pool.id }))
|
||||||
.sort(),
|
.sort(),
|
||||||
|
}
|
||||||
})
|
})
|
||||||
export default class TabAdvanced extends Component {
|
export default class TabAdvanced extends Component {
|
||||||
_setRemoteSyslogHosts = () =>
|
_setRemoteSyslogHosts = () =>
|
||||||
@ -62,9 +69,13 @@ export default class TabAdvanced extends Component {
|
|||||||
)
|
)
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { hosts, gpuGroups, pool } = this.props
|
const { hosts, gpuGroups, pool, hostsByMultipathing } = this.props
|
||||||
const { state } = this
|
const { state } = this
|
||||||
const { editRemoteSyslog } = state
|
const { editRemoteSyslog } = state
|
||||||
|
const {
|
||||||
|
enabled: hostsEnabledMultipathing,
|
||||||
|
disabled: hostsDisabledMultipathing,
|
||||||
|
} = hostsByMultipathing
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Container>
|
<Container>
|
||||||
@ -159,6 +170,29 @@ export default class TabAdvanced extends Component {
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</Container>
|
</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>
|
<h3 className='mt-1 mb-1'>{_('supplementalPackPoolNew')}</h3>
|
||||||
<Upgrade place='poolSupplementalPacks' required={2}>
|
<Upgrade place='poolSupplementalPacks' required={2}>
|
||||||
<SelectFiles
|
<SelectFiles
|
||||||
|
Loading…
Reference in New Issue
Block a user