diff --git a/CHANGELOG.md b/CHANGELOG.md
index f876827d9..e726c1d26 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
diff --git a/packages/xo-server/src/xapi-object-to-xo.js b/packages/xo-server/src/xapi-object-to-xo.js
index 86695d9a2..9ec83ee07 100644
--- a/packages/xo-server/src/xapi-object-to-xo.js
+++ b/packages/xo-server/src/xapi-object-to-xo.js
@@ -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',
diff --git a/packages/xo-web/src/common/action-button.js b/packages/xo-web/src/common/action-button.js
index d8b3a121c..70d80a561 100644
--- a/packages/xo-web/src/common/action-button.js
+++ b/packages/xo-web/src/common/action-button.js
@@ -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
diff --git a/packages/xo-web/src/common/intl/messages.js b/packages/xo-web/src/common/intl/messages.js
index 16b3c6fa6..6153ed785 100644
--- a/packages/xo-web/src/common/intl/messages.js
+++ b/packages/xo-web/src/common/intl/messages.js
@@ -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',
diff --git a/packages/xo-web/src/common/xo/index.js b/packages/xo-web/src/common/xo/index.js
index eb256f7f7..eb45d8033 100644
--- a/packages/xo-web/src/common/xo/index.js
+++ b/packages/xo-web/src/common/xo/index.js
@@ -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: ,
+ }).then(
+ () => Promise.all(map(ids, id => editHost(id, { multipathing }))),
+ noop
+ )
+}
+
export const fetchHostStats = (host, granularity) =>
_call('host.stats', { host: resolveId(host), granularity })
diff --git a/packages/xo-web/src/common/xo/multipathing-modal/index.js b/packages/xo-web/src/common/xo/multipathing-modal/index.js
new file mode 100644
index 000000000..1c6825c4c
--- /dev/null
+++ b/packages/xo-web/src/common/xo/multipathing-modal/index.js
@@ -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 (
+
+ {_('hostMultipathingWarning', {
+ nHosts: hostIds.length,
+ })}
+
+ {srIds.map(srId => (
+
+
+
+ ))}
+
+
+ )
+ }
+}
diff --git a/packages/xo-web/src/xo-app/host/tab-advanced.js b/packages/xo-web/src/xo-app/host/tab-advanced.js
index 8c4429088..685558c41 100644
--- a/packages/xo-web/src/xo-app/host/tab-advanced.js
+++ b/packages/xo-web/src/xo-app/host/tab-advanced.js
@@ -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 {
{_('hostIscsiName')} |
{host.iSCSI_name}
+
+ {_('multipathing')} |
+
+
+ |
+
{_('hostRemoteSyslog')} |
diff --git a/packages/xo-web/src/xo-app/pool/tab-advanced.js b/packages/xo-web/src/xo-app/pool/tab-advanced.js
index e7c4ac52b..24ba714a6 100644
--- a/packages/xo-web/src/xo-app/pool/tab-advanced.js
+++ b/packages/xo-web/src/xo-app/pool/tab-advanced.js
@@ -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 (
@@ -159,6 +170,29 @@ export default class TabAdvanced extends Component {
+ {_('multipathing')}
+
+
+ {_('enableAllHostsMultipathing')}
+ {' '}
+
+ {_('disableAllHostsMultipathing')}
+
+
{_('supplementalPackPoolNew')}
|