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')}